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