Expand description
The read model: a program’s maintained state as relations, and a small SQL over them.
docs/05-tier-lowering.md §5.3 names this as one of
the four things the data tier owes:
Read models … one-shot queries and pgwire access for the outside world:
psql, BI tools, DBeaver see materialized views as ordinary tables — the single cheapest trust-builder for adopting teams
§What a read model is here, and what it is not
§5.3’s row also says “generated tables in the same Postgres”, and that is not what this
builds. A read model is not a second copy of the state written on the append path; it is the
collection the fold already holds and the arrangement crate::engine already maintains,
projected. Three consequences, and they are the argument for it:
- A read model costs nothing per event. Nothing is written, nothing is projected, and the
sequencer is untouched — which is
26§26.2’s rule (“who advances it: not the sequencer”) applied to a second kind of reader rather than argued with. - It cannot disagree with the page. A durable projection is a second code path, and a second code path over the same events is a thing that can drift. These rows are read from the same arrangement the view renders from, so the recompute oracle already covers them.
- It is exactly as fresh as the query. A query advances the dataflow to the log’s head and
then reads, so a
SELECTissued after an ack sees that ack’s event. There is no projection lag because there is no projection.
What that costs is the one-transaction property 67
§67.1 held open: an append and its projection are still not one transaction, because there is
still no projection. §88.6 is the row-by-row list.
§Where the tables come from
| Table | Rows | Read from |
|---|---|---|
| a collection-valued field of the accumulator | its elements | the state value |
| the accumulator’s remaining scalar fields | one | the state value |
| a declared signal that does not read the session | its elements, or one | the maintained node |
The third row is the interesting one: a read model is a view that does not depend on who is
asking, which is the same cut §5.3 draws for arrangement sharing. A per_session signal is not
a table because a SQL client is not a session — it has no Session to be rendered for, and
inventing one would answer a question nobody asked.
§What this is not
It is not a query planner. 04 §4.2 keeps the
Query sub-language symbolic, and §20.5 holds beck explain query until an engine compiles one;
what parse accepts is a hand-written subset over one table at a time, with no joins, no
subqueries and no aggregation beyond count(*). It exists so that an outside tool can read what
the program holds, which is what §5.3’s row is for.
Structs§
- Answer
- What a query answered.
- Column
- Cond
- Schema
- Every table a program’s read model has.
- Select
- What a query asks for.
- SqlError
- Why a query could not be answered. The message reaches the client verbatim.
- Table
Enums§
- Cardinality
- How many rows a table can have, which is a fact about its shape rather than about its data.
- CmpOp
- Datum
- Item
- Source
- Where a table’s rows are read from.
- SqlTy
- The four SQL types a Beck scalar maps onto.
- Stmt
- A statement, which is a
selector one of the two things a client says before it asks for anything.
Traits§
- Rows
- Where a table’s rows come from. Implemented by whoever holds the running program.
Functions§
- at_path
- Follow a path of field names into a value.
- elements
- The elements of a collection value, in the order it holds them.
- parse
- Parse one statement.
- quote_
ident - A name as it has to be written in this SQL: bare when it can be, quoted when it cannot.
- version
- What
select version()answers.
Type Aliases§
- Cell
- A value in one column, or SQL NULL.