Skip to main content

Catalog

Struct Catalog 

Source
pub struct Catalog {
    pub cold_read_stats: ColdReadStats,
    /* private fields */
}

Fields§

§cold_read_stats: ColdReadStats

v7.39 (pg_stat blks knife) — see ColdReadStats.

Implementations§

Source§

impl Catalog

Source

pub const TEMP_NAME_MARKER: &'static str = "__spg_temp_"

v7.39 (round 436) — the marker every session’s temporary-table namespace starts with. Public so the catalog synths can tell a temp table from an ordinary one without knowing the session id.

Source

pub fn vacuum_all( &mut self, oldest_active_snapshot: u64, dry_run: bool, ) -> VacuumReport

v7.37.15 (Phase D) — fleet-wide vacuum pass. Walks every user table and reclaims rows whose delete-commit version is older than oldest_active_snapshot. Returns an aggregated report with per-table breakdown so hosts can emit metrics.

dry_run = true reports the work without doing it. Use it to estimate the cost before scheduling a real pass.

Source

pub const fn new() -> Self

Source

pub const fn functions(&self) -> &BTreeMap<String, FunctionDef>

v7.12.4 — read-only view of catalogued user-defined functions. Engine callers go through here to look up the function body before re-parsing it for invocation.

Source

pub fn create_function( &mut self, def: FunctionDef, or_replace: bool, ) -> Result<(), StorageError>

v7.12.4 — register a new user-defined function. With or_replace = false, errors if the name is taken. The engine validates the body before passing it here.

Source

pub fn functions_named(&self, name: &str) -> Vec<&FunctionDef>

v7.39 (read01 round 62) — every overload of name.

Source

pub fn function_by_key(&self, key: &str) -> Option<&FunctionDef>

v7.39 (read01 round 62) — one overload, by its signature key.

Source

pub fn drop_function_by_key(&mut self, key: &str) -> bool

v7.39 (read01 round 62) — drop ONE overload. true if it was there.

Source

pub fn drop_function(&mut self, name: &str) -> bool

v7.12.4 — remove a user-defined function by name. Returns true if a function was removed, false if none matched. Caller decides whether to surface if_exists semantics. v7.39 (read01 round 62) — with no signature, PG drops the function only when the name is unambiguous. SPG mirrors that: this removes EVERY overload of name, and the caller (ddl.rs) refuses the ambiguous case before getting here.

Source

pub fn schema_acl(&self) -> &[AclItem]

v7.17.0 — read-only handle to catalogued sequences. v7.39 (read01 round 60) — the public schema’s ACL (PG nspacl).

Source

pub fn schema_acl_mut(&mut self) -> &mut Vec<AclItem>

Source

pub fn database_acl(&self) -> &[AclItem]

v7.39 (read01 round 60) — the database’s ACL.

Source

pub fn database_acl_mut(&mut self) -> &mut Vec<AclItem>

Source

pub fn sequence_mut(&mut self, name: &str) -> Option<&mut SequenceDef>

v7.39 (read01 round 60) — mutable sequence access, for GRANT. v7.39 (round 469) — resolves the session’s temporary sequence first, like its read-only twin. nextval and setval reach the map through here, so a temporary sequence shadowing a permanent one advances the temporary one — measured against PG18, where the permanent sequence’s counter is untouched while the temp exists.

Source

pub fn function_mut(&mut self, name: &str) -> Option<&mut FunctionDef>

v7.39 (read01 round 61) — mutable function access, for GRANT.

Source

pub const fn sequences_all(&self) -> &BTreeMap<String, SequenceDef>

Every catalogued sequence, temp ones included under their mangled storage names. Listing code filters these through Self::listed_name; anything resolving ONE name by its logical spelling wants Self::sequence instead.

Source

pub fn sequence(&self, name: &str) -> Option<&SequenceDef>

v7.39 (round 469) — resolve one sequence by its logical name, the session’s temporary one winning over a permanent one of the same name. The same rule Self::resolve_index applies to tables.

Source

pub fn has_sequence(&self, name: &str) -> bool

Does a sequence of this logical name exist for this session?

Source

pub fn sequence_key(&self, name: &str) -> String

The storage key a sequence of this logical name resolves to — the session’s temp mangling when it has one, else the name itself.

Source

pub fn create_sequence( &mut self, def: SequenceDef, if_not_exists: bool, ) -> Result<(), StorageError>

v7.17.0 — register a new SEQUENCE. Errors if name collides with an existing sequence and if_not_exists is false.

Source

pub fn rename_sequence( &mut self, old: &str, new: &str, ) -> Result<(), StorageError>

v7.17.0 — remove a SEQUENCE by name. Returns true if a sequence was removed, false if none matched. Caller surfaces IF EXISTS semantics. v7.39 (read01 round 49) — ALTER SEQUENCE old RENAME TO new. Errors when old is missing or new is taken; the SequenceDef’s own name field is rewritten so it stays self-describing.

Source

pub fn drop_sequence(&mut self, name: &str) -> bool

Source

pub fn sequence_counters(&self) -> Vec<(String, i64, bool)>

v7.17.0 — atomic nextval. Increments last_value per increment, returns the new value, sets is_called. Returns an error on CYCLE-less overflow. v7.39 (round 497) — the counter state of every sequence, for carrying across a commit install.

A sequence’s VALUE is not transactional in PG: nextval advances shared state that a rollback does not give back, because two sessions must never receive the same number. SPG keeps sequences in the catalog, and a transaction works on a catalog CLONE, so installing that clone at COMMIT would restore whatever the counter was at BEGIN. These two let the install put the live counters back.

Source

pub fn restore_sequence_counters(&mut self, saved: &[(String, i64, bool)])

Restore counters saved by Self::sequence_counters, for the sequences that still exist. A sequence the transaction CREATED is absent from the saved set and keeps the value it was given.

Source

pub fn sequence_next_value(&mut self, name: &str) -> Result<i64, StorageError>

Source

pub fn sequence_current_value(&self, name: &str) -> Result<i64, StorageError>

v7.17.0 — currval. Errors if the session has never called nextval on this sequence (PG semantics). At the catalog level we approximate “session” with “is_called persisted”; the engine session-tracking layer can wrap this for the strict per-session semantics later.

Source

pub fn sequence_set_value( &mut self, name: &str, value: i64, is_called: bool, ) -> Result<i64, StorageError>

v7.17.0 — setval(name, value [, is_called]). PG returns value regardless. is_called=true means the NEXT nextval will return value + increment; is_called=false means the next nextval will return value.

Source

pub const fn views_all(&self) -> &BTreeMap<String, ViewDef>

v7.17.0 Phase 1.2 — read-only handle to catalogued views. Temp ones are in here under their mangled storage names; listing code filters through Self::listed_name, and anything resolving ONE name by its logical spelling wants Self::view.

Source

pub fn view(&self, name: &str) -> Option<&ViewDef>

v7.39 (round 469) — resolve one view by its logical name, the session’s temporary one winning over a permanent one of the same name.

Source

pub fn has_view(&self, name: &str) -> bool

Does a view of this logical name exist for this session?

Source

pub fn view_key(&self, name: &str) -> String

The storage key a view of this logical name resolves to.

Source

pub fn create_view( &mut self, def: ViewDef, or_replace: bool, if_not_exists: bool, ) -> Result<(), StorageError>

v7.17.0 Phase 1.2 — install a VIEW. or_replace=true overwrites an existing entry; if_not_exists=true is a silent no-op when the name is taken. Errors if both flags are off and the name collides.

Source

pub fn drop_view(&mut self, name: &str) -> bool

v7.17.0 Phase 1.2 — remove a view by name. Returns true if a view was removed.

Source

pub const fn materialized_views(&self) -> &BTreeMap<String, String>

v7.17.0 Phase 1.3 — read-only handle to the materialised- view source registry. Each entry pairs with a regular table of the same name that holds the cached rows.

Source

pub fn register_materialized_view(&mut self, name: String, body: String)

v7.17.0 Phase 1.3 — register a source for a materialised view. Caller has already created the backing table.

Source

pub fn drop_materialized_view_source(&mut self, name: &str) -> bool

v7.17.0 Phase 1.3 — drop the source registry entry. Returns true if a source was unregistered. Caller separately drops the backing table.

Source

pub const fn enum_types(&self) -> &BTreeMap<String, EnumDef>

v7.17.0 Phase 1.4 — read-only handle to user-defined ENUM catalog.

Source

pub fn create_enum_type(&mut self, def: EnumDef) -> Result<(), StorageError>

v7.17.0 Phase 1.4 — install a new ENUM type. Errors if name collides with an existing enum (no IF NOT EXISTS per PG semantics for CREATE TYPE).

Source

pub fn rename_enum_value( &mut self, type_name: &str, old: &str, new: &str, ) -> Result<(), StorageError>

v7.17.0 Phase 1.4 — drop an ENUM type by name. Returns true if a type was removed. v7.37 D.55 — ALTER TYPE … ADD VALUE. Appends label to an existing enum’s ordered label list, or inserts it before/after an existing label. if_not_exists makes a duplicate a no-op; otherwise a duplicate errors. Returns Ok(true) if a label was added, Ok(false) if it already existed (only possible under if_not_exists). v7.39 (read01 round 49) — ALTER TYPE t RENAME VALUE 'old' TO 'new'. The parser used to swallow this form as a no-op, so the rename was accepted and silently ignored. Renaming in place keeps the label’s sort position, which is what PG does (enumsortorder is untouched).

Source

pub fn set_comment(&mut self, key: &str, text: Option<&str>)

v7.39 (read01 round 50) — set (or, with None, remove) the comment on an object. key is the canonical "<kind>:<name>" form.

Source

pub fn comment(&self, key: &str) -> Option<&str>

v7.39 (read01 round 50) — the comment on an object, if any.

Source

pub fn set_db_role_setting( &mut self, database: &str, role: &str, param: &str, value: Option<&str>, )

v7.39 (round 547) — record a GUC default for a scope. An empty database or role name is PG’s oid 0 (“all”). None value removes just that parameter, as PG’s RESET does.

Source

pub fn create_replication_slot( &mut self, name: &str, plugin: &str, slot_type: &str, ) -> Result<(), String>

v7.39 (round 550) — create a replication slot. Err carries PG’s own message for a duplicate.

§Errors

When a slot of that name already exists.

Source

pub fn drop_replication_slot(&mut self, name: &str) -> Result<(), String>

§Errors

When no slot of that name exists — PG’s message, and the case that used to report success.

Source

pub const fn replication_slots(&self) -> &BTreeMap<String, (String, String)>

Source

pub fn reset_db_role_settings(&mut self, database: &str, role: &str)

PG’s RESET ALL: drops this scope’s whole entry, leaving the other scopes alone — measured on PG18, where ALTER ROLE r RESET ALL left the ALL, the database and the role-in-database rows.

Source

pub const fn db_role_settings( &self, ) -> &BTreeMap<(String, String), BTreeMap<String, String>>

Source

pub const fn comments(&self) -> &BTreeMap<String, String>

v7.39 (read01 round 50) — every (key, text) pair, for the pg_description view.

Source

pub fn drop_comments_for(&mut self, kind: &str, name: &str)

v7.39 (read01 round 50) — drop every comment whose key names obj (the object itself and, for a table, its columns). Called when the object is dropped so a later object of the same name doesn’t inherit a stale comment.

Source

pub fn add_enum_value( &mut self, type_name: &str, label: &str, if_not_exists: bool, position: Option<(bool, String)>, ) -> Result<bool, StorageError>

Source

pub fn drop_enum_type(&mut self, name: &str) -> bool

Source

pub const fn domain_types(&self) -> &BTreeMap<String, DomainDef>

v7.17.0 Phase 1.5 — read-only handle to DOMAIN catalog.

Source

pub fn create_domain_type(&mut self, def: DomainDef) -> Result<(), StorageError>

v7.17.0 Phase 1.5 — install a DOMAIN. Errors on collision with an existing domain.

Source

pub fn drop_domain_type(&mut self, name: &str) -> bool

v7.17.0 Phase 1.5 — drop a DOMAIN by name.

Source

pub const fn composite_types(&self) -> &BTreeMap<String, CompositeDef>

v7.37.42-T2 ζ-B — read-only handle to user-defined COMPOSITE catalog. Used by the engine to resolve ColumnSchema.user_composite_type lookups + by information_schema-style introspection.

Source

pub fn create_composite_type( &mut self, def: CompositeDef, ) -> Result<(), StorageError>

v7.37.42-T2 ζ-B — install a new COMPOSITE type. Errors if name already exists in the composite registry (PG forbids IF NOT EXISTS on CREATE TYPE composite; the engine surfaces the collision with the existing name).

Source

pub fn drop_composite_type(&mut self, name: &str) -> bool

v7.37.42-T2 ζ-B — drop a COMPOSITE type by name. Returns true if a type was removed.

Source

pub const fn user_schemas(&self) -> &BTreeSet<String>

v7.17.0 Phase 1.6 — read-only handle to the user-created schema registry. Built-in schemas (public, pg_catalog, information_schema) are NOT included here; use schema_exists for the full check.

Source

pub fn schema_exists(&self, name: &str) -> bool

v7.17.0 Phase 1.6 — schema-name resolver. Returns true for built-in schemas + every user-CREATEd one. Used by CREATE SCHEMA collision checks and (future) by information_schema.schemata.

Source

pub fn create_schema( &mut self, name: String, if_not_exists: bool, ) -> Result<(), StorageError>

v7.17.0 Phase 1.6 — register a new schema. Errors if the name already exists and if_not_exists=false. Built-in names cannot be redeclared.

Source

pub fn drop_schema(&mut self, name: &str) -> Result<bool, StorageError>

v7.17.0 Phase 1.6 — drop a user-created schema. Returns true if a schema was removed. Built-in names always return false (cannot be dropped). Tables that previously used the schema as a prefix keep their bare name and stay queryable — this is the “prefix routing, not isolation” posture documented in v7.17 Phase 1.6.

Source

pub fn alter_sequence( &mut self, name: &str, increment: Option<i64>, min_value: Option<i64>, max_value: Option<i64>, start: Option<i64>, restart: Option<Option<i64>>, cache: Option<i64>, cycle: Option<bool>, owned_by: Option<Option<(String, String)>>, ) -> Result<(), StorageError>

v7.17.0 — ALTER SEQUENCE option merge. Caller-provided updates overwrite the matching fields; unset fields keep their stored values. RESTART variants update last_value directly per PG: RESTART resets to current start; RESTART WITH n resets to n.

Source

pub fn triggers(&self) -> &[TriggerDef]

v7.12.4 — read-only slice of all catalogued triggers. Engine row-write paths filter this by (table, event, timing) and fire matches in slice order.

Source

pub fn triggers_mut(&mut self) -> &mut Vec<TriggerDef>

v7.15.0 — mutable handle to the trigger slice for ALTER TABLE … RENAME COLUMN, which rewrites every update_columns entry that referenced the renamed column.

Source

pub fn create_trigger( &mut self, def: TriggerDef, or_replace: bool, ) -> Result<(), StorageError>

v7.12.4 — register a new trigger. With or_replace = false, errors when a trigger with the same name already exists on the same table (PG scoping rule — trigger names are per-table, not global). Trigger function must already exist in the catalog at registration time.

Source

pub fn drop_trigger(&mut self, name: &str, table: &str) -> bool

v7.12.4 — remove a trigger by (name, table). Returns true if one was removed.

Source

pub fn rules(&self) -> &[RuleDef]

v7.39 (round 139) — the catalogued query-rewrite RULEs.

Source

pub fn statistics_ext(&self) -> &[StatisticsExtDef]

v7.39 (round 280) — the catalogued extended-statistics objects.

Source

pub fn large_objects(&self) -> &BTreeMap<u32, Vec<u8>>

v7.39 (round 287) — every large object, ascending by OID.

Source

pub fn large_object(&self, oid: u32) -> Option<&[u8]>

The bytes of one large object, or None when no such OID exists.

Source

pub fn create_large_object( &mut self, oid: u32, bytes: Vec<u8>, ) -> Result<u32, String>

Create a large object. oid of 0 means “pick one” — PG’s lo_create(0) / lo_creat(-1) spelling. Errors when the requested OID is taken.

Source

pub fn put_large_object( &mut self, oid: u32, offset: usize, data: &[u8], ) -> Result<(), String>

Overwrite len bytes at offset (0-based), growing the object with zero bytes if the write starts past the end — PG’s lo_put semantics.

Source

pub fn truncate_large_object( &mut self, oid: u32, len: usize, ) -> Result<(), String>

v7.39 (round 306) — lo_truncate. PG’s truncate sets the object to exactly len bytes in BOTH directions: it shortens, and it GROWS with zero fill when len exceeds the current size (measured — lo_truncate(fd, 8) over a 4-byte object leaves eight bytes, the last four zero).

Remove a large object. false when the OID was not there.

Source

pub fn create_statistics_ext( &mut self, def: StatisticsExtDef, ) -> Result<(), String>

Register one. Err(name) when the name is taken.

Source

pub fn drop_statistics_ext(&mut self, name: &str) -> bool

Drop one by name; false when absent.

Source

pub fn create_rule( &mut self, def: RuleDef, or_replace: bool, ) -> Result<(), StorageError>

v7.39 (round 139) — register a RULE. Its target relation (table or view) must exist; or_replace overwrites a same-(name,table) rule.

Source

pub fn drop_rule(&mut self, name: &str, table: &str) -> bool

v7.39 (round 139) — drop a RULE by (name, table).

Source

pub fn create_table(&mut self, schema: TableSchema) -> Result<(), StorageError>

Source

pub fn set_temp_prefix(&mut self, prefix: Option<String>)

v7.39 (round 436) — install the calling session’s temp namespace. None disables temp resolution entirely (a session that never made one pays a single Option check per lookup).

Source

pub fn temp_name_for(&self, name: &str) -> Option<String>

The mangled storage name a temp table of name takes in this session, or None when the session has no temp namespace.

Source

pub fn get(&self, name: &str) -> Option<&Table>

Source

pub fn get_mut(&mut self, name: &str) -> Option<&mut Table>

Source

pub fn dirty_tables(&self) -> &BTreeSet<String>

v7.39 (round 496) — the tables changed through this handle since Self::clear_dirty_tables. See dirty_tables.

Source

pub fn clear_dirty_tables(&mut self)

v7.39 (round 496) — start a fresh recording window. A transaction’s shadow calls this at BEGIN so the set means “changed by this tx”.

Source

pub fn install_table(&mut self, name: &str, table: Table)

v7.39 (round 496) — put table in at name, replacing any table already there and keeping the rest of the catalog untouched.

The commit-time table-granularity merge needs exactly this: take the latest committed catalog, then overwrite only the tables the transaction changed.

Source

pub fn tables_position_of(&self, name: &str) -> Option<usize>

v7.37.42 (docker-fair SCALARSQ attack) — resolve a table name to its insertion-order index ONCE, so callers that need to fetch the same table many times (per-row PK probes in correlated scalar subqueries) can avoid the per-call BTreeMap<String, usize> string descent. The returned index is stable for the lifetime of the catalog snapshot the caller holds (same engine read guard).

Source

pub fn tables_at(&self, idx: usize) -> Option<&Table>

Direct positional fetch counterpart to [tables_position_of]. idx must come from tables_position_of against the same catalog snapshot — out-of-range returns None.

Source

pub fn apply_redo(&mut self, changes: &[RowChange]) -> Result<(), StorageError>

v7.34 (crash-recovery P0 #2) — replay a row-level redo log onto this catalog (the RowChange physical-redo apply primitive that row-level WAL recovery will use in place of statement re-execution). Applies each change in order via the same Table mutators the engine used — no uniqueness/FK/parse/plan: the original execution already validated, replay trusts and applies. Positions are physical and only valid when replayed from the matching checkpoint baseline in original order (see RowChange docs).

A change naming an absent table, or whose position is out of range, is a corrupt/misaligned log and surfaces as an error rather than a silent skip.

Source

pub fn enable_redo_all(&mut self)

v7.34 (crash-recovery P0 #2) — enable row-level redo capture on every table (the engine calls this before a mutating statement when persistence is on; idempotent, keeps any in-flight capture).

Source

pub fn drain_redo(&mut self) -> Vec<RowChange>

v7.34 — drain the row-level redo captured across all tables, in table order then per-table apply order, and stop capturing. The engine calls this after a successful mutating statement and writes the returned RowChanges to the WAL in place of the SQL text.

Source

pub fn table_count(&self) -> usize

Source

pub fn drop_table(&mut self, name: &str) -> bool

v7.14.0 — remove a table by name. Returns true when the table existed (and is now gone), false when it didn’t. Used by DROP TABLE from pg_dump / mysqldump preambles where the dump re-creates schema and starts with DROP TABLE IF EXISTS.

Source

pub fn rename_table(&mut self, old: &str, new: &str) -> Result<(), StorageError>

v7.16.2 — rename a table (mailrs round-10 A.5). Updates the schema name, the catalog name → index map, and rewrites every reference dangling at the table name:

  • every FK on every OTHER table whose parent_table pointed at the old name now points at the new name, so FK enforcement keeps working
  • every trigger watching the table updates its table field Returns Ok on success; Err(StorageError::TableNotFound) when the old name isn’t in the catalog and Err(StorageError::DuplicateTable) when the new name is already taken.
Source

pub fn rename_index(&mut self, old: &str, new: &str) -> Result<(), StorageError>

v7.16.2 — rename an index by name. Walks every table since the index lives on its owning table; updates the name in place. Errors with IndexNotFound when no index matches. mailrs round-10 A.5.

Source

pub fn drop_named_index(&mut self, name: &str) -> bool

v7.14.0 — remove a named index across the catalog. Returns true when found + dropped.

Source

pub fn table_names(&self) -> Vec<String>

Borrow-free copy of every table’s name in catalog order (= insertion order, matching the on-disk encoding).

Source

pub fn listed_name<'a>(&self, stored: &'a str) -> Option<&'a str>

v7.39 (round 437) — how a stored table name should appear to the CALLING session in a catalog listing (SHOW TABLES, pg_class, information_schema, …):

  • an ordinary table → its own name
  • this session’s temporary table → its logical name, prefix stripped
  • another session’s temporary table → None, i.e. not listed

Measured on both oracles: MariaDB 11 and PG 18 each list the calling session’s own temporary tables and neither lists anybody else’s. Round 436 stored temp tables under a prefix without teaching the listings about it, so the mangled names leaked to every client.

Source

pub fn visible_table_names(&self) -> Vec<String>

The listing names of every table this session may see, in catalog order. See Catalog::listed_name.

Source

pub fn load_segment_bytes( &mut self, bytes: Vec<u8>, ) -> Result<u32, StorageError>

v5.1: register a cold-tier segment that already lives in memory (caller did the file read). Returns the segment_id that RowLocator::Cold { segment_id, .. } will reference — currently this is just the index into cold_segments, but treat it as an opaque token.

Storage is no_std, so file I/O is the caller’s responsibility — spg-server reads the file and forwards the bytes here. The bytes stay resident in the catalog for the life of the Catalog, parsed only once.

Source

pub fn load_segment_bytes_at( &mut self, target_id: u32, bytes: Vec<u8>, ) -> Result<(), StorageError>

v6.7.3 — register a cold-tier segment at a specific id. Used by the spg-server manifest-boot path so segments whose neighbouring ids were retired by compaction still get back the same segment_id they had pre-restart (the RowLocator::Cold { segment_id } baked into the BTree-index snapshot persists across restart and must continue to resolve).

Pads the Vec with None slots up to target_id if needed. Errors when the target slot is already occupied (would stomp another segment), the parse fails, or target_id exceeds u32::MAX.

Source

pub fn tombstone_segment(&mut self, segment_id: u32) -> Result<(), StorageError>

v6.7.3 — retire a cold-tier segment slot (compaction-driven). The physical file is the caller’s concern (typically kept on disk until the next CHECKPOINT writes a manifest that no longer lists it); this just flips the in-memory slot to None so later cold lookups for segment_id resolve as “unknown” instead of returning a stale row.

No-op when the slot is already None. Errors only when segment_id is out of bounds.

Source

pub fn cold_segment_count(&self) -> usize

Number of active (non-tombstoned) cold segments.

Source

pub fn has_any_cold_segments(&self) -> bool

v7.37.42 (docker-fair SCALARSQ attack 3) — short-circuit guard for scan loops that conditionally walk the cold tier. Returns false when the catalog has never loaded a cold segment (or all segments are tombstoned), so callers can skip the per-table cold PK-index walk entirely on hot-only databases. O(N segments); typical N is small (single-digit) so the check is sub-µs.

Source

pub fn cold_segment_slot_count(&self) -> usize

Slot count including tombstones (= the next id the no-arg load_segment_bytes would allocate).

Source

pub fn cold_segment_ids_global(&self) -> Vec<u32>

v6.2.7 — list every active cold-tier segment id known to this catalog (skips compaction tombstones since v6.7.3). Used by EXPLAIN ANALYZE to annotate scan nodes with the segments they could have walked.

Source

pub fn hot_tier_bytes(&self) -> u64

v5.2.1: sum of Table::hot_bytes across every table. The v5.2 freezer compares this against SPG_HOT_TIER_BYTES (parsed at server startup; default 4 GiB) and wakes when the budget is crossed. Pre-freezer (v5.2.1) this is measurement-only — the counter exposes whether the budget is being approached without triggering any demotion.

Source

pub fn freeze_oldest_to_cold( &mut self, table_name: &str, index_name: &str, max_rows: usize, ) -> Result<FreezeReport, StorageError>

v5.2.2: freeze the first max_rows rows of table_name’s hot tier into a brand-new cold-tier segment. The named BTree index supplies the per-row PK (its column must be an integer type — v5.2.2 only supports IndexKey::Int PKs, matching the index_key_as_u64 constraint used by the cold-tier lookup path). On success returns a FreezeReport with the freshly-allocated segment id, the count of rows that moved, the encoded segment bytes (so the caller can persist them to disk for later reload via SPG_PRELOAD_COLD_SEGMENT), and the hot-tier byte delta that was reclaimed.

Semantics:

  1. The first max_rows rows (by hot-tier position — same as insertion order under v4.39 PersistentVec) are read.
  2. Rows are sorted ascending by PK and serialised into a new segment via encode_segment.
  3. The hot rows are dropped via Table::delete_rows; the rebuild_indices it triggers regenerates Hot locators for every remaining row (their positions shift down by max_rows). Existing Cold locators in this index — from a previous freeze — are also rebuilt but with empty payload since rebuild reads only self.rows; this routine re-registers them at the end of the call so the user-visible state preserves all prior cold locators.
  4. The new segment is loaded into self.cold_segments via Catalog::load_segment_bytes (allocating a fresh segment_id). New Cold locators are registered on the named index — one per frozen row.

v5.2.2 limits (relaxed in later sub-versions):

  • INSERT-only flow: subsequent UPDATE/DELETE on a frozen row returns a stale-locator error (no promote-on-write until v5.2.3).
  • Single-table scope: callers iterate tables themselves.
  • All-or-nothing: returns Err and leaves catalog unchanged if any step fails before the atomic swap point.

Errors:

  • StorageError::Corrupt for missing table/index, non-BTree index, non-integer PK column, max_rows == 0, or max_rows > row_count.
  • The encoder’s SegmentError surfaces as Corrupt (the only realistic source is “a single row is larger than the page size”; SPG schemas don’t hit it in practice).
Source

pub fn cold_segment(&self, segment_id: u32) -> Option<&OwnedSegment>

v5.1: borrow the cold segment at segment_id. Used by the spg-server preload path to enumerate (key, locator) pairs after loading a segment, so it can call Table::register_cold_locators without re-parsing the bytes.

Source

pub fn resolve_cold_locator( &self, table_name: &str, segment_id: u32, key: &IndexKey, ) -> Option<Row<'static>>

v5.1: resolve a single RowLocator::Cold to its underlying Row. Decoupled from Catalog::lookup_by_pk so callers iterating a multi-locator slice (e.g. the engine’s index seek path) can dispatch per locator instead of getting back only the first row for a key. Returns None when the segment isn’t registered, the key isn’t u64-coercible, or the segment doesn’t actually carry the key (bloom or page- index reject).

Source

pub fn lookup_by_pk( &self, table: &str, index_name: &str, key: &IndexKey, ) -> Option<Row<'_>>

v5.1: indexed PK lookup that dispatches per locator, returning the first matching row from either the hot tier (Table::rows) or a registered cold segment.

The cold path requires the index column to be coercible to a u64 (the segment’s PK type) and the segment payload to be a encode_row_body_dense-encoded row body for the same schema. v5.1 ships this for BIGINT / INT / SMALLINT PKs; other types fall through to hot-only behavior.

Returns None if (a) the table or index doesn’t exist, (b) the key isn’t in the index at all, or (c) the key was resolved to a stale locator (Hot index out of range, Cold segment id unknown, segment lookup miss). Does not surface segment-decode errors — those would indicate corrupted cold-tier files and should be caught at Catalog::load_segment_bytes time.

Source

pub fn promote_cold_row( &mut self, table_name: &str, index_name: &str, key: &IndexKey, ) -> Result<Option<usize>, StorageError>

v5.2.3: promote a frozen row back to the hot tier so an UPDATE / DELETE can mutate it. Reads the cold-tier row body (decoded from its registered segment), pushes it into table.rows via Table::insert (which also adds a fresh Hot(new_idx) locator on index_name), then retires the shadowed Cold locator via Table::remove_cold_locators_for_key. The cold-tier row in the segment file becomes garbage — recoverable when a future cold-segment compaction job lands.

Returns:

  • Ok(Some(new_hot_idx)) when the key resolved through a cold locator and the promote completed. new_hot_idx is the position the row now occupies in table.rows.
  • Ok(None) when the key has no Cold locator on the index (already hot, or wasn’t present at all). Callers treat this as “nothing to do here, fall back to the hot-only path”.

Errors when the table / index doesn’t exist, the index isn’t BTree, the cold segment is missing / can’t decode the row, or the inferred row body fails Table::insert validation.

Source

pub fn shadow_cold_row( &mut self, table_name: &str, index_name: &str, key: &IndexKey, ) -> Result<usize, StorageError>

v5.2.3: shadow a frozen row’s index entry. Used by DELETE when the row to remove lives in a cold-tier segment — the row body stays in the segment file (becoming garbage) but every Cold locator for key on index_name is removed so PK lookups stop returning it.

Returns the number of cold locators retired (0 when the key has no cold entries — the DELETE fell on a hot row or a key that was already absent). Errors when the table / index doesn’t exist or the index isn’t BTree.

Cold-segment compaction (which merges shadowed-heavy segments and reclaims their disk footprint) lands in a later v5.x sub-version; until then, repeated UPDATE/DELETE of cold rows can amplify cold-segment disk usage by up to 1-2× — still well under typical LSM-tree shadowing because SPG segments are bulk-baked, not write-merged.

Source

pub fn prepare_freeze_slice( &self, table_name: &str, index_name: &str, row_range: Range<usize>, ) -> Result<FreezeSlice, StorageError>

v6.7.4 — read-only slice preparation for the parallel freezer. Walks rows in row_range, builds the (pk_u64, encoded_body, IndexKey) triples that the coordinator’s k-way merge consumes, sorts the slice by pk_u64, and returns a FreezeSlice.

Caller invariants:

  • row_range.end <= table.rows.len() (caller’s job to compute the partition).
  • All slices passed to commit_freeze_slices must cover a contiguous half-open range [0, total_max_rows) with no gaps and no overlaps. The coordinator validates this invariant before committing.

&self-only — multiple workers can run this concurrently against the same Catalog reference under the engine’s write lock (workers don’t mutate; the coordinator does).

Source

pub fn commit_freeze_slices( &mut self, table_name: &str, index_name: &str, slices: Vec<FreezeSlice>, ) -> Result<FreezeReport, StorageError>

v6.7.4 — coordinator commit step. Merges N FreezeSlices into one segment via the standard encode_segment path, atomically swaps the catalog state (delete the union row range + register Cold locators + load the segment).

Validates that the slices cover a contiguous, gap-free, overlap-free half-open range starting at index 0 (the freezer always freezes “oldest first” — same semantics as the single-threaded Catalog::freeze_oldest_to_cold).

Empty slices → no-op success (returns a zero-row report without mutating). Total row count = Σ slice.rows.len().

Source

pub fn compact_cold_segments( &mut self, table_name: &str, index_name: &str, target_segment_bytes: u64, ) -> Result<CompactReport, StorageError>

v6.7.3 — compact every cold segment on (table, index) whose OwnedSegment::bytes().len() is below target_segment_bytes into a single larger merged segment. Rows present in source segment payloads but no longer referenced by any RowLocator::Cold on the index (DELETE’d + frozen rows retired via Catalog::shadow_cold_row) are GC’d in the merge.

Semantics:

  1. Walk the BTree index to collect every Cold locator that targets a small (< threshold) segment. Each such (key, segment_id) becomes a row in the merged segment; payload is looked up from the source segment in-place.
  2. Encode the collected rows into one new segment via encode_segment; register it via Catalog::load_segment_bytes (allocating a fresh merged_segment_id at the end of cold_segments).
  3. Rewrite the BTree index in one pass: every RowLocator::Cold { segment_id ∈ sources } becomes RowLocator::Cold { segment_id = merged_id, page_offset = 0 }. Hot locators are untouched.
  4. Tombstone every source slot via Catalog::tombstone_segment. Source segment payloads are no longer reachable through the catalog; the on-disk files are the caller’s concern.

On fewer than 2 candidate segments the catalog is not mutated and a no-op report (merged_segment_id: None, sources: []) is returned. This is the routine case — a freshly-frozen table has at most 1 small segment, no merge possible.

Atomicity: every mutating step runs after the read-only gather phase, so a panic before the merge encode leaves the catalog unchanged. The mutation block itself (load + rewrite + tombstone) takes only &mut self — callers serialise the engine write lock outside this function.

Errors when the table / index doesn’t exist, the index isn’t BTree, the index column type isn’t u64-coercible (cold-tier pre-condition), or a source segment fails its in-place row-body lookup (would indicate prior catalog corruption).

Source§

impl Catalog

Source

pub fn serialize(&self) -> Vec<u8>

Serialize the whole catalog (schema + every row) into a self-contained byte buffer. Format is documented above the impl block.

Source

pub fn deserialize(buf: &[u8]) -> Result<Self, StorageError>

Deserialize a previously-serialized catalog. Rejects bad magic, version mismatch, unknown tags, truncation, and trailing bytes.

Trait Implementations§

Source§

impl Clone for Catalog

Source§

fn clone(&self) -> Catalog

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Catalog

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Catalog

Source§

fn default() -> Catalog

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.