Skip to main content

Table

Struct Table 

Source
pub struct Table { /* private fields */ }

Implementations§

Source§

impl Table

Source

pub fn new(schema: TableSchema) -> Self

Source

pub fn rowids(&self) -> &PersistentVec<RowId>

v7.37.15 (Phase C.1) — read-only access to the stable row ids parallel to rows(). rowids().len() == rows().len() is the load-bearing lock-step invariant (asserted in debug builds at every mutation boundary alongside headers).

Source

pub fn rel_id(&self) -> RelId

v7.37.15 (Phase C.1) — this relation’s stable identity. RelId::UNASSIGNED for a bare Table::new; a real id once the catalog stamps it.

Source

pub fn assign_dense_rowids(&mut self)

v7.37.15 (Phase C.1) — rebuild the rowids vec so it is dense 1..=rows.len() and reset the allocator above it. Used on the load / snapshot-restore path where rows arrive without ids (pre-V6 envelope): every row gets a fresh id, sufficient while ids are process-local bookkeeping. Keeps the lock-step invariant against the freshly-loaded rows.

Source

pub fn dead_rows(&self) -> u64

v7.37.16 (autovacuum) — number of tombstoned-but-present hot rows. Incrementally maintained; drives the engine’s autovacuum threshold.

Source

pub fn bump_write_stats(&mut self, ins: u64, upd: u64, del: u64)

v7.39 (pg_stat knife A) — bump the volatile write counters the engine’s DML dispatcher reports per statement.

Source

pub fn write_stats(&self) -> (u64, u64, u64)

(n_tup_ins, n_tup_upd, n_tup_del) for pg_stat_user_tables.

Source

pub fn maintenance_stamps(&self) -> (Option<i64>, Option<i64>)

v7.39 (pg_stat knife C) — maintenance stamps for pg_stat_user_tables ((last_autovacuum_us, last_analyze_us)).

Source

pub fn stamp_autovacuum(&mut self, unix_us: i64)

Source

pub fn stamp_analyze(&mut self, unix_us: i64)

Source

pub fn scan_stats(&self) -> &ScanStats

v7.39 (pg_stat knife B) — the scan counters (read side of pg_stat_user_tables).

Source

pub fn note_seq_scan(&self)

v7.39 (pg_stat knife B) — one sequential scan over the visible rows, reported by engine scan loops that walk headers directly (parallel shards, the aggregate full scan) instead of scan_visible.

Source

pub fn note_index_scan(&self, fetched: u64)

v7.39 (pg_stat knife B) — one index scan returning fetched rows (the engine’s index-seek paths report here).

Source

pub fn headers(&self) -> &PersistentVec<RowHeader>

v7.37.15 (Phase A.2) — read-only access to the per-row MVCC visibility headers. headers().len() == rows().len() is the load-bearing invariant; Phase B scan paths consult headers()[idx] to decide visibility.

Source

pub fn insert_with_xmin( &mut self, row: Row<'static>, xmin: u64, ) -> Result<(), StorageError>

v7.37.15 (Phase C) — engine writer path. Same as [insert] but stamps xmin on the new row’s header with the writing transaction’s id (caller-supplied; obtained from the engine’s monotonic version counter). The fresh insert is alive (xmax = XMAX_ALIVE); a later UPDATE / DELETE will set xmax to a later version, leaving the row physically present until vacuum reclaims it (Phase D).

Callers in crate::row_header::next_version order:

  1. allocate version V via next_version()
  2. call insert_with_xmin(row, V)
  3. update any indexes (as insert does)

xmin = XMIN_FROZEN short-circuits to plain insert behaviour so the legacy in-memory / WAL-replay paths keep returning identical results when they end up here.

Source

pub fn set_prune_horizon(&mut self, horizon: u64)

v7.39 (round 493) — publish the snapshot floor the insert path may prune dead index entries under. See prune_horizon.

The engine sets this from vacuum_oldest_active() before a statement’s inserts. 0 disables pruning, which is the default and is always safe.

Source

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

v7.37.15 (Phase D) — single-table vacuum pass. Walks the header vec and physically removes any row whose delete commit is older than oldest_active_snapshot. Returns the number of reclaimable rows (with dry_run == true) or the number actually reclaimed.

oldest_active_snapshot is the floor of every live snapshot’s version — the engine maintains this; hosts pass it through.

Phase D ships the storage primitive. Hosts (spg-embedded / spg-server) schedule the pass on their own thread.

Source

pub fn mark_row_deleted( &mut self, position: usize, xmax: u64, ) -> Result<(), StorageError>

v7.37.15 (Phase C) — mark the row at position as deleted by version xmax. The row stays physically present; later vacuum (Phase D) reclaims it once no live snapshot can still see it.

Returns Err(Corrupt) on out-of-bounds and silently no-ops when the row is already tombstoned (a later DELETE on an already-deleted row should not change xmax — the original deletion wins).

Source

pub fn mark_rows_deleted(&mut self, positions: &[usize], xmax: u64) -> usize

v7.37.16 — batch form of Table::mark_row_deleted: stamp xmax on every alive, in-bounds position and record ONE RowChange::Tombstone carrying all affected RowIds (the codec and replay already handle multi-rowid records). The per-row form paid one redo record — a Vec alloc plus a log push — PER ROW, ~800 ns/row on a 10k-row gate-on DELETE (heavy_write del_10k). Semantics match the single-row form: already-tombstoned keeps its original xmax (first-deleter-wins), out-of-bounds is skipped. Returns the number of rows NEWLY tombstoned.

Source

pub fn extract_tx_writeset(&self, v: u64) -> TxWriteSet

v7.37.17 (Phase E RC rebase) — extract the write-set one writer version left on this table, expressed against stable [RowId]s so it can be replayed onto a FRESHER catalog clone whose physical slots differ. inserted carries INSERT rows and the new versions of UPDATEs (xmin == v); tombstoned carries the ids DELETE / UPDATE-old-version stamped (xmax == v). A row both inserted and tombstoned by the same version appears in both lists; replay applies inserts first, tombstones second — net effect identical.

Source

pub fn tombstone_conflicts(&self, rids: &[RowId], v: u64) -> Vec<RowId>

v7.37.17 (Phase E4 fix) — read-only conflict probe for a write-set’s tombstones against THIS (fresher) relation: a target RowId that is gone, or already tombstoned by a DIFFERENT version, is a write-write conflict. Callers use this BEFORE replay_tx_writeset so a conflicting UPDATE can drop its paired insert too (atomicity of tombstone+insert pairs).

Source

pub fn replay_tx_writeset(&mut self, ws: &TxWriteSet, v: u64) -> Vec<RowId>

v7.37.17 (Phase E RC rebase) — replay a write-set extracted from an OLDER clone of this relation onto this (fresher) one, keeping the original RowIds. Deliberately does NOT capture redo: a replay re-expresses writes the transaction already made, it is not a new mutation (the redo story rides the eventual COMMIT). Returns the ids whose tombstone could not be applied because the row is gone or already tombstoned by a DIFFERENT version — the write-write conflict surface (RC skips them per PG semantics; RR/SER turn them into serialization_failure — Phase E3).

Source

pub fn enable_redo(&mut self)

v7.34 (crash-recovery P0 #2) — start capturing row-level redo into this table (engine call before a mutating statement when persistence is on). Idempotent; existing captured changes are kept.

Source

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

v7.34 — drain the captured redo changes and stop capturing. Returns the physical RowChanges applied since enable_redo, in apply order (empty when capture was off or nothing changed).

Source

pub const fn hot_bytes(&self) -> u64

Total encoded byte size of every row currently in the hot tier (self.rows). See struct docs for the maintenance contract. Returns 0 for an empty table.

Source

pub const fn cold_row_count(&self) -> u64

v6.7.0 — cached count of cold-tier rows. See struct field docs for the staleness contract.

Source

pub fn set_cold_row_count(&mut self, n: u64)

v6.7.0 — overwrite the cached count. Called by the engine’s analyze_one_table after walking the indices.

Source

pub fn mark_cold_row_count_stale(&mut self)

v6.7.0 — mark the cached count as potentially out of date. Called by freezer / promote / DELETE paths so a subsequent spg_statistic read knows the number may not reflect the current state.

Source

pub const fn cold_row_count_stale(&self) -> bool

v6.7.0 — report whether the cached count is known to be out of date. Exposed for completeness; the virtual table surface returns the cached value regardless.

Source

pub const fn has_cold_rows_fast(&self) -> bool

v7.36 — O(1) “could this table possibly have cold rows?” predicate, intended for perf-critical executor hot paths that just need to skip the cold-tier branch when there’s definitely nothing there. Reads the cached cold_row_count:

  • cache fresh + cache == 0 → return false (fast path)
  • cache stale → return true (conservative; the executor pays the cold-aware path’s iter_cold_rows_* cost but stays correct)
  • cache fresh + cache > 0 → return true count_cold_locators remains the right call for the EXACT count (ANALYZE etc.) — its O(N) walk is unsuitable per join stage.
Source

pub fn cold_capable_indices(&self) -> impl Iterator<Item = &Index>

r944 — every BTree index a cold row could have been filed under.

The freeze writes a row’s locator into exactly ONE index (register_cold_locators takes a single index name) and the freezer picks that index by its own rule, so a reader that guesses a different one finds nothing. Round 943 is that bug: the freezer chose the first BTree index over any integer column, the scan looked at the first index on the primary key’s column, and 15 frozen rows of 40 vanished from a plain SELECT.

Union over all of them rather than guessing one. Because each row’s locator exists in exactly one index, the union yields every row once and needs no visited-set.

Deliberately NOT filtered to declared-unique indices. Freezing through an index whose keys repeat is a real limitation — resolve_cold_locator resolves BY KEY and cannot say which of two rows sharing one was meant — but that limit belongs to the freeze, which builds the segment keyed that way. Filtering it here only hides rows that were frozen anyway, which is the bug rather than a guard against it; the freezer’s own tests freeze tables whose integer index carries no uniqueness constraint.

Source

pub fn count_cold_locators(&self) -> u64

v6.7.0 — walk every BTree index and count RowLocator::Cold entries; return the MAX across indices. The freeze path (freeze_oldest_to_cold) writes cold locators to ONE designated index — that index ends up with the full per-row count. MAX-across-indices yields the precise count when a PK-style index exists; for multi-index tables without a covering index it’s a lower bound (rare in practice). Caller responsibility: only invoke under engine.write() or after taking ownership; the walk is O(N) over every (key, locator) pair.

Source

pub const fn schema(&self) -> &TableSchema

Source

pub const fn schema_mut(&mut self) -> &mut TableSchema

v6.7.2 — mutable schema accessor for ALTER TABLE paths. Used by Engine::exec_alter_table to flip per-table settings like hot_tier_bytes.

Source

pub const fn rows(&self) -> &PersistentVec<Row<'static>>

v4.39: returns the persistent row vector by reference. Callers that used to take &[Row] should switch to .iter() (via IntoIterator for &PersistentVec) or .get(i) for indexing.

Source

pub const fn row_count(&self) -> usize

Source

pub fn is_row_visible(&self, idx: usize, snapshot: &Snapshot) -> bool

v7.37.15 (Phase B) — answer “is row at idx visible under snapshot?” without exposing the header internals to the engine. Callers in scan paths consult this BEFORE yielding the row.

Defensive: out-of-bounds idx and the (impossible, asserted) length mismatch return false, mirroring “row is not there so it’s not visible.” Production scans never see either.

Phase A always returns true because every header is RowHeader::frozen() and Snapshot::unbounded() accepts every header. The full visibility behaviour engages once Phase C writers start stamping real xmin/xmax.

Source

pub fn is_all_visible(&self) -> bool

v7.37.15 (Phase D) — true iff every row in this table is known-all-visible to every snapshot (frozen xmin + alive xmax). When true, scan_visible skips the per-row check entirely — the scan degenerates to a plain rows().iter().

Maintained lazily: any insert/update that stamps a non- frozen xmin / xmax clears the cached flag; the next call to this method recomputes by walking the header vec. The walk is O(n) in the rare case (only when an MVCC writer ran on this table); steady-state legacy workloads hit the cached true and scan at pre-v7.37.15 speed.

Phase D wires this into the engine’s hot-tier scan optimisation; the bit also serves the per-segment all- visible bitmap (each cold segment is a separately tracked all_visible bit, but cold segments are frozen wholesale so they’re trivially true).

Source

pub fn position_visible(&self, idx: usize, snapshot: &Snapshot) -> bool

v7.37.15 (Phase B / D) — iterate over (idx, row) pairs whose header is visible under snapshot. This is the engine-side drop-in replacement for for (i, r) in t.rows().iter().enumerate() at scan sites. The check is a single branch + atomic register read inside the snapshot path; with Snapshot::unbounded the optimiser folds the gate away.

'a lifetime on snapshot keeps the helper zero-cost in the hot loop — no Arc bump, no allocation. v7.39 (round 560) — is the row at this position visible to the snapshot? Exposed so an index-only walk can decide without fetching the row it is deciding about.

Source

pub fn header_runs(&self) -> HeaderRuns<'_>

v7.39 (round 562) — the same question asked many times over ascending positions, without descending the header trie for each one.

A profile of the server serving a 100k-row index-only range put 27% of the connection thread’s CPU on the per-row visibility test. The headers are a PersistentVec — a 32-way trie — so position_visible is four dependent pointer loads per row. A sequential scan never pays that: it walks rows and headers in lockstep. An index walk cannot, but its positions arrive in ascending order and a leaf holds 32 of them, so keeping the run between calls turns 32 descents into one.

A position outside the held run just descends, so an index whose order is uncorrelated with position costs what it costs today.

Source

pub fn count_visible(&self, snapshot: &Snapshot) -> usize

v7.39 (round 559) — how many rows a snapshot sees, without touching a single one of them.

count(*) already short-circuits to rows.len() in the aggregate layer, so the O(1) part was never the problem: the cost is UPSTREAM, materialising every visible row so that layer can take its length. scan_visible zips the row trie with the headers, and a count needs only the headers.

Measured over pgwire on 500k rows, SELECT count(*):

    PG18 (2 parallel workers)   8.2 ms
    PG18 (parallelism off)     10.3 ms
    SPG                        16.5 ms   = 33 ns/row

— 1.6x slower than a single-threaded PG on the commonest aggregate there is, which no ledger entry recorded.

Source

pub fn scan_visible<'a, 'b>( &'a self, snapshot: &'b Snapshot, ) -> impl Iterator<Item = (usize, &'a Row<'static>)> + 'b
where 'a: 'b,

Source

pub fn resume_slot_after(&self, last: RowId, hint: usize) -> usize

The hot-tier slot a scan should resume at, given the last RowId it consumed and where that row used to sit.

Slots move. vacuum reclaims tombstones by rebuilding the row vector, so every position after the first reclaimed one shifts down — a reader that remembered a bare index would silently skip or repeat rows. Row ids do not move: they are allocated monotonically and never reused, which makes them the only stable way to say “carry on after this row”.

hint is the position that row occupied when it was read. It is still right whenever nothing was reclaimed under the reader, so the check costs one lookup; the binary search is the fallback for when it is not, and it works because appends only ever push larger ids and reclaiming preserves their order.

Source

pub fn scan_visible_from<'a, 'b>( &'a self, start: usize, snapshot: &'b Snapshot, ) -> impl Iterator<Item = (usize, &'a Row<'static>)> + 'b
where 'a: 'b,

The same visibility-gated walk as Table::scan_visible, resuming at hot-tier index start.

A server-side cursor hands out its result in batches and has to continue where the previous batch stopped. Restarting the walk per batch and discarding a growing prefix would make an N-batch drain quadratic in the row count, so the resume point is a parameter rather than something the caller skips over.

start is a hot-tier position, not a RowId: callers that resume across a compaction must re-derive it, which is why the cursor path only resumes tables with no cold segments.

note_seq_scan fires only for start == 0. One cursor drained in 300 batches is one sequential scan of the table, and counting it 300 times would misreport pg_stat_user_tables.seq_scan.

Source

pub fn indices_mut(&mut self) -> &mut [Index]

v6.8.0 — exposed for the engine layer to patch Index::included_columns post-creation. Could fold into add_index once the engine’s IF-NOT-EXISTS guard moves up, but the patch shape is the minimal change for v6.8.0.

Source

pub fn indices(&self) -> &[Index]

Source

pub fn next_auto_value(&self, col_pos: usize) -> Option<i64>

Compute the next AUTO_INCREMENT value for the column at col_pos. Defined as max(existing) + 1, falling back to 1 when the column currently holds no integer values. NULL / non- integer cells are skipped. Returns None when the column isn’t an integer type.

Source

pub fn index_on(&self, column_position: usize) -> Option<&Index>

Return the first index defined over column_position, if any. (v0.8 supports at most one index per column logically; the search just picks the first match.)

Source

pub fn insert(&mut self, row: Row<'static>) -> Result<(), StorageError>

Insert one row after validating it matches the schema (length + type). Returns StorageError on mismatch — the table is left unchanged. Updates every defined index with the new row’s key.

Source

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

Build a new B-tree index over the named column. Rebuilds from existing rows. Errors if column_name doesn’t exist or the index name is taken.

Source

pub fn ensure_excl_range_index(&mut self, column_position: usize)

v7.39 (round 215) — ensure a range-exclusion index exists on column_position, building it from the current rows. Idempotent: a second call for the same column is a no-op. Called at CREATE TABLE / ALTER ADD EXCLUDE and on catalog load (rebuild-from-constraints). Tombstoned rows are indexed too (they are filtered by the consumer via is_deleted() at query time — the established index pattern).

Source

pub fn excl_range_index( &self, column_position: usize, ) -> Option<&PersistentBTreeMap<(i128, u8), Vec<RowLocator>>>

v7.39 (round 215) — the range-exclusion index on column_position, if one was built. The EXCLUDE enforcement path probes its predecessor + successors to find candidate overlaps in O(log n).

Source

pub fn add_nsw_index( &mut self, name: String, column_name: &str, m: usize, ) -> Result<(), StorageError>

Build a new NSW (HNSW-flavoured) index over the named column. Required for ORDER BY col <-> literal LIMIT k to plan as a graph traversal instead of a full scan. Column must be a Vector type. m is the maximum number of neighbours per node.

Source

pub fn rebuild_nsw_index( &mut self, name: &str, new_encoding: Option<VecEncoding>, ) -> Result<(), StorageError>

v6.0.4 — synchronous rebuild of the named NSW index. If new_encoding is Some(target) and differs from the column’s current encoding, every stored cell at the indexed column is re-coded into the target encoding before the new graph builds. Returns IndexNotFound if no index by that name exists and Unsupported for non-NSW indexes (BTree REBUILD is a no-op the engine layer rejects, not a storage-level concept).

Holds the caller’s &mut self for the duration — no concurrency / staging / WAL-replay machinery in v6.0.4. The “live” optimisation lands as v6.0.4.1.

Source

pub fn restore_nsw_index( &mut self, name: String, column_name: &str, graph: NswGraph, ) -> Result<(), StorageError>

Restore an NSW index from a pre-built graph (used on deserialize). Skips the bulk-build pass since the topology is already known. Returns DuplicateIndex or ColumnNotFound on schema mismatch as usual.

Source

pub fn restore_btree_index( &mut self, name: String, column_name: &str, map: PersistentBTreeMap<IndexKey, Vec<RowLocator>>, ) -> Result<(), StorageError>

Restore a BTree index from a pre-built (IndexKey, Vec<RowLocator>) map. Used by Catalog::deserialize when reading a v9 (or later) catalog snapshot — the map travels on disk so cold-tier locators survive a round-trip, instead of being rebuilt from self.rows (which would lose every Cold entry). Same error contract as Table::add_index.

Source

pub fn restore_brin_index( &mut self, name: String, column_name: &str, column_type: DataType, ) -> Result<(), StorageError>

v6.7.1 — public restore counterpart for BRIN indices. Used by Catalog::deserialize when a v10 snapshot carries a BRIN index entry. BRIN carries no in-memory data — only the column_type snapshot is restored.

Source

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

v6.7.1 — public CREATE INDEX counterpart for BRIN. Creates the index entry with a snapshot of the indexed column’s current DataType.

Source

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

v7.12.3 — Build a new GIN inverted index over a tsvector column. Populates posting lists from existing rows. Errors if the column doesn’t exist, isn’t TsVector, or the index name is taken.

Source

pub fn restore_gin_index( &mut self, name: String, column_name: &str, map: PersistentBTreeMap<String, Vec<RowLocator>>, ) -> Result<(), StorageError>

v7.12.3 — Restore a GIN index from a deserialised snapshot. Mirrors Self::restore_btree_index but takes the GIN’s word → Vec<RowLocator> posting-list map (already populated from the catalog stream) instead of an IndexKey map.

Source

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

v7.15.0 — gin_trgm_ops GIN over a TEXT column. Walks every row, shingles the cell into PG-compatible trigrams, and builds the posting-list map. NULL / non-TEXT cells contribute nothing (no trigrams).

Source

pub fn restore_gin_trgm_index( &mut self, name: String, column_name: &str, map: PersistentBTreeMap<String, Vec<RowLocator>>, ) -> Result<(), StorageError>

v7.15.0 — restore a trigram-GIN from its catalog snapshot payload. Mirrors Self::restore_gin_index.

Source

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

v7.17.0 Phase 2.2 — MySQL FULLTEXT KEY GIN over a TEXT column. Walks every row, tokenises the cell into lower- cased word lexemes (fts_simple::simple_lex — same rule as to_tsvector('simple', text)), and builds the posting-list map. NULL / non-TEXT cells contribute nothing (no lexemes).

Source

pub fn restore_gin_fulltext_index( &mut self, name: String, column_name: &str, map: PersistentBTreeMap<String, Vec<RowLocator>>, ) -> Result<(), StorageError>

v7.17.0 Phase 2.2 — restore a fulltext-GIN from its catalog snapshot payload. Mirrors Self::restore_gin_trgm_index.

Source

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

v7.37.8(sentori Epic 5 P2)— JSONB-GIN over a Json / Jsonb column. Walks every row, extracts canonical (path, leaf) tokens via crate::jsonb_gin::extract_tokens, and builds the posting-list map. NULL or non-Json cells contribute no tokens(<col> @> <jsonb> against a NULL row is always false so absence here is correct).

Source

pub fn restore_gin_jsonb_index( &mut self, name: String, column_name: &str, map: PersistentBTreeMap<String, Vec<RowLocator>>, ) -> Result<(), StorageError>

v7.37.8 — restore a JSONB-GIN from its catalog snapshot payload. Mirrors Self::restore_gin_fulltext_index.

Source

pub fn register_cold_locators<I>( &mut self, index_name: &str, locators: I, ) -> Result<usize, StorageError>
where I: IntoIterator<Item = (IndexKey, RowLocator)>,

v5.1: register cold-tier locators on a BTree index. Used after Catalog::load_segment_bytes to wire every cold- tier row’s PK back to its segment so Catalog::lookup_by_pk can resolve it. Each call appends to the index — keys that already have hot or cold locators keep them. Returns the number of locators registered.

Pre-v5.2 (freezer) this is the only path that adds Cold variants to a PB; post-freezer the background freezer thread produces these as a batch under the engine write lock and this API becomes its in-memory primitive.

Errors if index_name doesn’t exist or names an NSW graph (NSW indices don’t carry per-key row locators — they’re vector-search structures).

Source

pub fn register_gin_cold_locators<I>( &mut self, index_name: &str, locators: I, ) -> Result<usize, StorageError>
where I: IntoIterator<Item = (String, RowLocator)>,

v7.12.3 — GIN-side parallel to Self::register_cold_locators. Re-attaches word → cold RowLocator posting-list entries after the from-rows rebuild loop. Errors when the index doesn’t exist or isn’t a GIN. Both tsvector-GIN and trigram-GIN variants share posting-list shape (String → Vec<RowLocator>), so this helper accepts either.

Source

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

v5.2.3: remove every Cold locator currently registered on index_name under the given key. Hot locators for the same key are left in place — useful when a row has just been promoted hot-side and the caller wants the old Cold pointer retired without losing the new hot entry.

Returns the number of cold locators removed (0 when the key has only hot entries or the key isn’t present at all). Errors when the index doesn’t exist or isn’t a BTree.

Source

pub fn add_column(&mut self, col: ColumnSchema, fill_value: Value<'static>)

v7.13.0 — append a new column to the schema and back-fill every existing row with fill_value. Used by the engine’s ALTER TABLE t ADD COLUMN … handler (mailrs round-5 G1). Indices on existing columns keep working — column positions don’t shift since the new column lands at the end — so no index rebuild is needed.

Source

pub fn set_partial_predicate(&mut self, idx: usize, pred: Option<String>)

v7.15.0 — replace the partial-index predicate source on the index at slot idx. Used by ALTER TABLE … RENAME COLUMN after the engine rewrites column-identifier references in the predicate source text. Pure metadata edit; index rows are unaffected (they’re keyed by column position, not predicate text).

Source

pub fn rename_column(&mut self, col_pos: usize, new_name: &str)

v7.15.0 — rename the column at col_pos to new_name. The on-disk row encoding is positional, so no row rewrite is needed; only the schema’s column name changes. Indices, UCs, FKs all key off column positions and are unaffected. Source-text references that hold the column name (CHECK predicates, partial-index predicates, runtime DEFAULT expressions, trigger UPDATE OF lists) are rewritten by the engine before this helper is called — the storage layer doesn’t depend on spg-sql and so can’t re-parse the predicate sources itself.

Source

pub fn drop_column(&mut self, col_pos: usize)

v7.13.3 — drop the column at col_pos. Removes the entry from the schema, the value from every row, any index that references the column (pure drop, not shift), and shifts every remaining index/UC/FK column position that pointed past col_pos down by one. Used by ALTER TABLE t DROP COLUMN <c> (mailrs round-7 S8). FK dependents on this column must already have been removed by the caller (CASCADE path); the helper assumes only same-column index removal is needed.

Source

pub fn truncate(&mut self)

v4.4: delete the rows at the given positions in one pass. positions must be unique; ordering doesn’t matter. Indices are rebuilt from scratch (cheaper than tracking incremental shifts across both B-tree and NSW). Returns the number of rows removed. v7.17.0 Phase 1.3 — wipe every row. Used by REFRESH MATERIALIZED VIEW; same effect as delete_rows((0..N).into()) but skips the per-position bookkeeping for the all-removed fast path. Indices are rebuilt (empty).

Source

pub fn delete_rows(&mut self, positions: &[usize]) -> usize

Source

pub fn delete_rows_no_index(&mut self, positions: &[usize]) -> usize

v7.37.5 (mailrs crash-recovery Ask 3) — row-only delete for the WAL-replay batch path: removes the rows + decrements hot_bytes, does NOT call rebuild_indices() and does NOT capture redo. The caller is responsible for invoking rebuild_indices_pub once after a sequence of *_no_index mutations on this table. Skipping the per-call rebuild closes the O(records × rows × indices × log rows) replay blow-up (5000 DELETEs × 100k × 13 × ln 100k ≈ minutes → seconds). Returns the number of rows actually removed (dedup + bounds- filtered identically to delete_rows).

Source

pub fn rebuild_indices_pub(&mut self)

v7.37.5 — public alias for the private rebuild_indices helper. Used by Catalog::apply_redo to coalesce per-record rebuilds across a batch of RowChanges into one rebuild per touched table.

Source

pub fn set_rows_and_rebuild_indices_with_rowids( &mut self, new_rows: PersistentVec<Row<'static>>, new_hot_bytes: u64, rowids: &[RowId], headers: &[RowHeader], )

v7.37.5 (mailrs crash-recovery Ask 3) — replace the table’s row vector + hot_bytes in one shot, then rebuild every index from the new rows. Used by Catalog::apply_redo’s batched run: a contiguous slice of RowChanges targeting this table is composed into a final (PersistentVec<Row>, hot_bytes) pair via in-memory bookkeeping, then handed to this method ONCE for index regeneration. Replaces N per- record rebuild_indices calls with 1 per run. v7.39 (flip crash-replay P0) — like Self::set_rows_and_rebuild_indices but KEEPS the caller’s per-slot RowIds. Redo replay applies one WAL record per statement; reassigning ids between records broke every later record’s tombstone targets (they name the ids the crashed process allocated), resurrecting deleted rows. The id allocator advances past every preserved id so post-replay inserts never collide.

Source

pub fn set_rows_and_rebuild_indices( &mut self, new_rows: PersistentVec<Row<'static>>, new_hot_bytes: u64, )

Source

pub fn insert_no_index(&mut self, row: Row<'static>) -> Result<(), StorageError>

v7.37.5 (mailrs crash-recovery Ask 3) — row-only insert for the WAL-replay batch path: pushes the row + bumps hot_bytes, and does NOT update any index (B-tree, GIN, NSW). The caller is responsible for invoking rebuild_indices_pub once after a sequence of *_no_index mutations on this table. Schema validation (arity + per-column type compatibility) is applied so a malformed redo log surfaces honestly.

Source

pub fn update_row_no_index( &mut self, position: usize, new_values: Vec<Value<'static>>, ) -> Result<(), StorageError>

v7.37.5 (mailrs crash-recovery Ask 3) — row-only update for the WAL-replay batch path: replaces the row at position + adjusts hot_bytes, and does NOT touch any index. Skipping the per-update incremental index work is safe because the trailing rebuild_indices_pub regenerates indices from self.rows in their final state.

Source

pub fn update_row( &mut self, position: usize, new_values: Vec<Value<'static>>, ) -> Result<(), StorageError>

v4.4: replace the row at position with new_values (must match the schema arity + types). v7.20: index maintenance is incremental — only indices whose key value changed are touched (B-tree entry move in place; NSW / BRIN / GIN fall back to a full rebuild when their column changed).

Trait Implementations§

Source§

impl Clone for Table

Source§

fn clone(&self) -> Table

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 Table

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl !Freeze for Table

§

impl RefUnwindSafe for Table

§

impl Send for Table

§

impl Sync for Table

§

impl Unpin for Table

§

impl UnsafeUnpin for Table

§

impl UnwindSafe for Table

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.