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