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

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