Skip to main content

nodedb_sql/engine_rules/
rules.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! The `EngineRules` trait: single source of truth for per-engine DML routing.
4
5use crate::error::Result;
6use crate::types::SqlPlan;
7
8use super::params::{
9    AggregateParams, DeleteParams, InsertParams, InsertSelectParams, MergeParams, PointGetParams,
10    ScanParams, TruncateParams, UpdateFromParams, UpdateParams, UpsertParams,
11};
12
13/// Engine-specific planning rules.
14///
15/// Each engine type implements this trait to produce the correct `SqlPlan`
16/// variant for each operation, or return an error if the operation is not
17/// supported. This is the single source of truth for operation routing —
18/// no downstream code should ever check engine type to decide routing.
19pub trait EngineRules {
20    /// Plan an INSERT. Returns `Err` if the engine does not support inserts
21    /// (e.g. timeseries routes to `TimeseriesIngest` instead).
22    fn plan_insert(&self, params: InsertParams) -> Result<Vec<SqlPlan>>;
23    /// Plan an `INSERT ... SELECT` into a collection on this engine. The
24    /// Control Plane copies the rows and gives each a fresh surrogate, which
25    /// the document engines support. Every other engine returns
26    /// `Err(SqlError::Unsupported)`.
27    fn plan_insert_select(&self, params: InsertSelectParams) -> Result<Vec<SqlPlan>>;
28    /// Plan an UPSERT (insert-or-merge). Returns `Err` for append-only or
29    /// columnar engines that don't support merge semantics.
30    fn plan_upsert(&self, params: UpsertParams) -> Result<Vec<SqlPlan>>;
31    /// Plan a table scan (SELECT without point-get optimization).
32    fn plan_scan(&self, params: ScanParams) -> Result<SqlPlan>;
33    /// Plan a point lookup by primary key. Returns `Err` for engines
34    /// that don't support O(1) key lookups (e.g. timeseries).
35    fn plan_point_get(&self, params: PointGetParams) -> Result<SqlPlan>;
36    /// Plan an UPDATE. Returns `Err` for append-only engines.
37    fn plan_update(&self, params: UpdateParams) -> Result<Vec<SqlPlan>>;
38    /// Plan an `UPDATE target SET ... FROM src WHERE ...`.
39    ///
40    /// Returns `Err(SqlError::Unsupported)` for engines that cannot participate
41    /// as an update target in a cross-table update (timeseries, kv-with-opaque-keys).
42    fn plan_update_from(&self, params: UpdateFromParams) -> Result<Vec<SqlPlan>>;
43    /// Plan a DELETE (point or bulk).
44    fn plan_delete(&self, params: DeleteParams) -> Result<Vec<SqlPlan>>;
45    /// Plan a TRUNCATE. Returns `Err(SqlError::Unsupported)` for engines
46    /// that carry no whole-collection clear on the SQL surface (array).
47    fn plan_truncate(&self, params: TruncateParams) -> Result<Vec<SqlPlan>>;
48    /// Plan a GROUP BY / aggregate query.
49    fn plan_aggregate(&self, params: AggregateParams) -> Result<SqlPlan>;
50    /// Plan a MERGE statement.
51    ///
52    /// Returns `Err(SqlError::Unsupported)` for engines that do not support
53    /// MERGE semantics (everything except `document_schemaless` and
54    /// `document_strict`).
55    fn plan_merge(&self, params: MergeParams) -> Result<Vec<SqlPlan>>;
56    /// True when the engine accepts fields the schema never declares, so a
57    /// read of an undeclared name resolves to NULL instead of raising
58    /// `SqlError::UnknownColumn`.
59    fn accepts_undeclared_columns(&self) -> bool;
60    /// Columns every collection on this engine carries whether or not the
61    /// DDL declares them. A reference to one resolves like a declared column.
62    fn implicit_columns(&self) -> &'static [&'static str] {
63        &[]
64    }
65    /// True when the engine stores one value whose column name the statement
66    /// picks. Such a collection resolves any name while it declares no value
67    /// column, and closes to the declared set once it names one.
68    fn value_column_name_is_free(&self) -> bool {
69        false
70    }
71}