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