Expand description
Phase 2 — the SQLite leaf: a zero-copy lending [RowStream] over a real
rusqlite cursor. This is the hardest backend for the canonical value model
(value.rs) to satisfy, and the reason the model is designed here rather
than in the abstract: a SQLite statement has no row — only a prepared
statement, a step, and column_*(i) accessors whose text/blob pointers are
valid only until the next step/reset. The borrow checker turns a
use-after-step into a compile error, and forces an owned copy at exactly
the points the JS already copies.
What this proves (the spike’s exit criteria for the SQLite side):
- Zero copy on the transient path, including strings.
RowRef::col(i)is arusqliteget_ref— aValueRefborrowing the step buffer — mapped to aValue<'_>with no allocation (Str/Jsonare&[u8]straight into SQLite’s buffer). A filtered/pass-through scan that reads, compares, and drops rows allocates nothing per row (proven by the counting allocator intests/sqlite_zero_copy.rs). to_owned_row()is the one forced per-row copy, at the Node boundary — the single point where a value must outlive its step. Same trait method the memory backend implements as anArcbump (btree.rs); here it copies.- Step fallibility resolved (handoff decision 1 /
05OQ-9):next_rowstays infallible (uniform with the never-erroring memory backend); asqlite3_steperror is parked on the stream and re-raised at the first owning boundary viaSqliteRowStream::take_error— NEVER mapped to silent end-of-stream. - RAII statement cleanup (Primitive #2): dropping the stream drops the
Rowscursor (rusqlite resets the statement), so the next write does not hit “database is busy”.StmtGuardmakes that release observable — and is where a real prepared-statement pool returns itsPooledStmt.
Structs§
- Sqlite
Row - One borrowed SQLite row.
col(i)reads columnilazily and zero-copy: aStr/Jsoncell is a&[u8]pointing straight into SQLite’s step buffer (no allocation, no UTF-8 validation — bytewiseBINARYcompare on the hot path; validate at theto_ownedescape). - Sqlite
RowStream - The leaf row stream: a live rusqlite cursor (
Rows) plus the per-column type tags. Implements the foundations lending [RowStream]; eachnext_rowreborrowsself, so the returnedSqliteRowis invalidated by the next call — the SQLite cursor contract, now a compile-time invariant. - Stmt
Guard - RAII cursor accounting (Primitive #2). Mirrors the JS
finally/.return()that resets+returns the prepared statement so the next write doesn’t hit “database is busy”.rusqlite::Rowsalready resets the statement onDrop; this guard makes the release observable (and is where a real pool would return itsPooledStmt). Decrements its counter onDrop— on normal end, earlybreak,?, or early return — for free, nofinallyneeded. This is asqlite-only (native server) path: run the server with therelease-serverprofile (panic = "unwind"), whereDropalso runs on panic; under a plainreleasebuild (panic = "abort") a panic aborts the process and no destructor runs, so panic balance holds only in unwinding builds (release-server,cargo test). See WS02.
Enums§
- ColType
- Per-column type tag, resolved once from the schema at build time
(foundations §4). Makes value conversion ty-directed: an INTEGER storage
class becomes
IntorBooldepending on the column, TEXT becomesStror (unparsed)Json. The hot path never inspects a column name. - Sqlite
Error - A parked error, surfaced at the first owning/dyn boundary. Carries either a
sqlite3_stepfailure or a value-conversion failure; the productionRindleError(foundations §10) wraps the same set.
Functions§
- is_
exact_ f64_ integer - True when widening
itof64and narrowing it back preserves the exact integer. This deliberately accepts sparse, exactly representable integers above 2^53 (such as 2^54) while rejecting adjacent values that would lose precision. - select_
sql - Build
SELECT <c0>, <c1>, … FROM <table>projecting the declared columns inColIdorder. Result-row order ==columnsorder ==ColId, which is what makesRowRef::col(i)an O(1) array index (05§8.1). (The full constraint/start/filterWHERElowering is05§4.4; out of scope here — the spike threads its own predicates.)