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