From 28e281142bc6d71cc95f8632796c8994ce350d79 Mon Sep 17 00:00:00 2001 From: Luther Monson Date: Sun, 11 Oct 2026 15:53:31 -0700 Subject: [PATCH] chore(deps): bump turso 0.7.2 -> 0.8.2 Bumps the exact Turso pin to 0.8.2, the latest stable (ephpm#535). No litewire code change was needed; the workspace builds, lints and tests cleanly on it, MSRV 1.88 included. What changed upstream that reaches litewire or its embedders: - PRAGMA journal_mode changes are refused on a connection with CDC capture on. 0.7.2 accepted WAL -> MVCC there, and upstream documents that capture then stopped silently. New test journal_mode_change_on_a_capturing_connection_is_refused pins it (fails on 0.7.2, passes on 0.8.2). - The turso binding now gates each connection: execute_batch and the new batch/transactional_batch need exclusive use and return "connection is busy with another operation" while a Rows from the same connection is unfinished; a query/execute during a batch fails with "connection is busy executing a batch". litewire calls none of these. - TransactionBehavior gained Concurrent; Builder::with_io takes impl Into. File format and the turso_cdc schema ("v2") are unchanged. Checked with a two-binary probe (turso 0.7.2 and 0.8.2 builds): 0.7.2 files open under 0.8.2 with integrity_check ok, rows and indexes intact, and change_id continuing monotonically; a crash image with an uncheckpointed 0.7.2 WAL recovers; CdcTailer + apply_batch replay the whole mixed 0.7.2/0.8.2 log to an identical replica; 0.8.2 files open under 0.7.2. --- CHANGELOG.md | 8 +++ CONTRIBUTING.md | 2 +- Cargo.lock | 55 ++++++++++--------- Cargo.toml | 31 ++++++++--- README.md | 2 +- crates/litewire-turso/Cargo.toml | 2 +- crates/litewire-turso/src/lib.rs | 7 ++- .../litewire-turso/tests/cdc_replication.rs | 54 ++++++++++++++++++ docs/architecture.md | 2 +- 9 files changed, 122 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee73850..a90698e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ the commit that merges this changelog. ### Changed +- Turso engine bumped `=0.7.2` → `=0.8.2` (ephpm#535). No litewire API + change. Database files are compatible in both directions (0.7.2 files, + including one with an uncheckpointed WAL, open under 0.8.2 and the reverse; + the `turso_cdc` log schema stays "v2"). Behaviour changes from upstream: + `PRAGMA journal_mode` changes are now refused on a connection with CDC + capture on (previously accepted, after which capture silently stopped), and + the `turso` binding's `execute_batch` now fails instead of running while a + result set from the same connection is still open. - Declared MSRV corrected from 1.85 to **1.88** and now verified by a CI job. 1.85 was never true with the locked dependency graph: the Turso engine's `aristo` dependency requires rustc 1.88. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 205bed8..8884d5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,5 +78,5 @@ in CI. implemented. - Dependencies are added deliberately, with a comment in the workspace `Cargo.toml` explaining why that crate (see the existing entries). The - `turso` crate is pinned exactly (`=0.7.0`) — never bump it via + `turso` crate is pinned exactly (`=0.8.2`) — never bump it via `cargo update`. diff --git a/Cargo.lock b/Cargo.lock index 1cb8fd2..a884280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "antithesis_sdk" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08410fcac93669a476c006cd6c4512ac1e2b30fd117231a5d55d8a2c76599b82" +checksum = "a8ed6751597568a284314fd9e2db420665898600109dedc7d8d9b588f00ae9c4" dependencies = [ "libc", "libloading", @@ -993,7 +993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1305,11 +1305,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -2398,7 +2400,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3231,7 +3233,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3771,7 +3773,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4163,9 +4165,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "turso" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9491d7a80312c5abe66a4409e4dce02065503a235453c94b9e4133877e39ffc" +checksum = "a2ebd8a76c2b005a9f42de15e0fe232cd9c17d7c8acb571ddfa294e6c9fa1e44" dependencies = [ "thiserror 2.0.18", "tracing", @@ -4177,9 +4179,9 @@ dependencies = [ [[package]] name = "turso_core" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a833cc3bf8d4e6c101c504fa470f8ab4270c2202ff2591b61b2e373b4f20d9b" +checksum = "4fa832d4ab648bf139f1b18b29bef6d653b18fbd4a77b0e4846c57aaf28c215c" dependencies = [ "aegis", "aes", @@ -4202,6 +4204,8 @@ dependencies = [ "either", "fallible-iterator 0.3.0", "fastbloom", + "getrandom 0.2.17", + "getrandom 0.3.4", "hex", "icu_collator", "icu_locale", @@ -4228,6 +4232,7 @@ dependencies = [ "ryu", "serde_json", "shuttle", + "simdutf8", "simsimd", "smallvec", "strum", @@ -4247,9 +4252,9 @@ dependencies = [ [[package]] name = "turso_ext" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7452fe676450b6840e9eb80e512d14293265221154eba66aba3845dfc0d659f" +checksum = "07d9b915ee3c37d552597b07c05c127702a926ca0eac8843f248d693f4368155" dependencies = [ "chrono", "getrandom 0.4.2", @@ -4258,9 +4263,9 @@ dependencies = [ [[package]] name = "turso_macros" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2825eaa6b7b893693636947f988e252c1196393fa54d4b85637c70d616d92fa" +checksum = "476aa46d793a8aaae6d7a12f591d85eff039129264b1d441586528c88c3feacd" dependencies = [ "proc-macro2", "quote", @@ -4269,9 +4274,9 @@ dependencies = [ [[package]] name = "turso_parser" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e58d029f02e442df4be0b5036ce520b49c553505d9d52ef5a98fc4056e95b7" +checksum = "7e03bf6489a80da2e9bf08f22d0b8af4eb391dfa5e6f71e787031cd61a921a3c" dependencies = [ "bitflags", "memchr", @@ -4284,9 +4289,9 @@ dependencies = [ [[package]] name = "turso_sdk_kit" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c1dc1c0304348c39b97bc6b27cdcb1d7292454ebd0de0f30b5ee3a4c61f9bb" +checksum = "e7893b565a3405c8f44c97b6008bed55314535514c2536db1aaf2bb9b687705a" dependencies = [ "bindgen", "env_logger", @@ -4301,9 +4306,9 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4708b5fe678b8730c85964e3d378f75e18c23c4409971360b2209f4e53e4956" +checksum = "af97d8302c41404272bb6e3612abe61f604acd23a2062fde52249641007c59ff" dependencies = [ "proc-macro2", "quote", @@ -4312,9 +4317,9 @@ dependencies = [ [[package]] name = "turso_sync_engine" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8f03e430240d590d209ac874db52773b7382c627cd604a685affde77622b09" +checksum = "f188ee2458899c017b95d1d100b87b2a22628a48b9a8e91e753006af6e8fd391" dependencies = [ "base64 0.22.1", "bytes", @@ -4335,9 +4340,9 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cb47d056c3ec567745761fa6f832358ca30ef9eb0435fdcfb77c558e70089" +checksum = "28e1232dce3a4e517db5ba5e9767b0703676a7f59270ed4f5d0118000120b9b0" dependencies = [ "bindgen", "env_logger", @@ -4714,7 +4719,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 13a2478..cda61ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,16 +76,29 @@ sqlparser = { version = "0.57", features = ["serde", "visitor"] } rusqlite = { version = "0.32", features = ["bundled", "column_decltype", "functions"] } # Turso Database — the ground-up Rust rewrite of SQLite # (github.com/tursodatabase/turso). Pinned exactly: the engine is Beta and -# API/behavior move between releases. 0.7.2 is the latest stable release on -# the 0.7 line (July 2026); 0.8.x is pre-release only and deliberately not -# taken. Bump deliberately, never via `cargo update`. +# API/behavior move between releases. Bump deliberately, never via +# `cargo update`. # -# 0.7.0 -> 0.7.2 picks up tursodatabase/turso#7855 ("close vtab cursors -# before transaction finalization", merged 2026-07-16, first released in -# 0.7.2): a vdbe fix for pragma-backed table-valued functions where a vtab -# cursor left open across transaction finalization could misbehave. 0.7.0 -# predates it. -turso = { version = "=0.7.2", default-features = false } +# 0.7.0 -> 0.7.2 picked up tursodatabase/turso#7855 ("close vtab cursors +# before transaction finalization"). +# +# 0.7.2 -> 0.8.2 (ephpm#535; 0.8.2 is the latest stable, October 2026). +# Changes that reach litewire or its embedders: +# - `PRAGMA journal_mode` changes are refused on a connection with CDC +# capture on (0.7.2 accepted them and capture stopped silently); pinned by +# tests/cdc_replication.rs. +# - The Rust binding gates each connection: `Connection::execute_batch` (and +# the new `batch`/`transactional_batch`) need exclusive use and fail with +# "connection is busy with another operation" while a `Rows` from the same +# connection (or a clone of it) is still being read; a `query`/`execute` +# started during a batch fails with "connection is busy executing a +# batch". It errors rather than waits. litewire calls none of these. +# - `TransactionBehavior` gained a `Concurrent` variant; `Builder::with_io` +# takes `impl Into`. +# - File format and the CDC log schema (`turso_cdc`, version "v2") are +# unchanged: 0.7.2 files, including an uncheckpointed WAL, open under +# 0.8.2 and 0.8.2 files open under 0.7.2 (checked with a two-binary probe). +turso = { version = "=0.8.2", default-features = false } # HTTP (Hrana) axum = "0.8" diff --git a/README.md b/README.md index 445794b..cfd7d8d 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ and `.tds("127.0.0.1:1433")` add those frontends to the same builder. |---------|-------------|----------| | `Rusqlite` | `backend-rusqlite` | Direct in-process SQLite | | `HranaClient` | `backend-hrana-client` | Remote SQLite via the Hrana HTTP protocol (sqld / Turso) | -| `Turso` | `turso` | **Experimental** — [Turso Database](https://github.com/tursodatabase/turso) engine (Rust rewrite of SQLite, Beta upstream; pinned `=0.7.2`). See `crates/litewire-turso` docs for limitations (no `VACUUM`, no multi-process access) | +| `Turso` | `turso` | **Experimental** — [Turso Database](https://github.com/tursodatabase/turso) engine (Rust rewrite of SQLite, Beta upstream; pinned `=0.8.2`). See `crates/litewire-turso` docs for limitations (no `VACUUM`, no multi-process access) | | Custom | implement `Backend` trait | Bring your own | The `HranaClient` backend connects to [sqld](https://github.com/tursodatabase/libsql) via HTTP (per-session Hrana streams plus write admission control), so litewire's wire frontends can sit in front of a sqld server. diff --git a/crates/litewire-turso/Cargo.toml b/crates/litewire-turso/Cargo.toml index 1acb062..7c1cc43 100644 --- a/crates/litewire-turso/Cargo.toml +++ b/crates/litewire-turso/Cargo.toml @@ -21,6 +21,6 @@ turso = { workspace = true } litewire-backend = { workspace = true, features = ["rusqlite"] } tokio = { workspace = true, features = ["full"] } # Temp files for CDC replication tests that need two independent -# file-backed databases (Turso 0.7.2's shared-memory `:memory:` isn't +# file-backed databases (Turso's shared-memory `:memory:` isn't # suitable for primary/replica pairs). tempfile = "3" diff --git a/crates/litewire-turso/src/lib.rs b/crates/litewire-turso/src/lib.rs index 6a52daa..631e6eb 100644 --- a/crates/litewire-turso/src/lib.rs +++ b/crates/litewire-turso/src/lib.rs @@ -5,8 +5,9 @@ //! //! # Status //! -//! The Turso engine is **Beta** (pinned here at `turso = "=0.7.0"`, the -//! first non-pre release of the 0.7 line, July 2026). Upstream does not +//! The Turso engine is **Beta** (pinned here at `turso = "=0.8.2"`, +//! October 2026; see the workspace `Cargo.toml` for what each bump +//! changed). Upstream does not //! yet position it as a production SQLite replacement. This backend exists //! to gather evidence (benchmarks, file-format round-trips, crash-recovery //! smokes) behind an explicit opt-in — it is not a default anywhere. @@ -40,7 +41,7 @@ //! same data. (No temp-file workaround is needed here, unlike the rusqlite //! backend, because connections derive from a single engine instance.) //! -//! # Known-unsupported operations (Turso 0.7.0) +//! # Known-unsupported operations (Turso 0.7.0, unchanged through 0.8.2) //! //! Returned as clear [`BackendError`]s rather than silent misbehavior: //! diff --git a/crates/litewire-turso/tests/cdc_replication.rs b/crates/litewire-turso/tests/cdc_replication.rs index fea41fe..35fc02a 100644 --- a/crates/litewire-turso/tests/cdc_replication.rs +++ b/crates/litewire-turso/tests/cdc_replication.rs @@ -621,3 +621,57 @@ async fn apply_batch_reports_an_empty_batch_instead_of_panicking() { .expect_err("an empty batch is malformed, not a silent no-op"); assert!(err.to_string().contains("empty batch"), "got: {err}"); } + +/// A `journal_mode` change on a capturing connection must fail loudly, and +/// capture must still be on afterwards. +/// +/// Turso supports only `wal` and `mvcc`; any other mode is a silent no-op, +/// so `mvcc` is the one real change. Turso 0.7.2 accepted WAL -> MVCC on a +/// capturing connection (this test fails there), and upstream documents +/// that capture then stops silently while `capture_data_changes_conn` still +/// reports it on — later writes commit without reaching `turso_cdc`, so they +/// never replicate. 0.8.0 rejects the change instead (upstream "core: reject +/// journal_mode changes while CDC capture is active"). This pins that: a +/// regression would be an unreplicated write, which nothing else notices. +#[tokio::test] +async fn journal_mode_change_on_a_capturing_connection_is_refused() { + let (primary, _p) = temp_factory().await; + let p_conn = primary.raw_connection().unwrap(); + enable_cdc(&p_conn).await.unwrap(); + p_conn + .execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)", ()) + .await + .unwrap(); + + // `query`, not `execute`: the pragma returns the resulting mode as a + // row, which `execute` reports as "unexpected row" on any engine. + let attempt = async { + let mut rows = p_conn.query("PRAGMA journal_mode = mvcc", ()).await?; + while rows.next().await?.is_some() {} + Ok::<_, turso::Error>(()) + }; + let err = attempt + .await + .expect_err("changing journal_mode while capturing must be refused"); + assert!( + err.to_string().contains("CDC capture is active"), + "unexpected error: {err}" + ); + + // Capture is still live: the next write reaches the log. + p_conn + .execute("INSERT INTO t VALUES (1, 'after')", ()) + .await + .unwrap(); + let mut tailer = CdcTailer::new(&primary, 0); + let rows: Vec = drain_batches(&mut tailer) + .await + .into_iter() + .flat_map(|b| b.rows) + .collect(); + assert!( + rows.iter() + .any(|r| r.table_name.as_deref() == Some("t") && r.change_type == 1), + "the INSERT after the refused PRAGMA must be captured; got {rows:?}" + ); +} diff --git a/docs/architecture.md b/docs/architecture.md index 7bc462c..1bcd620 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -171,7 +171,7 @@ Three implementations ship: sqld's single-writer lock is queued on this side instead of thrashed. * **`Turso`** (`crates/litewire-turso`, feature `turso`) — **experimental** in-process backend on the Turso Database engine (the Rust rewrite of SQLite, - Beta upstream, pinned `=0.7.0`). Async-native (no `spawn_blocking`). Known + Beta upstream, pinned `=0.8.2`). Async-native (no `spawn_blocking`). Known unsupported: `VACUUM`, `ATTACH`/`DETACH`, multi-process access to one file, byte-exact non-UTF-8 `TEXT` round-trips. It also exposes an experimental CDC tail/apply API (`crates/litewire-turso/src/cdc.rs`) for external replication