Skip to main content

nodedb_sql/engine_rules/
spatial.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Engine rules for spatial columnar collections.
4
5use crate::engine_rules::*;
6use crate::error::{Result, SqlError};
7use crate::types::*;
8
9pub struct SpatialRules;
10
11impl EngineRules for SpatialRules {
12    fn plan_insert(&self, p: InsertParams) -> Result<Vec<SqlPlan>> {
13        Ok(vec![SqlPlan::Insert {
14            collection: p.collection,
15            engine: EngineType::Spatial,
16            rows: p.rows,
17            column_defaults: p.column_defaults,
18            if_absent: p.if_absent,
19        }])
20    }
21
22    /// Spatial extends columnar and inherits the same upsert semantics:
23    /// duplicate PK tombstones the prior row; the new row (or merged form
24    /// when `on_conflict_updates` is non-empty) is appended.
25    fn plan_upsert(&self, p: UpsertParams) -> Result<Vec<SqlPlan>> {
26        Ok(vec![SqlPlan::Upsert {
27            collection: p.collection,
28            engine: EngineType::Spatial,
29            rows: p.rows,
30            column_defaults: p.column_defaults,
31            on_conflict_updates: p.on_conflict_updates,
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 spatial collection '{}'",
40                    p.collection
41                ),
42            });
43        }
44        // Plain scan on spatial collection — no spatial predicate involved.
45        // Spatial predicates (ST_DWithin, ST_Contains, etc.) are detected
46        // in select.rs and routed to SpatialScan directly, bypassing this.
47        Ok(SqlPlan::Scan {
48            collection: p.collection,
49            alias: p.alias,
50            engine: EngineType::Spatial,
51            filters: p.filters,
52            projection: p.projection,
53            sort_keys: p.sort_keys,
54            limit: p.limit,
55            offset: p.offset,
56            distinct: p.distinct,
57            window_functions: p.window_functions,
58            temporal: p.temporal,
59        })
60    }
61
62    fn plan_point_get(&self, p: PointGetParams) -> Result<SqlPlan> {
63        Ok(SqlPlan::PointGet {
64            collection: p.collection,
65            alias: p.alias,
66            engine: EngineType::Spatial,
67            key_column: p.key_column,
68            key_value: p.key_value,
69        })
70    }
71
72    fn plan_update(&self, p: UpdateParams) -> Result<Vec<SqlPlan>> {
73        Ok(vec![SqlPlan::Update {
74            collection: p.collection,
75            engine: EngineType::Spatial,
76            assignments: p.assignments,
77            filters: p.filters,
78            target_keys: p.target_keys,
79            returning: p.returning,
80        }])
81    }
82
83    fn plan_update_from(&self, p: UpdateFromParams) -> Result<Vec<SqlPlan>> {
84        Ok(vec![SqlPlan::UpdateFrom {
85            collection: p.collection,
86            engine: EngineType::Spatial,
87            source: p.source,
88            target_join_col: p.target_join_col,
89            source_join_col: p.source_join_col,
90            assignments: p.assignments,
91            target_filters: p.target_filters,
92            returning: p.returning,
93        }])
94    }
95
96    fn plan_delete(&self, p: DeleteParams) -> Result<Vec<SqlPlan>> {
97        Ok(vec![SqlPlan::Delete {
98            collection: p.collection,
99            engine: EngineType::Spatial,
100            filters: p.filters,
101            target_keys: p.target_keys,
102        }])
103    }
104
105    fn plan_aggregate(&self, p: AggregateParams) -> Result<SqlPlan> {
106        let base_scan = SqlPlan::Scan {
107            collection: p.collection,
108            alias: p.alias,
109            engine: EngineType::Spatial,
110            filters: p.filters,
111            projection: Vec::new(),
112            sort_keys: Vec::new(),
113            limit: None,
114            offset: 0,
115            distinct: false,
116            window_functions: Vec::new(),
117            temporal: crate::temporal::TemporalScope::default(),
118        };
119        Ok(SqlPlan::Aggregate {
120            input: Box::new(base_scan),
121            group_by: p.group_by,
122            aggregates: p.aggregates,
123            having: p.having,
124            limit: p.limit,
125            grouping_sets: None,
126            sort_keys: Vec::new(),
127        })
128    }
129
130    fn plan_merge(&self, p: MergeParams) -> Result<Vec<SqlPlan>> {
131        Err(SqlError::Unsupported {
132            detail: format!(
133                "MERGE is not supported on spatial collection '{}'",
134                p.collection
135            ),
136        })
137    }
138}