Expand description
In-memory storage primitives.
v0.3 is intentionally simple: a flat catalog of tables, each holding rows
as Vec<Value> (positional, matching the table’s TableSchema). No MVCC,
no on-disk format — those land in later milestones.
Re-exports§
pub use self::bloom::BloomError;pub use self::bloom::BloomFilter;pub use self::row_locator::RowLocator;pub use self::row_locator::RowLocatorError;pub use self::segment::BRIN_SIDECAR_MAGIC;pub use self::segment::BrinSummary;pub use self::segment::OwnedSegment;pub use self::segment::SEGMENT_COMPRESS_ALGO_LZSS;pub use self::segment::SEGMENT_COMPRESS_ALGO_NONE;pub use self::segment::SEGMENT_MAGIC;pub use self::segment::SEGMENT_MAGIC_V2;pub use self::segment::SEGMENT_PAGE_BYTES;pub use self::segment::SegmentError;pub use self::segment::SegmentMeta;pub use self::segment::SegmentReader;pub use self::segment::derive_brin_summaries;pub use self::segment::encode_segment;pub use self::segment::wrap_v2_envelope;pub use self::segment::wrap_v2_envelope_with_brin;
Modules§
- bignum
- v7.38 (read01, T3) — arbitrary-precision decimal for NUMERIC values that
overflow
i128(PG’s NUMERIC is unbounded; SPG’si128fast path tops out near 38 digits). Clean-room schoolbook arithmetic on base-10^9 limbs (each limb holds 9 decimal digits, little-endian), a sign, and a decimalscale. This is phase C1: representation + add / sub / mul / cmp + thei128bridge - bloom
- v5.0 —
BloomFilter, byte-keyed probabilistic set with a known false-positive ceiling. The v5 cold-tier segment files prefix a Bloom built over their PK column so alookup(pk)that doesn’t exist in a segment is rejected without touching the page index or the data pages — gating ~99 % of cross-segment probes away from disk I/O. - fts_
simple - v7.17.0 Phase 2.2 — lower-cased word tokenisation that mirrors
to_tsvector('simple', text). Lives in storage (not engine) because thecrate::IndexKind::GinFulltextposting-list build / rebuild / insert paths all run in storage and the engine crate can’t be a build-time dependency from here. - halfvec
- v6.0.3 — halfvec: IEEE-754 binary16 (
F16) per-element storage. - jsonb_
gin - v7.37.8(sentori Epic 5 P2)— JSONB → GIN posting-list tokens.
- persistent
- Persistent (structural-sharing) vector — the v4.38 building block for the
v4.39 cheap-
Catalog::clonemigration. - persistent_
btree - Persistent (structural-sharing) B-tree map — the v4.40 building block for
migrating
Table::indicesoffalloc::collections::BTreeMap. - priv_
bits - v7.39 (read01 round 57) — the table-privilege bits, in PG’s
aclitemrendering order (arwdDxtm). The order matters:relacloutput is byte-compared against PG. - quantize
- v6.0.0 — SQ8 scalar quantization for vector columns.
- row_
header - v7.37.15 (Phase A) — per-row MVCC visibility header.
- row_
locator - v5.1 — two-tier row pointer. The PB secondary index used to
map
IndexKey → Vec<usize>, where eachusizewas a row position inTable::rows: PersistentVec<Row>(the hot tier). v5.1 widens that toVec<RowLocator>so a single key can point to a mix of rows in the in-memory hot tier and rows that have been frozen to immutable cold-tier segment files (spg-storage::segment). - segment
- v5.0 — cold-tier segment file codec. A
Segmentis an immutable, PK-sorted file of(u64_key, row_bytes)entries with three sidecar sections for fast probing: aBloomFilterover the keys, a page index, and the payload pages themselves. The v5 freezer (v5.2 work) writes one segment per “freeze batch”; the v5.1 two-tier catalog probes the bloom first, then the page index, then a single 4 KiB page read — so a missed cold-tier probe costs at most ~bloom.contains()time, and a hit costs one disk seek + page-internal binary search. - snapshot
- v7.37.15 (Phase A) — per-statement / per-transaction snapshot.
- trgm
- v7.15.0 — trigram extraction for
pg_trgm-compatible GIN indexes. Mirrors PG’spg_trgmextension closely enough thatgin_trgm_opsindexes built on the same source produce the same trigram set andsimilarity(a, b)returns the same Jaccard ratio. - vacuum
- v7.37.15 (Phase D) — vacuum primitives.
Structs§
- AclItem
- v7.39 (read01 round 57) — one PG
aclitem: whatgranteemay do to a table, and who granted it. Renders asgrantee=privs/grantor, with an EMPTY grantee meaning PUBLIC (=r/owner). - Catalog
- Check
Constraint - v7.9.19 — composite UNIQUE / PRIMARY KEY constraint persisted
on the table schema. The leading column always has a BTree
index (created at CREATE TABLE time); INSERT enforcement
scans that index for collisions on the full column tuple.
v7.39 (read01 round 48) — a
CHECKconstraint: the SQL name the user gave it (viaADD CONSTRAINT <name> CHECK (...)or the inlineCONSTRAINT <name> CHECK (...)form) plus the predicate source.Nonename = unnamed, in which casepg_constraintsynthesises PG’s<table>_<col>_checkform. Names are persisted in the constraint-name appendix (FILE_VERSION 60+); older catalogs deserialise withNone. - Cold
Read Stats - Catalog: insertion-ordered
Vec<Table>for stable iter / serialize, plus aBTreeMap<String, usize>sidecar index soget/get_mutrun in O(log n) instead of the old linear scan with per-element string compares. - Column
Schema - Each bool is an independent, separately-persisted column attribute
(
nullable,auto_increment,is_unsigned,identity_always) that the catalog appendix reads and writes by name. Packing them into a bitflags word would buy nothing and would put a decoding step between the on-disk format and every reader of the schema. - Compact
Report - v6.7.3 — outcome of a
Catalog::compact_cold_segmentscall. The catalog state has already been mutated when this is returned: the merged segment is loaded intocold_segments, the source segment slots are tombstoned (None), and every BTree-indexRowLocator::Coldthat previously pointed at a source now points at the merged segment. The caller’s remaining job is to persistmerged_segment_bytesunder<db>.spg/segments/seg_<merged_segment_id>.spgand update the in-memorysegment_id → pathmap (remove the source ids, add the merged id) so the next CHECKPOINT writes a manifest that no longer lists the retired sources. - Composite
Def - v7.37.42-T2 ζ-B — catalogued user-defined COMPOSITE type
(
CREATE TYPE name AS (field_name field_type, ...)). Order matters: PG composite literals are positional, and SPG mirrors that. Stored as ordered(name, DataType)pairs to keep the codec straightforward and to allow eventualValue::Compositebodies to encode positionally. Persisted in catalog FILE_VERSION 52+; older catalogs deserialise with an empty composite_types map. Composite types can be used as a column type by spelling the composite’s name; the resolution fromColumnSchema.user_composite_type = Some(name)happens at the engine boundary (parallel touser_enum_type/user_domain_type). The dense storage shape — JSON-text body keyed by the composite’s field list — keeps the codec free of recursiveValuebodies until the full Value::Composite arena migration in a later phase. - Domain
Check - v7.39 (round 260) — one named CHECK on a domain. PG auto-names an
unnamed one
<domain>_check, then_check1,_check2, … (probed). - Domain
Def default/checksare stored as Display-form source sospg-storagestays free ofspg-sqldependency — same pattern as FunctionDef / ViewDef.- EnumDef
- v7.17.0 Phase 1.4 — catalogued user-defined ENUM type. The
label vector is order-preserving (PG enum ordering follows the
declared order). At INSERT/UPDATE on a column bound to this
enum, the engine looks up the value against
labelsand rejects non-members. - Excl
Range Index - v7.39 (round 215) — a per-table range-exclusion index: an incrementally
maintained map from a range column’s lower-bound key
(
range_excl_index_key) to the physical row locators carrying that bound. Lets EXCLUDE enforcement find the few candidate rows a new range might overlap in O(log n) instead of scanning every row (measured O(N²), r213). Because the stored ranges under a validEXCLUDE (col WITH &&)are pairwise disjoint, a candidate overlaps only its predecessor or the successors whose lower bound precedes its upper — a handful of probes. - Exclusion
Constraint - v7.39 (round 210) — an
EXCLUDEconstraint. Forbids two distinct live rows from satisfying, for EVERY element,new.col <op> existing.col(e.g.EXCLUDE USING gist (during WITH &&)= no twoduringranges overlap). Unlike a uniqueness constraint the operator is not equality, so enforcement is a full live-row scan re-checking the operator (a real GiST index that answers overlap in O(log n) is a later perf phase). A NULL in any element column exempts the row (matching PG / UNIQUE NULL semantics). Persisted in catalog FILE_VERSION 72+. - Foreign
KeyConstraint - v7.6.1 — Storage-layer mirror of
spg_sql::ast::ForeignKeyConstraint. The engine’s CREATE TABLE path translates between the two; keeping them separate preserves the no-deps boundary betweenspg-storageandspg-sql. - Freeze
Report - v5.2.2: outcome of a successful
Catalog::freeze_oldest_to_coldcall. The catalog state has already been mutated by the time this is returned (hot rows dropped + segment registered + Cold locators flipped). The caller’s only remaining concern issegment_bytes— persist them to disk under<db>.spg/segments/seg_<id>.spgso a future restart can reload via the v5.1SPG_PRELOAD_COLD_SEGMENTpath. (v5.3’s manifest will subsume this manual step.) - Freeze
Slice - v6.7.4 — read-only output of
Catalog::prepare_freeze_slice. Carries every row body + key in a contiguous hot-row range, already encoded and sorted by PK so the coordinator’s merge step is a k-way merge over already-sorted streams. - Function
Def - v7.12.4 — catalogued user-defined function.
bodyis the raw source text between$$ ... $$; the engine re-parses it on invocation. This keeps the storage codec stable when the PL/pgSQL surface grows (no breaking-change risk on the disk format). - Index
- A single-column secondary index. v2.0 carries either a B-tree map (the default — used for equality / range lookups on scalar columns) or a navigable-small-world graph (used for kNN over vector columns).
- Interval
Span - v7.37.5 β-P4 — element type for
Value::IntervalArray. Mirrors the{months, days, micros}shape of scalarValue::Interval, broken out as a named struct soIntervalArray’s element type is concrete (24 bytes, packed) instead of an enum-boxed Value. All three dimensions are independent —IntervalSpan { days: 1, .. }is distinct fromIntervalSpan { micros: 86_400_000_000, .. }per PG byte-equal. - NswGraph
- Multi-layer HNSW graph (v2.13). Each node is assigned a
top_level; it appears in layers0..=top_level. Higher layers are sparser, so search starts from the entry at the top layer, greedy-descends to layer 0, and beam-searches there. Layer 0 keeps a larger neighbour budget (m_max_0 = 2 * mper the HNSW paper); upper layers cap atm. The struct name staysNswGraphso external users / on-disk callers don’t have to track a rename — the algorithm changed, the data slot didn’t. - Point2D
- v7.37.5 ε — PG
pointbuilding block. Shared by every other geometric type (lseg / path / box / polygon / circle all reduce to compositions ofPoint2D). Packed{x: f64, y: f64}, 16 B, on-disk LE field order matches the PG binary point format byte-for-byte (so a future binary BIND path lands without rearrangement). - Policy
Def - v7.39 (RLS) — one
CREATE POLICYobject, stored per table. Theusing_expr/with_check_exprhold the qualifying expression’sDisplayform (re-parsed and evaluated per row at enforcement time, exactly likeTableSchema.checks);Nonemeans the clause was absent.rolesempty = PUBLIC. Persisted in the policy appendix (FILE_VERSION 59+). - Range
Span - v7.37.5 δ — single-range bounds without the kind tag. Used as
the element type of
Value::Multirange { kind, ranges }so a multirange carries one sharedRangeKindplus N bounds-only spans (saves 1 byte/elem vs duplicating the kind). The five other fields mirrorValue::Rangeexactly. - Row
- One table row — values are positional and must match
TableSchema.columnsin length and (modulo NULL) inDataType. - RuleDef
- v7.39 (round 139) — a catalogued query-rewrite RULE. Stored flat like
TriggerDef, keyed by(name, table). Command / WHEN text is deparsed SQL re-parsed at rewrite time (the same round-trip trick asTriggerDef.when_condition). Persisted from FILE_VERSION 71. - Scan
Stats - v7.39 (pg_stat knife B) — per-table scan counters, bumped from
&selfread paths. Clone (tx shadow catalogs clone tables) copies the current values; the counters are volatile like PG’s cumulative stats. - Sequence
Def - v7.17.0 — catalogued SEQUENCE. PG semantics: a counter object
returning monotonically increasing values via
nextval(name).last_valueis the most recent value handed out;is_calledis false until the firstnextval/setval. Stored separately from tables in the catalog. - Statistics
ExtDef - v7.39 (round 280) — one
CREATE STATISTICSobject. - Table
- Table
Schema - Trigger
Def - v7.12.4 — catalogued trigger. References its function by name; the function must exist at TRIGGER creation time (forward references are deferred to v7.12.5+).
- TsLexeme
- v7.12.0 — one entry in a
Value::TsVector. The lexeme is the (already-tokenised + stemmed in v7.12.1+) word;positionsis a strictly-ascending list of 1-based positions;weightis the PG weight letter (A=3, B=2, C=1, D=0) — v7.12.0 defaults every lexeme to D, the v7.12.2 ranking path consumes the weight. - TxWrite
Set - v7.17.0 Phase 1.5 — catalogued user-defined DOMAIN. A domain
is a named CHECK-constrained alias over a built-in type;
columns bound to it inherit the base type plus the CHECK
predicates + NOT NULL + DEFAULT at INSERT/UPDATE time.
v7.37.17 (Phase E RC rebase) — the write-set one writer version left
on a table, addressed by stable
row_header::RowIds so it can be replayed onto a fresher clone of the relation whose physical slots differ. Produced byTable::extract_tx_writeset, consumed byTable::replay_tx_writeset. - Uniqueness
Constraint - ViewDef
- v7.17.0 Phase 1.2 — catalogued VIEW. The body is stored as the
raw source text the parser saw between
ASand the statement terminator; the engine re-parses on each invocation. Same pattern asFunctionDef— keepsspg-storagefree ofspg-sqldependency.
Enums§
- Collation
- v7.17.0 Phase 2.5 — column-level text collation. Drives the
engine’s WHERE / GROUP BY equality routing for
Value::Text. Only two variants are modelled in v7.17: - Data
Type - Runtime type tags.
Vector { dim, encoding }/Varchar(max)/Char(size)are parameterised; the parameter travels with both the column schema and the on-wire serialised representation. - FkAction
- v7.6.1 — referential action tag. Mirrors
spg_sql::ast::FkAction. - Index
Key - Key type accepted by secondary indices. Float / NULL / Vector values
can’t participate in a B-tree index —
f64is onlyPartialOrd, NULL has SQL-three-valued semantics, and Vector belongs to the (future) HNSW path. Index lookups on those columns fall back to full scan. - Index
Kind - Match
Type - v7.38 (read01, T29) — FK MATCH type. Mirrors
spg_sql::ast::MatchType. - Mysql
IntWidth - v7.39 (round 386, type-fidelity epic P1) — the declared MySQL narrow
integer type for a column whose storage
DataTypecannot express it. MySQLTINYINT(i8, -128..127) collapses toDataType::SmallInt(i16) andMEDIUMINT(24-bit) toDataType::Int(i32) — both wider than the declared type, so a range check againsttyalone accepts out-of-range values (INSERT 128 INTO TINYINTis stored silently where MariaDB strict raises ERROR 1264). This annotation records the lost width so the write path (epic P2) can enforce the real bounds.SMALLINT/INT/BIGINTneed no marker — their storageDataTypeis already faithful. Sparse: only TINYINT / MEDIUMINT columns carry it; persisted in the FILE_VERSION 81+ appendix, older catalogs deserialise as None. - NswMetric
- Distance metric used at NSW search time. The graph topology is
always built with
L2; querying withInnerProduct/Cosinereuses the same edges but ranks candidates by the chosen metric. For the corpus-sized graphs this loses negligible recall vs building separate per-metric graphs. - Numeric
Kind - A row-cell value, including SQL
NULL.Floatusesf64; NaN compares non-equal to itself (PG behaviour) —PartialEqis derived so callers must opt into NaN-aware comparison if they need stronger guarantees. - Partition
Bound - v7.37.6-B — partition 边界 literal。
- Partition
Kind - v7.37.6-B — 分区策略。
- Partition
Role - v7.37.6-B — partition 三态(parent / range child / default child)。
- Policy
Cmd - v7.39 (RLS) — the command a policy applies to.
ALLis the default and covers every command; the others scope the policy to one statement kind. Persisted as a single byte in the policy appendix (FILE_VERSION 59+). - Range
Kind - v7.17.0 Phase 3.P0-38 — pins the element type of a range value or column. Wire OIDs: Int4=3904, Int8=3926, Num=3906, Ts=3908, TsTz=3910, Date=3912.
- RowChange
- In-memory table: schema + a persistent row vector + secondary indices.
- Sequence
Data Type - v7.17.0 — sequence integer width.
- Storage
Error - TsQuery
Ast - v7.12.0 — parse tree for a PG
tsquery. v7.12.0 ships the type + codec only; theto_tsquery/plainto_tsquerylexer lands in v7.12.1 and the@@evaluator in v7.12.2. - Value
- VecEncoding
- In-cell encoding for
DataType::Vector. Mirrorsspg_sql::ast::VecEncoding— kept here so storage stays dep-free ofspg-sql. The engine bridges between the two at DDL-execution time.
Constants§
- CURRENT_
ROW_ CODEC_ VERSION - v7.37 (round 833) — the codec version to decode a row that
encode_row_body_densehas just produced. - FN_
IMMUTABLE - FN_
PARALLEL_ RESTRICTED - FN_
PARALLEL_ SAFE - FN_
PARALLEL_ UNSAFE - v7.39 (round 322, V46) —
FunctionDef.parallelcodes: PG’spg_proc.proparallelletters. - FN_
STABLE - FN_
VOLATILE - v7.39 (round 322, V46) —
FunctionDef.volatilitycodes: PG’spg_proc.provolatileletters. - NSW_
DEFAULT_ M - Default neighbor degree (M) for the NSW graph. Picked at construction time and persisted with the index.
Functions§
- decode_
redo_ log - v7.34, extended v7.37.15 (Epic W slice 1) — decode a row-level redo
log written by
encode_redo_log. - decode_
row_ body_ dense - decode_
row_ body_ dense_ pruned - Inverse of
encode_row_body_dense. Reads one row’s body frombytesand returns it plus the number of bytes consumed (so a caller decoding a back-to-back stream of rows can advance its cursor). ReturnsStorageError::Corrupton truncation, bad UTF-8, or unknown cell tags. v7.37 (round 923) —decode_row_body_densethat does not build the columns the caller will not read. - encode_
redo_ log - v7.34 (crash-recovery P0 #2), extended v7.37.15 (Epic W slice 1) — encode a row-level redo log to bytes for a WAL record.
- encode_
row_ body_ dense - Encode one row’s body in the v3.0.2 dense format (
FILE_VERSION8): per-row NULL bitmap (1 bit/col, ceil(cols/8) bytes), then each non-NULL cell aswrite_value_body. Same wire shape the catalog snapshot writes per row inside its rows-block. Exposed pub so v5.1+ cold-tier segment writers can produce row payloads that the catalogdecode_row_body_densedecodes 1:1. - encode_
row_ body_ dense_ into - v7.37 (round 883) —
encode_row_body_denseappending to a buffer the caller owns. - encode_
row_ body_ dense_ masked_ into - v7.37 (round 995) —
encode_row_body_dense_intothat does not STORE the columns the caller will not read. - format_
uuid - v7.17.0 — render a
Value::Uuidpayload as the canonical lowercase 8-4-4-4-12 hyphenated form PGtextcast surfaces. - function_
arg_ names - v7.39 (read01 round 65) — the declared argument NAMES of a function (
""for a bare type with no name). - function_
arg_ types - The declared argument TYPES of a function, out of its
args_repr("(x INT, y DOUBLE PRECISION)"→["int", "float"]). An entry may be a bare type with no name ("(INT)"). - function_
signature_ key - function_
signature_ key_ legacy - v7.39 (round 315, V19) — the signature key as computed BEFORE the multi-word fix, used only to recognise what an older image wrote.
- is_
builtin_ schema - v7.17.0 Phase 1.6 — built-in schema names that every Catalog
understands without an explicit CREATE SCHEMA. Used by
Catalog::schema_existsand the engine’s schema-qualified lookup path. - is_
multiword_ type_ phrase - v7.39 (round 344, V49) — re-exported from
spg_sql, which owns the SQL type spellings. This crate carried a byte-identical copy because the two were siblings that did not depend on each other; spg-sql is a dependency-free leaf, so the dependency is acyclic and the publish order already puts it first. One list, one place to keep it right. v7.12.4 — map a bare type-name identifier (the form that appears in a function arg list or RETURNS clause) to aColumnTypeName. ReturnsNonefor unknown / extension types so the caller can preserve them asFunctionArgType::Raw/FunctionReturn::Other. - mysql_
ci_ fold - v7.39 (round 363, M4 P1) — MySQL’s default accent- and
case-insensitive fold (
utf8mb4_uca1400_ai_ci). - mysql_
compare_ fold - v7.39 (round 375) — the fold used to COMPARE / GROUP / de-dup text on
the MySQL dialect. Its default collation is PAD SPACE: trailing spaces
do not affect a comparison (
'a' = 'a ','' = ' ', measured on MariaDB 11), so they are stripped before the case/accent fold. Only literal spaces pad — a tab or other whitespace is significant — and this is NOT used byLIKE, whose pattern treats a trailing space literally. - normalize_
type_ name - Fold PG’s type aliases so a signature key is stable across spellings. Unknown names pass through lower-cased — consistency is what the key needs.
- nsw_
assign_ level - Deterministic level assignment, seeded on the row index so the same
insert order reproduces the same topology. Distribution is roughly
HNSW-flavoured with
mL ≈ 1/ln(M) ≈ 0.36for M=16: each 4-bit chunk that comes up zero promotes the node one layer (so P(level ≥ L) ≈ (1/16)^L). - nsw_
index_ on - Find any NSW index on a column. Used by the planner to decide
whether an
ORDER BY col <-> literal LIMIT kquery can skip the brute-force scan. - nsw_
query - Public wrapper: run an NSW kNN search and return the top-k row indices ordered by ascending distance under the given metric.
- parse_
uuid_ str - v7.17.0 — parse a PG-canonical UUID text representation into the
16-byte network-order layout used by
Value::Uuid. Accepted input shapes (all case-insensitive): - range_
excl_ index_ key - v7.39 (round 215) — the lower-bound sort key for a range value, used by
the range-exclusion index. The bound as an
i128(unbounded lower =i128::MIN, sorting first) plus an inclusivity rank (inclusive lower sorts before exclusive at the same value,[3before(3). ReturnsNonefor range kinds whose bound isn’t an integer scalar (numrange’s numeric/bignum), for empty ranges, and for non-range values — the caller then keeps the O(n) scan rather than risk an unsound order. Int4/Int8/ Date/Ts/TsTz all reduce here (tstzrange bounds areValue::Timestamp). Maintenance (index build) and query (overlap probe) MUST agree on this key, so both sides call exactly this function. - resolve_
stored_ function_ key - v7.39 (round 315, V19) — which catalogued function does a persisted ACL key refer to?
- row_
body_ encoded_ len - Fast computation of the byte length
encode_row_body_densewould produce, without allocating the output buffer. Mirrors the encoder’s per-column body sizing so the v5.2.1Table::hot_bytesincremental counter doesn’t pay an alloc-per-insert tax. Returns the exact sameusizeasencode_row_body_dense(row, schema).len(). - unresolved_
tombstone_ count - v7.37.15 (Epic W durable-tombstone slice) — read the process-wide
count of redo tombstones that could not be resolved to a row by
RowIdduringapply_redo. See [UNRESOLVED_TOMBSTONES]. - unresolved_
tombstones - v7.39 (flip crash-replay P0) — observability read for the replay tombstones that could not be resolved to a row (each one is a resurrected delete).
Type Aliases§
- RowOwned
- Owned
Row— values areValue<'static>. Used everywhere a row must outlive a query-scoped arena. - Value
Owned - Owned
Value— heap-bearing variants areCow::Owned. Used everywhere a Value must outlive a query-scoped arena (catalog defaults, persistent storage, public APIs).