Rindle docs and package mapSkip to main content

Module graph

Module graph 

Source
Expand description

Primitive #1: the operator graph as an arena + NodeId, NOT Rc<RefCell<dyn Operator>>.

Why this is the make-or-break decision (verified against join.ts): #pushChildChange sets in-progress state then re-enters parent.fetch() while a push is in flight, and self-joins put the same source in the graph twice. The “obvious” Rc<RefCell<dyn Operator>> translation would hit a runtime BorrowMutError on exactly that pattern. The arena fixes it: the whole pipeline is one Vec<Operator> addressed by NodeId, every fetch/push takes &self (a shared borrow of the graph), and per-operator mutable state lives behind Cell/RefCell scoped to a single statement — never held across a vend. Reentrant fetch-during-push is then just another shared borrow. No cycles-of-owners, no borrow conflict.

The leaf MemorySource (the connection/read/COW machinery) lives in memory_source.rs; this module hosts the operator graph that wires it up (SourceConn, Join, View, Collector) and drives the eager push fan-out (Graph::source_push), which is inherently graph-level (it must reach downstream operators by NodeId).

Structs§

ConnId
A connection handle into a source’s connection table. Generational {idx, gen} (like NodeId/StorageId) so a source can RECYCLE a torn-down connection’s slot — the ConnTable bumps the generation on free and checks it on every access, so a stale handle to a recycled slot fail-fasts. Distinct from NodeId. Same role as 05’s ConnId.
CountingSource
A Source decorator that counts the rows crossing the connection boundary — the one new measurement the analyze query diagnostic needs (designs-implemented/ANALYZE-QUERY-DESIGN.md §3.4). It wraps any object-safe Source and delegates every method to inner unchanged, except fetch, whose returned RowFlow is wrapped in a lazy .inspect that bumps emitted once per row as the row is pulled. Because it counts inner.fetch()’s final stream — after the backend’s overlay/start/constraint/filter chain and across both the ordered and unordered fetch paths — it sees exactly what the pipeline sees, on either backend (SQLite or memory), and a downstream Take that abandons the stream early counts only what it consumed (the correct “emitted into the pipeline” semantics).
Graph
JoinPrecheckBounds
The two bounds of the join membership pre-check (design 311 §2.5), host-settable via Graph::set_join_precheck_bounds.
JoinPrecheckStats
Per-graph counters for the join membership pre-check (design 311 §8) — the always-on, per-Graph twin of the build-gated process-global EngineMetrics counters (join_probe_hit / join_probe_miss / join_precheck_disabled_*). Plain Cell adds on a !Send graph, so they cost nothing measurable.
NodeId
A handle to an operator slot in the Graph arena. idx is the slot index; gen is the slot’s generation, bumped every time the slot is freed (a pipeline teardown — Graph::destroy_pipeline). The generation is what makes slot REUSE safe: a stale handle to a torn-down pipeline carries the old gen, so it fails the generation check in Graph::node instead of silently aliasing the new tenant of a recycled slot. Distinct from physical removal (which would shift indices and is impossible here) — the slot keeps its index across reuse.
PipelineManifest
The exact arena ids one build_pipeline (+ its sink) created — captured by the graph’s recording mode (Graph::begin_recording/Graph::take_recording) so the pipeline can be torn down precisely with Graph::destroy_pipeline.
StorageId
Index into the graph’s parallel storage arena (foundations §6.4). A stateful operator (Take/Cap/Exists, spec 07) is handed one of these at build time and reads/writes its scratch state via Graph::storage. The operator never owns its store: the indirection is what lets the client keep it in RAM (MemoryStorage) while the server later spills it to SQLite (10 §1.1) without touching operator code. Distinct newtype from NodeId so the two index spaces can’t be confused. Generational for the same reason (storage slots are freed + reused on teardown).

Enums§

CollectedChange
A change recorded by a Collector — the spike’s analogue of Catch’s pushes. Lets a test assert the exact change sequence a source fans out. Equality compares rows cell-by-cell via compare_values (null == null) — OwnedValue deliberately has no derived PartialEq (it would invite the wrong comparator), so we spell the row equality out.
JoinPrecheckState
A read-only snapshot of one join’s parent-key set (op::join_util::ParentKeySet) for inspection and tests (Graph::join_precheck_state).

Traits§

Source
The connection + read contract a leaf backend owns locally (04 §4.7 / 05 §4.8). MemorySource implements it; the SQLite TableSource implements the same trait, so connect/fetch/schema/destroy — and everything downstream of the NodeStream they vend — are backend-identical.

Type Aliases§

EmitCounter
A shared per-source counter of rows a source emitted into the pipeline. One cell per wrapped source node (so the count is already keyed per NodeId); a child-driven join that re-fetches the same leaf under many constraints accumulates across those fetches into the same cell. Rc<Cell<..>> (not atomic) — a Graph is !Send, single-threaded by construction.