1use super::rewrite::rewrite_query_scalars;
10use super::scalar::{is_builtin_aggregate, lower_scalar_expression};
11use super::{
12 AccessPathPlan, AggregateClassifier, AssignmentPlan, ComputePlan, CteCyclePlan, CtePlan,
13 CteSearchPlan, Expr, ExpressionPlan, FromClause, JoinExecutionStrategy, MergeWhenPlan,
14 NoRegisteredAggregates, OrderBy, OrderPlan, Projection, ProjectionPlan, QueryBlockPlan,
15 QueryPlan, RelationalPlan, ScalarExpr, SelectStmt, SourcePlan, TableFunctionPlan, CTE,
16};
17
18impl QueryPlan {
19 pub fn rewrite_scalar_expressions(&mut self, rewrite: &mut dyn FnMut(&mut ScalarExpr)) {
22 rewrite_query_scalars(self, rewrite);
23 }
24
25 #[must_use]
26 pub fn lower(statement: SelectStmt) -> Self {
27 Self::lower_with(statement, &NoRegisteredAggregates)
28 }
29
30 #[must_use]
31 pub fn lower_with(mut statement: SelectStmt, aggregates: &dyn AggregateClassifier) -> Self {
32 let ctes = lower_ctes(&statement.with, aggregates);
33 statement.with.clear();
34 let root = lower_relational_root(statement, aggregates);
35 Self { ctes, root }
36 }
37}
38
39pub(super) fn lower_ctes(ctes: &[CTE], aggregates: &dyn AggregateClassifier) -> Vec<CtePlan> {
40 ctes.iter()
41 .map(|cte| CtePlan {
42 name: cte.name.clone(),
43 columns: cte.columns.clone(),
44 recursive: cte.recursive,
45 materialization: cte.materialization,
46 search: cte.search.as_ref().map(|search| CteSearchPlan {
47 columns: search.columns.clone(),
48 breadth_first: search.breadth_first,
49 sequence_column: search.sequence_column.clone(),
50 }),
51 cycle: cte.cycle.as_ref().map(|cycle| CteCyclePlan {
52 columns: cycle.columns.clone(),
53 mark_column: cycle.mark_column.clone(),
54 mark_value: lower_scalar_expression(
55 cycle.mark_value.clone(),
56 aggregates,
57 &mut Vec::new(),
58 ),
59 mark_default: lower_scalar_expression(
60 cycle.mark_default.clone(),
61 aggregates,
62 &mut Vec::new(),
63 ),
64 path_column: cycle.path_column.clone(),
65 }),
66 query: Box::new(QueryPlan::lower_with((*cte.query).clone(), aggregates)),
67 })
68 .collect()
69}
70
71pub(super) fn lower_assignments(
72 assignments: Vec<(String, Expr)>,
73 aggregates: &dyn AggregateClassifier,
74 subqueries: &mut Vec<QueryPlan>,
75) -> Vec<AssignmentPlan> {
76 assignments
77 .into_iter()
78 .map(|(column, expression)| AssignmentPlan {
79 column,
80 value: lower_scalar_expression(expression, aggregates, subqueries),
81 })
82 .collect()
83}
84
85pub(super) fn lower_merge_when(
86 clause: uqa_sql::ast::MergeWhen,
87 aggregates: &dyn AggregateClassifier,
88 subqueries: &mut Vec<QueryPlan>,
89) -> MergeWhenPlan {
90 let mut lower_optional = |expression: Option<Expr>| {
91 expression.map(|expression| lower_scalar_expression(expression, aggregates, subqueries))
92 };
93 match clause {
94 uqa_sql::ast::MergeWhen::UpdateMatched {
95 condition,
96 assignments,
97 } => {
98 let condition = lower_optional(condition);
99 let assignments = lower_assignments(assignments, aggregates, subqueries);
100 MergeWhenPlan::UpdateMatched {
101 condition,
102 assignments,
103 }
104 }
105 uqa_sql::ast::MergeWhen::DeleteMatched { condition } => MergeWhenPlan::DeleteMatched {
106 condition: lower_optional(condition),
107 },
108 uqa_sql::ast::MergeWhen::UpdateNotMatchedBySource {
109 condition,
110 assignments,
111 } => {
112 let condition = lower_optional(condition);
113 let assignments = lower_assignments(assignments, aggregates, subqueries);
114 MergeWhenPlan::UpdateNotMatchedBySource {
115 condition,
116 assignments,
117 }
118 }
119 uqa_sql::ast::MergeWhen::DeleteNotMatchedBySource { condition } => {
120 MergeWhenPlan::DeleteNotMatchedBySource {
121 condition: lower_optional(condition),
122 }
123 }
124 uqa_sql::ast::MergeWhen::InsertNotMatched {
125 condition,
126 columns,
127 values,
128 } => {
129 let condition = lower_optional(condition);
130 let values = values
131 .into_iter()
132 .map(|value| lower_scalar_expression(value, aggregates, subqueries))
133 .collect();
134 MergeWhenPlan::InsertNotMatched {
135 condition,
136 columns,
137 values,
138 }
139 }
140 uqa_sql::ast::MergeWhen::NothingMatched { condition } => MergeWhenPlan::NothingMatched {
141 condition: lower_optional(condition),
142 },
143 uqa_sql::ast::MergeWhen::NothingNotMatched { condition } => {
144 MergeWhenPlan::NothingNotMatched {
145 condition: lower_optional(condition),
146 }
147 }
148 uqa_sql::ast::MergeWhen::NothingNotMatchedBySource { condition } => {
149 MergeWhenPlan::NothingNotMatchedBySource {
150 condition: lower_optional(condition),
151 }
152 }
153 }
154}
155pub(super) fn lower_relational_root(
156 mut statement: SelectStmt,
157 aggregates: &dyn AggregateClassifier,
158) -> RelationalPlan {
159 if statement.set_op.is_none() && !statement.values.is_empty() {
160 let mut subqueries = Vec::new();
161 let rows = statement
162 .values
163 .into_iter()
164 .map(|row| {
165 row.into_iter()
166 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries))
167 .collect()
168 })
169 .collect();
170 return RelationalPlan::Values { rows, subqueries };
171 }
172 let Some(set_op) = statement.set_op.take() else {
173 return RelationalPlan::QueryBlock(Box::new(QueryBlockPlan::lower_with(
174 statement, aggregates,
175 )));
176 };
177
178 let left = if let Some(left) = set_op.left {
179 QueryPlan::lower_with(*left, aggregates)
180 } else {
181 QueryPlan {
182 ctes: Vec::new(),
183 root: RelationalPlan::QueryBlock(Box::new(QueryBlockPlan::lower_with(
184 statement, aggregates,
185 ))),
186 }
187 };
188 let right = QueryPlan::lower_with(set_op.right, aggregates);
189 let mut subqueries = Vec::new();
190 RelationalPlan::SetOp {
191 kind: set_op.kind,
192 all: set_op.all,
193 left: Box::new(left),
194 right: Box::new(right),
195 order_by: set_op
196 .combined_order_by
197 .into_iter()
198 .map(|order| OrderPlan::lower_with(order, aggregates, &mut subqueries))
199 .collect(),
200 limit: set_op
201 .combined_limit
202 .map(|expr| Box::new(lower_scalar_expression(expr, aggregates, &mut subqueries))),
203 with_ties: set_op.combined_with_ties,
204 offset: set_op
205 .combined_offset
206 .map(|expr| Box::new(lower_scalar_expression(expr, aggregates, &mut subqueries))),
207 subqueries,
208 }
209}
210
211impl QueryBlockPlan {
212 fn lower_with(statement: SelectStmt, aggregates: &dyn AggregateClassifier) -> Self {
213 debug_assert!(statement.with.is_empty());
214 debug_assert!(statement.set_op.is_none());
215 let mut subqueries = Vec::new();
216 let projections: Vec<ProjectionPlan> = statement
217 .projections
218 .into_iter()
219 .map(|projection| ProjectionPlan::lower_with(projection, aggregates, &mut subqueries))
220 .collect();
221 let is_aggregate =
222 |name: &str| is_builtin_aggregate(name) || aggregates.is_registered_aggregate(name);
223 let has_aggregate = !statement.group_by.is_empty()
224 || !statement.grouping_sets.is_empty()
225 || statement.having.is_some()
226 || projections
227 .iter()
228 .any(|projection| projection.expr.contains_aggregate(&is_aggregate));
229 let has_window = projections
230 .iter()
231 .any(|projection| projection.expr.contains_window());
232 let compute = if has_aggregate {
233 ComputePlan::Aggregate
234 } else if has_window {
235 ComputePlan::Window
236 } else {
237 ComputePlan::Project
238 };
239 Self {
240 projections,
241 from: statement
242 .from
243 .map(|source| SourcePlan::lower_with(source, aggregates, &mut subqueries)),
244 r#where: statement
245 .r#where
246 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries)),
247 compute,
248 group_by: statement
249 .group_by
250 .into_iter()
251 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries))
252 .collect(),
253 grouping_sets: statement
254 .grouping_sets
255 .into_iter()
256 .map(|set| {
257 set.into_iter()
258 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries))
259 .collect()
260 })
261 .collect(),
262 group_distinct: statement.group_distinct,
263 having: statement
264 .having
265 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries)),
266 order_by: statement
267 .order_by
268 .into_iter()
269 .map(|order| OrderPlan::lower_with(order, aggregates, &mut subqueries))
270 .collect(),
271 limit: statement
272 .limit
273 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries)),
274 with_ties: statement.with_ties,
275 offset: statement
276 .offset
277 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries)),
278 distinct: statement.distinct,
279 distinct_on: statement
280 .distinct_on
281 .into_iter()
282 .map(|expr| lower_scalar_expression(expr, aggregates, &mut subqueries))
283 .collect(),
284 subqueries,
285 access: AccessPathPlan::Row,
286 locking: statement.locking,
287 }
288 }
289
290 #[must_use]
294 pub fn expressions(&self) -> Vec<&ScalarExpr> {
295 let mut expressions = Vec::new();
296 if let Some(source) = &self.from {
297 source.push_expressions(&mut expressions);
298 }
299 if let Some(filter) = &self.r#where {
300 expressions.push(filter);
301 }
302 for projection in &self.projections {
303 expressions.push(&projection.expr);
304 }
305 expressions.extend(&self.group_by);
306 for set in &self.grouping_sets {
307 expressions.extend(set);
308 }
309 if let Some(having) = &self.having {
310 expressions.push(having);
311 }
312 expressions.extend(self.order_by.iter().map(|order| &order.expr));
313 if let Some(limit) = &self.limit {
314 expressions.push(limit);
315 }
316 if let Some(offset) = &self.offset {
317 expressions.push(offset);
318 }
319 expressions.extend(&self.distinct_on);
320 expressions
321 }
322}
323
324impl SourcePlan {
325 #[must_use]
327 pub fn visible_qualifier(&self) -> Option<&str> {
328 match self {
329 Self::Table {
330 qualifier, alias, ..
331 } => Some(alias.as_deref().unwrap_or(qualifier)),
332 Self::Function {
333 output_name, alias, ..
334 } => Some(alias.as_deref().unwrap_or(output_name)),
335 Self::FunctionGroup {
336 functions, alias, ..
337 } => alias.as_deref().or_else(|| {
338 functions
339 .first()
340 .map(|function| function.output_name.as_str())
341 }),
342 Self::Values {
343 alias,
344 internal_relation,
345 ..
346 } => internal_relation
347 .is_none()
348 .then_some(alias.as_deref())
349 .flatten(),
350 Self::Subquery { alias, .. } => alias.as_deref(),
351 Self::Join { alias, .. } => alias.as_deref(),
352 }
353 }
354
355 pub(super) fn lower_with(
356 source: FromClause,
357 aggregates: &dyn AggregateClassifier,
358 subqueries: &mut Vec<QueryPlan>,
359 ) -> Self {
360 match source {
361 FromClause::Table {
362 name,
363 qualifier,
364 alias,
365 include_descendants,
366 } => Self::Table {
367 name,
368 qualifier,
369 alias,
370 include_descendants,
371 },
372 FromClause::Join {
373 left,
374 right,
375 kind,
376 on,
377 using,
378 natural,
379 alias,
380 column_aliases,
381 lateral,
382 } => Self::Join {
383 left: Box::new(Self::lower_with(*left, aggregates, subqueries)),
384 right: Box::new(Self::lower_with(*right, aggregates, subqueries)),
385 kind,
386 on: on.map(|expr| lower_scalar_expression(expr, aggregates, subqueries)),
387 using,
388 natural,
389 alias,
390 column_aliases,
391 lateral,
392 strategy: JoinExecutionStrategy::Auto,
393 },
394 FromClause::Values {
395 rows,
396 alias,
397 column_aliases,
398 internal_relation,
399 internal_column_types,
400 } => Self::Values {
401 rows: rows
402 .into_iter()
403 .map(|row| {
404 row.into_iter()
405 .map(|expr| lower_scalar_expression(expr, aggregates, subqueries))
406 .collect()
407 })
408 .collect(),
409 alias,
410 column_aliases,
411 internal_relation,
412 internal_column_types,
413 },
414 FromClause::Function {
415 name,
416 output_name,
417 relation,
418 args,
419 alias,
420 column_aliases,
421 ordinality,
422 column_types,
423 } => Self::Function {
424 name,
425 binding: None,
426 output_name,
427 relation,
428 args: args
429 .into_iter()
430 .map(|expr| lower_scalar_expression(expr, aggregates, subqueries))
431 .collect(),
432 alias,
433 column_aliases,
434 ordinality,
435 column_types,
436 },
437 FromClause::FunctionGroup {
438 functions,
439 alias,
440 column_aliases,
441 ordinality,
442 } => Self::FunctionGroup {
443 functions: functions
444 .into_iter()
445 .map(|function| TableFunctionPlan {
446 name: function.name,
447 binding: None,
448 output_name: function.output_name,
449 relation: function.relation,
450 args: function
451 .args
452 .into_iter()
453 .map(|expr| lower_scalar_expression(expr, aggregates, subqueries))
454 .collect(),
455 column_aliases: function.column_aliases,
456 column_types: function.column_types,
457 })
458 .collect(),
459 alias,
460 column_aliases,
461 ordinality,
462 },
463 FromClause::Subquery {
464 body,
465 alias,
466 column_aliases,
467 } => Self::Subquery {
468 body: Box::new(QueryPlan::lower_with(*body, aggregates)),
469 alias,
470 column_aliases,
471 },
472 }
473 }
474
475 fn push_expressions<'a>(&'a self, output: &mut Vec<&'a ScalarExpr>) {
476 match self {
477 Self::Table { .. } | Self::Subquery { .. } => {}
478 Self::Join {
479 left, right, on, ..
480 } => {
481 left.push_expressions(output);
482 right.push_expressions(output);
483 if let Some(on) = on {
484 output.push(on);
485 }
486 }
487 Self::Values { rows, .. } => {
488 for row in rows {
489 output.extend(row);
490 }
491 }
492 Self::Function { args, .. } => output.extend(args),
493 Self::FunctionGroup { functions, .. } => {
494 for function in functions {
495 output.extend(&function.args);
496 }
497 }
498 }
499 }
500
501 pub fn collect_tables(&self, output: &mut Vec<(String, Option<String>)>) {
502 match self {
503 Self::Table {
504 name,
505 qualifier,
506 alias,
507 ..
508 } => output.push((
509 name.clone(),
510 Some(alias.as_ref().unwrap_or(qualifier).clone()),
511 )),
512 Self::Join { left, right, .. } => {
513 left.collect_tables(output);
514 right.collect_tables(output);
515 }
516 Self::Values { .. }
517 | Self::Function { .. }
518 | Self::FunctionGroup { .. }
519 | Self::Subquery { .. } => {}
520 }
521 }
522}
523
524impl ProjectionPlan {
525 pub(super) fn lower_with(
526 projection: Projection,
527 aggregates: &dyn AggregateClassifier,
528 subqueries: &mut Vec<QueryPlan>,
529 ) -> Self {
530 Self {
531 expr: lower_scalar_expression(projection.expr, aggregates, subqueries),
532 alias: projection.alias,
533 }
534 }
535}
536
537impl OrderPlan {
538 fn lower_with(
539 order: OrderBy,
540 aggregates: &dyn AggregateClassifier,
541 subqueries: &mut Vec<QueryPlan>,
542 ) -> Self {
543 Self {
544 expr: lower_scalar_expression(order.expr, aggregates, subqueries),
545 descending: order.descending,
546 nulls: order.nulls,
547 }
548 }
549}
550
551impl ExpressionPlan {
552 #[must_use]
553 pub fn lower(expression: Expr) -> Self {
554 Self::lower_with(expression, &NoRegisteredAggregates)
555 }
556
557 pub(super) fn lower_with(expression: Expr, aggregates: &dyn AggregateClassifier) -> Self {
558 let mut subqueries = Vec::new();
559 let scalar = lower_scalar_expression(expression, aggregates, &mut subqueries);
560 Self { scalar, subqueries }
561 }
562}