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