Rindle docs and package mapSkip to main content

Crate rindle_replica

Crate rindle_replica 

Source
Expand description

§Embedded SQLite with incremental live queries

Db provides one controlled SQL writer, ordinary SQL reads, and live queries over file-backed SQLite. It captures row changes automatically and derives query deltas through the rindle engine. Use it when an application owns its database and needs live results without implementing change capture or transaction ordering.

Cluster distributes queries across worker threads. Its channel exposes provisional deltas and commit progress; ClusterConsumer owns the drain loop and adds normalized subscriptions. The raw rindle crate is a lower-level choice for applications that already own source storage and change capture.

§Transactions and delivery

Every row write must pass through the controlled writer. The preupdate hook captures changes, and the engine derives against a read-only pre-commit snapshot plus an in-memory batch overlay. Derivation does not write SQL or replay triggers.

Db derives before commit, commits the durable data, then calls subscribers synchronously. Cluster workers can emit Changed slices before commit; callers must stage them until the hosting worker emits ClusterEvent::Progressed. See Update and ClusterEvent for recovery and delivery boundaries.

Query handles expose changes rather than maintaining an application view. Apply changes in order, or use a higher-level consumer. Subscribe immediately after registration and call Query::destroy when finished; dropping a handle does not unregister it. Db and the Cluster coordinator are !Send and stay on one thread.

§Storage and schema

Fresh embedded databases use ordinary WAL. Existing WAL/WAL2 files retain their mode; OpenOptions selects a journal explicitly. Foreign keys are enforced by default. Register every user table that a transaction can write, including cascade targets. Captured foreign-key cascades become ordinary row changes.

Triggers remain unsupported on registered tables, and generated columns are rejected. Use the schema DDL methods before registration. To change a registered table’s shape, close the runtime, migrate the database, then reopen and register it.

Number-domain INTEGER cells must round-trip exactly through f64; admission and capture reject values that would round. The storage layer can preserve exact BIGINT/INT64 cells, but maintained queries reject a required exact-integer column. See the schema guide for surface limits.

use rindle::table;
use rindle_replica::{Db, QueryId};

let db = Db::open("app.db")?;
db.exec_ddl("CREATE TABLE IF NOT EXISTS issue (id INTEGER PRIMARY KEY, title TEXT NOT NULL)")?;
db.register_table("issue")?;
let query = db.query(QueryId(1), table("issue").build())?;
query.subscribe(|update| println!("{update:?}"));

let mut tx = db.write()?;
tx.exec("INSERT INTO issue (title) VALUES ('First live query')", &[])?;
tx.commit()?; // subscribers run after the SQL commit
let count: i64 = db.read(|conn| conn.query_row("SELECT count(*) FROM issue", [], |row| row.get(0)))?;
assert!(count >= 1);
query.destroy();

Modules§

schema_envelope
The replicated-SQL schema envelope: what SQLite objects the public surface accepts, how a table is introspected into ReplicatedTableSchema, and how a DDL/migration batch is applied under the registered-table guard with an audit trail.
session
The shared interactive-mutation-session engine (REPLICATOR-INTERACTIVE-TXN-DESIGN.md §4).
sql
Shared SQL statement surface used by both the read follower and the write master.
wire_json
The replica’s JSON wire surface: the shared write-plane codec re-exported wholesale, plus the two renderings that read the ENGINE’s own types and therefore cannot live in the write plane — the flat-change / wire-schema dialect every native host ships to its client (flat_changes_to_json, wire_schema_to_json) and the SSR assembled snapshot (assembled_snapshot_to_json).
writeplane
The shared write-plane machinery for the public one-shot SQL surface (303-PUBLIC-SQL-COORDINATOR-EXTRACTION-PLAN.md). PR 0 landed two layers, PRs 1–4 the coordinator’s batch, DDL, script, and migration slices:

Structs§

AggTable
A synthetic aggregate base table the server ships in place of an aggregate’s child rows: the reduce’s (group_key…, value) output, given a base-table home on the client (§3.2). Columns are [child_field…, "count"] (mirroring the engine’s agg_relationship_reldef / Reduce::count_by output), the leading key_len of which are the group key (and the PK). Derived purely from the AST — there is no such table in the DB — so agg_table_schemas feeds the publisher’s hello + PK map for it.
AnalyzeJoin
One flippable EXISTS join’s routing decision.
AnalyzePlan
The chosen plan — both layers (ANALYZE-QUERY-DESIGN.md §3.1).
AnalyzeReport
The full analyze report for one query — the /analyze response body.
AnalyzeSource
The numbers for one source leaf — the row the CLI renders per table.
AnalyzeSpec
Everything one standalone analyze query run needs, snapshotted off the live engine so the run itself can happen on any thread: the DB path, the registered tables, and the engine configuration. Built by Cluster::analyze_spec; consumed by analyze_standalone. Plain owned data (Send), no engine handles.
AnalyzeTiming
Per-phase wall-clock of the throwaway run (ANALYZE-QUERY-DESIGN.md §3.2). hydrate dominates — it drives every source fetch and therefore all the scan/emit counting.
AnalyzeTotals
Query-wide totals. outputRows (final deduped IVM output) vs. emitted (rows sources pushed in) is a third useful gap — a big spread means the pipeline filters/joins a lot above the sources.
BaseColumn
A column in a BaseTableSchema.
BaseTableSchema
One base table’s externally-described schema (name + ordered columns with types + PK column names), the shape /schema serves for client codegen (DRIZZLE-MIGRATIONS-DESIGN.md §6.2).
Cluster
The multi-threaded replica handle (cheap Rc clone). Single-thread-use on the coordinator side; a server runs it on a dedicated owner thread and continuously drains the ClusterEvent channel returned by open on whatever thread it likes — but it must never STOP draining while writes flow (the channel-out is bounded; see open’s draining contract).
ClusterConsumer
The cluster-backed consumer (coordinator side). !Send — lives on one thread; the drain pushes batches/progress/faults into the sink from its own thread.
ClusterMutationWrite
One server-side mutation’s open cluster transaction (see ClusterConsumer::begin_mutation). Dropping without committing rolls back.
ClusterWriteTxn
An open write transaction on the cluster’s single writer connection. Run SQL with exec/exec_batch; commit runs the snapshot/commit handshake. Workers can deliver provisional changes while SQL is still running. Dropping rolls back SQL; if chunks were already streamed, affected queries fault and need a new registration. See ClusterEvent.
CommitInfo
What one committed transaction did, beyond its data effects: the new global tx id (the commit version cv the optimistic protocol stamps on outgoing batches). Moved to the apply plane (the shared write-transaction state machine mints it — design 309); re-exported here so rindle_replica::CommitInfo is unchanged. What one committed transaction did, beyond its data effects: the new global tx id (the commit version cv the optimistic protocol stamps on outgoing batches). Client lmid advances are NOT reported here — _rindle_client_mutations rows ride the capture like any data and reach each client through its own system query (§8.2).
Db
A single-thread SQLite runtime with live queries. Clones share the same writer, engine, and registrations through Rc; they do not create independent databases. This handle is !Send. Query and write handles keep the shared runtime alive.
DdlActions
The destructive DDL actions observed so far (design 227, third review pass): which main-schema tables were DROPPED or ALTERED, reported by SQLite’s authorizer as each statement was prepared. This is the real parser’s verdict, so comments between tokens, script-valued slots, quoting, and schema-qualified names are all handled by construction — the lexical scanners this replaced were evadable by each in turn.
DdlApplyReport
The full, ordered apply report of one ddl entry. Persisted — as JSON, in the same transaction as the entry’s idempotency marker — so a replay (an in-connection retry after a later step failed, or a crash before cursor advancement) re-drives bounce decisions from durable truth instead of observing nothing. Desired-index effects normally commit in this same transaction; old reports are retained for conservative compatibility repair.
DdlStep
One executed statement of a ddl entry plus the destructive actions the authorizer attributed to it. Order is the EXECUTION order — load-bearing for the desired-index bookkeeping (design 227 fourth review pass): in DROP TABLE t; CREATE TABLE t; CREATE INDEX ix ON t the index belongs to the final table, so its recording must follow the drop’s purge, not race it.
DerivationPool
The worker-pool seam for an external writer. See the module docs. Single-thread-use, like Cluster: the pool handle lives on the writer’s thread (the hooks fire there), and only Send data crosses to the workers.
Drain
The running drain: owns the two worker/forwarder/loop threads. Dropping (or shutdown) stops the loop; the forwarder exits when the cluster’s channel-out closes (i.e. when the Cluster is dropped).
DrainHandle
The coordinator-side handle: a cheap clone of the control sender. Lives on the Node main thread; its methods are called from the napi Db surface as queries are registered/destroyed, connections come and go, and commits land.
Engine
The per-thread IVM unit (see the module docs). Internal to the Db/Cluster openers, and public via the crate’s embedded-engine seam (design 308): a host that owns its own write path — the SQLite extension’s commit hook — constructs one over a derivation connection, registers tables/queries, and drives apply_batch itself.
FamilyExtraction
A parameterized query family’s identity and template (design 310 §3), re-exported from rindle-wire so a daemon groups subscriptions with the same types the engine binds on. The result of a successful extraction: the family the query belongs to, the template its pipeline compiles from, and the binding this query is.
FamilyKey
A parameterized query family’s identity and template (design 310 §3), re-exported from rindle-wire so a daemon groups subscriptions with the same types the engine binds on. Every daemon-side input that can affect the emitted rows of a family: the same four fields as QueryKey, with the canonical template bytes in place of the canonical AST bytes.
FamilyTemplate
A parameterized query family’s identity and template (design 310 §3), re-exported from rindle-wire so a daemon groups subscriptions with the same types the engine binds on. The compiled half of a family: the stripped AST the builder lowers, plus the names of the holed columns (the partition key, resolved to ColIds by the builder) and each hole’s position among the original top-level conjuncts (what instantiate needs to be an exact inverse).
ForeignKeyAudit
The result of a foreign_key_check pass.
ForeignKeyViolation
One row of PRAGMA foreign_key_check: a child row whose declared reference has no parent.
InitialSnapshotColumn
InitialSnapshotCommit
InitialSnapshotIdentity
InitialSnapshotStore
The single SQLite owner for an initial snapshot. PhantomData<Rc<()>> makes the lifecycle explicitly thread-confined even on platforms where rusqlite’s connection is movable.
InitialSnapshotTable
JoinPrecheckReport
Per-registered-query bookkeeping in the shared graph: the [PipelineManifest] (the exact node/storage slots its build created, for teardown) and the caller’s opaque QueryId tag. Keyed by the query’s change-sink NodeId. The engine does nothing with the tag (no de-dup / no refcount — that is the caller’s concern); it is retained for correlation/observability and echoed back via the Query handle. What Engine::join_precheck_report returns: the join membership pre-check’s bounds and per-query join states on one engine (design 311 §8 inspection).
MaintenanceOptions
Which steps a maintenance pass performs and the thresholds that gate them. Default is the server’s tuning: optimize on, reclaim up to 1000 freelist pages once ≥1000 have built up (~4 MiB at a 4 KiB page), passive checkpoint on.
MaintenanceReport
What a maintenance pass actually did — returned for observability/tests. A pass that ran while a write transaction was open reports skipped.
MutationEnvelope
The upstream wire envelope (§8.1): one named-mutator invocation. Mutations are totally ordered per client by mid; the wire carries name + args, never code.
MutationOutcome
What Db::apply_mutations did: every transaction it committed (in order — applied mutations and lmid-only failure commits alike, each with its crate::CommitInfo). Duplicate (already-processed) envelopes commit nothing.
MutationReject
Why a mutator refused a mutation (permission/validation/SQL failure). Converts from the common error shapes so ? works inside a mutator body.
MutatorRegistry
The server’s registry of named mutators (§4.2). One of the two registries (the client’s optimistic twin lives in the client engine); the wire only carries names, so sharing mutator code is a deployment choice, not a protocol mode.
NodeData
An owned, fully-materialized node: a row plus its relationship subtrees (keyed by relationship slot). Re-exported from the engine (where it is CaughtNode). A fully-materialized node: the row cells plus eagerly drained relationships. The comparison unit for a fetch/push assertion, and the owned node a Graph::add_change_sink consumer receives.
NormalizeFold
The server-side serializer (§4): one per registered normalized query. Fold each committed transaction’s CaughtChanges with fold to get that tick’s NormalizedOps. The footprint persists across ticks; the snapshot is simply the first fold over the hydrate batch (every row 0→1 ⇒ all Adds).
NormalizedBatch
One committed transaction’s normalized ops (or the seq-0 hydrate snapshot). Ops apply in order into one client Db.write() transaction.
NormalizedHello
The subscription handshake (§3), sent once before any NormalizedBatch. Slimmer than the flat Hello: flat per-table schemas, no nested view schema or per-level sort.
NormalizedPublisher
Sender side: wraps a NormalizeFold, stamps batches with the subscription epoch + normalized_fp, and drives the gap-free seq. The caller drains the change-sink (the replica’s per-query CaughtChanges) and hands them to snapshot / commit.
NormalizedSubscriber
Receiver side: the protocol state machine that validates the envelope and emits clean ops for the caller (the base-store fold / TS NormalizedSync) to apply. It does not itself hold the base store — it owns only epoch/seq/fp state.
OpenOptions
Everything an opener can be configured with, in one struct-with-Default — so every combination (planner × operator storage × journal) is expressible without a ladder of positional open_with_* rungs. Taken by Db::open_with, Cluster::open_with, and ClusterConsumer::open_with; the named rungs (open, open_with_planning, open_with_journal, …) are conveniences that fill one field each.
PoolGate
The commit-verdict gate, held between “all workers pinned + pushed” and the host COMMIT’s outcome. Releasing it sends each worker its terminal TxFinish with the verdict inline; dropping it without either releases the workers as an abort (no worker hangs).
PoolTxn
An in-flight transaction across the pool: every live worker holds a pinned pre-commit snapshot, awaiting pushed chunks and the terminal gate. Dropping it without finish releases the workers’ snapshots as an abort.
ProgressFrame
The connection-level progress frame (§8.6): advances the client’s coherent release point (cv_min). Pure release signal — mutation confirmation does NOT ride it: lmid is a row in the client-mutations table (_rindle_client_mutations, rindle-replica’s CLIENT_MUTATIONS_TABLE), delivered through the client’s own per-client system query like any other data, so it is released by the same cv_min that releases the commit’s effects (transactionally coherent by construction). Emitted per the poke rule (§8.4); standalone only to advance cv_min during a quiet window.
ProgressTracker
See the module docs. Single-thread (Db) semantics; one instance per server.
PublicAuthorizerGuard
Query
A registered live query. Subscribe to receive its raw change events.
QueryId
A caller-supplied opaque tag for a registered query, echoed back via Query::id. The engine never interprets it — it does no id generation, hashing, de-duplication, or refcounting. Identity and de-dup of “the same query” are the caller’s concern: a layer that materializes/keeps each query’s state can map its own ids to Query handles and reuse them, which is exactly the place that owns the per-subscriber hydration the engine’s raw change stream deliberately does not. Pass any scheme that suits you (a content hash, a uuid, a counter).
ReadConn
A read-only connection for the daemon’s raw-read path. See the module docs for why this is separate from the coordinator’s read-write reader.
ReplicatedColumn
One column accepted by the replicated-SQL schema envelope.
ReplicatedTableSchema
A persistent ordinary table accepted by the replicated-SQL schema envelope.
RoomRowConflict
One CAS miss: the row’s authoritative current image (None = absent).
SharedQueryInfo
The query-wide pieces a server needs to frame each subscriber’s hello for a shared query (see ClusterConsumer::register_shared_query): the deterministic table set and its normalized_fp. The footprint fold itself lives on the drain thread.
SourceHead
Persisted source accounting at the applied cursor. Portable bases carry this so lag/change-count stamps resume from restored history instead of zero.
SqlColumn
SqlReadRows
The result of a raw parameterized read (ReadConn::read_sql): the result columns in order and each row’s cells (mapped from their raw SQLite storage class). The caller owns how to render this — e.g. zipping columns with each row into keyed objects.
SqlStatementRequest
StatementResult
StorageReport
What Engine::storage_report returns: how much operator scratch state one registered query’s pipeline holds right now (design 310 impl plan D5’s leak probe, lifted to the cluster boundary — see Cluster::__test_storage_entries).
StoredSourceCheckpoint
TableMeta
What a table registration records (the column order + PK columns drive Mutation→SQL; the per-column ColType feeds schema codegen via /schema).
TableNode
The query’s table tree: each frame’s base table plus, per relationship slot (in the query-local RelId order), the child frame’s tree. It is the path-free replacement for PathSeg: folding a CaughtChange::Child descends children[rel] to learn the child’s table, then throws the parent row away.
TableSchema
The discovered shape of a registered table: columns in cid (== [ColId]) order plus the primary-key column ids. Feeds TableSource::try_new. Clone so the coordinator can fan one discovered schema out to every worker (it is Send: ColumnDef is Box<str> + scalars). Public via the crate’s embedded-engine seam (design 308): a host that drives crate::Engine directly discovers with discover and registers the result.
TableWireSchema
One base table’s flat schema on the wire: its ordered column names (wire rows are positional against them) and the primary-key column indices. The client validates these against its own typed schema and registers any table it hasn’t yet (e.g. an EXISTS witness table). No sort — base tables sort by PK in the memory source, and the result sort is the client’s local engine’s concern (§3).
TxId
A global, monotonic, durable-with-the-data transaction id.
WalCheckpoint
The three integers a PRAGMA wal_checkpoint returns. Moved to the apply plane (ApplyStore::checkpoint_truncate is the TRUNCATE flavor’s home — design 309); re-exported here so rindle_replica::WalCheckpoint and the maintenance report are unchanged. One PRAGMA wal_checkpoint(TRUNCATE) outcome (ApplyStore::checkpoint_truncate).
WriteTxn
An open write transaction on the single writer connection. Run ordinary SQL with exec/exec_batch; the preupdate hook captures row deltas. commit derives against a read-only pre-commit snapshot and batch overlay, commits SQL, then calls subscribers. Dropping without committing rolls back and delivers no events.

Enums§

ChangeEvent
An owned, fully-materialized change event off the pipeline — the raw delta the engine emits (re-exported from the engine, where it is CaughtChange). Nested relationships are carried in NodeData::relationships. A caught downstream change. Mirrors catch.ts expandChange output: an Edit carries only the two rows (no node, catch.ts:104-109); a Child carries the parent row, the relationship slot, and the nested change (catch.ts:110-118).
ClusterEvent
Events from a Cluster’s bounded channel. Registration emits a committed Hydrated baseline. Later Changed slices can arrive before the transaction commits. Buffer them until Progressed from the worker that hosts the query. For a combined result across workers, wait for every relevant worker.
ColType
A column’s declared type (from the host schema) — drives SQLite affinity at DDL time.
DdlMigrationError
A standalone authority’s checked DDL apply can fail either in the cluster/schema machinery or in the shared write-plane policy checks. Keeping the latter typed preserves stable migration error codes all the way to the host’s HTTP renderer.
FaultCause
Why a query faulted (ClusterEvent::Faulted) — the classification the host’s mode machine consumes (FOLLOWER-LAG-SHED §6.6 item 3: today the error’s identity died inside the worker as a bare faulted = true, making a deadline bail indistinguishable from an ordinary derive fault).
ForeignKeys
Whether SQLite enforces declared foreign keys on one connection.
InitialSnapshotOpen
JournalMode
The WAL-family journal mode an opener ensures on a fresh file (design 306 D5). Under the default Wal request an existing wal or wal2 file keeps its mode (D3 — the opener accepts both). An explicit Wal2 request is a requirement, not a preference: the opener attempts the conversion and fails loudly if the file does not end up in wal2 — the daemon’s fleet contract (followers, backups) must never be silently unhonored.
Mutation
One positional mutation (cells aligned to the table’s column order).
NormalizedApplied
The outcome of applying a NormalizedBatch.
NormalizedOp
One normalized change. Self-identifies by table name — the client routes each row to a base table directly, with no tree path and no slot map (§3). Rows are positional (aligned to that table’s column order). OwnedValue is serializable because the crate enables rindle’s serde feature, so the op serializes for the wire / oracle directly.
NormalizedProtocolError
A protocol violation the NormalizedSubscriber surfaces. All but a duplicate are fatal — the only repair is a re-hydrate under a new epoch (§5.3).
OperatorStorage
Where stateful operators (take / cap / reduce) keep their scratch state.
ReplicaError
Everything the write plane — and the rindle-replica IVM half above it — can fail with. Wraps the engine’s own errors verbatim (Rindle / Build) and adds the wrapper-level faults (connection open, capability asserts, unsupported columns).
RoomFlushOutcome
The outcome of a CAS-guarded room flush (ClusterConsumer::commit_room_flush).
SqlArgs
StatementClass
StatementRunError
Update
A query baseline or incremental changes. A Hydrated contains the full result as Add events and replaces any previous baseline. Changed contains deltas to apply in order. Delivery depends on the runtime:

Constants§

APPLIED_DDL_TABLE
The follower’s DDL idempotency journal: one row per applied ddl entry, keyed by the migration id (or the entry offset when a source ships none). Unregistered bookkeeping like the offsets table — never captured/fanned. A crash-window replay (the ddl re-delivered before its cursor advanced) dedups against this BEFORE re-applying, so an already-applied reshape is skipped exactly rather than re-run-and-inferred-from-the-error (see ApplyStore::exec_ddl_with_marker).
CDC_BOOTSTRAP_TABLE
CLIENT_MUTATIONS_TABLE
The replicated bookkeeping table carrying each client’s high-water mutation id. Single-_ prefixed: captured by CDC (unlike __replica_meta) and hosted by the engine like any base table — each client’s lmid flows to it through its own one-row system query, transactionally with the data (§8.2).
FOREIGN_KEY_AUDIT_ROW_CAP
Default cap for foreign_key_check. A broken store can violate on every row, and an audit that answers “yes, and here is where it starts” is worth more than one that tries to materialize millions of rows.
OUTCOME_RETENTION_LMIDS
Retention bound for ROOM_MUTATION_OUTCOMES_TABLE rows, by lmid distance — never by time (Slice I-ii). When a room flush advances a (doc, client) ledger row to lmid, rows with mid ≤ lmid − K prune in the same transaction. K = 512 mirrors the room shell’s per-client recorded-outcome FIFO cap (MAX_RECORDED_OUTCOMES_PER_CLIENT = 512, packages/room/src/shell.ts) — the two ends of the outcome-resolution surface degrade at the same depth. The accepted loss class is the H-v one: a pruned mid reads as applied through the daemon, exactly as an evicted map entry re-answers with silence on the room socket; a client is only ever that far behind its own ledger with a backlog ≥ K in flight.
READ_ROW_CAP
Max rows a single raw read may return before it is rejected. A hard ceiling against an unbounded result set exhausting memory; well above any reasonable one-shot read (add a LIMIT / narrow the query if you hit it).
READ_TIME_BUDGET
Wall-clock budget a single raw read may run before it is interrupted. Generous — an interactive API read should finish in milliseconds; this only fences a pathological query (cartesian blow-up, runaway recursive CTE) from wedging the reader.
READ_VM_BUDGET
Approximate SQLite VM-instruction ceiling for one read statement. The progress handler is the in-band backstop to the wall-clock watchdog and is reset after every statement.
ROOM_CLIENT_MUTATIONS_TABLE
The domain-scoped ledger for room-flush lmid co-commits (RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md §7.1). Keyed by (doc, client_id) — one gapless mid stream per domain, one ledger row per domain — so a room flush and a slow-path daemon mutation for the SAME client never collide on a shared row (the Rev 1 data-loss bug, §8.5’s “ledger isolation” invariant). CLIENT_MUTATIONS_TABLE stays exclusively the slow-path stream; room flushes retarget here.
ROOM_MUTATION_OUTCOMES_TABLE
The durable twin of the H-iv-b mutationOutcome frame ({mid, kind, reason?, name?, args?}), keyed (doc, client_id, mid) — the §4 lifecycle’s outcome-resolution surface for THE NAMED INVARIANT: never retire a room-domain entry off a daemon-carried lmid without outcome resolution (§3.3/§7.5). After downgrade the room socket that ordered outcome-before-ack is gone, so non-applied verdicts must be readable through the daemon subscription plane like the §7.1 ledger row. Rows are written by Slice I-ii’s flush split — this slice only creates + registers the table; an absent row for a covered mid reads as applied (only non-applied outcomes are recorded, matching the room shell’s recorded-outcome map).
ROOM_PLACEMENT_TABLE
The room placement-fence table (RINDLE-REALTIME-DESIGN.md §2.5): one row per doc, bumped by every claim. A flush carrying an epoch below the current claim is fenced — validated with the apply on the single-threaded engine, so the check and the commit are atomic. Unregistered bookkeeping like SOURCE_OFFSETS_TABLE.
ROOM_WATERMARK_TABLE
The §4.2 downgrade fence: (doc, flush_seq), co-committed monotonically in every room flush’s transaction (ClusterConsumer::commit_room_flush). Cross-authority cvs are incomparable, so the fence is data that RIDES THE ECHO: a downgraded client keeps its frozen ghost source until its daemon subscription delivers flush_seq ≥ finalFlushSeq — proof the store it fell back to holds the room’s final flush, whatever the authority shape (single daemon / lagging read-follower / PG).
SCOPE_SESSIONS_TABLE
The §4.1 occupancy table (RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md): one row per (scope, session), upserted by the api-server on every labeled lease mint/renewal and aged out lazily by expires_at (mark/refresh/age-out — lease expiry needs no hook anywhere). The row delta IS the upgrade doorbell: a solo client’s only live connection is its daemon subscription, so the 1→2 wake signal must materialize as a row in the store it is subscribed to — which is why Db::enable_realtime_lifecycle registers this table rather than just creating it.
SOURCE_OFFSETS_TABLE
The durable per-change-source cursor table (CHANGE-SOURCE-DESIGN.md §4). One row per source; the offset upsert is co-transactional with the batch it covers (ClusterConsumer::commit_normalized_with_offset). Daemon bookkeeping — NOT registered for capture (same posture as _rindle_sql_outcomes; the consumer’s own resume position is meaningless on any other host).
SOURCE_OFFSET_WHOLE_RUN
The chunk_seq sentinel meaning “the whole run at this offset is durably applied” — the common case (every pure-row run and every run-boundary commit). A genuine value < this is a mid-run checkpoint (offset, chunk_seq) left by the commit-at-DDL-boundary follower (RELAY-DDL-DESIGN.md §6.6): chunks 0..=chunk_seq of offset are applied, the tail is not. The resume/dedup compare is the keyset (offset, chunk_seq), with the incoming begin(R) treated as (R, WHOLE_RUN) — so a whole run sorts at/above any of its mid-run positions. i64::MAX is safe as a sentinel: chunk_seq is a 0-based within-run ordinal (one per spilled ≤CHUNK_ROWS chunk), so a real value reaching i64::MAX is physically impossible. Mirrors the relay fan-out’s ScanPos “past the end of this run’s chunks” sentinel (rindle-replicator).
SQL_BIND_LIMIT
SQL_BIND_VALUE_BYTE_LIMIT
SQL_RESULT_BYTE_LIMIT
SQL_RESULT_ROW_LIMIT
SQL_TEXT_LIMIT
Named v1 request limits. They are deliberately conservative relative to SQLite’s own limits; exceeding one is a request/result error, never silent truncation.

Traits§

DrainSink
The sink the drain delivers finished output to. Implemented by the napi layer over a ThreadsafeFunction (→ JS onEvent) and by tests over a collector. Called on the drain thread, so keep each call cheap (marshal + hand off).
MutationSql
The SQL flavor of the design’s MutationTx (§4.2) — the abstract write handle a server mutator runs against. Statements execute inside the mutation’s own open write transaction, and query reads through the same connection, so a mutator sees its own uncommitted writes and the effects of every lower-mid mutation — exactly what read-dependent mutators need (§4.1).

Functions§

agg_table_name
The synthetic base-table NAME for a relationship count aggregate (§3.1): a content hash of the aggregate’s definition — child table, kind, the group key (correlation child fields), and the child where filter — so two queries with the same definition share one table (cross-query refcount on the client) while a different filter gets a different table (no (table, pk) collision). The parent correlation field is excluded: the count for a given group key is the same whichever parent joins it.
agg_table_schemas
Collect a synthetic AggTable for every relationship count aggregate in ast, recursively (a nested aggregate under a materialized related is included). The caller (the replica consumer / the publisher’s schema list) advertises these alongside the real base-table schemas so the client can register + validate them.
analyze_standalone
Run one analyze query COLD on the calling thread over a private connection to spec.path, touching no engine, no worker, and no live statement cache.
build_mutation_sql
Build the SQL + bound params for one positional mutation against a table’s columns + PK column indices (the controlled writer is the same SQLite shape on every path).
build_query_parts
The query-wide pieces a normalized subscription needs, derived once from the AST + the surfaced-table schemas: the NormalizeFold (the shared footprint serializer over the pipeline’s full rows), the projected wire table set and its normalized_fp, and the ProjMap that drives project-at-emit. Splitting this out lets a shared engine query own ONE fold + projection while each subscriber builds its own NormalizedHello from the same projected table set (hello_from_parts) — fold, projection, and hello agree by construction.
canonical_migration_checksum
Lowercase SHA-256 of the compact JSON encoding of a normalized statement vector.
cas_cell_matches
The CAS cell comparator: [values_identical] (total, null == null, one number domain), widened over SQLite’s boolean storage — a wire true matches a stored 1 (BOOLEAN columns round-trip through INTEGER affinity).
classify_statement
Classify by the first executable top-level keyword, after stripping comments and accounting for CTE bodies. sqlite3_stmt_readonly remains an execution-time cross-check.
create_table_ddl
CREATE TABLE IF NOT EXISTS for a typed column spec + PK column indices. Shared by the single-thread and cluster-backed cores so their schemas are identical.
decode_wire_value
Decode one v1 SQL wire value. Integers use a tagged decimal string; finite REALs use JSON numbers and infinities use the tagged float form. There is deliberately no BLOB tag in v1.
discover_table_schema
Discover a table’s columns + primary key via pragma_table_info.
encode_wire_value
ensure_unique_pk_index
Ensure the table has a UNIQUE index covering exactly the PK columns, which TableSource requires for row-identity point lookups. Idempotent.
extract_family
A parameterized query family’s identity and template (design 310 §3), re-exported from rindle-wire so a daemon groups subscriptions with the same types the engine binds on. Extract ast’s family, if it can join one (module docs). Ok(None) ⇒ the query cannot join any family and falls back to its QueryKey — today’s path, byte for byte. Err only on the (practically unreachable) serialization failure QueryKey shares.
foreign_key_check
Walk every declared foreign key and report violating rows — the opt-in audit.
hello_from_parts
The per-subscriber handshake for a (possibly shared) normalized query at epoch, from the query’s deterministic table set + normalized_fp (see build_query_parts).
install_public_authorizer
Install the reserved-object authorizer on conn for the lifetime of the returned guard. The authorizer denies any statement that touches a reserved _rindle_* / hct_* / sqlite_* object (and any connection-local action — ATTACH/PRAGMA/DETACH), matching the fail-closed rule the public /v1/sql/* execute path already enforces. Exposed so callers outside this module (the mutation facade’s raw statement executor) can bracket a bare conn.execute with the same guard the run_statement path uses. class is the statement class being run.
introspect_replicated_table
Introspect and validate one persistent application table.
introspect_schema_envelope
Introspect every public application table and validate the complete replicated SQL schema envelope. Internal bookkeeping objects remain excluded.
is_internal_schema_name
Internal bookkeeping objects are not part of the public SQL schema envelope.
normalize_migration_statements
Normalize migration carrier slots by splitting each one and flattening retained statement bytes in order. Delimiters and trimmed outer whitespace are not identity; comments and whitespace retained inside each statement remain byte-significant. A slot-less migration is a valid version-only journal entry; a supplied slot must still contain a statement.
normalized_fp
Fingerprint a tables set by name (resolving PK to column names), independent of internal column numbering. tables must satisfy validate_normalized_schema; the publisher constructors guarantee that invariant, and NormalizedSubscriber::open validates received schemas before calling this function.
open_journal
The journal-aware connection-opening ritual (journal negotiation + the hardening/ planner pragmas + regexp) moved to the apply plane (design 309): the headless apply store and every engine connection here must open IDENTICALLY, so there is one implementation. Re-exported at the historical paths — crate::parallel::open_journal is the embedded-engine seam’s public opener (design 308). Open one read-write connection (mirrors Db::open’s per-connection setup). Used for the apply store’s writer, the cluster coordinator’s writer, and each IVM worker.
restore_bootstrap
Restore a follower’s data file from a self-describing snapshot — the §10.4 bootstrap path, taken by a brand-new follower OR one that fell off the master’s retention window (cursor-too-old, CHANGE-SOURCE-DESIGN.md §10.2). The snapshot is an ordinary, consistent SQLite follower snapshot (including a canonical portable base materialized by rindle-backup), so restoring is:
restore_bootstrap_with_journal
restore_bootstrap with an explicit target JournalMode. The snapshot arrives in rollback (delete) mode; the hop below passes through delete before requesting the target mode (wal2 cannot be entered from wal directly — §10.3 — and the symmetric hop is harmless for wal), then asserts the file actually landed in it.
rewrite_aggregates
Rewrite an AST for a client engine reading synced aggregate tables — the Rust twin of TypeScript rewriteAggregates (packages/normalized/src/agg-table.ts), byte-for-byte in its choice of table name because both call agg_table_name.
rewrite_aggregates_with_local
rewrite_aggregates, with the L1 local-table carve-out (201-LOCAL-ONLY-TABLES-DESIGN.md §5.2): a count over a table is_local accepts is a native IVM reduce with no server-authoritative __agg_* base, so it is left alone — rewriting it would point the relationship at a synthetic table nothing ever feeds.
run_ddl_statement
Execute a DDL-class statement after the host has established the migration transaction and quiesced other writers. Keeping this entry point explicit prevents ordinary transaction callers from accidentally admitting DDL.
run_statement
Prepare through rusqlite’s per-connection cache, bind either argument form, step through every row (including DML RETURNING), and materialize the common result shape.
set_foreign_keys
Put conn in posture and verify it took.
split_migration_file
Split one migration-file carrier, treating an exact Drizzle breakpoint physical line as an explicit boundary before passing every carrier through the shared SQL scanner. The breakpoint comment itself is not canonical migration content.
split_sql_script
Split an ordered autocommit script at top-level semicolons. Delimiters inside quoted values, identifiers, and comments are preserved. Empty/comment-only segments are ignored.
split_sql_script_allow_empty
The migration-file variant of split_sql_script. It uses the same quote/comment/trigger aware scanner but permits a comment-only carrier slot so a whole file can be flattened without inventing a second SQL lexer.
sql_cell_to_owned
One result cell from its raw SQLite storage class — the storage-class twin of decode_wire_value’s JSON one. No engine coercion (a caller here is writing SQL, not feeding the pipeline); BLOB has no OwnedValue form. Shared by the single-thread writer, the parallel Cluster mutator-read path and ReadConn — and re-exported (sql_cell_to_owned) for the replicator write-master’s session reads, which MUST convert cells identically or the two masters’ /mutate-session/query replies drift (REPLICATOR-INTERACTIVE-TXN-DESIGN.md §7).
statement_is_insert
Whether the statement action can update SQLite’s connection-local last-insert-rowid counter. The host still checks the target table’s WITHOUT ROWID metadata before reporting it.
table_tree
Derive the TableNode tree for ast. A pure function of the AST — it needs only table names (all present inline: the root ast.table, each subquery’s table), never the source schemas, so no resolve closure. Mirrors view_schema’s slot walk exactly (normalize the frame, take [query_local_slot_names], resolve each slot name to its subquery, recurse) but carries the table where the schema carries the alias.
unsafe_int_in_normalized_batch
The first Int in this batch’s rows outside Number.MAX_SAFE_INTEGER, if any — the 09.8 strict_i64 walk for the normalized wire (design 226 Stage A). A JS-facing boundary with strict mode on refuses the batch with a typed error instead of letting the cell encode round it (wire_json’s Int → f64 collapse).
validate_schema_envelope
Validate the complete replicated SQL schema envelope without retaining its introspection result.
value_type_of
Map a declared SQLite column type to the engine’s [ValueType], following SQLite type-affinity rules (with BOOLEAN/JSON special-cased, and the exact BIGINT/INT8 declarations mapped to the exact-i64 plane — design 226 §4.1). Returns None for BLOB and untyped columns — the engine’s OwnedValue has no Blob variant, so we reject rather than silently null them.

Type Aliases§

Binding
A parameterized query family’s identity and template (design 310 §3), re-exported from rindle-wire so a daemon groups subscriptions with the same types the engine binds on. A family member’s parameter tuple: the canonical literal of each holed conjunct, in hole order. A [CanonKey] (impl plan D1) — the same type the engine’s partition membership test and the daemon’s per-partition demux key by, so the three agree by construction. Send + Sync (the payloads are Arc<str>), so it crosses worker command channels.
ConnId
A consumer-assigned connection identifier (one per subscriber/WebSocket). The drain groups queries by connection for the cv_min/poke computation; it never interprets it.
PkMap
Table → primary-key column indices (into that table’s row), supplied at construction.
ProjMap
Project-at-emit map (PROJECTION-SUPPORT-DESIGN.md §5.2): table name → the base column indices this query syncs for it, in ascending (= projected wire) order. Only projected tables appear; a table absent here syncs full. An empty map ⇒ no projection, so emission is a pass-through (a '*' query is byte-identical, §7).
ServerMutator
A server-side mutator: authoritative, run once, against the open write transaction. May read its own transaction’s uncommitted writes via MutationSql::query (read- dependent mutators see the current base, §4.1). An Err rejects the mutation.
SubId
A subscription id — one per attached subscriber (the routing identity the DrainSink delivers on, surfaced as a QueryId in the sink callbacks). Distinct from the engine query id: many subscribers can share one engine query (dedup), each with its own epoch / seq cursor / connection route.