A SQLite fork that replaces the B-tree storage engine with a content-addressed
prolly tree,
giving Git-like version control on a SQL database. The parser, planner, and
VDBE stay upstream-derived above SQLite's btree.h seam; below it, a
single-file chunk store backs prolly trees instead of SQLite pages.
Why DoltLite? DoltLite can be embedded in any language enabling local-first use cases for Dolt.
You can read more about DoltLite, including its origin story, on the DoltHub blog.
Prebuilt binaries: github.com/dolthub/doltlite/releases.
Each install method places the same set of files (paths shown for /usr/local):
bin/doltlite,bin/doltlite-remotesrv— the CLI shell and remote sync serverinclude/doltlite.h— embedding header (sqlite3_*C API;#include <doltlite.h>)include/doltlite_remotesrv.h— in-process remote server APIlib/libdoltlite.a— static librarylib/libdoltlite.{so,dylib}— shared library
sudo bash -c 'curl -fsSL https://github.com/dolthub/doltlite/releases/latest/download/install.sh | bash'
.deb packages ship for both amd64 and arm64. Substitute $ARCH below:
VER=$(curl -fsSL https://api.github.com/repos/dolthub/doltlite/releases/latest | jq -r .tag_name | sed 's/^v//')
ARCH=amd64 # or arm64
BASE=https://github.com/dolthub/doltlite/releases/download/v${VER}
wget ${BASE}/libdoltlite0_${VER}_${ARCH}.deb ${BASE}/doltlite_${VER}_${ARCH}.deb
sudo dpkg -i libdoltlite0_*.deb doltlite_*.deb
Add libdoltlite-dev_${VER}_${ARCH}.deb for the header and static library.
Download doltlite-tools-win-x64-<ver>.zip from
releases, extract doltlite.exe, add to PATH.
Language-specific wrappers around libdoltlite. Each one exposes the bundled
SQLite version's public sqlite3_* API surface plus the Dolt version-control
functions, subject to the storage-engine exceptions.
| Language | Distribution | Source |
|---|---|---|
| Python | pip install doltlite |
dolthub/doltlite-python |
| Ruby | gem install doltlite |
dolthub/doltlite-ruby |
| Node.js / Bun | npm install @dolthub/doltlite |
dolthub/doltlite-node |
| Browser / WASM | npm install @dolthub/doltlite-wasm |
this repo (packaging/npm, built from ext/wasm) |
| Swift (iOS / macOS) | SwiftPM: https://github.com/dolthub/doltlite-swift |
dolthub/doltlite-swift (XCFramework built by packaging/swift) |
| Android | Gradle: com.dolthub:doltlite-android |
dolthub/doltlite-android (AAR + JNA) |
cd build
../configure
make
./doltlite :memory:
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-zlib make tcl
mkdir -p build && cd build
../configure
make doltlite.exe
./doltlite.exe :memory:
To verify the engine:
SELECT doltlite_engine();
-- prollyTo build stock SQLite instead (for comparison):
make DOLTLITE_PROLLY=0 sqlite3
Vendored SQLite ext/wasm, defaulting to the Doltlite engine. Build generated
SQLite sources first, then wasm:
./configure
make sqlite3.c sqlite3.h sqlite3ext.h
make -C ext/wasm
# → ext/wasm/jswasm/{sqlite3.js,sqlite3.mjs,sqlite3.wasm}
make -C ext/wasm DOLTLITE_WASM=0 # upstream SQLite wasm instead
make -C ext/wasm dist # zip packagePublic C API is the bundled SQLite declarations under sqlite3_* names via
doltlite.h. Port supported programs by switching the include/link to
libdoltlite; APIs tied to SQLite's pager, page format, or journaling differ
(see SQLite Compatibility). Dolt features are SQL
functions (dolt_commit, dolt_branch, …) and virtual tables
(dolt_log, dolt_diff_<table>, …).
Loadable extensions use doltliteext.h (rebranded sqlite3ext.h, shipped in
the amalgamation zip). The shared library exports only sqlite3_* and
doltliteServe* (doltlite_remotesrv.h); prolly/chunk-store/doltlite*
internals and vendored crypto are hidden. The static archive is unfiltered for
tests and tooling.
cd build
../configure
make doltlite-lib # libdoltlite.a and libdoltlite.dylib/.so
# Static (recommended) or dynamic
gcc -o myapp myapp.c -I/path/to/build libdoltlite.a -lpthread -lz
gcc -o myapp myapp.c -I/path/to/build -L/path/to/build -ldoltlite -lpthread -lz
sudo make install # honours --prefix / DESTDIR; then:
gcc -o myapp myapp.c -ldoltlite -lpthread -lzmake install also installs SQLite-named artifacts (sqlite3.h,
libsqlite3.*, …) from this tree — release packages omit those so they do not
collide with system SQLite. Use a private --prefix if that matters.
Same flow (commits, branches, merges, diffs, tags) in each language.
C (examples/quickstart.c) — based on the
SQLite quickstart:
cd build
gcc -o quickstart ../examples/quickstart.c -I. libdoltlite.a -lpthread -lz
./quickstartPython (examples/quickstart.py) — stdlib
sqlite3 with the doltlite
package (bundles libdoltlite):
pip install doltlite
python3 examples/quickstart.pyNeeds a Python whose _sqlite3 links a shared libsqlite3 (distro,
Homebrew, pyenv, or conda). Avoid python-build-standalone (uv python install
defaults), the python.org macOS installer, and Apple system Python — they
static-link SQLite and cannot preload libdoltlite. Local-build preload
recipes (including macOS) are in the
doltlite-python README.
Go (examples/go/main.go) — uses
mattn/go-sqlite3 with the libsqlite3
build tag:
cd examples/go
CGO_CFLAGS="-I../../build" CGO_LDFLAGS="../../build/libdoltlite.a -lz -lpthread" \
go build -tags libsqlite3 -o quickstart .
./quickstartVersion control operations are exposed as SQL functions and virtual tables.
Per-connection, not persisted. Used by dolt_commit, dolt_merge,
dolt_cherry_pick, and dolt_revert. dolt_commit --author overrides once.
SELECT dolt_config('user.name', 'Tim Sehn');
SELECT dolt_config('user.email', 'tim@dolthub.com');
SELECT dolt_config('user.name');
-- Tim SehnSELECT dolt_add('users');
SELECT dolt_add('-A');
SELECT dolt_commit('-m', 'Add users table');
SELECT dolt_commit('-A', '-m', 'Initial commit');
SELECT dolt_commit('-am', 'Initial commit'); -- like git commit -am
SELECT dolt_commit('-m', 'Fix data', '--author', 'Alice <alice@example.com>');SELECT * FROM dolt_status;
-- table_name | staged | status
-- users | 1 | modified
-- orders | 0 | new tableRow-level working/staged edits; set staged to stage/unstage. DELETE
discards unstaged rows (staged rows must be unstaged first).
SELECT id, staged, diff_type, to_id, to_rating, to_confidence,
from_rating, from_confidence
FROM dolt_workspace_ratings;
UPDATE dolt_workspace_ratings
SET staged = TRUE
WHERE to_confidence > from_confidence;
SELECT dolt_commit('-m', 'accept higher-confidence edits');Patterns skipped by dolt_add and hidden from dolt_status (tables stay in
the working set). Create once per repo, then insert patterns (*/% =
any; ? = one char). Most-specific match wins; equal-specificity conflicts
error.
CREATE TABLE dolt_ignore(
pattern TEXT NOT NULL,
ignored TINYINT NOT NULL,
PRIMARY KEY(pattern)
);
INSERT INTO dolt_ignore VALUES ('tmp_*', 1);
INSERT INTO dolt_ignore VALUES ('tmp_keep', 0); -- un-ignore-- Tables changed across commit history
SELECT * FROM dolt_diff WHERE table_name = 'users';
-- Row/cell counts between refs
SELECT * FROM dolt_diff_stat('v1.0', 'HEAD');
SELECT * FROM dolt_diff_stat('v1.0', 'HEAD', 'users');
-- Per-table added / dropped / renamed / modified
SELECT * FROM dolt_diff_summary('v1.0', 'HEAD');
-- Schema-level (tables, views, indexes)
SELECT * FROM dolt_schema_diff('v1.0', 'v2.0');
-- Ordered, executable SQLite statements (schema rebuilds when ALTER cannot express)
SELECT * FROM dolt_patch('v1.0', 'v2.0');
SELECT * FROM dolt_patch('v1.0', 'v2.0', 'users');
SELECT * FROM dolt_patch('v1.0..v2.0');
SELECT * FROM dolt_patch('main...feature', 'users');
SELECT statement FROM dolt_patch('HEAD', 'WORKING')
WHERE diff_type = 'data'
ORDER BY statement_order;
-- Per-table row history (to_/from_ columns + commit metadata + diff_type).
-- One vtable per user table. to_commit = 'WORKING' is staged + working.
SELECT * FROM dolt_diff_users;
SELECT * FROM dolt_diff_users WHERE to_id = 42;
SELECT * FROM dolt_diff_users WHERE to_commit = 'WORKING';
-- TVF form: snapshots at two refs (table name is in the module, like Dolt).
-- Two dots = endpoints; three dots = merge base to right endpoint.
SELECT * FROM dolt_diff_users('HEAD~1', 'HEAD');
SELECT * FROM dolt_diff_users('v1.0', 'WORKING');
SELECT * FROM dolt_diff_users('main..feature');
SELECT * FROM dolt_diff_users('main...feature');
SELECT d.*
FROM dolt_diff_users AS d
JOIN dolt_log('v1.0..HEAD') AS l ON l.commit_hash = d.to_commit;-- Commit history
SELECT * FROM dolt_log;
SELECT * FROM dolt_log('feature');
SELECT * FROM dolt_log('main..feature');
-- commit_hash | committer | email | date | messageTwo per-table virtual tables for time travel:
-- Every version of every row across all commits
SELECT * FROM dolt_history_users WHERE id = 42;
-- The table as it existed at a specific commit / branch / tag
SELECT * FROM dolt_at_users('abc123...');
SELECT * FROM dolt_at_users('feature');
SELECT * FROM dolt_at_users('v1.0');Most recent commit that set each live row's current value:
SELECT * FROM dolt_blame_users;
-- id | commit | commit_date | committer | email | messageFirst-parent walk from HEAD: blame updates when a row differs from the
first parent (or from the merge base at merges). Schema-only changes
(ALTER TABLE ADD COLUMN) do not update blame.
Views and triggers from the branch-scoped sqlite_schema (not ordinary
tables/indexes). Switches with dolt_checkout:
CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1;
CREATE TRIGGER audit_users AFTER UPDATE ON users
BEGIN INSERT INTO audit VALUES(new.id, 'updated'); END;
SELECT dolt_commit('-Am', 'Add view and trigger');
SELECT * FROM dolt_schemas;
-- type | name | fragment | extra | sql_mode
-- view | active_users | CREATE VIEW active_users AS SELECT ... | |
-- trigger | audit_users | CREATE TRIGGER audit_users AFTER UPDATE...| |Use sqlite_schema or dolt_schema_diff for the full schema surface.
SELECT dolt_reset('--soft'); -- unstage all, keep working changes
SELECT dolt_reset('--hard'); -- discard all uncommitted changesNew commit that applies the inverse of a target commit onto HEAD
(message Revert '<original message>'). Cannot revert the initial commit.
SELECT dolt_revert('abc123...');
-- Returns new commit hash, or "Revert completed with N conflict(s)"Each connection tracks its own active branch (and session view of HEAD / staging). Uncommitted work belongs to the branch, not the connection — see Concurrency.
SELECT dolt_branch('feature');
SELECT dolt_checkout('feature');
SELECT active_branch();
SELECT * FROM dolt_branches;
SELECT dolt_branch('-d', 'feature');Open a branch at connect time via the database path (CLI, C API, or bindings):
./doltlite my.db@feature
./doltlite my.db/featuresqlite3_open("my.db@feature", &db);SELECT dolt_tag('v1.0'); -- tag HEAD
SELECT dolt_tag('v1.0', 'abc123...'); -- tag a commit
SELECT dolt_tag('-d', 'v1.0');
SELECT * FROM dolt_tags;Three-way, row-level merge into the current branch. Non-conflicting row edits auto-merge; same-row edits become conflicts (see below).
SELECT dolt_merge('feature');
-- Returns commit hash (clean merge), or "Merge completed with N conflict(s)"Always one row (is_merging = 0 and other columns NULL when idle):
SELECT * FROM dolt_merge_status;
-- is_merging | source | source_commit | target | unmerged_tables
-- 1 | feature | 0f470f8440... | refs/heads/main | orders, usersunmerged_tables is the name-ordered union of tables with data conflicts,
constraint violations, or schema conflicts. Merge state is in the working set,
so other connections see it too; source is recovered from the branch at the
merge commit when possible, otherwise the commit hash.
SELECT * FROM dolt_conflicts;
-- table | num_conflicts
-- users | 2
-- Per-table rows: base_/our_/their_ columns, diff_types, dolt_conflict_id
SELECT * FROM dolt_conflicts_users;
DELETE FROM dolt_conflicts_users WHERE dolt_conflict_id = 5; -- keep working value
SELECT dolt_conflicts_resolve('--ours', 'users');
SELECT dolt_conflicts_resolve('--theirs', 'users');
SELECT dolt_commit('-A', '-m', 'msg');
-- Error: "cannot commit: unresolved merge conflicts"Conflicts are never durable: they exist only in the transaction that produced
them. Resolve there; COMMIT is refused while any remain, and an autocommit
merge that conflicts is rolled back whole. Nothing conflicted is left on disk
for a later connection. Dolt can commit a conflicted working set — this is a
deliberate divergence.
Merges apply cell-by-cell and do not run referential actions inline.
Post-merge, violating rows land in dolt_constraint_violations_<table>
(summary: dolt_constraint_violations).
SELECT * FROM dolt_constraint_violations;
-- table | num_violations
-- child | 1
SELECT violation_type, pk, violation_info
FROM dolt_constraint_violations_child;
-- foreign key | 2 | {"Columns":["v1"],"ReferencedTable":"parent",...}
DELETE FROM dolt_constraint_violations_child WHERE pk = 2;Types match Dolt: foreign key, unique index, check constraint. FK/CHECK
violators stay in the base table; unique-index losers (highest rowid) are
evicted into the violations vtable. dolt_commit refuses while any remain
(--force bypasses). Re-scan:
SELECT dolt_verify_constraints([--all] [--output-only] [table...]);.
Apply one commit's changes onto the current branch (parent→commit diff as a
three-way merge; conflicts like dolt_merge). Ranges / multi-commit are not
supported.
SELECT dolt_cherry_pick('abc123...');
-- Returns new commit hash, or "Cherry-pick completed with N conflict(s)"Replay this branch onto an upstream. Atomic: conflict/error restores the
pre-rebase branch. Interactive (-i) edits a plan table before apply:
SELECT dolt_rebase('main');
-- "Successfully rebased and updated refs/heads/feat"
SELECT dolt_rebase('-i', 'main');
-- Working branch dolt_rebase_<orig> + dolt_rebase plan rows (default pick).
-- action: pick | drop | reword | squash | fixup; edit commit_message /
-- rebase_order with normal SQL.
UPDATE dolt_rebase SET action='drop' WHERE commit_message='debug';
UPDATE dolt_rebase SET action='squash' WHERE commit_message='fixup';
SELECT dolt_rebase('--continue');
SELECT dolt_rebase('--abort');SELECT dolt_merge_base('abc123...', 'def456...');-- Commit hash (branch, tag, raw hash, HEAD, HEAD~N / HEAD^N)
SELECT dolt_hashof('main');
SELECT dolt_hashof('HEAD~2');
-- Table root (one-arg form includes uncommitted working edits)
SELECT dolt_hashof_table('users');
SELECT dolt_hashof_table('users', 'main');
-- Whole catalog (moves when any table root or membership changes)
SELECT dolt_hashof_db();
SELECT dolt_hashof_db('HEAD');Results are 40-char lowercase hex. _table / _db are history-independent:
identical (key, value) sets hash the same regardless of insert order or
branch. Property tests: test/vc_oracle_hashof_test.sh.
Stop-the-world mark-and-sweep over branches, tags, history, catalogs, and prolly nodes; rewrites the file with only live chunks. Safe and idempotent.
SELECT dolt_gc();
-- "12 chunks removed, 45 chunks kept"Git-like push / fetch / pull / clone between databases.
SELECT dolt_remote('add', 'origin', 'file:///path/to/remote.doltlite');
SELECT dolt_push('origin', 'main');
SELECT dolt_clone('file:///path/to/source.doltlite');
SELECT dolt_fetch('origin', 'main');
SELECT dolt_pull('origin', 'main'); -- fetch + fast-forward
SELECT * FROM dolt_remotes;Same ops as filesystem remotes; the URL includes the database name:
SELECT dolt_remote('add', 'origin', 'http://myserver:8080/mydb.db');
SELECT dolt_push('origin', 'main');
SELECT dolt_clone('http://myserver:8080/mydb.db');Warning
The server binds to 127.0.0.1 by default. Bound anywhere else, it warns at
startup about each protection left unconfigured: --cert/--key for TLS, and
--auth-keys for authentication. These are independent — TLS encrypts but does
not authenticate, and without --auth-keys every client that can reach the port
may read the served databases and push to them. Configure both, or place the
server behind a reverse proxy that provides equivalent TLS and authentication.
Standalone HTTP server for a directory of databases (make doltlite-remotesrv
in build/):
./doltlite-remotesrv -p 8080 /path/to/databases/
./doltlite-remotesrv -p 8080 --bind 0.0.0.0 /path/to/databases/ # all interfaces
./doltlite-remotesrv -p 8443 --bind 0.0.0.0 \
--cert server.crt --key server.key \
--auth-keys /path/to/authorized-keys --audience db.example.com \
/path/to/databases/
Each .db is at http://host:8080/filename.db (or the HTTPS URL). Clients use
the system trust store (DOLTLITE_CA_FILE for a private CA); credentials live
in ~/.doltlite/creds (SELECT dolt_creds_new();). Default HTTP timeout is
30s (DOLTLITE_HTTP_TIMEOUT_MS). Embeddable as doltliteServeAsync in
doltlite_remotesrv.h. Transfers are content-addressed.
SELECT dolt_version();
-- e.g. "v0.11.38" (from git describe at compile time)Header-based auto-detect: stock SQLite files use the original B-tree engine; everything else is prolly. Typical hybrid: versioned tables on the DoltLite main DB, high-write operational tables on an attached stock SQLite file. Version control applies only to the DoltLite-format main database.
ATTACH DATABASE '/path/to/events.sqlite' AS ops;
SELECT * FROM ops.events WHERE type='click';
SELECT * FROM threads; -- main DB, no prefix
SELECT t.title, e.type
FROM threads t
JOIN ops.events e ON t.id = e.thread_id;
-- Migrate either direction
INSERT INTO threads SELECT * FROM ops.threads;
INSERT INTO ops.archive SELECT * FROM threads WHERE archived=1;
CREATE TABLE local_events AS SELECT * FROM ops.events;
DETACH DATABASE ops;Auto-detect reads an existing file's header, so it cannot classify a file that
does not exist yet: a database created by DoltLite is DoltLite-format. To create
a stock SQLite file instead, open it with doltlite_engine=sqlite:
doltlite 'file:/path/to/new.sqlite?doltlite_engine=sqlite'
The parameter selects the engine for a database being created and is ignored
once the file has content, so it can never reinterpret an existing database.
.backup and VACUUM INTO apply it for you when the source is a stock file, so
their output is a stock file too.
VACUUM on a stock database rewrites pages as SQLite does; on a DoltLite
database it garbage-collects unreachable chunks. .backup/.restore and
sqlite3_backup_* work within either format, but not between them — there is no
defined conversion, so a mixed pair is refused rather than half-copied.
Each connection selects a branch independently and recovers that branch's
working set when it checks it out. There is no dolt_stash: checkout does not
shelve uncommitted work between branches. Writer serialization, snapshot pins,
and multiproc rules are spelled out under Concurrency.
DoltLite targets SQLite SQL semantics and uses the bundled SQLite version's
public C declarations and sqlite3_* symbol names. That is API-surface
compatibility, not a claim that storage-coupled APIs keep SQLite pager or file
format semantics.
For a DoltLite-format main database, the compatibility contract is:
- DoltLite uses its own on-disk format. Standard SQLite files are detected and routed to SQLite's original B-tree engine, but Dolt version-control features are available only on DoltLite-format databases.
- No SQLite rollback-journal, WAL, or shared-memory sidecar is created.
PRAGMA journal_modereportswalas a compatibility value and ignores requests to change it. A passive WAL checkpoint is a no-op; other checkpoint modes are rejected. PRAGMA auto_vacuumreports0; attempts to enable it andPRAGMA incremental_vacuumare no-ops.VACUUMruns DoltLite garbage collection instead of rebuilding SQLite pages.- Text is stored as UTF-8. Requests for a UTF-16 database encoding leave
PRAGMA encodingatUTF-8. - The built-in
BINARY,NOCASE, andRTRIMcollations are supported.sqlite3_create_collation*returnsSQLITE_ERRORbecause persisted prolly sort keys cannot depend on application callbacks. - A table with a non-
INTEGER PRIMARY KEYis keyed by that primary key and has no separaterowidcolumn. sqlite3_backup_step()copies a file-backed DoltLite main database as one operation; its page-count argument is not incremental. Backing up an in-memory or non-main DoltLite database is unsupported.sqlite3_serialize()does not expose a DoltLite-format main database as a SQLite page image; it returnsNULLand sets the size output to-1.
The machine-readable contract and its test mapping live in
test/sqlite_compatibility_contract.tsv.
The inherited-suite backlog lives with the assertions it gates, in
test/known_testfixture_divergences.txt:
each line names one assertion and carries its disposition as
class=intentional|unsupported|harness|engine-gap, plus issue=<number> where
one is required. Gates classified as engine-gap are bugs to fix, not
compatibility promises.
DoltLite supports multiple connections and processes on one database file, but it is not a free-for-all multi-writer server. Coordination is explicit: a graph lock sidecar serializes durable writers, write transactions pin a chunk-store snapshot, and multi-step version-control ops re-check HEAD under the lock before advancing a branch tip.
For a DoltLite-format main database, the concurrency contract is:
- Per-connection branch selection. Each connection holds its own active branch and session view of HEAD and staging (see Per-Session Branching). The uncommitted working set belongs to the branch, so another connection that selects that branch recovers it. Two connections may sit on different branches of the same file at once.
- One durable writer at a time. A connection that holds an explicit write
transaction owns the graph lock. A peer that tries to begin a concurrent
write gets
SQLITE_BUSY(or a retryable busy class) until the owner commits or rolls back. After the lock is free, the peer can retry successfully. - Snapshot-safe write upgrades. A transaction that has established a read
snapshot cannot upgrade to a writer after a peer advances the store; the
upgrade returns
SQLITE_BUSY_SNAPSHOTinstead of mixing catalogs. Once a write transaction begins, it holds the graph lock and pins its snapshot until commit or rollback. - Readers stay live. A reader can see already-committed data while another
process holds an uncommitted write. An open iterator completes safely while
another process runs GC. Readers do not create SQLite
-wal/-shmsidecars. - Multi-process commits are CAS-safe. A process that races
dolt_commitagainst a peer either wins a clean tip advance or loses with a busy / conflict outcome. The loser's stale tip must not clobber the winner's commit. Sequential multiproc commits both land; forked SQL transaction writers leave consistent table and index state. - VC ops re-confirm HEAD under the lock. Merge, cherry-pick, and revert use
locked compare-and-advance; pull and rebase use operation-specific locked
branch expectations. A peer commit between planning and ref update yields
SQLITE_BUSYinstead of a lost update. - GC cooperates with writers.
dolt_gc/VACUUMmay be deferred or report busy while a writer holds the graph lock; after the writer finishes, GC completes without dropping reachable data. Multiproc GC-vs-commit and GC-vs-GC races leave committed rows intact. - Conflicts are never durable. A conflicted merge exists only inside the transaction that produced it. Commit is refused while conflicts remain; nothing conflicted is left on disk for a later connection to inherit. Constraint violations still persist (see Constraint Violations on Merge).
The machine-readable contract and its test mapping live in
test/concurrency_contract.tsv. Multiproc and
multi-connection C harnesses (multi_process_*, concurrent_*) are the
behavioral oracles; the contract test asserts that every claim still points at
a real check or source needle. Nightly stress soaks those harnesses for hours;
PR CI runs them at shorter budgets via test/run_c_tests.sh and
build-test.
DoltLite does not use the SQLite page format. Primary databases are a
single content-addressed chunk-store file (magic DLTC / 0x444C5443) with
prolly-tree chunks, a WAL of chunk/root records, and refs for branches and
tags. Stock SQLite files are still detected and opened for ordinary SQL (see
Using Existing SQLite Databases); version
control requires a DoltLite-format file.
Chunk-store version 12 is the on-disk format frozen for the DoltLite beta. Version 12 includes every nested format written into the store, including:
| Layer | Constant | Value |
|---|---|---|
| Chunk-store header | CHUNK_STORE_VERSION |
12 |
| Working-set blob | WS_FORMAT_VERSION |
v5 |
| Catalog entries | CATALOG_FORMAT_V5 |
0x46 |
| Refs blob | refs serializer | v7 |
| Commit blob | DOLTLITE_COMMIT_V2 |
v2 |
- Writers stamp version 12 and emit the nested formats above.
- Readers require an exact
CHUNK_STORE_VERSIONmatch. A different version returnsSQLITE_NOTADB; there is no silent reinterpretation or automatic rewrite on open. - Every file produced by a beta or later version-12 release remains readable
and writable by later version-12 builds. An incompatible change to any nested
format requires a
CHUNK_STORE_VERSIONbump even when that format has its own marker. - Bumping
CHUNK_STORE_VERSIONrequires updating this section, adding a corpus entry undertest/format-corpus/, updatingtest/storage_format_contract.tsv, and documenting whether version 12 is open-only, migrated, or refused.
The frozen version-12 file and its generation recipe live in
test/format-corpus/v12/. The machine-readable
contract is
test/storage_format_contract.tsv; CI runs
test/storage_format_contract_test.sh
to verify the fixture, read and extend it, run GC, reject other header versions,
and keep evidence needles live.
Nightly DoltLite-versus-SQLite numbers: performance-report.md. Per-release comparisons ship on GitHub releases.
PR CI runs paired sysbench-style workloads (int / text / blob / composite PK)
and a short version-control latency suite against the PR base, with automatic
remeasurement on borderline regressions. Details live in
.github/workflows/benchmark.yml.
Complexity properties asserted in CI (test/doltlite_perf.sh,
test/doltlite_structural.sh):
- O(log n) point SELECT / UPDATE / DELETE by primary key
- O(n log n) bulk INSERT inside an explicit transaction
- O(changes)
dolt_diffbetween commits (not proportional to table size) - Structural sharing between versions (small edits add little file growth)
- GC reclaims unreachable chunks without dropping reachable data
cd build
../configure && make
# DoltLite shell suites (branch / commit / merge / remotes / …)
bash ../test/run_doltlite_tests.sh
# C unit / multiproc / stress harnesses
bash ../test/run_c_tests.sh
# Upstream SQLite TCL suite (prolly engine) — one CI bucket
bash ../test/run_testfixture.sh "SQLite regression core-sql" 300 \
$(tr '\n' ' ' < ../test/regression-buckets/core-sql.txt)
# Differential oracles (need stock sqlite3 and/or dolt on PATH)
bash ../test/sql_oracle_test.sh ./doltlite ./sqlite3
bash ../test/vc_oracle_workspace_test.sh ./doltlite dolt
# sqllogictest corpus (needs Fossil + corpus checkout)
bash ../test/run_sqllogictest.sh ./doltlite ./sqlite3 /path/to/sqllogictestCI wiring, coverage floors, and full bucket lists are in
.github/workflows/test.yml and
AGENTS.md. Contract suites
(sqlite_compatibility_contract_test.sh, concurrency_contract_test.sh,
storage_format_contract_test.sh) gate the README contracts above.
Inherited TCL allowlists:
test/known_testfixture_divergences.txt,
test/known_testfixture_crashes.txt.
Both carry a class= disposition per gate; the totals are pinned by
test/known_testfixture_exception_ratchet.txt.
Same prolly-tree design as Dolt —
content-addressed immutable nodes with rolling-hash boundaries — in C under
SQLite's btree.h seam. Engine code is src/prolly_*.c and src/chunk_*.c;
dolt_* SQL surfaces are src/doltlite_*.c; src/prolly_btree.c dispatches
the btree API.
Deeper comparison: Dolt vs DoltLite Storage.
