Rindle docs and package mapSkip to main content

Db

Struct Db 

Source
pub struct Db { /* private fields */ }
Expand description

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.

Implementations§

Source§

impl Db

Source

pub fn enable_client_mutations(&self) -> Result<(), ReplicaError>

One-time (idempotent) setup for the client-mutations protocol: create CLIENT_MUTATIONS_TABLE and register it like any base table (CDC capture + engine-hosted source), so lmid rides the change stream co-transactionally with the data AND is queryable — each client’s one-row system query is an ordinary registered query over it (§8.2). Rejected while a write transaction is open (DDL would be invisible to the engine’s worker until commit, like register_table).

Source

pub fn enable_realtime_lifecycle(&self) -> Result<(), ReplicaError>

One-time (idempotent) setup for the §4 realtime lifecycle (RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md): create the lifecycle tables and register them — plus ROOM_CLIENT_MUTATIONS_TABLE — like any base table (CDC capture + engine-hosted source), because each is load-bearing as SUBSCRIBABLE data, not just durable state:

  • SCOPE_SESSIONS_TABLE — the occupancy-row delta arriving through a solo client’s daemon subscription IS the upgrade doorbell (§4.1);
  • ROOM_WATERMARK_TABLE — the downgrade fence clears only when the client’s daemon subscription delivers the room’s final flush_seq (§4.2);
  • ROOM_MUTATION_OUTCOMES_TABLE — outcome resolution with no room socket alive (created + registered here; POPULATED by Slice I-ii’s flush split);
  • ROOM_CLIENT_MUTATIONS_TABLE — “after downgrade, the doc-scoped ledger row is ordinary footprint data” (§7.1, load-bearing for §7.5). Slice C created it deliberately direct-SQL-only; the lifecycle is what needs it readable through the daemon subscription plane, so its registration lands here, not in enable_client_mutations.

Requires enable_client_mutations to have run first: that call owns the room ledger’s DDL, and re-issuing a second copy here would be one edit away from two drifting schemas — enforced with a loud error (the require_enabled idiom) instead. Rejected while a write transaction is open, like the precedent.

Source

pub fn client_lmid(&self, client_id: &str) -> Result<u64, ReplicaError>

The high-water mutation id durably recorded for client_id (0 if none — a new client). What a server stamps on a (re)connecting client’s handshake so it can drop already-confirmed pending mutations. Requires enable_client_mutations.

Source

pub fn apply_mutations( &self, registry: &MutatorRegistry, envelopes: &[MutationEnvelope], ) -> Result<MutationOutcome, ReplicaError>

Apply a client’s mutation push: each envelope’s mutator runs in its own transaction (with the co-transactional lmid upsert), in the order given. Per envelope, against its client’s stored lmid:

  • mid ≤ lmid — already processed: skipped (idempotent redelivery).
  • mid == lmid + 1 — applied; on mutator failure (error / panic / unknown name) the effects roll back and lmid still advances in an lmid-only commit (processed-as-no-op — there is no rejection signal).
  • mid > lmid + 1 — a gap (the client must send contiguously, so this should be impossible): the push fails with ReplicaError::Mutation at that envelope (prior envelopes stay applied — they are already durable).

Every committed transaction fires the registered queries’ subscriptions as usual and is reported (with its crate::CommitInfo) in the outcome, so the caller can drive a progress tracker / poke layer. Must be called with no write transaction open (it manages its own).

Source§

impl Db

Source

pub fn open(path: impl AsRef<Path>) -> Result<Db, ReplicaError>

Open a replica over a file-backed SQLite database (derivation needs WAL + multiple connections, which an in-memory DB cannot provide).

A fresh file gets ordinary journal_mode = wal (design 306 D5 — the file stays openable by any stock SQLite build); an existing wal or wal2 file keeps its mode. Asserts sqlite3_threadsafe() != 0, failing loud rather than silently degrading.

Source

pub fn open_with_planning( path: impl AsRef<Path>, plan_queries: bool, ) -> Result<Db, ReplicaError>

Like open, but with explicit control over the cost-based join-flip planner. When plan_queries is true, query runs the planner (a SqliteCostModel over the worker connection, cached) at registration to annotate flip before lowering. Result-preserving; the plan is frozen per registration. The planner is server-side (table-source) only and open enables it by default; pass false here to opt out (e.g. to pin the unplanned path).

Source

pub fn open_with_options( path: impl AsRef<Path>, plan_queries: bool, operator_storage: OperatorStorage, ) -> Result<Db, ReplicaError>

Like open_with_planning, but also selects the operator-scratch-state backend. Pass OperatorStorage::SqliteSpill to spill stateful-operator state (take/cap/reduce) to a private on-disk temp database instead of keeping it in RAM. This reduces operator scratch memory; it does not cap all memory used by the runtime or the application.

Source

pub fn open_with( path: impl AsRef<Path>, opts: OpenOptions, ) -> Result<Db, ReplicaError>

The full-combination opener: every OpenOptions field is honored, so any planner × operator-storage × journal combination is one call — the named rungs above are conveniences over this.

Source

pub fn maintain( &self, opts: &MaintenanceOptions, ) -> Result<MaintenanceReport, ReplicaError>

Run one online maintenance pass on the writer connection — see Cluster::maintain for the full contract. Best-effort and bounded; skipped (reporting MaintenanceReport::skipped()) while a write transaction is open so it never disturbs in-flight work.

Source

pub fn analyze_full(&self) -> Result<(), ReplicaError>

Run a full ANALYZE on the writer to build deep planner statistics (sqlite_stat4) — the single-thread parity for Cluster::analyze_full. Call once after a bulk load so the planner can seek (not scan) an ORDER BY … LIMIT n view’s displacement re-fetch (GitHub #68). Restores the bounded analysis_limit afterward. Rejected while a write transaction is open.

Source

pub fn register_table(&self, table: &str) -> Result<(), ReplicaError>

Register a base table with the engine: discover its columns + primary key from the schema, ensure the PK has the UNIQUE index TableSource requires, and build the shared source. Idempotent per table. Rejects BLOB-typed columns and tables without a primary key.

The table must be plain — SQL triggers and generated columns are rejected (see the crate-level “supported usage” docs). Foreign-key cascades are represented by the explicit row deltas observed by the preupdate hook.

Source

pub fn query(&self, query_id: QueryId, ast: Ast) -> Result<Query, ReplicaError>

Register a live query from a Zero-wire [Ast] (build it with rindle::table(..) or deserialize via Db::query_json) under the caller-supplied QueryId tag (echoed via Query::id; the engine never interprets it — see QueryId). Lowers the AST into the shared engine, hydrates it, and returns a Query handle to subscribe to. BuildError (unknown table/column, unsupported shape) is surfaced synchronously here.

Each call builds its own pipeline — the engine does not de-duplicate, even for an identical query_id/AST. A caller that wants to share one pipeline across requesters dedups at its own layer (it holds the per-requester hydration state the raw change stream does not). Subscribe before any later writes: the handle’s initial hydration is cached at registration, not kept current. Explicitly call Query::destroy to unregister; dropping the handle does not stop the query.

Source

pub fn query_json( &self, query_id: QueryId, json: &str, ) -> Result<Query, ReplicaError>

Convenience: parse a Zero-wire AST from JSON, then Db::query under query_id.

Source

pub fn analyze_query(&self, ast: &Ast) -> Result<AnalyzeReport, ReplicaError>

Analyze ast COLD and report where its time and rows go (ANALYZE-QUERY-DESIGN.md) — the single-thread parity for Cluster::analyze_query. Read-only: it builds and drops a throwaway instrumented pipeline over a pinned read snapshot, registering no materialization and leaving every live query untouched. BuildError (unknown table/column, unsupported shape) surfaces as Err. Rejected while a write transaction is open (its snapshot is not yet committed).

Source

pub fn set_max_delta_bytes(&self, max_bytes: usize)

Tune the derivation memory budget (design 306 D4): the estimated bytes of folded rows one transaction may hold — across every table it touches, not per table — before its derivation gives up. The default is [rindle_sqlite::DEFAULT_MAX_DELTA_BYTES] (256 MiB).

Overflow is a shed, not a failure: the transaction still commits; the engine rebuilds and every registered query re-hydrates from the committed state (subscribers receive a fresh Update::Hydrated — see Update). Lower it to bound derivation memory harder (shedding sooner); raise it to keep huge bulk loads deriving incrementally at the cost of memory. Applies to every registered table, now and later, and survives the shed rebuild itself.

The accounting is an upper-bound estimate of the delta’s own heap — row buffers plus per-index bookkeeping — described at [rindle_sqlite::DeltaBudget]. It is not a process RSS limit: SQLite’s page cache, the query pipelines, and the re-hydration this shed triggers are all outside it.

Source

pub fn write(&self) -> Result<WriteTxn, ReplicaError>

Open the single-writer transaction. Run ordinary SQL through the returned WriteTxn; commit derives + delivers each query’s incremental events, rollback (or drop) leaves every view untouched. Errors if a write txn is already open (there is exactly one writer).

Source

pub fn committed_tx_id(&self) -> TxId

The last durably-committed global tx id (0 if none yet).

Source

pub fn read<T>( &self, f: impl FnOnce(&Connection) -> Result<T>, ) -> Result<T, ReplicaError>

Run SQL against a physically read-only connection using SQLite snapshot semantics. This separate connection cannot see an open WriteTxn’s uncommitted writes. SQLITE_OPEN_READ_ONLY prevents a callback from bypassing capture with writes. Schema changes belong in Self::exec_ddl, and row mutations in Self::write.

Source

pub fn foreign_key_audit( &self, max_rows: usize, ) -> Result<ForeignKeyAudit, ReplicaError>

Walk every declared foreign key and report violating rows — the opt-in audit.

PRAGMA foreign_key_check reports what is in the file, not what SQLite would have refused, so it answers the same question whatever OpenOptions::foreign_keys this replica was opened with. It is a full scan of the referencing tables — a deliberate operation, never a per-commit step. Pass FOREIGN_KEY_AUDIT_ROW_CAP unless you have a reason not to, or 0 for no cap.

Source

pub fn view_schema(&self, ast: &Ast) -> Result<Schema, ReplicaError>

The hierarchical view [rindle::value::Schema] ast materializes to over the registered tables (shape + sort + singular + in-view relationships) — derived identically to query. A layer that ships views to a remote (the flat-change/wire schema + fingerprint) needs this without registering the query. BuildError (unknown table/column) surfaces as Err.

Source

pub fn read_snapshot( &self, query_id: QueryId, ) -> Result<Vec<ChangeEvent>, ReplicaError>

Fetch the registered query through its pipeline as a fresh set of hydration Add events. This traverses current sources; it does not read a cached view assembled from prior Changed events. It does not consume subscriber events. An unregistered query yields an empty snapshot. Read failures return Err. Use distinct query IDs when reading snapshots by ID.

Source

pub fn exec_ddl(&self, sql: &str) -> Result<(), ReplicaError>

Run schema DDL (CREATE TABLE …) against the writer connection in autocommit — the supported way to define the plain base tables you then Self::register_table and write through (crate-level “supported usage”). Rejected while a write transaction is open (DDL there would be invisible to the engine’s separate worker until commit). Accepts schema DDL (CREATE/ALTER/DROP/REINDEX) plus the historical bounded ANALYZE maintenance call; row-changing statements are rejected. The complete batch and schema-envelope validation commit atomically, and row-producing DDL is rejected too.

Trait Implementations§

Source§

impl Clone for Db

Source§

fn clone(&self) -> Db

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl Freeze for Db

§

impl !RefUnwindSafe for Db

§

impl !Send for Db

§

impl !Sync for Db

§

impl Unpin for Db

§

impl UnsafeUnpin for Db

§

impl !UnwindSafe for Db

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,