pub struct Catalog {
pub cold_read_stats: ColdReadStats,
/* private fields */
}Fields§
§cold_read_stats: ColdReadStatsv7.39 (pg_stat blks knife) — see ColdReadStats.
Implementations§
Source§impl Catalog
impl Catalog
Sourcepub const TEMP_NAME_MARKER: &'static str = "__spg_temp_"
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.
Sourcepub fn vacuum_all(
&mut self,
oldest_active_snapshot: u64,
dry_run: bool,
) -> VacuumReport
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.
pub const fn new() -> Self
Sourcepub const fn functions(&self) -> &BTreeMap<String, FunctionDef>
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.
Sourcepub fn create_function(
&mut self,
def: FunctionDef,
or_replace: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn functions_named(&self, name: &str) -> Vec<&FunctionDef>
pub fn functions_named(&self, name: &str) -> Vec<&FunctionDef>
v7.39 (read01 round 62) — every overload of name.
Sourcepub fn function_by_key(&self, key: &str) -> Option<&FunctionDef>
pub fn function_by_key(&self, key: &str) -> Option<&FunctionDef>
v7.39 (read01 round 62) — one overload, by its signature key.
Sourcepub fn drop_function_by_key(&mut self, key: &str) -> bool
pub fn drop_function_by_key(&mut self, key: &str) -> bool
v7.39 (read01 round 62) — drop ONE overload. true if it was there.
Sourcepub fn drop_function(&mut self, name: &str) -> bool
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.
Sourcepub fn schema_acl(&self) -> &[AclItem]
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).
pub fn schema_acl_mut(&mut self) -> &mut Vec<AclItem>
Sourcepub fn database_acl(&self) -> &[AclItem]
pub fn database_acl(&self) -> &[AclItem]
v7.39 (read01 round 60) — the database’s ACL.
pub fn database_acl_mut(&mut self) -> &mut Vec<AclItem>
Sourcepub fn sequence_mut(&mut self, name: &str) -> Option<&mut SequenceDef>
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.
Sourcepub fn function_mut(&mut self, name: &str) -> Option<&mut FunctionDef>
pub fn function_mut(&mut self, name: &str) -> Option<&mut FunctionDef>
v7.39 (read01 round 61) — mutable function access, for GRANT.
Sourcepub const fn sequences_all(&self) -> &BTreeMap<String, SequenceDef>
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.
Sourcepub fn sequence(&self, name: &str) -> Option<&SequenceDef>
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.
Sourcepub fn has_sequence(&self, name: &str) -> bool
pub fn has_sequence(&self, name: &str) -> bool
Does a sequence of this logical name exist for this session?
Sourcepub fn sequence_key(&self, name: &str) -> String
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.
Sourcepub fn create_sequence(
&mut self,
def: SequenceDef,
if_not_exists: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn rename_sequence(
&mut self,
old: &str,
new: &str,
) -> Result<(), StorageError>
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.
pub fn drop_sequence(&mut self, name: &str) -> bool
Sourcepub fn sequence_counters(&self) -> Vec<(String, i64, bool)>
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.
Sourcepub fn restore_sequence_counters(&mut self, saved: &[(String, i64, bool)])
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.
pub fn sequence_next_value(&mut self, name: &str) -> Result<i64, StorageError>
Sourcepub fn sequence_current_value(&self, name: &str) -> Result<i64, StorageError>
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.
Sourcepub fn sequence_set_value(
&mut self,
name: &str,
value: i64,
is_called: bool,
) -> Result<i64, StorageError>
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.
Sourcepub const fn views_all(&self) -> &BTreeMap<String, ViewDef>
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.
Sourcepub fn view(&self, name: &str) -> Option<&ViewDef>
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.
Sourcepub fn has_view(&self, name: &str) -> bool
pub fn has_view(&self, name: &str) -> bool
Does a view of this logical name exist for this session?
Sourcepub fn view_key(&self, name: &str) -> String
pub fn view_key(&self, name: &str) -> String
The storage key a view of this logical name resolves to.
Sourcepub fn create_view(
&mut self,
def: ViewDef,
or_replace: bool,
if_not_exists: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn drop_view(&mut self, name: &str) -> bool
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.
Sourcepub const fn materialized_views(&self) -> &BTreeMap<String, String>
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.
Sourcepub fn register_materialized_view(&mut self, name: String, body: String)
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.
Sourcepub fn drop_materialized_view_source(&mut self, name: &str) -> bool
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.
Sourcepub const fn enum_types(&self) -> &BTreeMap<String, EnumDef>
pub const fn enum_types(&self) -> &BTreeMap<String, EnumDef>
v7.17.0 Phase 1.4 — read-only handle to user-defined ENUM catalog.
Sourcepub fn create_enum_type(&mut self, def: EnumDef) -> Result<(), StorageError>
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).
Sourcepub fn rename_enum_value(
&mut self,
type_name: &str,
old: &str,
new: &str,
) -> Result<(), StorageError>
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).
Sourcepub fn set_comment(&mut self, key: &str, text: Option<&str>)
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.
Sourcepub fn comment(&self, key: &str) -> Option<&str>
pub fn comment(&self, key: &str) -> Option<&str>
v7.39 (read01 round 50) — the comment on an object, if any.
Sourcepub fn set_db_role_setting(
&mut self,
database: &str,
role: &str,
param: &str,
value: Option<&str>,
)
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.
Sourcepub fn create_replication_slot(
&mut self,
name: &str,
plugin: &str,
slot_type: &str,
) -> Result<(), String>
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.
Sourcepub fn drop_replication_slot(&mut self, name: &str) -> Result<(), String>
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.
pub const fn replication_slots(&self) -> &BTreeMap<String, (String, String)>
Sourcepub fn reset_db_role_settings(&mut self, database: &str, role: &str)
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.
pub const fn db_role_settings( &self, ) -> &BTreeMap<(String, String), BTreeMap<String, String>>
Sourcepub const fn comments(&self) -> &BTreeMap<String, String>
pub const fn comments(&self) -> &BTreeMap<String, String>
v7.39 (read01 round 50) — every (key, text) pair, for the
pg_description view.
Sourcepub fn drop_comments_for(&mut self, kind: &str, name: &str)
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.
pub fn add_enum_value( &mut self, type_name: &str, label: &str, if_not_exists: bool, position: Option<(bool, String)>, ) -> Result<bool, StorageError>
pub fn drop_enum_type(&mut self, name: &str) -> bool
Sourcepub const fn domain_types(&self) -> &BTreeMap<String, DomainDef>
pub const fn domain_types(&self) -> &BTreeMap<String, DomainDef>
v7.17.0 Phase 1.5 — read-only handle to DOMAIN catalog.
Sourcepub fn create_domain_type(&mut self, def: DomainDef) -> Result<(), StorageError>
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.
Sourcepub fn drop_domain_type(&mut self, name: &str) -> bool
pub fn drop_domain_type(&mut self, name: &str) -> bool
v7.17.0 Phase 1.5 — drop a DOMAIN by name.
Sourcepub const fn composite_types(&self) -> &BTreeMap<String, CompositeDef>
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.
Sourcepub fn create_composite_type(
&mut self,
def: CompositeDef,
) -> Result<(), StorageError>
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).
Sourcepub fn drop_composite_type(&mut self, name: &str) -> bool
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.
Sourcepub const fn user_schemas(&self) -> &BTreeSet<String>
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.
Sourcepub fn schema_exists(&self, name: &str) -> bool
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.
Sourcepub fn create_schema(
&mut self,
name: String,
if_not_exists: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn drop_schema(&mut self, name: &str) -> Result<bool, StorageError>
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.
Sourcepub 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>
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.
Sourcepub fn triggers(&self) -> &[TriggerDef]
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.
Sourcepub fn triggers_mut(&mut self) -> &mut Vec<TriggerDef>
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.
Sourcepub fn create_trigger(
&mut self,
def: TriggerDef,
or_replace: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn drop_trigger(&mut self, name: &str, table: &str) -> bool
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.
Sourcepub fn statistics_ext(&self) -> &[StatisticsExtDef]
pub fn statistics_ext(&self) -> &[StatisticsExtDef]
v7.39 (round 280) — the catalogued extended-statistics objects.
Sourcepub fn large_objects(&self) -> &BTreeMap<u32, Vec<u8>>
pub fn large_objects(&self) -> &BTreeMap<u32, Vec<u8>>
v7.39 (round 287) — every large object, ascending by OID.
Sourcepub fn large_object(&self, oid: u32) -> Option<&[u8]>
pub fn large_object(&self, oid: u32) -> Option<&[u8]>
The bytes of one large object, or None when no such OID exists.
Sourcepub fn create_large_object(
&mut self,
oid: u32,
bytes: Vec<u8>,
) -> Result<u32, String>
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.
Sourcepub fn put_large_object(
&mut self,
oid: u32,
offset: usize,
data: &[u8],
) -> Result<(), String>
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.
Sourcepub fn truncate_large_object(
&mut self,
oid: u32,
len: usize,
) -> Result<(), String>
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).
Sourcepub fn unlink_large_object(&mut self, oid: u32) -> bool
pub fn unlink_large_object(&mut self, oid: u32) -> bool
Remove a large object. false when the OID was not there.
Sourcepub fn create_statistics_ext(
&mut self,
def: StatisticsExtDef,
) -> Result<(), String>
pub fn create_statistics_ext( &mut self, def: StatisticsExtDef, ) -> Result<(), String>
Register one. Err(name) when the name is taken.
Sourcepub fn drop_statistics_ext(&mut self, name: &str) -> bool
pub fn drop_statistics_ext(&mut self, name: &str) -> bool
Drop one by name; false when absent.
Sourcepub fn create_rule(
&mut self,
def: RuleDef,
or_replace: bool,
) -> Result<(), StorageError>
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.
Sourcepub fn drop_rule(&mut self, name: &str, table: &str) -> bool
pub fn drop_rule(&mut self, name: &str, table: &str) -> bool
v7.39 (round 139) — drop a RULE by (name, table).
pub fn create_table(&mut self, schema: TableSchema) -> Result<(), StorageError>
Sourcepub fn set_temp_prefix(&mut self, prefix: Option<String>)
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).
Sourcepub fn temp_name_for(&self, name: &str) -> Option<String>
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.
pub fn get(&self, name: &str) -> Option<&Table>
pub fn get_mut(&mut self, name: &str) -> Option<&mut Table>
Sourcepub fn dirty_tables(&self) -> &BTreeSet<String>
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.
Sourcepub fn clear_dirty_tables(&mut self)
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”.
Sourcepub fn install_table(&mut self, name: &str, table: Table)
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.
Sourcepub fn tables_position_of(&self, name: &str) -> Option<usize>
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).
Sourcepub fn tables_at(&self, idx: usize) -> Option<&Table>
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.
Sourcepub fn apply_redo(&mut self, changes: &[RowChange]) -> Result<(), StorageError>
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.
Sourcepub fn enable_redo_all(&mut self)
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).
Sourcepub fn drain_redo(&mut self) -> Vec<RowChange>
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.
pub fn table_count(&self) -> usize
Sourcepub fn drop_table(&mut self, name: &str) -> bool
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.
Sourcepub fn rename_table(&mut self, old: &str, new: &str) -> Result<(), StorageError>
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_tablepointed at the old name now points at the new name, so FK enforcement keeps working - every trigger watching the table updates its
tablefield ReturnsOkon success;Err(StorageError::TableNotFound)when the old name isn’t in the catalog andErr(StorageError::DuplicateTable)when the new name is already taken.
Sourcepub fn rename_index(&mut self, old: &str, new: &str) -> Result<(), StorageError>
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.
Sourcepub fn drop_named_index(&mut self, name: &str) -> bool
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.
Sourcepub fn table_names(&self) -> Vec<String>
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).
Sourcepub fn listed_name<'a>(&self, stored: &'a str) -> Option<&'a str>
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.
Sourcepub fn visible_table_names(&self) -> Vec<String>
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.
Sourcepub fn load_segment_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<u32, StorageError>
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.
Sourcepub fn load_segment_bytes_at(
&mut self,
target_id: u32,
bytes: Vec<u8>,
) -> Result<(), StorageError>
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.
Sourcepub fn tombstone_segment(&mut self, segment_id: u32) -> Result<(), StorageError>
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.
Sourcepub fn cold_segment_count(&self) -> usize
pub fn cold_segment_count(&self) -> usize
Number of active (non-tombstoned) cold segments.
Sourcepub fn has_any_cold_segments(&self) -> bool
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.
Sourcepub fn cold_segment_slot_count(&self) -> usize
pub fn cold_segment_slot_count(&self) -> usize
Slot count including tombstones (= the next id the
no-arg load_segment_bytes would allocate).
Sourcepub fn cold_segment_ids_global(&self) -> Vec<u32>
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.
Sourcepub fn hot_tier_bytes(&self) -> u64
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.
Sourcepub fn freeze_oldest_to_cold(
&mut self,
table_name: &str,
index_name: &str,
max_rows: usize,
) -> Result<FreezeReport, StorageError>
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:
- The first
max_rowsrows (by hot-tier position — same as insertion order under v4.39PersistentVec) are read. - Rows are sorted ascending by PK and serialised into a new
segment via
encode_segment. - The hot rows are dropped via
Table::delete_rows; therebuild_indicesit triggers regeneratesHotlocators for every remaining row (their positions shift down bymax_rows). ExistingColdlocators in this index — from a previous freeze — are also rebuilt but with empty payload since rebuild reads onlyself.rows; this routine re-registers them at the end of the call so the user-visible state preserves all prior cold locators. - The new segment is loaded into
self.cold_segmentsviaCatalog::load_segment_bytes(allocating a freshsegment_id). NewColdlocators 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
Errand leaves catalog unchanged if any step fails before the atomic swap point.
Errors:
StorageError::Corruptfor missing table/index, non-BTreeindex, non-integer PK column,max_rows == 0, ormax_rows > row_count.- The encoder’s
SegmentErrorsurfaces asCorrupt(the only realistic source is “a single row is larger than the page size”; SPG schemas don’t hit it in practice).
Sourcepub fn cold_segment(&self, segment_id: u32) -> Option<&OwnedSegment>
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.
Sourcepub fn resolve_cold_locator(
&self,
table_name: &str,
segment_id: u32,
key: &IndexKey,
) -> Option<Row<'static>>
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).
Sourcepub fn lookup_by_pk(
&self,
table: &str,
index_name: &str,
key: &IndexKey,
) -> Option<Row<'_>>
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.
Sourcepub fn promote_cold_row(
&mut self,
table_name: &str,
index_name: &str,
key: &IndexKey,
) -> Result<Option<usize>, StorageError>
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_idxis the position the row now occupies intable.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.
Sourcepub fn shadow_cold_row(
&mut self,
table_name: &str,
index_name: &str,
key: &IndexKey,
) -> Result<usize, StorageError>
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.
Sourcepub fn prepare_freeze_slice(
&self,
table_name: &str,
index_name: &str,
row_range: Range<usize>,
) -> Result<FreezeSlice, StorageError>
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_slicesmust 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).
Sourcepub fn commit_freeze_slices(
&mut self,
table_name: &str,
index_name: &str,
slices: Vec<FreezeSlice>,
) -> Result<FreezeReport, StorageError>
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().
Sourcepub fn compact_cold_segments(
&mut self,
table_name: &str,
index_name: &str,
target_segment_bytes: u64,
) -> Result<CompactReport, StorageError>
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:
- 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. - Encode the collected rows into one new segment via
encode_segment; register it viaCatalog::load_segment_bytes(allocating a freshmerged_segment_idat the end ofcold_segments). - Rewrite the BTree index in one pass: every
RowLocator::Cold { segment_id ∈ sources }becomesRowLocator::Cold { segment_id = merged_id, page_offset = 0 }. Hot locators are untouched. - 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
impl Catalog
Sourcepub fn serialize(&self) -> Vec<u8> ⓘ
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.
Sourcepub fn deserialize(buf: &[u8]) -> Result<Self, StorageError>
pub fn deserialize(buf: &[u8]) -> Result<Self, StorageError>
Deserialize a previously-serialized catalog. Rejects bad magic, version mismatch, unknown tags, truncation, and trailing bytes.