Skip to main content

nodedb_sql/engine_rules/
array.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Engine rules for the ND sparse array engine.
4//!
5//! Array operations live behind a dedicated DDL/DML surface
6//! (`CREATE ARRAY`, `INSERT INTO ARRAY`, `DELETE FROM ARRAY`,
7//! `DROP ARRAY`). The standard SQL DML pathways are unsupported by
8//! design — the planner refuses them with a hint pointing to the
9//! correct surface (use ARRAY_SLICE / ARRAY_AGG for table-valued reads).
10
11use crate::engine_rules::*;
12use crate::error::{Result, SqlError};
13use crate::types::*;
14
15pub struct ArrayRules;
16
17impl EngineRules for ArrayRules {
18    fn plan_insert(&self, _p: InsertParams) -> Result<Vec<SqlPlan>> {
19        Err(unsupported(
20            "INSERT",
21            "use INSERT INTO ARRAY <name> COORDS (...) VALUES (...)",
22        ))
23    }
24
25    fn plan_upsert(&self, _p: UpsertParams) -> Result<Vec<SqlPlan>> {
26        Err(unsupported("UPSERT", "arrays do not support UPSERT"))
27    }
28
29    fn plan_scan(&self, _p: ScanParams) -> Result<SqlPlan> {
30        Err(unsupported(
31            "SELECT",
32            "use ARRAY_SLICE or ARRAY_AGG for table-valued array reads",
33        ))
34    }
35
36    fn plan_point_get(&self, _p: PointGetParams) -> Result<SqlPlan> {
37        Err(unsupported(
38            "point lookup",
39            "arrays have no primary key; use ARRAY_SLICE for coord-range reads",
40        ))
41    }
42
43    fn plan_update(&self, _p: UpdateParams) -> Result<Vec<SqlPlan>> {
44        Err(unsupported(
45            "UPDATE",
46            "arrays are write-by-coord; re-INSERT to overwrite",
47        ))
48    }
49
50    fn plan_update_from(&self, _p: UpdateFromParams) -> Result<Vec<SqlPlan>> {
51        Err(unsupported(
52            "UPDATE ... FROM",
53            "arrays are write-by-coord; re-INSERT to overwrite",
54        ))
55    }
56
57    fn plan_delete(&self, _p: DeleteParams) -> Result<Vec<SqlPlan>> {
58        Err(unsupported(
59            "DELETE",
60            "use DELETE FROM ARRAY <name> WHERE COORDS IN ((...), ...)",
61        ))
62    }
63
64    fn plan_aggregate(&self, _p: AggregateParams) -> Result<SqlPlan> {
65        Err(unsupported(
66            "GROUP BY",
67            "use ARRAY_AGG for table-valued array aggregates",
68        ))
69    }
70
71    fn plan_merge(&self, _p: MergeParams) -> Result<Vec<SqlPlan>> {
72        Err(unsupported(
73            "MERGE",
74            "use INSERT INTO ARRAY / DELETE FROM ARRAY for array engine mutations",
75        ))
76    }
77}
78
79fn unsupported(op: &str, hint: &str) -> SqlError {
80    SqlError::Unsupported {
81        detail: format!("operation {op} not supported on array engine; {hint}"),
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    fn ip() -> InsertParams {
90        InsertParams {
91            collection: "g".into(),
92            columns: vec![],
93            rows: vec![],
94            column_defaults: vec![],
95            if_absent: false,
96        }
97    }
98
99    #[test]
100    fn every_arm_is_unsupported() {
101        let r = ArrayRules;
102        assert!(matches!(
103            r.plan_insert(ip()).unwrap_err(),
104            SqlError::Unsupported { .. }
105        ));
106        assert!(matches!(
107            r.plan_upsert(UpsertParams {
108                collection: "g".into(),
109                columns: vec![],
110                rows: vec![],
111                column_defaults: vec![],
112                on_conflict_updates: vec![],
113            })
114            .unwrap_err(),
115            SqlError::Unsupported { .. }
116        ));
117    }
118}