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