1use nodedb_types::DatabaseId;
13
14use crate::catalog::SqlCatalog;
15use crate::types::{Filter, FilterExpr, MergePlanAction, SqlExpr, SqlPlan};
16
17use super::catalog_expr_fold::fold_expr;
18use super::catalog_plan_shapes::{fold_aggregates, fold_projection, fold_sort_keys, fold_windows};
19use super::catalog_plan_validate::validate_catalog_exprs;
20
21pub fn fold_catalog_exprs_in_plan(
28 plan: SqlPlan,
29 catalog: &dyn SqlCatalog,
30 database_id: DatabaseId,
31 tenant_id: u64,
32) -> crate::Result<SqlPlan> {
33 validate_catalog_exprs(&plan, catalog, database_id, tenant_id)?;
34 Ok(walk_plan(plan, catalog, database_id, tenant_id))
35}
36
37fn walk_plan(
38 plan: SqlPlan,
39 catalog: &dyn SqlCatalog,
40 database_id: DatabaseId,
41 tenant_id: u64,
42) -> SqlPlan {
43 match plan {
44 SqlPlan::Scan {
45 collection,
46 alias,
47 engine,
48 mut filters,
49 mut projection,
50 mut sort_keys,
51 limit,
52 offset,
53 distinct,
54 mut window_functions,
55 temporal,
56 } => {
57 for f in &mut filters {
58 fold_filter(f, catalog, database_id, tenant_id);
59 }
60 fold_projection(&mut projection, catalog, database_id, tenant_id);
61 fold_sort_keys(&mut sort_keys, catalog, database_id, tenant_id);
62 fold_windows(&mut window_functions, catalog, database_id, tenant_id);
63 SqlPlan::Scan {
64 collection,
65 alias,
66 engine,
67 filters,
68 projection,
69 sort_keys,
70 limit,
71 offset,
72 distinct,
73 window_functions,
74 temporal,
75 }
76 }
77
78 SqlPlan::Union { inputs, distinct } => SqlPlan::Union {
79 inputs: inputs
80 .into_iter()
81 .map(|input| walk_plan(input, catalog, database_id, tenant_id))
82 .collect(),
83 distinct,
84 },
85
86 SqlPlan::Intersect { left, right, all } => SqlPlan::Intersect {
87 left: Box::new(walk_plan(*left, catalog, database_id, tenant_id)),
88 right: Box::new(walk_plan(*right, catalog, database_id, tenant_id)),
89 all,
90 },
91
92 SqlPlan::Except { left, right, all } => SqlPlan::Except {
93 left: Box::new(walk_plan(*left, catalog, database_id, tenant_id)),
94 right: Box::new(walk_plan(*right, catalog, database_id, tenant_id)),
95 all,
96 },
97
98 SqlPlan::Cte { definitions, outer } => SqlPlan::Cte {
99 definitions: definitions
100 .into_iter()
101 .map(|(name, plan)| (name, walk_plan(plan, catalog, database_id, tenant_id)))
102 .collect(),
103 outer: Box::new(walk_plan(*outer, catalog, database_id, tenant_id)),
104 },
105
106 SqlPlan::Join {
107 left,
108 right,
109 on,
110 join_type,
111 condition,
112 limit,
113 mut projection,
114 mut filters,
115 } => {
116 for f in &mut filters {
117 fold_filter(f, catalog, database_id, tenant_id);
118 }
119 let condition = condition.map(|e| fold_expr(e, catalog, database_id, tenant_id));
120 fold_projection(&mut projection, catalog, database_id, tenant_id);
121 SqlPlan::Join {
122 left: Box::new(walk_plan(*left, catalog, database_id, tenant_id)),
123 right: Box::new(walk_plan(*right, catalog, database_id, tenant_id)),
124 on,
125 join_type,
126 condition,
127 limit,
128 projection,
129 filters,
130 }
131 }
132
133 mut plan @ (SqlPlan::PointGet { .. } | SqlPlan::RangeScan { .. }) => {
134 match &mut plan {
135 SqlPlan::PointGet { projection, .. } | SqlPlan::RangeScan { projection, .. } => {
136 fold_projection(projection, catalog, database_id, tenant_id);
137 }
138 _ => unreachable!(),
139 }
140 plan
141 }
142
143 mut plan @ (SqlPlan::DocumentIndexLookup { .. }
144 | SqlPlan::Update { .. }
145 | SqlPlan::Delete { .. }) => {
146 match &mut plan {
147 SqlPlan::DocumentIndexLookup {
148 filters,
149 projection,
150 sort_keys,
151 window_functions,
152 ..
153 } => {
154 for filter in filters {
155 fold_filter(filter, catalog, database_id, tenant_id);
156 }
157 fold_projection(projection, catalog, database_id, tenant_id);
158 fold_sort_keys(sort_keys, catalog, database_id, tenant_id);
159 fold_windows(window_functions, catalog, database_id, tenant_id);
160 }
161 SqlPlan::Delete { filters, .. } => {
162 for filter in filters {
163 fold_filter(filter, catalog, database_id, tenant_id);
164 }
165 }
166 SqlPlan::Update {
167 assignments,
168 filters,
169 ..
170 } => {
171 for (_, expr) in assignments {
172 let owned = std::mem::replace(expr, SqlExpr::Wildcard);
173 *expr = fold_expr(owned, catalog, database_id, tenant_id);
174 }
175 for filter in filters {
176 fold_filter(filter, catalog, database_id, tenant_id);
177 }
178 }
179 _ => unreachable!(),
180 }
181 plan
182 }
183
184 SqlPlan::UpdateFrom {
185 collection,
186 engine,
187 source,
188 target_join_col,
189 source_join_col,
190 mut assignments,
191 mut target_filters,
192 returning,
193 } => {
194 for (_, expr) in &mut assignments {
195 let owned = std::mem::replace(expr, SqlExpr::Wildcard);
196 *expr = fold_expr(owned, catalog, database_id, tenant_id);
197 }
198 for filter in &mut target_filters {
199 fold_filter(filter, catalog, database_id, tenant_id);
200 }
201 SqlPlan::UpdateFrom {
202 collection,
203 engine,
204 source: Box::new(walk_plan(*source, catalog, database_id, tenant_id)),
205 target_join_col,
206 source_join_col,
207 assignments,
208 target_filters,
209 returning,
210 }
211 }
212
213 SqlPlan::InsertSelect {
214 target,
215 source,
216 limit,
217 } => SqlPlan::InsertSelect {
218 target,
219 source: Box::new(walk_plan(*source, catalog, database_id, tenant_id)),
220 limit,
221 },
222
223 SqlPlan::Aggregate {
224 input,
225 mut group_by,
226 group_by_aliases,
227 output_order,
228 mut aggregates,
229 mut having,
230 limit,
231 grouping_sets,
232 mut sort_keys,
233 } => {
234 for expr in &mut group_by {
235 let owned = std::mem::replace(expr, SqlExpr::Wildcard);
236 *expr = fold_expr(owned, catalog, database_id, tenant_id);
237 }
238 fold_aggregates(&mut aggregates, catalog, database_id, tenant_id);
239 for filter in &mut having {
240 fold_filter(filter, catalog, database_id, tenant_id);
241 }
242 fold_sort_keys(&mut sort_keys, catalog, database_id, tenant_id);
243 SqlPlan::Aggregate {
244 input: Box::new(walk_plan(*input, catalog, database_id, tenant_id)),
245 group_by,
246 group_by_aliases,
247 output_order,
248 aggregates,
249 having,
250 limit,
251 grouping_sets,
252 sort_keys,
253 }
254 }
255
256 SqlPlan::LateralTopK {
257 outer,
258 outer_alias,
259 inner_collection,
260 mut inner_filters,
261 mut inner_order_by,
262 inner_limit,
263 correlation_keys,
264 lateral_alias,
265 mut projection,
266 left_join,
267 } => {
268 for filter in &mut inner_filters {
269 fold_filter(filter, catalog, database_id, tenant_id);
270 }
271 fold_sort_keys(&mut inner_order_by, catalog, database_id, tenant_id);
272 fold_projection(&mut projection, catalog, database_id, tenant_id);
273 SqlPlan::LateralTopK {
274 outer: Box::new(walk_plan(*outer, catalog, database_id, tenant_id)),
275 outer_alias,
276 inner_collection,
277 inner_filters,
278 inner_order_by,
279 inner_limit,
280 correlation_keys,
281 lateral_alias,
282 projection,
283 left_join,
284 }
285 }
286
287 SqlPlan::LateralLoop {
288 outer,
289 outer_alias,
290 inner,
291 correlation_predicates,
292 lateral_alias,
293 mut projection,
294 outer_row_cap,
295 left_join,
296 } => {
297 fold_projection(&mut projection, catalog, database_id, tenant_id);
298 SqlPlan::LateralLoop {
299 outer: Box::new(walk_plan(*outer, catalog, database_id, tenant_id)),
300 outer_alias,
301 inner: Box::new(walk_plan(*inner, catalog, database_id, tenant_id)),
302 correlation_predicates,
303 lateral_alias,
304 projection,
305 outer_row_cap,
306 left_join,
307 }
308 }
309
310 SqlPlan::Merge {
311 target,
312 engine,
313 source,
314 target_join_col,
315 source_join_col,
316 source_alias,
317 mut clauses,
318 returning,
319 } => {
320 for clause in &mut clauses {
321 for filter in &mut clause.extra_predicate {
322 fold_filter(filter, catalog, database_id, tenant_id);
323 }
324 match &mut clause.action {
325 MergePlanAction::Update { assignments } => {
326 for (_, expr) in assignments {
327 let owned = std::mem::replace(expr, SqlExpr::Wildcard);
328 *expr = fold_expr(owned, catalog, database_id, tenant_id);
329 }
330 }
331 MergePlanAction::Insert { values, .. } => {
332 for expr in values {
333 let owned = std::mem::replace(expr, SqlExpr::Wildcard);
334 *expr = fold_expr(owned, catalog, database_id, tenant_id);
335 }
336 }
337 MergePlanAction::Delete | MergePlanAction::DoNothing => {}
338 }
339 }
340 SqlPlan::Merge {
341 target,
342 engine,
343 source: Box::new(walk_plan(*source, catalog, database_id, tenant_id)),
344 target_join_col,
345 source_join_col,
346 source_alias,
347 clauses,
348 returning,
349 }
350 }
351
352 mut plan @ (SqlPlan::VectorSearch { .. }
353 | SqlPlan::TextSearch { .. }
354 | SqlPlan::SpatialScan { .. }
355 | SqlPlan::RecursiveScan { .. }) => {
356 match &mut plan {
357 SqlPlan::VectorSearch {
358 filters,
359 projection,
360 ..
361 } => {
362 for filter in filters {
363 fold_filter(filter, catalog, database_id, tenant_id);
364 }
365 fold_projection(projection, catalog, database_id, tenant_id);
366 }
367 SqlPlan::TextSearch {
368 filters,
369 projection,
370 ..
371 } => {
372 for filter in filters {
373 fold_filter(filter, catalog, database_id, tenant_id);
374 }
375 fold_projection(projection, catalog, database_id, tenant_id);
376 }
377 SqlPlan::SpatialScan {
378 attribute_filters,
379 projection,
380 ..
381 } => {
382 for filter in attribute_filters {
383 fold_filter(filter, catalog, database_id, tenant_id);
384 }
385 fold_projection(projection, catalog, database_id, tenant_id);
386 }
387 SqlPlan::RecursiveScan {
388 base_filters,
389 recursive_filters,
390 projection,
391 ..
392 } => {
393 for filter in base_filters {
394 fold_filter(filter, catalog, database_id, tenant_id);
395 }
396 for filter in recursive_filters {
397 fold_filter(filter, catalog, database_id, tenant_id);
398 }
399 fold_projection(projection, catalog, database_id, tenant_id);
400 }
401 _ => unreachable!(),
402 }
403 plan
404 }
405
406 mut plan @ (SqlPlan::MultiVectorSearch { .. }
407 | SqlPlan::SparseSearch { .. }
408 | SqlPlan::HybridSearch { .. }
409 | SqlPlan::HybridSearchTriple { .. }) => {
410 match &mut plan {
411 SqlPlan::MultiVectorSearch { projection, .. }
412 | SqlPlan::SparseSearch { projection, .. }
413 | SqlPlan::HybridSearch { projection, .. }
414 | SqlPlan::HybridSearchTriple { projection, .. } => {
415 fold_projection(projection, catalog, database_id, tenant_id);
416 }
417 _ => unreachable!(),
418 }
419 plan
420 }
421
422 other => other,
424 }
425}
426
427fn fold_filter(
428 filter: &mut Filter,
429 catalog: &dyn SqlCatalog,
430 database_id: DatabaseId,
431 tenant_id: u64,
432) {
433 fold_filter_expr(&mut filter.expr, catalog, database_id, tenant_id);
434}
435
436fn fold_filter_expr(
437 expr: &mut FilterExpr,
438 catalog: &dyn SqlCatalog,
439 database_id: DatabaseId,
440 tenant_id: u64,
441) {
442 match expr {
443 FilterExpr::Expr(sql_expr) => {
444 let owned = std::mem::replace(sql_expr, SqlExpr::Wildcard);
445 *sql_expr = fold_expr(owned, catalog, database_id, tenant_id);
446 }
447 FilterExpr::And(children) | FilterExpr::Or(children) => {
448 for child in children {
449 fold_filter_expr(&mut child.expr, catalog, database_id, tenant_id);
450 }
451 }
452 FilterExpr::Not(child) => {
453 fold_filter_expr(&mut child.expr, catalog, database_id, tenant_id);
454 }
455 FilterExpr::Comparison { .. }
457 | FilterExpr::InList { .. }
458 | FilterExpr::Between { .. }
459 | FilterExpr::IsNull { .. }
460 | FilterExpr::IsNotNull { .. } => {}
461 }
462}