Skip to main content

Module ledger

Module ledger 

Source
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):

  1. exactly one ledger row per domain;
  2. BEGIN IMMEDIATE;
  3. re-read the version inside that transaction;
  4. reject a future version before any mutation (SqliteStoreError::SchemaFromTheFuture);
  5. 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§

LedgerReport
Outcome of apply_domain_migrations.
Migration
One schema migration step for a domain.
SchemaDomain
A store’s schema domain: its ledger name plus the ordered migration list.
SchemaObject
One exact main.sqlite_schema object name owned by a domain.
SchemaPredecessor
Frozen verifier for one released predecessor version.

Enums§

SchemaObjectKind
SQLite catalog object kind owned by a schema domain.

Functions§

apply_domain_migrations
Bring domain up to date in the file behind conn, 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.