The next evolution of SQLite is here! Read Announcement

Turso v0.5.0 is now released with the following changes:
You can find the full changelog here.
To install Turso, see the getting started instructions.
SQLite is fast, but its single-writer model limits scalability. Turso solves this with a new transaction mode, BEGIN CONCURRENT, inspired by SQLite's long-standing experimental feature that has not been merged to mainline, at least for now. We first introduced concurrent writes as a tech preview built on multi-version concurrency control (MVCC), adapting the Hekaton MVCC approach to SQLite's B-Tree storage architecture.
In 0.5, concurrent writes move from tech preview to beta. You enable MVCC with PRAGMA journal_mode = 'mvcc', and the feature is now available for limited production use. As with the rest of Turso, beta means: it may still contain bugs and unexpected behavior — use caution with production data and ensure you have backups. See the concurrent writes documentation for details.
The improvements to MVCC in this release are across the board:
Commit dependency tracking. Turso now implements Hekaton's speculative read protocol: transactions track commit dependencies at validation time rather than aborting eagerly, allowing more concurrency under contention (#5328).
Checkpoint improvements. The MVCC logical log has been rewritten from scratch with defined crash recovery semantics and a rolling checksum for corruption detection (#5109). The logical log is now automatically checkpointed when it grows beyond a threshold, mirroring the WAL autocheckpoint behavior (#5008). Several checkpoint correctness fixes were also landed, including a panic when deleting index keys in interior B-tree nodes (#5363) and ensuring dropped root page tracking survives restart (#5365).
Garbage collection. The in-memory MVCC version index now performs low-watermark-based garbage collection, reclaiming row versions that are no longer visible to any active transaction (#5087).
Savepoint support. MVCC now supports both implicit statement-level savepoints (#4524) and named savepoints (#5206), providing proper rollback on constraint violations and explicit SAVEPOINT/ROLLBACK TO within concurrent transactions.
Conflict detection. Conflict detection has been significantly hardened: integer primary key conflicts are now caught at commit time (#5014), unique index conflicts are properly checked (#5091), and delete-delete conflicts against B-tree-resident tombstones are now detected (#5572). A snapshot isolation bug was also fixed (#5520).
Lock-free rowid allocation. Concurrent transactions no longer contend on rowid allocation thanks to a monotonic lock-free allocator (#5598).
Per-database MVCC for attached databases. ATTACHed databases now each get their own independent MVCC transaction state (#5643).
Verification. MVCC is now tested with a concurrent simulator that injects faults during crash recovery (#5570), Antithesis for autonomous fault injection (#5380), the Hermitage test suite for isolation level correctness (#5102), and Elle for transactional consistency checking with both list-append and rw-register models under chaotic workloads (#5605, #5628).
Turso 0.5 ships with experimental full-text search powered by Tantivy, a Rust-native search engine (#4593). Unlike SQLite's FTS5, which uses shadow tables with its own tokenizer pipeline, Turso's FTS is implemented as a pluggable index method that stores Tantivy's inverted index segments directly inside the SQLite B-tree — no external files, no sidecar directories. Your full-text index lives in the same database file as your data.
The SQL interface follows the CREATE INDEX ... USING syntax:
CREATE INDEX idx_posts_search ON posts USING fts (title, body);
-- Query with relevance ranking
SELECT *, fts_score(title, body, 'turso database') AS score
FROM posts
WHERE fts_match(title, body, 'turso database')
ORDER BY score DESC
LIMIT 10;
-- Highlight matching terms
SELECT fts_highlight(body, 'turso', '<b>', '</b>') FROM posts;
Tokenizers and per-field weights are configurable at index creation time:
CREATE INDEX idx ON docs USING fts (title, body)
WITH (tokenizer='simple', weights='title=2.0,body=1.0');
The feature is opt-in (behind the fts feature flag) and currently read-only during DML — queries work against a materialized snapshot of matching rowids, ensuring safe concurrent access (#5594, #5641). Performance work has eliminated redundant B-tree passes and added caching with a two-tier LRU (64 MB for metadata, 128 MB for index chunks) (#5256). FTS is also available in the JavaScript SDK (#5557).
The query optimizer and execution engine saw major work in this release, closing the gap with SQLite on complex analytical queries and pulling ahead on join strategies.
Hash joins. Hash joins landed in 0.4 but were inner-only. In 0.5 they gained bloom filters for early probe-side filtering (#4395), LEFT and FULL OUTER join support with build/probe chaining (#5096), partition spilling to disk when the build side exceeds the memory budget (#5001, #4754), safe build-side materialization planning (#4395), and general performance improvements (#4747). Several correctness fixes were also shipped for collation handling (#5387), table mask calculations (#4901), and WHERE term consumption (#5646).
Semi/anti joins. Correlated EXISTS and NOT EXISTS subqueries are now decorrelated into semi and anti joins, avoiding repeated per-row subquery execution (#5604).
Multi-index OR scans. Disjunctive predicates like WHERE a = 1 OR b = 2 can now combine seeks across multiple indexes, with the results merged via a RowID bitmap (#4958). Follow-up work enabled multi-index OR scans during joins, propagated output cardinality estimates, and stabilized DML on multi-index scan results (#5512, #5204).
Cost estimation refactor. The cost model was overhauled: IN lists and uncorrelated IN subqueries now participate in the constraint model, LIKE and closed-range predicates (BETWEEN, a >= x AND a <= y) constrain output cardinality, sort cost is properly accounted for instead of a hardcoded constant, and all fanout/selectivity constants were moved to a centralized cost_params module (#4815, #4864).
FROM-clause and CTE materialization. Subqueries in the FROM clause and common table expressions can now be materialized into ephemeral tables, giving the optimizer freedom to pick better join orders for the outer query (#4913).
TPC-H performance. AggContext was refactored to eliminate allocation overhead in aggregate-heavy queries (#4779). Combined with general query execution speedups (#4391) and insert performance improvements (#4824), several TPC-H queries saw significant end-to-end improvements.
STRICT tables are no longer experimental (#5119). In SQLite, STRICT tables enforce column type constraints at insertion time — a column declared INTEGER will reject a text value instead of silently coercing it. Turso now supports this fully, including CHECK constraints (#5040).
SQLite has no type system beyond its five storage classes (NULL, INTEGER, REAL, TEXT, BLOB). Even with STRICT tables, you're limited to these base types — there's no way to define a uuid or varchar(255) that the database actually understands and enforces. Turso 0.5 changes this with user-defined types via CREATE TYPE (#5254). A user-defined type maps a high-level value to a SQLite base type through ENCODE/DECODE expressions, with optional validation parameters:
CREATE TYPE uuid(value text) BASE blob
ENCODE uuid_blob(value)
DECODE uuid_str(value)
DEFAULT uuid4_str()
OPERATOR '<';
CREATE TYPE varchar(value text, maxlen integer) BASE text
ENCODE CASE WHEN length(value) <= maxlen THEN value
ELSE RAISE(ABORT, 'value too long for varchar') END
DECODE value
OPERATOR '<';
CREATE TABLE users (
id uuid,
name varchar(255)
) STRICT;
Turso ships with built-in types including uuid, boolean, json, jsonb, date, time, timestamp, smallint, bigint, inet, and varchar. Users can define their own with arbitrary SQL expressions for encoding/decoding logic. Types are stored in the database itself and loaded on open, so they travel with the file.
Change data capture (CDC), which records row-level changes to a turso_cdc table for downstream sync consumers, has been promoted from unstable to stable. The PRAGMA has been renamed from PRAGMA unstable_capture_data_changes_conn to PRAGMA capture_data_changes_conn (#5610), with the old name kept as an alias for backward compatibility. See the CDC documentation for details.
CDC also gained a v2 record format (#5340). The new format adds a change_txn_id column and a COMMIT record type, enabling sync consumers to group changes by transaction and apply them atomically. Within a multi-row statement or an explicit BEGIN...COMMIT block, all rows share the same transaction ID with a single COMMIT record emitted at the end. A turso_cdc_version table now tracks the schema version per CDC table, with automatic detection and preservation of v1 tables on existing databases (#5271).
Turso 0.5 also ships with improved ATTACH database support, VACUUM INTO, an experimental Windows IOCP backend, prepared statement caching, and hundreds of SQLite compatibility fixes. Over 3,000 commits from 50+ contributors went into this release since v0.4.0. The full changelog is on GitHub. If you run into issues or have ideas, open an issue on GitHub or join us on Discord.