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}