1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//! Shared SQLite mechanics for Meerkat stores.
//!
//! This crate is the single home for the connection policy, schema-evolution,
//! and maintenance-fence machinery that was previously copy-pasted across the
//! store crates (six independently authored PRAGMA setups with divergent busy
//! timeouts, `CREATE TABLE IF NOT EXISTS` everywhere, and no version ledger).
//! See `docs/plans/storage-unification-plan.md` (Phase 3) in the workspace
//! root for the design rationale.
//!
//! What lives here:
//!
//! - [`profile`]: DDL-free connection opening under named policy profiles
//! ([`ConnectionProfile::Primary`], [`ConnectionProfile::ReadOnly`],
//! [`ConnectionProfile::Maintenance`]). Opening a connection never runs
//! schema DDL; stores apply their [`ledger`] domain after opening. The
//! optional [`OpenOptions::schema_preflight`] refuses a future file before
//! any mutating pragma, and [`WriteContact`] types each profile's honest
//! no-write guarantee (WAL reads may need sidecar files).
//! - [`ledger`]: the per-file migration ledger (`meerkat_schema(domain,
//! version)`) with the pinned concurrent-open transaction protocol and the
//! typed [`SqliteStoreError::SchemaFromTheFuture`] refusal. Ledger state
//! that is malformed (wrong shape, duplicate rows, non-positive versions)
//! is refused typed, never healed.
//! - [`json_column`]: the TEXT-or-BLOB tolerant JSON column codec.
//! - [`fence`]: the per-operation maintenance-fence guards. Store operations
//! take a shared guard; offline migration takes the exclusive side and
//! waits for in-flight operations to drain. Stores built on this crate hold
//! no bespoke opener, which is what makes them fence-aware for free.
//! - [`error`]: the crate error type plus the storage-level error
//! classification (transient / corrupt) that store crates layer their
//! staleness semantics on top of. Adoption contract: store crates route
//! every raw rusqlite error through [`classify_sqlite_error`] instead of
//! re-matching SQLite error codes locally.
//!
//! # Retryability
//!
//! Error classification alone does not authorize a retry: a transient failure
//! can land after a write committed but before success became observable, and
//! blind retry duplicates non-idempotent effects. Automatic retry is only
//! sound for idempotent or CAS-keyed operations; an indeterminate
//! non-idempotent write requires outcome reconciliation (read back, then
//! decide) before any retry. Bounded waiting for lock contention is the
//! connection busy handler's job, not a caller retry loop.
pub use ;
pub use ;
pub use JsonColumnBytes;
pub use ;
pub use ;