Skip to main content

Engine

Struct Engine 

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

Implementations§

Source§

impl Engine

Source

pub fn new() -> Self

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn restore(catalog: Catalog) -> Self

Construct an engine restored from a previously-snapshotted catalog (see snapshot()).

Source

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.

Source

pub const fn users(&self) -> &UserStore

Source

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.

Source

pub fn drop_user(&mut self, name: &str) -> Result<(), UserError>

Source

pub fn verify_user(&self, name: &str, password: &str) -> Option<Role>

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn execute(&mut self, sql: &str) -> Result<QueryResult, EngineError>

Source

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.

Source

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().

Source

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).

Source

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.

Source

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.

Source

pub fn plan_cache(&self) -> &PlanCache

v6.3.0 — read-only accessor for tests and v6.3.1 invalidation.

Source

pub fn plan_cache_mut(&mut self) -> &mut PlanCache

v6.3.0 — mutable accessor for v6.3.1 invalidation hooks.

Source

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.

Source

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: $1params[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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub const fn with_audit_providers( self, chain: AuditChainProvider, verify: AuditVerifier, ) -> Self

v6.5.3 — register audit chain provider + verifier.

Source

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).

Source

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.

Source

pub fn query_stats(&self) -> &QueryStats

v6.5.1 — read-only accessor for tests + v6.5.6 ops resets.

Source

pub fn query_stats_mut(&mut self) -> &mut QueryStats

v6.5.1 — mutable accessor (clear, etc).

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Debug for Engine

Source§

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

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

impl Default for Engine

Source§

fn default() -> Engine

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> 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> Same for T

Source§

type Output = T

Should always be Self
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.