1use nodedb_types::DatabaseId;
6
7use crate::catalog::SqlCatalog;
8use crate::types::{FilterExpr, MergePlanAction, SqlPlan};
9
10use super::catalog_expr_fold::validate_expr;
11use super::catalog_plan_shapes::{
12 validate_aggregates, validate_projection, validate_sort_keys, validate_windows,
13};
14
15pub(super) fn validate_catalog_exprs(
16 plan: &SqlPlan,
17 catalog: &dyn SqlCatalog,
18 database_id: DatabaseId,
19 tenant_id: u64,
20) -> crate::Result<()> {
21 match plan {
22 SqlPlan::Scan {
23 filters,
24 projection,
25 sort_keys,
26 window_functions,
27 ..
28 }
29 | SqlPlan::DocumentIndexLookup {
30 filters,
31 projection,
32 sort_keys,
33 window_functions,
34 ..
35 } => {
36 validate_filters(filters, catalog, database_id, tenant_id)?;
37 validate_projection(projection, catalog, database_id, tenant_id)?;
38 validate_sort_keys(sort_keys, catalog, database_id, tenant_id)?;
39 validate_windows(window_functions, catalog, database_id, tenant_id)?;
40 }
41 SqlPlan::PointGet { projection, .. } | SqlPlan::RangeScan { projection, .. } => {
42 validate_projection(projection, catalog, database_id, tenant_id)?;
43 }
44 SqlPlan::Update {
45 assignments,
46 filters,
47 ..
48 } => {
49 for (_, expr) in assignments {
50 validate_expr(expr, catalog, database_id, tenant_id)?;
51 }
52 validate_filters(filters, catalog, database_id, tenant_id)?;
53 }
54 SqlPlan::Delete { filters, .. } => {
55 validate_filters(filters, catalog, database_id, tenant_id)?
56 }
57 SqlPlan::UpdateFrom {
58 source,
59 assignments,
60 target_filters,
61 ..
62 } => {
63 validate_catalog_exprs(source, catalog, database_id, tenant_id)?;
64 for (_, expr) in assignments {
65 validate_expr(expr, catalog, database_id, tenant_id)?;
66 }
67 validate_filters(target_filters, catalog, database_id, tenant_id)?;
68 }
69 SqlPlan::InsertSelect { source, .. } => {
70 validate_catalog_exprs(source, catalog, database_id, tenant_id)?
71 }
72 SqlPlan::Aggregate {
73 input,
74 group_by,
75 aggregates,
76 having,
77 sort_keys,
78 ..
79 } => {
80 validate_catalog_exprs(input, catalog, database_id, tenant_id)?;
81 for expr in group_by {
82 validate_expr(expr, catalog, database_id, tenant_id)?;
83 }
84 validate_aggregates(aggregates, catalog, database_id, tenant_id)?;
85 validate_filters(having, catalog, database_id, tenant_id)?;
86 validate_sort_keys(sort_keys, catalog, database_id, tenant_id)?;
87 }
88 SqlPlan::Union { inputs, .. } => {
89 for input in inputs {
90 validate_catalog_exprs(input, catalog, database_id, tenant_id)?;
91 }
92 }
93 SqlPlan::Intersect { left, right, .. } | SqlPlan::Except { left, right, .. } => {
94 validate_catalog_exprs(left, catalog, database_id, tenant_id)?;
95 validate_catalog_exprs(right, catalog, database_id, tenant_id)?;
96 }
97 SqlPlan::Cte { definitions, outer } => {
98 for (_, definition) in definitions {
99 validate_catalog_exprs(definition, catalog, database_id, tenant_id)?;
100 }
101 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
102 }
103 SqlPlan::Join {
104 left,
105 right,
106 condition,
107 projection,
108 filters,
109 ..
110 } => {
111 validate_catalog_exprs(left, catalog, database_id, tenant_id)?;
112 validate_catalog_exprs(right, catalog, database_id, tenant_id)?;
113 if let Some(condition) = condition {
114 validate_expr(condition, catalog, database_id, tenant_id)?;
115 }
116 validate_projection(projection, catalog, database_id, tenant_id)?;
117 validate_filters(filters, catalog, database_id, tenant_id)?;
118 }
119 SqlPlan::LateralTopK {
120 outer,
121 inner_filters,
122 inner_order_by,
123 projection,
124 ..
125 } => {
126 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
127 validate_filters(inner_filters, catalog, database_id, tenant_id)?;
128 validate_sort_keys(inner_order_by, catalog, database_id, tenant_id)?;
129 validate_projection(projection, catalog, database_id, tenant_id)?;
130 }
131 SqlPlan::LateralLoop {
132 outer,
133 inner,
134 projection,
135 ..
136 } => {
137 validate_catalog_exprs(outer, catalog, database_id, tenant_id)?;
138 validate_catalog_exprs(inner, catalog, database_id, tenant_id)?;
139 validate_projection(projection, catalog, database_id, tenant_id)?;
140 }
141 SqlPlan::Merge {
142 source, clauses, ..
143 } => {
144 validate_catalog_exprs(source, catalog, database_id, tenant_id)?;
145 for clause in clauses {
146 validate_filters(&clause.extra_predicate, catalog, database_id, tenant_id)?;
147 match &clause.action {
148 MergePlanAction::Update { assignments } => {
149 for (_, expr) in assignments {
150 validate_expr(expr, catalog, database_id, tenant_id)?;
151 }
152 }
153 MergePlanAction::Insert { values, .. } => {
154 for expr in values {
155 validate_expr(expr, catalog, database_id, tenant_id)?;
156 }
157 }
158 MergePlanAction::Delete | MergePlanAction::DoNothing => {}
159 }
160 }
161 }
162 SqlPlan::VectorSearch {
163 filters,
164 projection,
165 ..
166 } => {
167 validate_filters(filters, catalog, database_id, tenant_id)?;
168 validate_projection(projection, catalog, database_id, tenant_id)?;
169 }
170 SqlPlan::MultiVectorSearch { projection, .. }
171 | SqlPlan::SparseSearch { projection, .. }
172 | SqlPlan::HybridSearch { projection, .. }
173 | SqlPlan::HybridSearchTriple { projection, .. } => {
174 validate_projection(projection, catalog, database_id, tenant_id)?;
175 }
176 SqlPlan::TextSearch {
177 filters,
178 projection,
179 ..
180 } => {
181 validate_filters(filters, catalog, database_id, tenant_id)?;
182 validate_projection(projection, catalog, database_id, tenant_id)?;
183 }
184 SqlPlan::SpatialScan {
185 attribute_filters,
186 projection,
187 ..
188 } => {
189 validate_filters(attribute_filters, catalog, database_id, tenant_id)?;
190 validate_projection(projection, catalog, database_id, tenant_id)?;
191 }
192 SqlPlan::RecursiveScan {
193 base_filters,
194 recursive_filters,
195 projection,
196 ..
197 } => {
198 validate_filters(base_filters, catalog, database_id, tenant_id)?;
199 validate_filters(recursive_filters, catalog, database_id, tenant_id)?;
200 validate_projection(projection, catalog, database_id, tenant_id)?;
201 }
202 _ => {}
203 }
204 Ok(())
205}
206
207fn validate_filters(
208 filters: &[crate::types::Filter],
209 catalog: &dyn SqlCatalog,
210 database_id: DatabaseId,
211 tenant_id: u64,
212) -> crate::Result<()> {
213 for filter in filters {
214 validate_filter_expr(&filter.expr, catalog, database_id, tenant_id)?;
215 }
216 Ok(())
217}
218
219fn validate_filter_expr(
220 expr: &FilterExpr,
221 catalog: &dyn SqlCatalog,
222 database_id: DatabaseId,
223 tenant_id: u64,
224) -> crate::Result<()> {
225 match expr {
226 FilterExpr::Expr(expr) => validate_expr(expr, catalog, database_id, tenant_id),
227 FilterExpr::And(children) | FilterExpr::Or(children) => {
228 validate_filters(children, catalog, database_id, tenant_id)
229 }
230 FilterExpr::Not(child) => {
231 validate_filter_expr(&child.expr, catalog, database_id, tenant_id)
232 }
233 _ => Ok(()),
234 }
235}