Expand description
Per-file schema migration ledger.
Every SQLite file carries a meerkat_schema(domain TEXT PRIMARY KEY, version INTEGER NOT NULL) table with exactly one row per schema domain.
Each store registers its ordered migrations, the exact released versions
it may upgrade, and every main.sqlite_schema object it owns.
§The pinned transaction protocol
Idempotent migration functions alone do not make concurrent opens safe, so the runner pins a minimal protocol (consumed unchanged by downstream adopters):
- exactly one ledger row per domain;
BEGIN IMMEDIATE;- re-read the version inside that transaction;
- reject a future version before any mutation
(
SqliteStoreError::SchemaFromTheFuture); - execute the pending migrations and the ledger update atomically in the
same transaction — custody is verified with a runner-owned savepoint
around each body, so a body that COMMITs or ROLLBACKs underneath the
runner is refused (
SqliteStoreError::MigrationBrokeTransaction) even when it re-BEGINs a fresh transaction afterwards.
A table merely named meerkat_schema is not trusted: before any read
the pinned column shape is validated against main’s catalog, versions
must be positive, and at most one row may exist per domain
(SqliteStoreError::LedgerMalformed otherwise). All ledger SQL is
main.-qualified, so a TEMP table shadowing the name can neither satisfy
nor bypass the ledger.
Concurrent opens race safely: the loser’s in-transaction re-read sees the winner’s committed version and applies nothing.
§Compatibility floor
A missing domain row is accepted only when none of that domain’s declared
objects exist. That is a fresh domain (possibly in a file containing
foreign co-tenant domains), so its dedicated initialize_current
function may build the current shape directly. A missing row plus an
owned table, index, trigger, or view is refused as
SqliteStoreError::UnledgeredDomainObjects; this runner never infers a
version from ambient DDL or stamps an unauthenticated historical shape.
A present row may be current or one of the exact released predecessor
versions declared by the domain. Pre-floor versions and gaps are refused
as SqliteStoreError::UnsupportedSchemaPredecessor. Eligibility is
re-established under the same BEGIN IMMEDIATE transaction as the DDL
and ledger update. The ledger table itself is not created until after that
decision, so a refusal leaves both schema and ledger unchanged.
Foreign domain rows (other stores co-tenanting the same file) are never read or written; the ledger keys strictly by domain name.
Structs§
- Ledger
Report - Outcome of
apply_domain_migrations. - Migration
- One schema migration step for a domain.
- Schema
Domain - A store’s schema domain: its ledger name plus the ordered migration list.
- Schema
Object - One exact
main.sqlite_schemaobject name owned by a domain. - Schema
Predecessor - Frozen verifier for one released predecessor version.
Enums§
- Schema
Object Kind - SQLite catalog object kind owned by a schema domain.
Functions§
- apply_
domain_ migrations - Bring
domainup to date in the file behindconn, per the pinned protocol. Returns the version movement. - domain_
version - Read a domain’s ledger version without applying anything.
- preflight_
schema_ eligibility - Establish read-only schema eligibility before a profile’s mutating pragmas: current and released predecessor rows must match their exact catalog fingerprints; future, pre-floor, gap, and unledgered-owned shapes are refused.
- verify_
released_ schema_ fingerprint - Verify an on-disk predecessor against a frozen released schema builder.