pub struct Engine { /* private fields */ }Implementations§
Source§impl Engine
impl Engine
pub fn new() -> Self
Sourcepub fn clone_snapshot(&self) -> CatalogSnapshot
pub fn clone_snapshot(&self) -> CatalogSnapshot
v7.11.0 — clone the engine’s committed catalog + read-time
state into a frozen CatalogSnapshot. Cheap (Catalog is
backed by PersistentVec; cloning is O(log n) per table).
Subsequent writes to this engine are invisible to the
snapshot; the snapshot is self-contained and can be moved
to another thread for concurrent execute_readonly_on_snapshot
calls. The basis for [AsyncReadHandle] in spg-embedded-tokio
and any other read-fanout pattern.
Sourcepub fn execute_readonly_on_snapshot(
snapshot: &CatalogSnapshot,
sql: &str,
) -> Result<QueryResult, EngineError>
pub fn execute_readonly_on_snapshot( snapshot: &CatalogSnapshot, sql: &str, ) -> Result<QueryResult, EngineError>
v7.11.1 — execute a read-only SQL statement against a
CatalogSnapshot without touching this engine. Same
semantics as execute_readonly but parameterised on the
snapshot’s catalog. Reject DDL/DML the same way
execute_readonly does. Static-on-Self so the caller can
dispatch without holding an Engine borrow alongside the
snapshot.
Sourcepub fn execute_readonly_on_snapshot_with_cancel(
snapshot: &CatalogSnapshot,
sql: &str,
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_readonly_on_snapshot_with_cancel( snapshot: &CatalogSnapshot, sql: &str, cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v7.11.1 — execute_readonly_on_snapshot with cooperative
cancellation. Builds a transient Engine over the snapshot
state, runs execute_readonly_with_cancel, drops. The
transient engine is cheap to construct (no I/O; everything
is just struct moves) and lets the existing read path stay
untouched.
Sourcepub fn restore(catalog: Catalog) -> Self
pub fn restore(catalog: Catalog) -> Self
Construct an engine restored from a previously-snapshotted catalog
(see snapshot()).
Sourcepub fn restore_envelope(buf: &[u8]) -> Result<Self, EngineError>
pub fn restore_envelope(buf: &[u8]) -> Result<Self, EngineError>
Restore an engine + user table from a v4.1 envelope produced
by snapshot_with_users(). Falls back to plain catalog-only
restore if the envelope magic isn’t present (so v3.x snapshot
files still load). v6.1.2 adds the optional publications
trailer (envelope v3); a v1/v2 envelope deserialises to an
empty publication table.
pub const fn users(&self) -> &UserStore
Sourcepub fn create_user(
&mut self,
name: &str,
password: &str,
role: Role,
salt: [u8; 16],
) -> Result<(), UserError>
pub fn create_user( &mut self, name: &str, password: &str, role: Role, salt: [u8; 16], ) -> Result<(), UserError>
salt is supplied by the caller (the host has a random
source; the engine is no_std). Caller should pass a fresh
16-byte random value per user.
pub fn drop_user(&mut self, name: &str) -> Result<(), UserError>
pub fn verify_user(&self, name: &str, password: &str) -> Option<Role>
Sourcepub const fn with_clock(self, clock: ClockFn) -> Self
pub const fn with_clock(self, clock: ClockFn) -> Self
Builder: attach a wall clock so NOW() / CURRENT_TIMESTAMP /
CURRENT_DATE evaluate to a real value instead of erroring out.
Sourcepub const fn with_salt_fn(self, f: SaltFn) -> Self
pub const fn with_salt_fn(self, f: SaltFn) -> Self
Builder: attach an OS-backed RNG for per-user password salts.
The host (spg-server) typically wires this to /dev/urandom.
Sourcepub const fn with_max_query_rows(self, n: usize) -> Self
pub const fn with_max_query_rows(self, n: usize) -> Self
Builder: cap the number of rows a single SELECT may return.
Exceeding the cap raises EngineError::RowLimitExceeded —
the bound is checked inside the executor so a runaway
catalog scan can’t allocate millions of rows before the
server gets a chance to reject the result.
Sourcepub const fn catalog(&self) -> &Catalog
pub const fn catalog(&self) -> &Catalog
The committed catalog. Note: during a transaction this returns the
pre-TX state — SELECT inside a TX goes through execute() and reads
the shadow. Tests that inspect outside-TX state should use this.
Sourcepub fn snapshot(&self) -> Vec<u8>
pub fn snapshot(&self) -> Vec<u8>
Serialize the committed catalog to bytes. v0.6 was full-snapshot; v0.9 adds the rule that an open TX’s shadow is never snapshotted — only the post-COMMIT state is persisted. v4.1 wraps the catalog in an envelope when there are users to persist; an empty user table snapshots as the bare catalog format (backwards-compat with v3.x readers). v6.1.2 adds publications to the envelope condition: either non-empty users OR non-empty publications now triggers the envelope path.
Sourcepub fn in_transaction(&self) -> bool
pub fn in_transaction(&self) -> bool
True when at least one TX slot is in flight. v4.41.1 runtime
invariant: at most one slot active at a time (dispatch holds
engine.write() across the entire wrap). v4.42 will let this
return true with multiple slots concurrently.
Sourcepub fn alloc_tx_id(&mut self) -> TxId
pub fn alloc_tx_id(&mut self) -> TxId
v4.41.1 allocate a fresh TX handle. Used by spg-server dispatch
to scope each implicit-wrap BEGIN..stmt..COMMIT to its own slot
in tx_catalogs. v4.42 — the commit-barrier leader allocates
one of these per task in its group, runs BEGIN+sql+COMMIT
sequentially under a single engine.write() so each task’s
mutations accumulate into shared state, then either keeps the
accumulated state (fsync OK) or restores the pre-image via
replace_catalog (fsync err).
Sourcepub fn replace_catalog(&mut self, catalog: Catalog)
pub fn replace_catalog(&mut self, catalog: Catalog)
v4.42 — atomically replace the live catalog. Used by the
commit-barrier leader to roll back a group whose batched
fsync failed: the leader snapshots engine.catalog().clone()
(O(1) Arc bump after the v4.39/v4.40 persistent migration)
at group start, sequentially applies each task’s BEGIN+sql+
COMMIT under the same write lock to accumulate mutations
into shared state, batches the WAL bytes, fsyncs once, and
on failure calls this with the pre-image to undo every
task in the group at once.
Does NOT touch tx_catalogs / current_tx. Any
explicit-TX slot from a concurrent client (created via the
legacy IMPLICIT_TX-less dispatch path or via the future
MVCC-readers v5+ work) has its own snapshot baked into the
slot — restoring self.catalog to the pre-image leaves
those slots untouched, exactly as they were when the leader
took the lock. The leader’s own implicit-TX slots are all
already discarded (exec_commit removed them as each
task’s COMMIT ran) by the time this is reached.
Sourcepub fn freeze_oldest_to_cold(
&mut self,
table_name: &str,
index_name: &str,
max_rows: usize,
) -> Result<FreezeReport, EngineError>
pub fn freeze_oldest_to_cold( &mut self, table_name: &str, index_name: &str, max_rows: usize, ) -> Result<FreezeReport, EngineError>
v6.7.0 — public shim around Catalog::freeze_oldest_to_cold
so tests + the spg-server freezer can drive a freeze without
reaching into the private active_catalog_mut. v6.7.4
parallel freezer will build on this surface.
Marks the table’s cached cold_row_count stale because the
freeze added cold locators that ANALYZE hasn’t yet refreshed.
Sourcepub fn receive_cold_segment(
&mut self,
segment_id: u32,
bytes: Vec<u8>,
) -> Result<(), EngineError>
pub fn receive_cold_segment( &mut self, segment_id: u32, bytes: Vec<u8>, ) -> Result<(), EngineError>
v6.7.5 — public shim used by the spg-server follower’s
segment-forwarding receiver. Registers a cold-tier segment
at a specific id (the master’s id, as transmitted on the
wire) so the follower’s BTree-Cold locators stay byte-
identical with the master’s. Wraps
Catalog::load_segment_bytes_at under the standard
clone-mutate-replace pattern.
Returns Ok(()) on success and on the “slot already
occupied” case — a follower mid-reconnect may receive a
segment chunk for a segment_id it already has on disk
(forwarded last session); the caller should treat that
path as a no-op rather than a fatal error.
Sourcepub fn compact_cold_segments_with_target(
&mut self,
target_segment_bytes: u64,
) -> Result<Vec<(String, String, CompactReport)>, EngineError>
pub fn compact_cold_segments_with_target( &mut self, target_segment_bytes: u64, ) -> Result<Vec<(String, String, CompactReport)>, EngineError>
v6.7.3 — public shim around Catalog::compact_cold_segments
driving every BTree index on every user table. Returns one
(table, index, report) triple for each merge that
actually happened (no-op (table, index) pairs are filtered
out so callers can size persist-side work to the live
merges). Caller is responsible for persisting each
report.merged_segment_bytes and updating the on-disk
segment registry; engine layer is no_std and never
touches disk.
Marks every touched table’s cached cold_row_count stale
— compaction GC’d some shadowed rows, so the count must be
re-derived on the next ANALYZE.
Sourcepub fn execute_readonly(&self, sql: &str) -> Result<QueryResult, EngineError>
pub fn execute_readonly(&self, sql: &str) -> Result<QueryResult, EngineError>
Read-only execute path. Succeeds for SELECT / SHOW TABLES
/ SHOW COLUMNS; returns EngineError::WriteRequired for
every other statement, so the caller can fall through to the
&mut self execute path under a write lock. Engine state is
not mutated even on the success path (rewrite_clock_calls
and resolve_order_by_position both mutate the locally-owned
AST, not self).
v4.0 concurrency: this is the entry point the server takes
under an RwLock::read() so multiple SELECT clients run in
parallel without serialising on a single mutex.
Sourcepub fn execute_readonly_with_cancel(
&self,
sql: &str,
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_readonly_with_cancel( &self, sql: &str, cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v4.5 — read path with cooperative cancellation. Token’s
is_cancelled is checked at the start (so a watchdog that
already fired returns Cancelled immediately) and at row-loop
checkpoints inside exec_select. SHOW paths are O(small) and
don’t bother checking.
pub fn execute(&mut self, sql: &str) -> Result<QueryResult, EngineError>
Sourcepub fn execute_with_cancel(
&mut self,
sql: &str,
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_with_cancel( &mut self, sql: &str, cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v4.5 — write path with cooperative cancellation. Same dispatch
as execute_in_with_cancel(sql, IMPLICIT_TX, cancel). Kept as
a separate entry point for backward-compat with the v4.5
public API.
Sourcepub fn execute_in(
&mut self,
sql: &str,
tx_id: TxId,
) -> Result<QueryResult, EngineError>
pub fn execute_in( &mut self, sql: &str, tx_id: TxId, ) -> Result<QueryResult, EngineError>
v4.41.1 multi-slot write entry. Routes sql through the TX
slot identified by tx_id so spg-server dispatch can scope
each implicit-wrap BEGIN..stmt..COMMIT to its own slot in
tx_catalogs. IMPLICIT_TX is the legacy single-slot path
every other caller (engine self-tests, replay, spg-embedded)
implicitly takes via execute() / execute_with_cancel().
Sourcepub fn execute_in_with_cancel(
&mut self,
sql: &str,
tx_id: TxId,
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_in_with_cancel( &mut self, sql: &str, tx_id: TxId, cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v4.41.1 write path with cooperative cancellation + explicit TX
scope. Sets self.current_tx for the duration of the call so
every exec_* helper transparently sees its TX’s shadow
catalog and savepoint stack; restores on exit so the field is
only valid mid-call (no leakage across calls).
Sourcepub fn prepare(&self, sql: &str) -> Result<Statement, ParseError>
pub fn prepare(&self, sql: &str) -> Result<Statement, ParseError>
v6.1.1 — parse and pre-process a SQL string ONCE so the
resulting Statement can be cached and re-executed via
Engine::execute_prepared. Returns the same Statement
the simple-query path would synthesise internally (clock
rewrites + ORDER BY position-ref resolution applied at
prepare time, since both are session-independent). The
$N placeholders in the SQL stay as Expr::Placeholder(n)
nodes; they’re resolved to concrete values per-call by
execute_prepared’s substitution walk.
Pgwire’s Parse (P) message lands here.
Sourcepub fn prepare_cached(&mut self, sql: &str) -> Result<Statement, ParseError>
pub fn prepare_cached(&mut self, sql: &str) -> Result<Statement, ParseError>
v6.3.0 — cached prepare. Returns a cloned Statement from
the plan cache on hit, runs the full prepare() path on miss
and inserts the resulting plan before returning. Skipping the
parse + JOIN-reorder pipeline on hit is the dominant win for
JDBC / sqlx / pgx clients that reuse the same SQL string.
Returns a cloned Statement (not a borrow) because the
pgwire layer owns its PreparedStmt map per-session and the
engine-level cache must stay available for other sessions.
Clone cost on a 5-table JOIN AST is well under the parse cost
it replaces.
Sourcepub fn plan_cache(&self) -> &PlanCache
pub fn plan_cache(&self) -> &PlanCache
v6.3.0 — read-only accessor for tests and v6.3.1 invalidation.
Sourcepub fn plan_cache_mut(&mut self) -> &mut PlanCache
pub fn plan_cache_mut(&mut self) -> &mut PlanCache
v6.3.0 — mutable accessor for v6.3.1 invalidation hooks.
Sourcepub fn describe_prepared(
&self,
stmt: &Statement,
) -> (Vec<u32>, Vec<ColumnSchema>)
pub fn describe_prepared( &self, stmt: &Statement, ) -> (Vec<u32>, Vec<ColumnSchema>)
v6.3.3 — Describe a prepared Statement without executing.
Returns (parameter_oids, output_columns). Empty
output_columns means the statement has no row-producing
shape we could resolve here (JOIN, subquery, non-SELECT, …)
— pgwire layer maps that to a NoData reply.
Sourcepub fn execute_prepared(
&mut self,
stmt: Statement,
params: &[Value],
) -> Result<QueryResult, EngineError>
pub fn execute_prepared( &mut self, stmt: Statement, params: &[Value], ) -> Result<QueryResult, EngineError>
v6.1.1 — execute a Statement previously returned by
Engine::prepare, substituting Expr::Placeholder(n)
nodes for the corresponding Value in params (1-based
per PG: $1 → params[0]). Bind-time string parameters
are decoded into typed Values by the pgwire layer before
this call so the resulting AST hits the same execution
path as a simple query — no SQL re-parse.
Pgwire’s Execute (E) message after a Bind (B) lands here.
Sourcepub const fn publications(&self) -> &Publications
pub const fn publications(&self) -> &Publications
v6.1.2 — read access to the publication catalog. Used by
the v6.1.5 publisher-side WAL filter, by SHOW PUBLICATIONS
(v6.1.3+), and by e2e tests that need to assert state without
going through the wire.
Sourcepub const fn subscriptions(&self) -> &Subscriptions
pub const fn subscriptions(&self) -> &Subscriptions
v6.1.4 — read access to the subscription catalog. Used by the subscription worker (read its own row to find its publications + last applied position), by SHOW SUBSCRIPTIONS, and by e2e tests asserting state directly.
Sourcepub fn subscription_advance(&mut self, name: &str, pos: u64) -> bool
pub fn subscription_advance(&mut self, name: &str, pos: u64) -> bool
v6.1.4 — write access to last_received_pos. Worker
calls this after each apply batch (under the engine’s
write-lock). Returns false when the subscription was
dropped between when the worker received the record and
when this call landed.
Sourcepub const fn with_activity_provider(self, f: ActivityProvider) -> Self
pub const fn with_activity_provider(self, f: ActivityProvider) -> Self
v6.5.2 — register a connection-state provider. spg-server
calls this at startup with a function that snapshots its
per-pgwire-connection registry. Engine reads through the
callback on SELECT * FROM spg_stat_activity.
Sourcepub const fn with_audit_providers(
self,
chain: AuditChainProvider,
verify: AuditVerifier,
) -> Self
pub const fn with_audit_providers( self, chain: AuditChainProvider, verify: AuditVerifier, ) -> Self
v6.5.3 — register audit chain provider + verifier.
Sourcepub const fn with_slow_query_log(
self,
threshold_us: u64,
logger: SlowQueryLogger,
) -> Self
pub const fn with_slow_query_log( self, threshold_us: u64, logger: SlowQueryLogger, ) -> Self
v6.5.6 — register a slow-query log callback. threshold_us
is the floor (in microseconds); only executes above the floor
fire the callback. spg-server wires this from
SPG_SLOW_QUERY_THRESHOLD_MS (default 100 ms).
Sourcepub fn set_plan_cache_max(&mut self, n: usize)
pub fn set_plan_cache_max(&mut self, n: usize)
v6.5.6 — operator knob for plan cache cap. spg-server reads
SPG_PLAN_CACHE_MAX env at startup; uses this to override
the compile-time default of 256.
Sourcepub fn query_stats(&self) -> &QueryStats
pub fn query_stats(&self) -> &QueryStats
v6.5.1 — read-only accessor for tests + v6.5.6 ops resets.
Sourcepub fn query_stats_mut(&mut self) -> &mut QueryStats
pub fn query_stats_mut(&mut self) -> &mut QueryStats
v6.5.1 — mutable accessor (clear, etc).
Sourcepub const fn statistics(&self) -> &Statistics
pub const fn statistics(&self) -> &Statistics
v6.2.0 — read access to the per-column statistics table.
Used by the planner (v6.2.2 selectivity functions read this),
by SELECT * FROM spg_statistic, and by e2e tests.
Sourcepub fn tables_needing_analyze(&self) -> Vec<String>
pub fn tables_needing_analyze(&self) -> Vec<String>
v6.2.1 — return tables whose modified-row count crossed the
auto-analyze threshold since the last ANALYZE on that table.
The threshold is 0.1 × max(row_count, MIN_ROWS_FOR_AUTO_ ANALYZE) — combines PG-style fractional + absolute lower
bound so a fresh / tiny table doesn’t get hammered on every
INSERT.
Designed to be cheap: walks every user table’s
Catalog::table_names() + reads statistics::modified_ since_last_analyze() (BTreeMap lookup). The background
worker calls this under engine.read() then drops the lock
before re-acquiring engine.write() for the actual ANALYZE.