Expand description
Postgres-free core of the pg_improv extension.
The extension shell reads rows over SPI, hands them to this crate as plain Rust values, and runs the SQL this crate returns. Everything decidable without a backend lives here:
- identifier validation and quoting (
validate_name,quote_ident); - catalog rows → [
Model] (build_model) and new derived measures (define); - bound fact-table rows → input cells with stable item minting
(
load_inputs), and unboundimprov.cellrows (load_cells); - engine results → upsert/delete statements (
result_rows,diff_statements).
The catalog encoding is shared with improv_storage_pg (the GUI’s store):
measure.formula is Target = expr text (JSON Formula is also read),
measure.value_type is number | boolean | text | datetime | enum, a
measure’s categories keep their stored order, and cell.coord is
{"<category id>": <item id>}.
Modules§
- live
- G1: map decoded
pgoutputrow changes onto live-engine edits. - pgoutput
- A pure decoder for the
pgoutputlogical-replication protocol, version 1 (the binary messagespg_logical_slot_peek_binary_changesreturns withproto_version '1'), andStream, which groups the decoded changes by source transaction.
Structs§
- Bound
Rows - The rows of one bound input measure, as read from its fact table: one
Option<String>per dimension (in the measure’s category order) and the value as the measure’s type reads it (None= SQL NULL). - Catalog
- One model’s catalog rows.
- Category
Row improv.categoryrow.- ItemRow
improv.itemrow.- Lambda
Row improv.lambdarow: a named LAMBDA.- Loaded
- What
load_inputsdid. - Measure
Row improv.measurerow.- Plan
- What a change to some input measures must recompute.
- Scope
- The coordinates a statement touched, over one relation’s dimension categories.
- Statement
- One SQL statement with column-major array parameters:
$1..$nare thetext[]dimension columns ofStatement::dims, then (upserts only)Statement::values.
Enums§
- Defined
- What
define_measurefound to do. - Error
- Everything that can go wrong in the pure core.
- Values
- An upsert’s value parameter:
float8[]for numbers, elsetext[]the SQL casts to the column type.
Constants§
- MAX_
NAME_ LEN - Longest accepted model/category/measure name. Postgres truncates
identifiers at 63 bytes and the result schema is
improv_<model>.
Functions§
- agg_
call - The SQL aggregate call of
binding.aggaggover the (quoted) value expressionv:sum(v), …,count(DISTINCT v). - agg_sql
- The canonical name of a
binding.agg(sum | avg | min | max | count | count_distinct); an error for anything else. - apply_
edits - Apply what-if
editstomodel’s input cells in memory and return the edited measures.editsis a JSON array of{"measure": name, "coord": {category: item}, "value": v | null}:va number, a bool, a string, or a datetime as an RFC 3339 string, by the measure’s type (measure and category names case-insensitive;nullremoves the cell). - bound_
value_ type - The value type of an input measure bound to a column of Postgres type
typname(pg_type.typname;shownis itsformat_type, for errors), read throughagg. - build_
model - Build a [
Model] (structure only, no input cells) from catalog rows. Every measure and named LAMBDA is added before any formula is parsed, so a text formula may reference any measure, whatever its id (a redefinition may read a measure defined after it), and call any lambda. - cell_
changes - The cells of
newthat differ fromold(Some), and the cells ofoldthatnewlacks (None), in key order. - check_
cell_ budget - Refuse a model whose input exceeds the
improv.max_cellsbudget. - check_
value_ column - Refuse a result table whose
valuecolumn (of typefound, asformat_typenames it) does not holdvt. - coord_
json - A result key (item names in
measure’s category order) as{"<category>": "<item>"}. - create_
table_ sql CREATE TABLE IF NOT EXISTSfor a derived measure’s result table (a measure defined through the GUI gets its table on the first recalc). A dimensionless measure’s table is justvalue, holding at most one row.- define
- Validate a
Target = exprdefinition againstmodeland return the catalog row for the new derived measureid. Its categories are the engine-inferred result dimensions (anOVERaggregation drops the aggregated category), which is exactly the result table’s shape. An existing target is an error (define_measurecan replace it). - define_
lambda - A
Name = LAMBDA(…)definition formodel:Nonewhentextdefines a measure, else a new named LAMBDA (Defined::Lambda), or withreplaceDefined::Unchangedwhen it has this definition already, else its redefinition, with the measures that call it to recompute. Its body may reduce over a category parameter (LAMBDA(x, c, MAX(x OVER c))). A name a measure has is an error, and so is a redefinition that would call a lambda calling it, or break or change the type or dimensions of a measure that calls it (or depends on one that does): each error names the dependent. - define_
measure define, or withreplacea redefinition of an existing derived measure:Defined::Unchangedwhen it parses to the formula it has, elseDefined::Replaced. A replacement is refused when its formula would read a measure that depends on the target (a cycle), when it changes the target’s value type while another measure reads it, and when it breaks or reshapes a dependent; each error names the dependent.- dependents
- The derived measures to recompute when
rootschange: the derived roots themselves and every derived measure that transitively reads a root, sorted by id. - diff_
statements - The statements that turn
old(the table’s rows) intonew(the engine’s rows) of avtmeasure: at most one batched upsert of new/changed rows and one batched delete of vanished rows. Unchanged rows (bit-identical value) are skipped; a value that is not avtis an error. With no dimension columns (a dimensionless measure’s one-row table) the upsert replaces the table’s row and the delete empties it. - dim_
columns - The lower-cased result-column names of a measure’s dimensions.
- drop_
lambda - The named LAMBDA called
name(any case) when nothing calls it,Nonewhen there is none: dropping a lambda a measure or another lambda still calls is an error naming them. - drop_
target - The measure called
name(any case), when no other measure reads it: dropping a measure a dependent still references is an error naming it. - load_
cells - Add unbound input cells (
improv.cellrows: measure id,coordJSON{"<category id>": <item id>},valueJSON scalar). Returns the count. - load_
inputs - Turn bound fact rows into input cells. Existing items keep their ids; new distinct values are minted above the model’s highest item id, in lexical order per category, so the same data always mints the same ids. A NULL value is no cell; a NULL dimension skips (and counts) the row; two rows on one coordinate are an error.
- measure_
ids - Measure ids by name (case-insensitive), in order.
- plan
- Plan the recompute after a statement changed
changedinput measures attouchedcoordinates: theirdependents, restricted totouchedwhen every measure those read is element-wise (an output cell then depends only on its inputs’ cells at its own coordinate, projected or broadcast), else in full. Nothing touched, nothing to do. - quote_
ident - Quote an identifier exactly like Postgres’
quote_identdoes when quoting is needed (always quoting is equivalent and simpler). - quote_
literal - A SQL string literal.
- referencing
- The other derived measures whose formula reads
iddirectly, by name, sorted. - result_
rows - Name the engine’s cells for
measurein its dimension order. Cells that are not of the measure’s type (errors) are left out. - result_
schema - The result schema of a model:
improv_<lower(model)>. - result_
table - The quoted, schema-qualified result table of a derived measure.
- scopable
- Whether a change to
changedcan only change cells at the touched coordinates: every measure theirdependentsread is element-wise. Then the inputs outside the touched coordinates’ projections need not even be loaded (scope_filter). - scope_
filter - For a binding whose measure ranges over
cats, read from dimension columnscols(same order), restricted to the touched rowsrows(dimension values inscope_catsorder, asScope::from_rowstakes them): the columns of the touched categories it has, and per column the touched values, row-aligned, each tuple once and none with a NULL.Nonewhen it has none of the touched categories, so every row is in scope. - scoped_
rel_ sql - Relation
rel(already quoted) restricted to the rows whosecols, as text, are one of the tuples passed as$1::text[],$2::text[], … (one array per column, row-aligned): a subquery to read a binding through (select_bound_sql‘srel) so an aggregated binding loads only the touched coordinates’ groups. - select_
bound_ sql SELECTof a binding’s fact rows: dims as text…, then the value read asvt(float8,bool,text, or a datetime asint8epoch milliseconds). Withagg, one row per coordinate, its rows’ values aggregated (abool’sminandmaxasbool_andandbool_or: SQL has nomax(bool)).relis aregclass::text, which Postgres has already quoted.- select_
result_ sql SELECTof a result table’s current rows (dims…, value read asvt, seeselect_bound_sql).- slot_
name - The logical replication slot (and publication) syncing
model:improv_<lower(model)>, within Postgres’ 63-byte name limit. - sql_
type - A result table’s
valuecolumn type forvt(asformat_typenames it). - sync_
trigger_ name - The sync trigger for
op(ins/upd/del):improv_<lower(model)>_sync_<op>, which must fit an identifier. - touched_
sql SELECTof the distinct dimension values (as text) in a statement’s transition tables, given the names of those present;Nonewhen there is nothing to read.- unknown_
items - The
(category, name)pairsbounduses thatmodelhas no item for, sorted. A persisting caller mints them throughimprov.mint_itemsand adds the returned items beforeload_inputs, so it mints nothing. - validate_
name - Accept
^[A-Za-z][A-Za-z0-9_]*$, at mostMAX_NAME_LENchars, and notvalue(any case: it is the result tables’ value column). - value_
json - A result value as JSON: a number (
"NaN","Infinity"or"-Infinity"when not finite, asfloat8prints them), a bool, a string, or a datetime as an RFC 3339 string. An error isnull. - value_
type_ name - The catalog spelling of a value type.
Type Aliases§
- Result
- Result alias for this crate.
- Result
Rows - A result table’s contents: item names in dimension order → value.