§12 Database Access
Status: finalized (v0.8, 2026-09-22) — written back on the landing of the server roadmap's P5 wave (the std/db data-access layer) (2026-09-22; for the execution rulings, see the execution record in internal design documents in the repository). The original draft (v0.8-d1, 2026-09-20) descends from server roadmap design v3. Capability/purity semantics belong to §8; cancellation semantics to §7.2 and §11.4. This chapter defines the driver contract, the connection pool, row mapping (the as-built runtime surface), transactions, and cancellation. Normative conventions follow the spec README. Implemented surface:
std/db/{db,pg,redis,pool,rowmap}.ct+tests/db/(replay with zero real databases; real targets nightly; the performance gate tests/db/perf, with a registered skip policy as measured).
12.1 The db.connect Capability Key (Normative)
- All database access goes through capability objects;
[caps]declares thedb.connectupper bound, and exceeding it = E4010. Connections are passed in as a DSN via capability-object parameters; programs must not establish connections from arbitrary strings on their own. - A
#[pure]function touching the database = E4020 (rejected at compile time). - Derived surface: transactions/prepared statements derived from an already-authorized connection inherit the authorization and do not consume the key again.
12.2 Driver Contract (Normative: Pure-Ctron Wire Protocol)
- Drivers MUST be pure-Ctron wire-protocol implementations — this is the basis for the auditability of the capability surface (C-binding drivers bypass language-level auditing and trigger W-level lints):
- PostgreSQL wire protocol v3: startup, simple query, extended query (Prepared Statement), SCRAM-SHA-256 authentication (cryptographic primitives from
std/crypto: SHA-256/HMAC/PBKDF2, pure Ctron, anchored on the RFC 6234/4231/6070 vectors; omitting SASLprep password normalization = a registered v0 policy, passwords taken as raw bytes, which follows RFC 5802 §5.1's recommendation; rejecting plus-only channel binding = a clean Err); - Redis RESP2: the GET/SET/DEL/EXPIRE/INCR fundamentals; pub/sub subscriptions are listed in the ecosystem tier.
- MySQL/Mongo and the like are outside the v1 contract; SQLite (the C-binding exception path) is listed as an aspiration tier and requires an explicit
#[trusted]audit surface when it lands.
12.3 Connection Pool and Backpressure (Normative)
- The pool is bounded (capacity required, same discipline as Channel); pool waits express backpressure through a bounded channel, and waits are cancellable (§12.4).
- Reset before returning to the pool (MUST): active transactions rolled back, session variables reset, prepared statements cleaned up per policy — reusing dirty connections across cancellations or across errors is forbidden.
12.4 Transactions and Cancellation Propagation (Normative)
begin / commit / rollbackas the explicit surface; queries respond to scope cancellation at suspension points (§7.2/§11.4): cancellation → the connection is rolled back, reset, and returned to the pool, never leaking cancellation semantics into a half-way commit.- Nested transactions are not supported (savepoints listed as an aspiration tier); errors propagate explicitly along
Result, without panicking.
12.5 Row Mapping (Normative; as-built = runtime binding, D-P5-1 deviation written back)
- Query-result row → struct binding: expanded at runtime (P5-wave as-built;
std/db/rowmap.ctOID table / column-name matching / type-family gates; missing column / type mismatch / NULL misfit / row out of bounds / value out of domain = reported as a runtimeResultErr, kinds noent/type/null/row-oob/val-domain). The original draft's "comptime expansion, reported at compile time" is unreachable under §8.4's reflection ban (type-producing functions reserved for v2) — this clause is the finalized write-back of design deviation D-P5-1; the deviation registration and exit ruling are in the P5 plan execution record. Negative anchorr7d_db_rowmap(the anchor's semantics accordingly changed from "reported at compile time" to "runtime Err"). @derive(DbRow)(an extension of the compiler-known derive set, generating binding code at compile time) = an aspiration tier, listed in the compilation swimlane; the OID/column-name matching surface of this clause is its runtime fallback position, and once it lands the runtime surface is demoted to a hand-written fallback path.- SQL text is not statically validated (no live-DB dependency; static SQL checking is listed as an aspiration tier).
- NULL →
Option[T]explicit mapping, no implicit zero values (the rowmap NULL Err surface + the caller-side wrapper mapping Option in a two-stage form).
12.6 Interface with std/json (Declared Prerequisite)
- The request body → struct → row-binding chain takes std/json numeric/boolean type fidelity as a declared prerequisite work item (as determined in the 2026-09-20 v3 review: the current json value model is a flattened string table and does not satisfy numeric fidelity); streaming/NDJSON parsing is listed in the ecosystem tier.
12.7 Correspondence with the Test Suite
- Protocol fixture replay (as-built: byte scripts constructed by hand against the public protocols, with each README registering its own recording policy; zero real-database dependency in CI); real databases (Postgres/Redis) are nightly targets only (
tests/db/nightly/, env-driven, with a registered SKIP policy when no target exists). tests/db/dual arms (interpreter + emit; 40 fixture-script surfaces + corpus + the x_ fd-true-source emit-only arm); decoder fuzzing and differential testing (underpinned by libpq/official resp implementations, referenced by the test line only) are listed for later; performance gate: the simple-query round trip vs libpq, isomorphic, ≤1.5× (tests/db/perf/, min-of-3; no libpq development surface on this machine = the registered SKIP policy, measured 2026-09-22).