Skip to main content

nodedb_sql/engine_rules/
kv.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Engine rules for key-value collections.
4
5use crate::engine_rules::*;
6use crate::error::{Result, SqlError};
7use crate::types::*;
8
9pub struct KvRules;
10
11impl EngineRules for KvRules {
12    fn plan_insert(&self, p: InsertParams) -> Result<Vec<SqlPlan>> {
13        // KV INSERT is handled separately via KvInsert plan variant.
14        // The caller (dml.rs) builds KvInsert directly because it needs
15        // to split key vs value columns. This method handles the case
16        // where a generic INSERT reaches KV — which should not happen
17        // because dml.rs checks engine type before calling engine_rules.
18        // But if it does, we error clearly.
19        Err(SqlError::Unsupported {
20            detail: format!(
21                "INSERT into KV collection '{}' must use the KV insert path",
22                p.collection
23            ),
24        })
25    }
26
27    fn plan_upsert(&self, _p: UpsertParams) -> Result<Vec<SqlPlan>> {
28        // KV upsert is handled via the KvInsert path (KV PUT is naturally an upsert).
29        // Same as plan_insert — KV INSERT uses KvInsert which is already upsert semantics.
30        Err(SqlError::Unsupported {
31            detail: "KV UPSERT must use the KV insert path (KV PUT is naturally an upsert)".into(),
32        })
33    }
34
35    fn plan_scan(&self, p: ScanParams) -> Result<SqlPlan> {
36        if p.temporal.is_temporal() {
37            return Err(SqlError::Unsupported {
38                detail: format!(
39                    "FOR SYSTEM_TIME / FOR VALID_TIME is not supported on KV collection '{}'",
40                    p.collection
41                ),
42            });
43        }
44        Ok(SqlPlan::Scan {
45            collection: p.collection,
46            alias: p.alias,
47            engine: EngineType::KeyValue,
48            filters: p.filters,
49            projection: p.projection,
50            sort_keys: p.sort_keys,
51            limit: p.limit,
52            offset: p.offset,
53            distinct: p.distinct,
54            window_functions: p.window_functions,
55            temporal: p.temporal,
56        })
57    }
58
59    fn plan_point_get(&self, p: PointGetParams) -> Result<SqlPlan> {
60        Ok(SqlPlan::PointGet {
61            collection: p.collection,
62            alias: p.alias,
63            engine: EngineType::KeyValue,
64            key_column: p.key_column,
65            key_value: p.key_value,
66            projection: p.projection,
67        })
68    }
69
70    fn plan_update(&self, p: UpdateParams) -> Result<Vec<SqlPlan>> {
71        Ok(vec![SqlPlan::Update {
72            collection: p.collection,
73            engine: EngineType::KeyValue,
74            assignments: p.assignments,
75            filters: p.filters,
76            target_keys: p.target_keys,
77            returning: p.returning,
78        }])
79    }
80
81    fn plan_update_from(&self, p: UpdateFromParams) -> Result<Vec<SqlPlan>> {
82        Err(SqlError::Unsupported {
83            detail: format!(
84                "UPDATE ... FROM is not supported on KV collection '{}'; \
85                 KV keys are opaque and cannot participate in cross-table join updates",
86                p.collection
87            ),
88        })
89    }
90
91    fn plan_delete(&self, p: DeleteParams) -> Result<Vec<SqlPlan>> {
92        Ok(vec![SqlPlan::Delete {
93            collection: p.collection,
94            engine: EngineType::KeyValue,
95            filters: p.filters,
96            target_keys: p.target_keys,
97        }])
98    }
99
100    fn plan_aggregate(&self, p: AggregateParams) -> Result<SqlPlan> {
101        let base_scan = SqlPlan::Scan {
102            collection: p.collection,
103            alias: p.alias,
104            engine: EngineType::KeyValue,
105            filters: p.filters,
106            projection: Vec::new(),
107            sort_keys: Vec::new(),
108            limit: None,
109            offset: 0,
110            distinct: false,
111            window_functions: Vec::new(),
112            temporal: crate::temporal::TemporalScope::default(),
113        };
114        Ok(SqlPlan::Aggregate {
115            input: Box::new(base_scan),
116            group_by: p.group_by,
117            group_by_aliases: Vec::new(),
118            output_order: Vec::new(),
119            aggregates: p.aggregates,
120            having: p.having,
121            limit: p.limit,
122            grouping_sets: None,
123            sort_keys: Vec::new(),
124        })
125    }
126
127    fn plan_merge(&self, p: MergeParams) -> Result<Vec<SqlPlan>> {
128        Err(SqlError::Unsupported {
129            detail: format!(
130                "MERGE is not supported on key-value collection '{}'",
131                p.collection
132            ),
133        })
134    }
135}