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
docs/23§23.9’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. §23.19 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, and it does not become one by having a join. What parse accepts
is a documented subset — a scan with a where, an order by and a limit; an equi-join, inner
or left; a group by with count, min, max and sum; distinct; and union — with the
from list fixing a left-deep join order rather than a cost model choosing one. It exists so
that an outside tool can read what the program holds, which is what §5.3’s row is for.
It does have a scalar expression language (Expr) — case, a call, a cast, in, is,
||, and the four POSIX regular-expression operators — and that is a debt pg_catalog called
in rather than a change of mind: psql writes all of them into the queries it sends the
catalogue (crate::pg), and a catalogue that is a read model is read by this SQL or by
nothing. It computes no arithmetic, because nothing asks for any. A subquery is parsed and
answered only where it is provably row-independent (see Eval::subquery); a correlated
one is refused by name, as is having.
What the relational half is made of is not here either. A join, a group by and a
distinct are compiled into a crate::plan::Plan by crate::query and run by
crate::engine, so the operators answering them are crate::plan::Op::Join,
crate::plan::Op::ArrangeBy, crate::plan::Op::GroupBy and
crate::plan::Op::Distinct — the ones a program’s own view compiles to
(docs/99 §99.9 item 9). This
module keeps the parser, the schema, the scan and the row-level where, order by and
limit that are the same either way.
Structs§
- Answer
- What a query answered.
- Column
- Eval
- What an
Expris evaluated against: the columns a row has, and the reader behind them. - Field
- One column of the rows a query produced, and the table name a reference may qualify it with.
- From
- One entry of the
fromlist: a table, what this query calls it, and what joins it. - Name
- A column reference, qualified by a table’s name in this query or not.
- Order
- One
order bykey. - 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
- Expr
- A scalar expression, evaluated over one row.
- Item
- OrderBy
- What an
order bykey names. - 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.
- cell_of
- One Beck value in one column, or NULL.
- elements
- The elements of a collection value, in the order it holds them.
- names_
of - The columns a query has, as a person is told about 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.
- resolve_
field - A column reference, as an index into the row a query produced.
- version
- What
select version()answers.
Type Aliases§
- Cell
- A value in one column, or SQL NULL.