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 execute_readonly_prepared_on_snapshot(
snapshot: &CatalogSnapshot,
stmt: Statement,
params: &[Value],
) -> Result<QueryResult, EngineError>
pub fn execute_readonly_prepared_on_snapshot( snapshot: &CatalogSnapshot, stmt: Statement, params: &[Value], ) -> Result<QueryResult, EngineError>
v7.18 — execute a previously-prepared Statement against a
CatalogSnapshot in read-only mode. Mirror of
Engine::execute_prepared for the fan-out read path:
substitutes Expr::Placeholder(n) nodes from params, then
dispatches through Engine::execute_readonly_stmt_with_cancel
(writes / DDL hit EngineError::WriteRequired). Static-on-Self
so multiple readonly threads can dispatch against the same
snapshot concurrently without an Engine borrow.
Schema drift contract. The Statement was prepared against
some prior catalog. If the snapshot’s catalog has since
diverged (DDL renamed / dropped a referenced column / table),
execution surfaces the normal EngineError — same shape as
PG’s “cached plan must not change result type”. Caller decides
whether to re-prepare; engine does NOT auto-retry.
Sourcepub fn execute_readonly_prepared_on_snapshot_with_cancel(
snapshot: &CatalogSnapshot,
stmt: Statement,
params: &[Value],
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_readonly_prepared_on_snapshot_with_cancel( snapshot: &CatalogSnapshot, stmt: Statement, params: &[Value], cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v7.18 — cancellable variant of
Engine::execute_readonly_prepared_on_snapshot.
Sourcepub fn describe_prepared_on_snapshot(
snapshot: &CatalogSnapshot,
stmt: &Statement,
) -> (Vec<u32>, Vec<ColumnSchema>)
pub fn describe_prepared_on_snapshot( snapshot: &CatalogSnapshot, stmt: &Statement, ) -> (Vec<u32>, Vec<ColumnSchema>)
v7.18 — describe a prepared Statement against a
CatalogSnapshot. Same (parameter_oids, output_columns)
shape as Engine::describe_prepared; resolves names
against the snapshot’s catalog instead of self. Pure
function — no engine state read.
Sourcepub fn is_readonly_sql(sql: &str) -> bool
pub fn is_readonly_sql(sql: &str) -> bool
v7.18 — does this SQL string classify as read-only? Parses
sql with the engine parser and consults
Statement::is_readonly(). A parse error returns false
(route to the writer path so the user sees the canonical
parse error from the writer’s simple-query dispatch).
Static-on-Self so the spg-sqlx connection layer can ask
without an Engine borrow.
Sourcepub fn prepare_on_snapshot(
snapshot: &CatalogSnapshot,
sql: &str,
) -> Result<Statement, ParseError>
pub fn prepare_on_snapshot( snapshot: &CatalogSnapshot, sql: &str, ) -> Result<Statement, ParseError>
v7.18 — parse + plan a SQL string against a
CatalogSnapshot. Mirror of Engine::prepare for the
readonly fan-out path: applies the same prepare-time
transforms (clock rewrite, GROUP BY ALL expansion, ORDER
BY position resolve, cost-based JOIN reorder) but resolves
catalog + statistics against the snapshot, not a live
engine. Static-on-Self — AsyncReadHandle::prepare calls
this without taking the writer lock so multiple read
handles can prepare concurrently against frozen views.
§Errors
Propagates ParseError from the parser. Schema
validation deferred to execute time, same as
Engine::prepare.
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 fn execute_prepared_with_cancel(
&mut self,
stmt: Statement,
params: &[Value],
cancel: CancelToken<'_>,
) -> Result<QueryResult, EngineError>
pub fn execute_prepared_with_cancel( &mut self, stmt: Statement, params: &[Value], cancel: CancelToken<'_>, ) -> Result<QueryResult, EngineError>
v7.17.0 Phase 2.3 — prepared-statement entry that honors a
caller-supplied CancelToken. Mirrors execute_prepared’s
current_tx save/restore so the extended-query path stays
transactionally consistent with the simple-query path.
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.
Sourcepub fn session_param(&self, name: &str) -> Option<&str>
pub fn session_param(&self, name: &str) -> Option<&str>
v7.12.1 — read a session parameter set via SET. Used by
the FTS function dispatcher to resolve the default config
for to_tsvector(text) / plainto_tsquery(text) etc.