1use crate::types::{
6 CtePlan, DocumentIndexLookupPlan, HybridSearchPlan, HybridSearchTriplePlan, LateralLoopPlan,
7 LateralTopKPlan, MergePlan, RangeScanPlan, RecursiveScanPlan, TextSearchPlan,
8 VectorPrimaryDeletePlan, VectorPrimaryUpdatePlan,
9};
10use nodedb_types::DatabaseId;
11
12use crate::catalog::SqlCatalog;
13use crate::types::{FilterExpr, MergePlanAction, SqlPlan};
14
15use super::catalog_expr_fold::validate_expr;
16use super::catalog_plan_shapes::{
17 validate_aggregates, validate_projection, validate_sort_keys, validate_windows,
18};
19
20pub(super) fn validate_catalog_exprs(
21 plan: &SqlPlan,
22 catalog: &dyn SqlCatalog,
23 database_id: DatabaseId,
24 tenant_id: u64,
25) -> crate::Result<()> {
26 match plan {
27 SqlPlan::Scan {
28 filters,
29 projection,
30 sort_keys,
31 window_functions,
32 ..
33 }
34 | SqlPlan::DocumentIndexLookup(DocumentIndexLookupPlan {
35 filters,
36 projection,
37 sort_keys,
38 window_functions,
39 ..
40 }) => {
41 validate_filters(filters, catalog, database_id, tenant_id)?;
42 validate_projection(projection, catalog, database_id, tenant_id)?;
43 validate_sort_keys(sort_keys, catalog, database_id, tenant_id)?;
44 validate_windows(window_functions, catalog, database_id, tenant_id)?;
45 }
46 SqlPlan::PointGet { projection, .. }
47 | SqlPlan::RangeScan(RangeScanPlan { projection, .. }) => {
48 validate_projection(projection, catalog, database_id, tenant_id)?;
49 }
50 SqlPlan::Update {
51 assignments,
52 filters,
53 ..
54 }
55 | SqlPlan::VectorPrimaryUpdate(VectorPrimaryUpdatePlan {
56 assignments,
57 filters,
58 ..
59 }) => {
60 for (_, expr) in assignments {
61 validate_expr(expr, catalog, database_id, tenant_id)?;
62 }
63 validate_filters(filters, catalog, database_id, tenant_id)?;
64 }
65 SqlPlan::Delete { filters, .. }
66 | SqlPlan::VectorPrimaryDelete(VectorPrimaryDeletePlan { filters, .. }) => {
67 validate_filters(filters, catalog, database_id, tenant_id)?
68 }
69 SqlPlan::UpdateFrom {
70 source,
71 assignments,
72 target_filters,
73 ..
74 } => {
75 validate_catalog_exprs(source, catalog, database_id, tenant_id)?;
76 for (_, expr) in assignments {
77 validate_expr(expr, catalog, database_id, tenant_id)?;
78 }
79 validate_filters(target_filters, catalog, database_id, tenant_id)?;
80 }
81 SqlPlan::InsertSelect { source, .. } => {
82 validate_catalog_exprs(source, catalog, database_id, tenant_id)?
83 }
84 SqlPlan::Aggregate {
85 input,
86 group_by,
87 aggregates,
88 having,
89 sort_keys,
90 ..
91 } => {
92 validate_catalog_exprs(input, catalog, database_id, tenant_id)?;
93 for expr in group_by {
94 validate_expr(expr, catalog, database_id, tenant_id)?;
95 }
96 validate_aggregates(aggregates, catalog, database_id, tenant_id)?;
97 validate_filters(having, catalog, database_id, tenant_id)?;
98 validate_sort_keys(sort_keys, catalog, database_id, tenant_id)?;
99 }
100 SqlPlan::Union { inputs, .. } => {
101 for input in inputs {
102 validate_catalog_exprs(input, catalog, database_id, tenant_id)?;
103 }
104 }
105 SqlPlan::Intersect { left, right, .. } | SqlPlan::Except { left, right, .. } => {
106 validate_catalog_exprs(left, catalog, database_id, tenant_id)?;
107 validate_catalog_exprs(right, catalog, database_id, tenant_id)?;
108 }
109 SqlPlan::Cte(CtePlan { definitions, outer }) => {
110 for (_, definition) in definitions {
111 validate_catalog_exprs(definition, catalog, database_id, tenant_id)?;
112 }
113 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
114 }
115 SqlPlan::Subquery {
118 input,
119 filters,
120 projection,
121 window_functions,
122 sort_keys,
123 ..
124 } => {
125 validate_catalog_exprs(input, catalog, database_id, tenant_id)?;
126 validate_filters(filters, catalog, database_id, tenant_id)?;
127 validate_projection(projection, catalog, database_id, tenant_id)?;
128 validate_windows(window_functions, catalog, database_id, tenant_id)?;
129 validate_sort_keys(sort_keys, catalog, database_id, tenant_id)?;
130 }
131 SqlPlan::Join {
132 left,
133 right,
134 condition,
135 projection,
136 filters,
137 ..
138 } => {
139 validate_catalog_exprs(left, catalog, database_id, tenant_id)?;
140 validate_catalog_exprs(right, catalog, database_id, tenant_id)?;
141 if let Some(condition) = condition {
142 validate_expr(condition, catalog, database_id, tenant_id)?;
143 }
144 validate_projection(projection, catalog, database_id, tenant_id)?;
145 validate_filters(filters, catalog, database_id, tenant_id)?;
146 }
147 SqlPlan::LateralTopK(LateralTopKPlan {
148 outer,
149 inner_filters,
150 inner_order_by,
151 projection,
152 ..
153 }) => {
154 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
155 validate_filters(inner_filters, catalog, database_id, tenant_id)?;
156 validate_sort_keys(inner_order_by, catalog, database_id, tenant_id)?;
157 validate_projection(projection, catalog, database_id, tenant_id)?;
158 }
159 SqlPlan::LateralLoop(LateralLoopPlan {
160 outer,
161 inner,
162 projection,
163 ..
164 }) => {
165 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
166 validate_catalog_exprs(inner, catalog, database_id, tenant_id)?;
167 validate_projection(projection, catalog, database_id, tenant_id)?;
168 }
169 SqlPlan::Merge(MergePlan {
170 source, clauses, ..
171 }) => {
172 validate_catalog_exprs(source, catalog, database_id, tenant_id)?;
173 for clause in clauses {
174 validate_filters(&clause.extra_predicate, catalog, database_id, tenant_id)?;
175 match &clause.action {
176 MergePlanAction::Update { assignments } => {
177 for (_, expr) in assignments {
178 validate_expr(expr, catalog, database_id, tenant_id)?;
179 }
180 }
181 MergePlanAction::Insert { values, .. } => {
182 for expr in values {
183 validate_expr(expr, catalog, database_id, tenant_id)?;
184 }
185 }
186 MergePlanAction::Delete | MergePlanAction::DoNothing => {}
187 }
188 }
189 }
190 SqlPlan::VectorSearch {
191 filters,
192 projection,
193 ..
194 } => {
195 validate_filters(filters, catalog, database_id, tenant_id)?;
196 validate_projection(projection, catalog, database_id, tenant_id)?;
197 }
198 SqlPlan::MultiVectorSearch { projection, .. }
199 | SqlPlan::SparseSearch { projection, .. } => {
200 validate_projection(projection, catalog, database_id, tenant_id)?;
201 }
202 SqlPlan::HybridSearch(HybridSearchPlan {
203 filters,
204 projection,
205 ..
206 })
207 | SqlPlan::HybridSearchTriple(HybridSearchTriplePlan {
208 filters,
209 projection,
210 ..
211 }) => {
212 validate_filters(filters, catalog, database_id, tenant_id)?;
213 validate_projection(projection, catalog, database_id, tenant_id)?;
214 }
215 SqlPlan::TextSearch(TextSearchPlan {
216 filters,
217 projection,
218 ..
219 }) => {
220 validate_filters(filters, catalog, database_id, tenant_id)?;
221 validate_projection(projection, catalog, database_id, tenant_id)?;
222 }
223 SqlPlan::SpatialScan {
224 attribute_filters,
225 projection,
226 ..
227 } => {
228 validate_filters(attribute_filters, catalog, database_id, tenant_id)?;
229 validate_projection(projection, catalog, database_id, tenant_id)?;
230 }
231 SqlPlan::RecursiveScan(RecursiveScanPlan {
232 base_filters,
233 recursive_filters,
234 projection,
235 ..
236 }) => {
237 validate_filters(base_filters, catalog, database_id, tenant_id)?;
238 validate_filters(recursive_filters, catalog, database_id, tenant_id)?;
239 validate_projection(projection, catalog, database_id, tenant_id)?;
240 }
241 SqlPlan::ConstantResult { .. }
242 | SqlPlan::Insert(_)
243 | SqlPlan::KvInsert(_)
244 | SqlPlan::Upsert(_)
245 | SqlPlan::Truncate { .. }
246 | SqlPlan::TimeseriesScan(_)
247 | SqlPlan::TimeseriesIngest(_)
248 | SqlPlan::RecursiveValue(_)
249 | SqlPlan::CreateArray(_)
250 | SqlPlan::DropArray { .. }
251 | SqlPlan::AlterArray(_)
252 | SqlPlan::InsertArray(_)
253 | SqlPlan::DeleteArray(_)
254 | SqlPlan::ArraySlice(_)
255 | SqlPlan::ArrayProject(_)
256 | SqlPlan::ArrayAgg(_)
257 | SqlPlan::ArrayElementwise(_)
258 | SqlPlan::ArrayFlush { .. }
259 | SqlPlan::ArrayCompact { .. }
260 | SqlPlan::VectorPrimaryInsert(_)
261 | SqlPlan::VectorPrimaryTruncate(_)
262 | SqlPlan::CreateIndex(_)
263 | SqlPlan::DropIndex(_) => {}
264 }
265 Ok(())
266}
267
268fn validate_filters(
269 filters: &[crate::types::Filter],
270 catalog: &dyn SqlCatalog,
271 database_id: DatabaseId,
272 tenant_id: u64,
273) -> crate::Result<()> {
274 for filter in filters {
275 validate_filter_expr(&filter.expr, catalog, database_id, tenant_id)?;
276 }
277 Ok(())
278}
279
280fn validate_filter_expr(
281 expr: &FilterExpr,
282 catalog: &dyn SqlCatalog,
283 database_id: DatabaseId,
284 tenant_id: u64,
285) -> crate::Result<()> {
286 match expr {
287 FilterExpr::Expr(expr) => validate_expr(expr, catalog, database_id, tenant_id),
288 FilterExpr::And(children) | FilterExpr::Or(children) => {
289 validate_filters(children, catalog, database_id, tenant_id)
290 }
291 FilterExpr::Not(child) => {
292 validate_filter_expr(&child.expr, catalog, database_id, tenant_id)
293 }
294 _ => Ok(()),
295 }
296}