1pub mod aggregate_expr;
13mod error;
14pub mod knn_optimizer;
15pub mod logical_plan;
16pub mod name_resolver;
17mod named_window;
18pub mod type_checker;
19pub mod typed_expr;
20pub mod types;
21
22#[cfg(test)]
23mod planner_tests;
24
25pub use aggregate_expr::{AggregateExpr, AggregateFunction};
26pub use error::PlannerError;
27pub use knn_optimizer::{KnnPattern, SortDirection, detect_knn_pattern};
28pub use logical_plan::{
29 JoinType, LogicalPlan, OffsetWindowFunction, RecursiveCteLimits, SetOperator,
30 TableFunctionKind, ValueWindowFunction, WindowExpr, WindowFunction,
31};
32pub use name_resolver::{NameResolver, ResolvedColumn};
33pub use type_checker::{ScopedTable, TypeChecker};
34pub use typed_expr::{
35 ProjectedColumn, Projection, SortExpr, TypedAssignment, TypedCaseWhen, TypedExpr, TypedExprKind,
36};
37pub use types::ResolvedType;
38
39use crate::ast::ddl::{
40 ColumnConstraint, ColumnDef, CreateIndex, CreateTable, DropIndex, DropTable,
41};
42use crate::ast::dml::{
43 Delete, FromItem, GroupByItem, Insert, InsertSource, LITERAL_TABLE, OrderByExpr, QueryBody,
44 Select, SelectItem, SetOperation as AstSetOperation, SetOperator as AstSetOperator, Update,
45 Values,
46};
47use crate::ast::expr::{Expr, ExprKind, Literal};
48use crate::ast::{PragmaValue, Spanned, Statement, StatementKind};
49use crate::catalog::{Catalog, ColumnMetadata, IndexMetadata, TableMetadata};
50use crate::{AlopexDialect, DataSourceFormat, Parser, SqlError, TableType};
51use named_window::resolve_named_windows;
52use std::collections::{HashMap, HashSet};
53
54#[derive(Clone)]
55struct PlannedRelation {
56 plan: LogicalPlan,
57 schema: Vec<ColumnMetadata>,
58 scope: Vec<ScopedTable>,
59}
60
61struct WindowSelectStages {
62 plan: LogicalPlan,
63 windows: Vec<WindowExpr>,
64 qualify: Option<TypedExpr>,
65 projection: Projection,
66 order_by: Vec<SortExpr>,
67 window_schema: Vec<ColumnMetadata>,
68}
69
70type CtePlans = HashMap<String, PlannedRelation>;
71
72fn direct_from_reference_count(items: &[FromItem], name: &str) -> usize {
73 items
74 .iter()
75 .map(|item| match item {
76 FromItem::Table {
77 name: table_name, ..
78 } => usize::from(table_name == name),
79 FromItem::Join { left, right, .. } => {
80 direct_from_item_reference_count(left, name)
81 + direct_from_item_reference_count(right, name)
82 }
83 FromItem::Derived { .. } | FromItem::Function { .. } => 0,
84 })
85 .sum()
86}
87
88fn direct_from_item_reference_count(item: &FromItem, name: &str) -> usize {
89 match item {
90 FromItem::Table {
91 name: table_name, ..
92 } => usize::from(table_name == name),
93 FromItem::Join { left, right, .. } => {
94 direct_from_item_reference_count(left, name)
95 + direct_from_item_reference_count(right, name)
96 }
97 FromItem::Derived { .. } | FromItem::Function { .. } => 0,
98 }
99}
100
101fn select_table_reference_count(select: &Select, name: &str) -> usize {
102 fn from_item_count(item: &FromItem, name: &str) -> usize {
103 match item {
104 FromItem::Table {
105 name: table_name, ..
106 } => usize::from(table_name == name),
107 FromItem::Join { left, right, .. } => {
108 from_item_count(left, name) + from_item_count(right, name)
109 }
110 FromItem::Derived { subquery, .. } => query_body_table_reference_count(subquery, name),
111 FromItem::Function { .. } => 0,
115 }
116 }
117
118 let from_count = select
119 .from
120 .iter()
121 .map(|item| from_item_count(item, name))
122 .sum::<usize>();
123 let set_count = select
124 .set_operations
125 .iter()
126 .map(|operation| query_body_table_reference_count(&operation.right, name))
127 .sum::<usize>();
128 let with_count = select.with.as_ref().map_or(0, |with| {
129 with.ctes
130 .iter()
131 .map(|cte| query_body_table_reference_count(&cte.query, name))
132 .sum()
133 });
134 from_count + set_count + with_count
135}
136
137fn values_table_reference_count(values: &Values, name: &str) -> usize {
138 let set_count = values
139 .set_operations
140 .iter()
141 .map(|operation| query_body_table_reference_count(&operation.right, name))
142 .sum::<usize>();
143 let with_count = values.with.as_ref().map_or(0, |with| {
144 with.ctes
145 .iter()
146 .map(|cte| query_body_table_reference_count(&cte.query, name))
147 .sum()
148 });
149 set_count + with_count
150}
151
152fn query_body_table_reference_count(body: &QueryBody, name: &str) -> usize {
153 match body {
154 QueryBody::Select(select) => select_table_reference_count(select, name),
155 QueryBody::Values(values) => values_table_reference_count(values, name),
156 }
157}
158
159fn cte_dependency_cycle(with: &crate::ast::WithClause) -> bool {
160 fn visit(node: usize, dependencies: &[Vec<usize>], state: &mut [u8]) -> bool {
161 state[node] = 1;
162 for &dependency in &dependencies[node] {
163 if state[dependency] == 1
164 || (state[dependency] == 0 && visit(dependency, dependencies, state))
165 {
166 return true;
167 }
168 }
169 state[node] = 2;
170 false
171 }
172
173 let dependencies = with
174 .ctes
175 .iter()
176 .map(|cte| {
177 with.ctes
178 .iter()
179 .enumerate()
180 .filter_map(|(dependency, candidate)| {
181 (query_body_table_reference_count(&cte.query, &candidate.name) > 0)
182 .then_some(dependency)
183 })
184 .collect::<Vec<_>>()
185 })
186 .collect::<Vec<_>>();
187 let mut state = vec![0; dependencies.len()];
188 (0..dependencies.len()).any(|node| state[node] == 0 && visit(node, &dependencies, &mut state))
189}
190
191fn expr_contains_subquery(expr: &Expr) -> bool {
192 match &expr.kind {
193 ExprKind::Literal { .. } | ExprKind::ColumnRef { .. } | ExprKind::VectorLiteral { .. } => {
194 false
195 }
196 ExprKind::BinaryOp { left, right, .. } => {
197 expr_contains_subquery(left) || expr_contains_subquery(right)
198 }
199 ExprKind::UnaryOp { operand, .. } => expr_contains_subquery(operand),
200 ExprKind::TruthPredicate { expr, .. } => expr_contains_subquery(expr),
201 ExprKind::IsDistinctFrom { left, right, .. } => {
202 expr_contains_subquery(left) || expr_contains_subquery(right)
203 }
204 ExprKind::Row { items } => items.iter().any(expr_contains_subquery),
205 ExprKind::Case {
206 operand,
207 branches,
208 else_expr,
209 } => {
210 operand.as_deref().is_some_and(expr_contains_subquery)
211 || branches.iter().any(|branch| {
212 expr_contains_subquery(&branch.when) || expr_contains_subquery(&branch.then)
213 })
214 || else_expr.as_deref().is_some_and(expr_contains_subquery)
215 }
216 ExprKind::FunctionCall {
217 args,
218 order_by,
219 within_group,
220 filter,
221 over,
222 ..
223 } => {
224 args.iter().any(expr_contains_subquery)
225 || order_by
226 .iter()
227 .any(|order| expr_contains_subquery(&order.expr))
228 || within_group
229 .iter()
230 .any(|order| expr_contains_subquery(&order.expr))
231 || filter.as_deref().is_some_and(expr_contains_subquery)
232 || over.as_ref().is_some_and(|window| {
233 window.partition_by.iter().any(expr_contains_subquery)
234 || window
235 .order_by
236 .iter()
237 .any(|order| expr_contains_subquery(&order.expr))
238 })
239 }
240 ExprKind::Cast { expr, .. }
241 | ExprKind::TryCast { expr, .. }
242 | ExprKind::IsNull { expr, .. } => expr_contains_subquery(expr),
243 ExprKind::Between {
244 expr, low, high, ..
245 } => {
246 expr_contains_subquery(expr)
247 || expr_contains_subquery(low)
248 || expr_contains_subquery(high)
249 }
250 ExprKind::Like {
251 expr,
252 pattern,
253 escape,
254 ..
255 } => {
256 expr_contains_subquery(expr)
257 || expr_contains_subquery(pattern)
258 || escape.as_deref().is_some_and(expr_contains_subquery)
259 }
260 ExprKind::InList { expr, list, .. } => {
261 expr_contains_subquery(expr) || list.iter().any(expr_contains_subquery)
262 }
263 ExprKind::ScalarSubquery { .. }
264 | ExprKind::InSubquery { .. }
265 | ExprKind::Exists { .. }
266 | ExprKind::Quantified { .. } => true,
267 }
268}
269
270fn from_item_contains_subquery(item: &FromItem) -> bool {
271 match item {
272 FromItem::Table { .. } => false,
273 FromItem::Derived { .. } | FromItem::Function { .. } => true,
276 FromItem::Join {
277 left,
278 right,
279 condition,
280 ..
281 } => {
282 from_item_contains_subquery(left)
283 || from_item_contains_subquery(right)
284 || condition.as_ref().is_some_and(expr_contains_subquery)
285 }
286 }
287}
288
289fn select_contains_subquery(select: &Select) -> bool {
290 select.projection.iter().any(|item| match item {
291 SelectItem::Expr { expr, .. } => expr_contains_subquery(expr),
292 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
293 }) || select.from.iter().any(from_item_contains_subquery)
294 || select
295 .selection
296 .as_ref()
297 .is_some_and(expr_contains_subquery)
298 || select.group_by.as_ref().is_some_and(|group| {
299 group
300 .iter()
301 .flat_map(GroupByItem::exprs)
302 .any(expr_contains_subquery)
303 })
304 || select.having.as_ref().is_some_and(expr_contains_subquery)
305 || select
306 .set_operations
307 .iter()
308 .any(|operation| query_body_contains_subquery(&operation.right))
309 || select
310 .order_by
311 .iter()
312 .any(|order| expr_contains_subquery(&order.expr))
313 || select.limit.as_ref().is_some_and(expr_contains_subquery)
314 || select.offset.as_ref().is_some_and(expr_contains_subquery)
315 || select.with.as_ref().is_some_and(|with| {
316 with.ctes
317 .iter()
318 .any(|cte| query_body_contains_subquery(&cte.query))
319 })
320}
321
322fn values_contains_subquery(values: &Values) -> bool {
323 values.rows.iter().flatten().any(expr_contains_subquery)
324 || values
325 .set_operations
326 .iter()
327 .any(|operation| query_body_contains_subquery(&operation.right))
328 || values
329 .order_by
330 .iter()
331 .any(|order| expr_contains_subquery(&order.expr))
332 || values.limit.as_ref().is_some_and(expr_contains_subquery)
333 || values.offset.as_ref().is_some_and(expr_contains_subquery)
334 || values.with.as_ref().is_some_and(|with| {
335 with.ctes
336 .iter()
337 .any(|cte| query_body_contains_subquery(&cte.query))
338 })
339}
340
341fn query_body_contains_subquery(body: &QueryBody) -> bool {
342 match body {
343 QueryBody::Select(select) => select_contains_subquery(select),
344 QueryBody::Values(values) => values_contains_subquery(values),
345 }
346}
347
348fn common_values_type(
349 current: &ResolvedType,
350 next: &ResolvedType,
351 span: crate::ast::Span,
352) -> Result<ResolvedType, PlannerError> {
353 use ResolvedType::{BigInt, Decimal, Double, Float, Integer, Null};
354
355 if *current == Null {
356 return Ok(next.clone());
357 }
358 if *next == Null || current == next {
359 return Ok(current.clone());
360 }
361 match (current, next) {
362 (Decimal { .. }, Decimal { .. }) => Ok(current.clone()),
363 (Integer, BigInt) | (BigInt, Integer) => Ok(BigInt),
364 (Integer | BigInt | Float | Double, Integer | BigInt | Float | Double) => Ok(Double),
365 _ => Err(PlannerError::type_mismatch(
366 current.type_name(),
367 next.type_name(),
368 span,
369 )),
370 }
371}
372
373#[derive(Debug, Clone)]
379pub struct PlannedStatement {
380 pub plan: LogicalPlan,
382 pub routing_input: RoutingInput,
384}
385
386impl PlannedStatement {
387 pub fn statement_kind(&self) -> &StatementKind {
389 &self.routing_input.statement_kind
390 }
391
392 pub fn table_references(&self) -> &[TableReference] {
394 &self.routing_input.table_references
395 }
396
397 pub fn diagnostics(&self) -> &[PlanningDiagnostic] {
400 &self.routing_input.diagnostics
401 }
402}
403
404#[derive(Debug, Clone)]
406pub struct RoutingInput {
407 pub statement_kind: StatementKind,
410 pub table_references: Vec<TableReference>,
412 pub diagnostics: Vec<PlanningDiagnostic>,
414}
415
416#[derive(Debug, Clone, PartialEq, Eq)]
418pub struct TableReference {
419 pub table_name: String,
421 pub access: TableReferenceAccess,
423 pub source: TableReferenceSource,
425}
426
427impl TableReference {
428 pub fn new(
429 table_name: impl Into<String>,
430 access: TableReferenceAccess,
431 source: TableReferenceSource,
432 ) -> Self {
433 Self {
434 table_name: table_name.into(),
435 access,
436 source,
437 }
438 }
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq)]
443pub enum TableReferenceAccess {
444 Read,
446 Write,
448 Create,
450 Drop,
452 Metadata,
454}
455
456#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458pub enum TableReferenceSource {
459 TopLevelPlanTableName,
461 LogicalPlanScan,
463 LogicalPlanMutationTarget,
465 LogicalPlanDdlTarget,
467 LogicalPlanIndexTarget,
469 TypedExprSubquery,
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq)]
475pub enum PlanningDiagnosticSeverity {
476 Info,
477 Warning,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq)]
482pub struct PlanningDiagnostic {
483 pub code: &'static str,
485 pub severity: PlanningDiagnosticSeverity,
487 pub message: String,
489}
490
491impl PlanningDiagnostic {
492 pub fn info(code: &'static str, message: impl Into<String>) -> Self {
493 Self {
494 code,
495 severity: PlanningDiagnosticSeverity::Info,
496 message: message.into(),
497 }
498 }
499
500 pub fn warning(code: &'static str, message: impl Into<String>) -> Self {
501 Self {
502 code,
503 severity: PlanningDiagnosticSeverity::Warning,
504 message: message.into(),
505 }
506 }
507}
508
509pub fn plan_sql_for_routing<C: Catalog + ?Sized>(
511 catalog: &C,
512 sql: &str,
513) -> Result<Vec<PlannedStatement>, SqlError> {
514 let statements = Parser::parse_sql(&AlopexDialect, sql).map_err(SqlError::from)?;
515 statements
516 .iter()
517 .map(|statement| plan_statement_for_routing(catalog, statement).map_err(SqlError::from))
518 .collect()
519}
520
521pub fn plan_statement_for_routing<C: Catalog + ?Sized>(
523 catalog: &C,
524 statement: &Statement,
525) -> Result<PlannedStatement, PlannerError> {
526 let planner = Planner::new(catalog);
527 let plan = planner.plan(statement)?;
528 let routing_input = routing_input_for_plan(statement, &plan)?;
529 Ok(PlannedStatement {
530 plan,
531 routing_input,
532 })
533}
534
535fn routing_input_for_plan(
536 statement: &Statement,
537 plan: &LogicalPlan,
538) -> Result<RoutingInput, PlannerError> {
539 let mut diagnostics = Vec::new();
540 let extractor = TableReferenceExtractor::new();
541 let table_references = extractor.extract_from_logical_plan(
542 plan,
543 table_reference_access(statement)?,
544 &mut diagnostics,
545 );
546
547 Ok(RoutingInput {
548 statement_kind: statement.kind.clone(),
549 table_references,
550 diagnostics,
551 })
552}
553
554#[derive(Debug, Default, Clone, Copy)]
556pub struct TableReferenceExtractor;
557
558impl TableReferenceExtractor {
559 pub fn new() -> Self {
560 Self
561 }
562
563 pub fn extract_from_logical_plan(
566 &self,
567 plan: &LogicalPlan,
568 root_access: TableReferenceAccess,
569 diagnostics: &mut Vec<PlanningDiagnostic>,
570 ) -> Vec<TableReference> {
571 let mut references = Vec::new();
572 self.extract_plan(
573 plan,
574 root_access,
575 TableReferenceSource::LogicalPlanScan,
576 diagnostics,
577 &mut references,
578 );
579 if references.is_empty() {
580 diagnostics.push(PlanningDiagnostic::info(
581 "ALOPEX-PLAN-ROUTE-001",
582 "statement has no physical table reference",
583 ));
584 }
585 references
586 }
587
588 pub fn extract_from_subquery_context(
590 &self,
591 plan: &LogicalPlan,
592 diagnostics: &mut Vec<PlanningDiagnostic>,
593 ) -> Vec<TableReference> {
594 let mut references = Vec::new();
595 self.extract_plan(
596 plan,
597 TableReferenceAccess::Read,
598 TableReferenceSource::TypedExprSubquery,
599 diagnostics,
600 &mut references,
601 );
602 references
603 }
604
605 fn extract_plan(
606 &self,
607 plan: &LogicalPlan,
608 root_access: TableReferenceAccess,
609 scan_source: TableReferenceSource,
610 diagnostics: &mut Vec<PlanningDiagnostic>,
611 references: &mut Vec<TableReference>,
612 ) {
613 match plan {
614 LogicalPlan::Scan { table, projection } => {
615 if table != LITERAL_TABLE {
616 push_table_reference(
617 references,
618 table,
619 TableReferenceAccess::Read,
620 scan_source,
621 );
622 }
623 self.extract_projection(projection, diagnostics, references);
624 }
625 LogicalPlan::Values { rows, .. } => {
626 for row in rows {
627 for value in row {
628 self.extract_typed_expr(value, diagnostics, references);
629 }
630 }
631 }
632 LogicalPlan::Filter { input, predicate } => {
633 self.extract_plan(input, root_access, scan_source, diagnostics, references);
634 self.extract_typed_expr(predicate, diagnostics, references);
635 }
636 LogicalPlan::Project { input, projection } => {
637 self.extract_plan(input, root_access, scan_source, diagnostics, references);
638 self.extract_projection(projection, diagnostics, references);
639 }
640 LogicalPlan::Join {
641 left,
642 right,
643 condition,
644 ..
645 }
646 | LogicalPlan::LateralJoin {
647 left,
648 right,
649 condition,
650 ..
651 } => {
652 self.extract_plan(
653 left,
654 TableReferenceAccess::Read,
655 scan_source,
656 diagnostics,
657 references,
658 );
659 self.extract_plan(
660 right,
661 TableReferenceAccess::Read,
662 scan_source,
663 diagnostics,
664 references,
665 );
666 if let Some(condition) = condition {
667 self.extract_typed_expr(condition, diagnostics, references);
668 }
669 }
670 LogicalPlan::TableFunction { args, .. } => {
671 for arg in args {
672 self.extract_typed_expr(arg, diagnostics, references);
673 }
674 }
675 LogicalPlan::Aggregate {
676 input,
677 group_keys,
678 aggregates,
679 having,
680 projection,
681 grouping_sets: _,
682 } => {
683 self.extract_plan(input, root_access, scan_source, diagnostics, references);
684 for expr in group_keys {
685 self.extract_typed_expr(expr, diagnostics, references);
686 }
687 for aggregate in aggregates {
688 if let Some(arg) = &aggregate.arg {
689 self.extract_typed_expr(arg, diagnostics, references);
690 }
691 }
692 if let Some(having) = having {
693 self.extract_typed_expr(having, diagnostics, references);
694 }
695 self.extract_projection(projection, diagnostics, references);
696 }
697 LogicalPlan::Window { input, windows } => {
698 self.extract_plan(input, root_access, scan_source, diagnostics, references);
699 for window in windows {
700 for expr in &window.partition_by {
701 self.extract_typed_expr(expr, diagnostics, references);
702 }
703 for sort_expr in &window.order_by {
704 self.extract_typed_expr(&sort_expr.expr, diagnostics, references);
705 }
706 match &window.function {
707 WindowFunction::Aggregate(aggregate) => {
708 if let Some(arg) = &aggregate.arg {
709 self.extract_typed_expr(arg, diagnostics, references);
710 }
711 }
712 WindowFunction::Lag(function) | WindowFunction::Lead(function) => {
713 self.extract_typed_expr(&function.value, diagnostics, references);
714 if let Some(offset) = &function.offset {
715 self.extract_typed_expr(offset, diagnostics, references);
716 }
717 if let Some(default) = &function.default {
718 self.extract_typed_expr(default, diagnostics, references);
719 }
720 }
721 WindowFunction::Ntile(argument) => {
722 self.extract_typed_expr(argument, diagnostics, references);
723 }
724 WindowFunction::Value(function) => match function {
725 ValueWindowFunction::FirstValue(value)
726 | ValueWindowFunction::LastValue(value) => {
727 self.extract_typed_expr(value, diagnostics, references);
728 }
729 ValueWindowFunction::NthValue { value, nth } => {
730 self.extract_typed_expr(value, diagnostics, references);
731 self.extract_typed_expr(nth, diagnostics, references);
732 }
733 },
734 WindowFunction::RowNumber
735 | WindowFunction::Rank
736 | WindowFunction::DenseRank
737 | WindowFunction::PercentRank
738 | WindowFunction::CumeDist => {}
739 }
740 }
741 }
742 LogicalPlan::SetOperation { left, right, .. } => {
743 self.extract_plan(left, root_access, scan_source, diagnostics, references);
744 self.extract_plan(right, root_access, scan_source, diagnostics, references);
745 }
746 LogicalPlan::RecursiveCte {
747 anchor,
748 recursive_term,
749 ..
750 } => {
751 self.extract_plan(anchor, root_access, scan_source, diagnostics, references);
752 self.extract_plan(
753 recursive_term,
754 root_access,
755 scan_source,
756 diagnostics,
757 references,
758 );
759 }
760 LogicalPlan::RecursiveReference { .. } => {}
761 LogicalPlan::Sort { input, order_by } => {
762 self.extract_plan(input, root_access, scan_source, diagnostics, references);
763 for sort_expr in order_by {
764 self.extract_typed_expr(&sort_expr.expr, diagnostics, references);
765 }
766 }
767 LogicalPlan::DistinctOn {
768 input, order_by, ..
769 } => {
770 self.extract_plan(input, root_access, scan_source, diagnostics, references);
771 for sort_expr in order_by {
772 self.extract_typed_expr(&sort_expr.expr, diagnostics, references);
773 }
774 }
775 LogicalPlan::Limit { input, .. } => {
776 self.extract_plan(input, root_access, scan_source, diagnostics, references);
777 }
778 LogicalPlan::Insert { table, values, .. } => {
779 push_table_reference(
780 references,
781 table,
782 root_access,
783 TableReferenceSource::LogicalPlanMutationTarget,
784 );
785 for row in values {
786 for value in row {
787 self.extract_typed_expr(value, diagnostics, references);
788 }
789 }
790 }
791 LogicalPlan::InsertSelect { table, source, .. } => {
792 push_table_reference(
793 references,
794 table,
795 root_access,
796 TableReferenceSource::LogicalPlanMutationTarget,
797 );
798 self.extract_plan(
799 source,
800 TableReferenceAccess::Read,
801 scan_source,
802 diagnostics,
803 references,
804 );
805 }
806 LogicalPlan::Update {
807 table,
808 assignments,
809 filter,
810 } => {
811 push_table_reference(
812 references,
813 table,
814 root_access,
815 TableReferenceSource::LogicalPlanMutationTarget,
816 );
817 for assignment in assignments {
818 self.extract_typed_expr(&assignment.value, diagnostics, references);
819 }
820 if let Some(filter) = filter {
821 self.extract_typed_expr(filter, diagnostics, references);
822 }
823 }
824 LogicalPlan::Delete { table, filter } => {
825 push_table_reference(
826 references,
827 table,
828 root_access,
829 TableReferenceSource::LogicalPlanMutationTarget,
830 );
831 if let Some(filter) = filter {
832 self.extract_typed_expr(filter, diagnostics, references);
833 }
834 }
835 LogicalPlan::CreateTable { table, .. } => push_table_reference(
836 references,
837 &table.name,
838 root_access,
839 TableReferenceSource::LogicalPlanDdlTarget,
840 ),
841 LogicalPlan::DropTable { name, .. } => push_table_reference(
842 references,
843 name,
844 root_access,
845 TableReferenceSource::LogicalPlanDdlTarget,
846 ),
847 LogicalPlan::CreateIndex { index, .. } => push_table_reference(
848 references,
849 &index.table,
850 root_access,
851 TableReferenceSource::LogicalPlanIndexTarget,
852 ),
853 LogicalPlan::DropIndex { name, .. } => diagnostics.push(PlanningDiagnostic::warning(
854 "ALOPEX-PLAN-ROUTE-003",
855 format!(
856 "DROP INDEX {name} does not expose a target table in the current logical plan"
857 ),
858 )),
859 LogicalPlan::Pragma { .. } => {}
860 }
861 }
862
863 fn extract_projection(
864 &self,
865 projection: &Projection,
866 diagnostics: &mut Vec<PlanningDiagnostic>,
867 references: &mut Vec<TableReference>,
868 ) {
869 if let Projection::Columns(columns) = projection {
870 for column in columns {
871 self.extract_typed_expr(&column.expr, diagnostics, references);
872 }
873 }
874 }
875
876 fn extract_typed_expr(
877 &self,
878 expr: &TypedExpr,
879 diagnostics: &mut Vec<PlanningDiagnostic>,
880 references: &mut Vec<TableReference>,
881 ) {
882 match &expr.kind {
883 TypedExprKind::Literal(_)
884 | TypedExprKind::ColumnRef { .. }
885 | TypedExprKind::VectorLiteral(_) => {}
886 TypedExprKind::BinaryOp { left, right, .. } => {
887 self.extract_typed_expr(left, diagnostics, references);
888 self.extract_typed_expr(right, diagnostics, references);
889 }
890 TypedExprKind::UnaryOp { operand, .. }
891 | TypedExprKind::Cast { expr: operand, .. }
892 | TypedExprKind::TryCast { expr: operand, .. }
893 | TypedExprKind::IsNull { expr: operand, .. } => {
894 self.extract_typed_expr(operand, diagnostics, references);
895 }
896 TypedExprKind::Case {
897 operand,
898 branches,
899 else_expr,
900 } => {
901 if let Some(operand) = operand {
902 self.extract_typed_expr(operand, diagnostics, references);
903 }
904 for branch in branches {
905 self.extract_typed_expr(&branch.when, diagnostics, references);
906 self.extract_typed_expr(&branch.then, diagnostics, references);
907 }
908 if let Some(else_expr) = else_expr {
909 self.extract_typed_expr(else_expr, diagnostics, references);
910 }
911 }
912 TypedExprKind::FunctionCall {
913 args,
914 filter,
915 order_by,
916 ..
917 } => {
918 for arg in args {
919 self.extract_typed_expr(arg, diagnostics, references);
920 }
921 if let Some(filter) = filter {
922 self.extract_typed_expr(filter, diagnostics, references);
923 }
924 for sort in order_by {
925 self.extract_typed_expr(&sort.expr, diagnostics, references);
926 }
927 }
928 TypedExprKind::Between {
929 expr, low, high, ..
930 } => {
931 self.extract_typed_expr(expr, diagnostics, references);
932 self.extract_typed_expr(low, diagnostics, references);
933 self.extract_typed_expr(high, diagnostics, references);
934 }
935 TypedExprKind::Like {
936 expr,
937 pattern,
938 escape,
939 ..
940 } => {
941 self.extract_typed_expr(expr, diagnostics, references);
942 self.extract_typed_expr(pattern, diagnostics, references);
943 if let Some(escape) = escape {
944 self.extract_typed_expr(escape, diagnostics, references);
945 }
946 }
947 TypedExprKind::InList { expr, list, .. } => {
948 self.extract_typed_expr(expr, diagnostics, references);
949 for item in list {
950 self.extract_typed_expr(item, diagnostics, references);
951 }
952 }
953 TypedExprKind::ScalarSubquery(subquery) => self.extract_plan(
954 subquery,
955 TableReferenceAccess::Read,
956 TableReferenceSource::TypedExprSubquery,
957 diagnostics,
958 references,
959 ),
960 TypedExprKind::InSubquery { expr, subquery, .. } => {
961 self.extract_typed_expr(expr, diagnostics, references);
962 self.extract_plan(
963 subquery,
964 TableReferenceAccess::Read,
965 TableReferenceSource::TypedExprSubquery,
966 diagnostics,
967 references,
968 );
969 }
970 TypedExprKind::Exists { subquery, .. } => self.extract_plan(
971 subquery,
972 TableReferenceAccess::Read,
973 TableReferenceSource::TypedExprSubquery,
974 diagnostics,
975 references,
976 ),
977 TypedExprKind::Quantified { expr, subquery, .. } => {
978 self.extract_typed_expr(expr, diagnostics, references);
979 self.extract_plan(
980 subquery,
981 TableReferenceAccess::Read,
982 TableReferenceSource::TypedExprSubquery,
983 diagnostics,
984 references,
985 );
986 }
987 }
988 }
989}
990
991fn push_table_reference(
992 references: &mut Vec<TableReference>,
993 table_name: &str,
994 access: TableReferenceAccess,
995 source: TableReferenceSource,
996) {
997 if !references.iter().any(|reference| {
998 reference.table_name == table_name
999 && reference.access == access
1000 && reference.source == source
1001 }) {
1002 references.push(TableReference::new(table_name, access, source));
1003 }
1004}
1005
1006#[derive(Debug)]
1007enum GenericHostStatement<'a> {
1008 CreateTable(&'a CreateTable),
1009 DropTable(&'a DropTable),
1010 CreateIndex(&'a CreateIndex),
1011 DropIndex(&'a DropIndex),
1012 Pragma {
1013 name: &'a str,
1014 value: &'a Option<PragmaValue>,
1015 },
1016 Select(&'a Select),
1017 Values(&'a Values),
1018 Insert(&'a Insert),
1019 Update(&'a Update),
1020 Delete(&'a Delete),
1021 Unsupported,
1022}
1023
1024fn classify_generic_host_statement(statement_kind: &StatementKind) -> GenericHostStatement<'_> {
1025 #[allow(unreachable_patterns)]
1028 match statement_kind {
1029 StatementKind::CreateTable(statement) => GenericHostStatement::CreateTable(statement),
1030 StatementKind::DropTable(statement) => GenericHostStatement::DropTable(statement),
1031 StatementKind::CreateIndex(statement) => GenericHostStatement::CreateIndex(statement),
1032 StatementKind::DropIndex(statement) => GenericHostStatement::DropIndex(statement),
1033 StatementKind::Pragma { name, value } => GenericHostStatement::Pragma { name, value },
1034 StatementKind::Select(statement) => GenericHostStatement::Select(statement),
1035 StatementKind::Values(statement) => GenericHostStatement::Values(statement),
1036 StatementKind::Insert(statement) => GenericHostStatement::Insert(statement),
1037 StatementKind::Update(statement) => GenericHostStatement::Update(statement),
1038 StatementKind::Delete(statement) => GenericHostStatement::Delete(statement),
1039 _ => GenericHostStatement::Unsupported,
1040 }
1041}
1042
1043fn unsupported_generic_statement(statement: &Statement) -> PlannerError {
1044 PlannerError::unsupported_feature(
1045 "statement kind for the generic SQL planner",
1046 "a statement-specific planner",
1047 statement.span,
1048 )
1049}
1050
1051fn table_reference_access(statement: &Statement) -> Result<TableReferenceAccess, PlannerError> {
1052 table_reference_access_for_classified(
1053 statement,
1054 classify_generic_host_statement(&statement.kind),
1055 )
1056}
1057
1058fn table_reference_access_for_classified(
1059 statement: &Statement,
1060 classified: GenericHostStatement<'_>,
1061) -> Result<TableReferenceAccess, PlannerError> {
1062 match classified {
1063 GenericHostStatement::Select(_) | GenericHostStatement::Values(_) => {
1064 Ok(TableReferenceAccess::Read)
1065 }
1066 GenericHostStatement::Insert(_)
1067 | GenericHostStatement::Update(_)
1068 | GenericHostStatement::Delete(_) => Ok(TableReferenceAccess::Write),
1069 GenericHostStatement::CreateTable(_) => Ok(TableReferenceAccess::Create),
1070 GenericHostStatement::DropTable(_) => Ok(TableReferenceAccess::Drop),
1071 GenericHostStatement::CreateIndex(_)
1072 | GenericHostStatement::DropIndex(_)
1073 | GenericHostStatement::Pragma { .. } => Ok(TableReferenceAccess::Metadata),
1074 GenericHostStatement::Unsupported => Err(unsupported_generic_statement(statement)),
1075 }
1076}
1077
1078pub struct Planner<'a, C: Catalog + ?Sized> {
1105 catalog: &'a C,
1106 name_resolver: NameResolver<'a, C>,
1107 type_checker: TypeChecker<'a, C>,
1108}
1109
1110impl<'a, C: Catalog + ?Sized> Planner<'a, C> {
1111 pub fn new(catalog: &'a C) -> Self {
1113 Self {
1114 catalog,
1115 name_resolver: NameResolver::new(catalog),
1116 type_checker: TypeChecker::new(catalog),
1117 }
1118 }
1119
1120 pub fn plan(&self, stmt: &Statement) -> Result<LogicalPlan, PlannerError> {
1131 self.plan_classified_statement(stmt, classify_generic_host_statement(&stmt.kind))
1132 }
1133
1134 fn plan_classified_statement(
1135 &self,
1136 stmt: &Statement,
1137 classified: GenericHostStatement<'_>,
1138 ) -> Result<LogicalPlan, PlannerError> {
1139 match classified {
1140 GenericHostStatement::CreateTable(statement) => self.plan_create_table(statement),
1142 GenericHostStatement::DropTable(statement) => self.plan_drop_table(statement),
1143 GenericHostStatement::CreateIndex(statement) => self.plan_create_index(statement),
1144 GenericHostStatement::DropIndex(statement) => self.plan_drop_index(statement),
1145 GenericHostStatement::Pragma { name, value } => self.plan_pragma(name, value),
1146
1147 GenericHostStatement::Select(statement) => self.plan_select(statement),
1149 GenericHostStatement::Values(statement) => self.plan_values(statement),
1150 GenericHostStatement::Insert(statement) => self.plan_insert(statement),
1151 GenericHostStatement::Update(statement) => self.plan_update(statement),
1152 GenericHostStatement::Delete(statement) => self.plan_delete(statement),
1153 GenericHostStatement::Unsupported => Err(unsupported_generic_statement(stmt)),
1154 }
1155 }
1156
1157 fn plan_pragma(
1158 &self,
1159 raw_name: &str,
1160 value: &Option<PragmaValue>,
1161 ) -> Result<LogicalPlan, PlannerError> {
1162 let name = raw_name.to_ascii_lowercase();
1163 if !matches!(name.as_str(), "cache_size" | "memory_limit" | "io_stats") {
1164 return Err(PlannerError::InvalidPragma {
1165 name,
1166 reason: "supported names are cache_size, memory_limit, and io_stats".to_string(),
1167 });
1168 }
1169 match name.as_str() {
1170 "cache_size" => match value {
1171 Some(PragmaValue::Int(v)) if *v > 0 => {}
1172 Some(PragmaValue::Int(_)) => {
1173 return Err(PlannerError::InvalidPragma {
1174 name,
1175 reason: "cache_size must be a positive page count".to_string(),
1176 });
1177 }
1178 Some(PragmaValue::Text(_)) => {
1179 return Err(PlannerError::InvalidPragma {
1180 name,
1181 reason: "cache_size requires an integer page count".to_string(),
1182 });
1183 }
1184 None => {}
1185 },
1186 "memory_limit" => {
1187 if let Some(PragmaValue::Int(v)) = value
1188 && *v < 0
1189 {
1190 return Err(PlannerError::InvalidPragma {
1191 name,
1192 reason: "memory_limit cannot be negative".to_string(),
1193 });
1194 }
1195 }
1196 "io_stats" => {
1197 if value.is_some() {
1198 return Err(PlannerError::InvalidPragma {
1199 name,
1200 reason: "io_stats does not accept a value".to_string(),
1201 });
1202 }
1203 }
1204 _ => unreachable!(),
1205 }
1206 Ok(LogicalPlan::Pragma {
1207 name,
1208 value: value.clone(),
1209 })
1210 }
1211
1212 fn plan_create_table(&self, stmt: &CreateTable) -> Result<LogicalPlan, PlannerError> {
1221 if !stmt.if_not_exists && self.catalog.table_exists(&stmt.name) {
1223 return Err(PlannerError::table_already_exists(&stmt.name));
1224 }
1225
1226 let columns: Vec<ColumnMetadata> = stmt
1228 .columns
1229 .iter()
1230 .map(|col| self.convert_column_def(col))
1231 .collect();
1232
1233 let primary_key = Self::extract_primary_key(stmt);
1235
1236 let mut table = TableMetadata::new(stmt.name.clone(), columns);
1239 if let Some(pk) = primary_key {
1240 table = table.with_primary_key(pk);
1241 }
1242 table.catalog_name = "default".to_string();
1243 table.namespace_name = "default".to_string();
1244 table.table_type = TableType::Managed;
1245 table.data_source_format = DataSourceFormat::Alopex;
1246 table.properties = HashMap::new();
1247
1248 Ok(LogicalPlan::CreateTable {
1249 table,
1250 if_not_exists: stmt.if_not_exists,
1251 with_options: stmt
1252 .with_options
1253 .iter()
1254 .map(|opt| (opt.key.clone(), opt.value.clone()))
1255 .collect(),
1256 })
1257 }
1258
1259 fn convert_column_def(&self, col: &ColumnDef) -> ColumnMetadata {
1261 let data_type = ResolvedType::from_ast(&col.data_type);
1262 let mut meta = ColumnMetadata::new(col.name.clone(), data_type);
1263
1264 for constraint in &col.constraints {
1266 meta = Self::apply_column_constraint(meta, constraint);
1267 }
1268
1269 meta
1270 }
1271
1272 fn apply_column_constraint(
1274 mut meta: ColumnMetadata,
1275 constraint: &ColumnConstraint,
1276 ) -> ColumnMetadata {
1277 match constraint {
1278 ColumnConstraint::NotNull { .. } => {
1279 meta.not_null = true;
1280 }
1281 ColumnConstraint::PrimaryKey { .. } => {
1282 meta.primary_key = true;
1283 meta.not_null = true; }
1285 ColumnConstraint::Unique { .. } => {
1286 meta.unique = true;
1287 }
1288 ColumnConstraint::Default { value: expr, .. } => {
1289 meta.default = Some(expr.clone());
1290 }
1291 }
1292 meta
1293 }
1294
1295 fn extract_primary_key(stmt: &CreateTable) -> Option<Vec<String>> {
1297 use crate::ast::ddl::TableConstraint;
1298
1299 if let Some(TableConstraint::PrimaryKey { columns, .. }) = stmt.constraints.first() {
1303 return Some(columns.clone());
1304 }
1305
1306 let pk_columns: Vec<String> = stmt
1308 .columns
1309 .iter()
1310 .filter(|col| col.constraints.iter().any(Self::is_primary_key_constraint))
1311 .map(|col| col.name.clone())
1312 .collect();
1313
1314 if pk_columns.is_empty() {
1315 None
1316 } else {
1317 Some(pk_columns)
1318 }
1319 }
1320
1321 fn is_primary_key_constraint(constraint: &ColumnConstraint) -> bool {
1323 matches!(constraint, ColumnConstraint::PrimaryKey { .. })
1324 }
1325
1326 fn plan_drop_table(&self, stmt: &DropTable) -> Result<LogicalPlan, PlannerError> {
1330 if !stmt.if_exists && !self.table_exists_in_default(&stmt.name) {
1332 return Err(PlannerError::TableNotFound {
1333 name: stmt.name.clone(),
1334 line: stmt.span.start.line,
1335 column: stmt.span.start.column,
1336 });
1337 }
1338
1339 Ok(LogicalPlan::DropTable {
1340 name: stmt.name.clone(),
1341 if_exists: stmt.if_exists,
1342 })
1343 }
1344
1345 fn table_exists_in_default(&self, name: &str) -> bool {
1346 match self.catalog.get_table(name) {
1347 Some(table) => table.catalog_name == "default" && table.namespace_name == "default",
1348 None => false,
1349 }
1350 }
1351
1352 fn plan_create_index(&self, stmt: &CreateIndex) -> Result<LogicalPlan, PlannerError> {
1359 if !stmt.if_not_exists && self.catalog.index_exists(&stmt.name) {
1361 return Err(PlannerError::index_already_exists(&stmt.name));
1362 }
1363
1364 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
1366
1367 self.name_resolver
1369 .resolve_column(table, &stmt.column, stmt.span)?;
1370
1371 let mut index = IndexMetadata::new(
1375 0,
1376 stmt.name.clone(),
1377 stmt.table.clone(),
1378 vec![stmt.column.clone()],
1379 );
1380
1381 if let Some(method) = stmt.method {
1382 index = index.with_method(method);
1383 }
1384
1385 let options: Vec<(String, String)> = stmt
1386 .options
1387 .iter()
1388 .map(|opt| (opt.key.clone(), opt.value.clone()))
1389 .collect();
1390 if !options.is_empty() {
1391 index = index.with_options(options);
1392 }
1393
1394 Ok(LogicalPlan::CreateIndex {
1395 index,
1396 if_not_exists: stmt.if_not_exists,
1397 })
1398 }
1399
1400 fn plan_drop_index(&self, stmt: &DropIndex) -> Result<LogicalPlan, PlannerError> {
1404 if !stmt.if_exists && !self.index_exists_in_default(&stmt.name) {
1406 return Err(PlannerError::index_not_found(&stmt.name));
1407 }
1408
1409 Ok(LogicalPlan::DropIndex {
1410 name: stmt.name.clone(),
1411 if_exists: stmt.if_exists,
1412 })
1413 }
1414
1415 fn index_exists_in_default(&self, name: &str) -> bool {
1416 match self.catalog.get_index(name) {
1417 Some(index) => index.catalog_name == "default" && index.namespace_name == "default",
1418 None => false,
1419 }
1420 }
1421
1422 fn plan_select(&self, stmt: &Select) -> Result<LogicalPlan, PlannerError> {
1431 self.plan_select_relation(stmt, &[], &CtePlans::new())
1432 .map(|relation| relation.plan)
1433 }
1434
1435 fn plan_values(&self, stmt: &Values) -> Result<LogicalPlan, PlannerError> {
1436 self.plan_values_relation(stmt, &[], &CtePlans::new())
1437 .map(|relation| relation.plan)
1438 }
1439
1440 fn plan_query_body_relation(
1441 &self,
1442 body: &QueryBody,
1443 outer_scope: &[ScopedTable],
1444 enclosing_ctes: &CtePlans,
1445 ) -> Result<PlannedRelation, PlannerError> {
1446 match body {
1447 QueryBody::Select(select) => {
1448 self.plan_select_relation(select, outer_scope, enclosing_ctes)
1449 }
1450 QueryBody::Values(values) => {
1451 self.plan_values_relation(values, outer_scope, enclosing_ctes)
1452 }
1453 }
1454 }
1455
1456 fn plan_ctes(
1457 &self,
1458 stmt: &Select,
1459 enclosing_ctes: &CtePlans,
1460 ) -> Result<CtePlans, PlannerError> {
1461 self.plan_with_clause(stmt.with.as_ref(), enclosing_ctes)
1462 }
1463
1464 fn plan_with_clause(
1465 &self,
1466 with: Option<&crate::ast::WithClause>,
1467 enclosing_ctes: &CtePlans,
1468 ) -> Result<CtePlans, PlannerError> {
1469 let Some(with) = with else {
1470 return Ok(enclosing_ctes.clone());
1471 };
1472 let mut declared_names = HashSet::new();
1473 for cte in &with.ctes {
1474 if !declared_names.insert(&cte.name) {
1475 return Err(PlannerError::invalid_expression(format!(
1476 "common table expression '{}' is defined more than once",
1477 cte.name
1478 )));
1479 }
1480 }
1481 if with.recursive && cte_dependency_cycle(with) {
1482 return self.plan_recursive_cte(with, enclosing_ctes);
1483 }
1484
1485 let mut plans = enclosing_ctes.clone();
1486 let mut local_names = HashSet::new();
1487 for cte in &with.ctes {
1488 if !local_names.insert(cte.name.clone()) {
1489 return Err(PlannerError::invalid_expression(format!(
1490 "common table expression '{}' is defined more than once",
1491 cte.name
1492 )));
1493 }
1494 let mut relation = self.plan_query_body_relation(&cte.query, &[], &plans)?;
1495 if !cte.columns.is_empty() {
1496 if cte.columns.len() != relation.schema.len() {
1497 return Err(PlannerError::cte_column_count_mismatch(
1498 &cte.name,
1499 cte.columns.len(),
1500 relation.schema.len(),
1501 cte.span,
1502 ));
1503 }
1504
1505 let mut column_names = HashSet::new();
1506 for column_name in &cte.columns {
1507 if !column_names.insert(column_name) {
1508 return Err(PlannerError::duplicate_cte_column(
1509 &cte.name,
1510 column_name,
1511 cte.span,
1512 ));
1513 }
1514 }
1515
1516 for (column, column_name) in relation.schema.iter_mut().zip(&cte.columns) {
1517 column.name.clone_from(column_name);
1518 }
1519 }
1520 plans.insert(cte.name.clone(), relation);
1521 }
1522 Ok(plans)
1523 }
1524
1525 fn plan_recursive_cte(
1526 &self,
1527 with: &crate::ast::WithClause,
1528 enclosing_ctes: &CtePlans,
1529 ) -> Result<CtePlans, PlannerError> {
1530 if with.ctes.len() != 1 {
1531 return Err(PlannerError::unsupported_feature(
1532 "recursive WITH containing anything other than exactly one common table expression",
1533 "a future version",
1534 with.span,
1535 ));
1536 }
1537
1538 let cte = &with.ctes[0];
1539 let mut column_names = HashSet::new();
1540 for column_name in &cte.columns {
1541 if !column_names.insert(column_name) {
1542 return Err(PlannerError::duplicate_cte_column(
1543 &cte.name,
1544 column_name,
1545 cte.span,
1546 ));
1547 }
1548 }
1549
1550 let QueryBody::Select(body) = cte.query.as_ref() else {
1551 return Err(PlannerError::unsupported_feature(
1552 "recursive common table expression whose outer body is not SELECT",
1553 "a future version",
1554 cte.span,
1555 ));
1556 };
1557 if body.set_operations.len() != 1 {
1558 return Err(PlannerError::unsupported_feature(
1559 "recursive common table expression without exactly one UNION or UNION ALL",
1560 "a future version",
1561 cte.span,
1562 ));
1563 }
1564 if !body.order_by.is_empty()
1565 || body.limit.is_some()
1566 || body.offset.is_some()
1567 || body.limit_with_ties
1568 {
1569 return Err(PlannerError::unsupported_feature(
1570 "ORDER BY, LIMIT, OFFSET, or FETCH inside a recursive common table expression",
1571 "a future version",
1572 body.span,
1573 ));
1574 }
1575
1576 let operation = &body.set_operations[0];
1577 if operation.operator != AstSetOperator::Union {
1578 return Err(PlannerError::unsupported_feature(
1579 "recursive common table expression using an operator other than UNION or UNION ALL",
1580 "a future version",
1581 operation.span,
1582 ));
1583 }
1584
1585 let mut anchor = body.clone();
1586 anchor.with = None;
1587 anchor.set_operations.clear();
1588 if select_table_reference_count(&anchor, &cte.name) != 0 {
1589 return Err(PlannerError::unsupported_feature(
1590 "recursive common table expression without an anchor term that does not reference itself",
1591 "a future version",
1592 anchor.span,
1593 ));
1594 }
1595
1596 let QueryBody::Select(recursive_term) = operation.right.as_ref() else {
1597 return Err(PlannerError::unsupported_feature(
1598 "recursive common table expression whose recursive term is not SELECT",
1599 "a future version",
1600 operation.span,
1601 ));
1602 };
1603 if select_contains_subquery(recursive_term) {
1604 return Err(PlannerError::unsupported_feature(
1605 "subquery in a recursive term",
1606 "a future version",
1607 recursive_term.span,
1608 ));
1609 }
1610 let total_references = select_table_reference_count(recursive_term, &cte.name);
1611 let direct_references = direct_from_reference_count(&recursive_term.from, &cte.name);
1612 if total_references != 1 || direct_references != 1 {
1613 return Err(PlannerError::unsupported_feature(
1614 "recursive term without exactly one direct self-reference",
1615 "a future version",
1616 recursive_term.span,
1617 ));
1618 }
1619 if recursive_term.with.is_some() || !recursive_term.set_operations.is_empty() {
1620 return Err(PlannerError::unsupported_feature(
1621 "nested WITH or set operation in a recursive term",
1622 "a future version",
1623 recursive_term.span,
1624 ));
1625 }
1626
1627 let mut anchor_relation = self.plan_select_relation(&anchor, &[], enclosing_ctes)?;
1628 if cte.columns.is_empty() {
1629 let mut anchor_names = HashSet::new();
1630 for column in &anchor_relation.schema {
1631 if !anchor_names.insert(&column.name) {
1632 return Err(PlannerError::duplicate_cte_column(
1633 &cte.name,
1634 &column.name,
1635 cte.span,
1636 ));
1637 }
1638 }
1639 } else {
1640 if cte.columns.len() != anchor_relation.schema.len() {
1641 return Err(PlannerError::cte_column_count_mismatch(
1642 &cte.name,
1643 cte.columns.len(),
1644 anchor_relation.schema.len(),
1645 cte.span,
1646 ));
1647 }
1648 for (column, name) in anchor_relation.schema.iter_mut().zip(&cte.columns) {
1649 column.name.clone_from(name);
1650 }
1651 }
1652
1653 let mut recursive_scope = enclosing_ctes.clone();
1654 recursive_scope.insert(
1655 cte.name.clone(),
1656 PlannedRelation {
1657 plan: LogicalPlan::RecursiveReference {
1658 name: cte.name.clone(),
1659 schema: anchor_relation.schema.clone(),
1660 },
1661 schema: anchor_relation.schema.clone(),
1662 scope: vec![ScopedTable::new(
1663 TableMetadata::new(&cte.name, anchor_relation.schema.clone()),
1664 0,
1665 )],
1666 },
1667 );
1668 let recursive_relation =
1669 self.plan_select_relation(recursive_term, &[], &recursive_scope)?;
1670 if recursive_relation.schema.len() != anchor_relation.schema.len() {
1671 return Err(PlannerError::set_operation_column_count_mismatch(
1672 anchor_relation.schema.len(),
1673 recursive_relation.schema.len(),
1674 operation.span,
1675 ));
1676 }
1677 for (anchor_column, recursive_column) in anchor_relation
1678 .schema
1679 .iter()
1680 .zip(&recursive_relation.schema)
1681 {
1682 if anchor_column.data_type != recursive_column.data_type {
1683 return Err(PlannerError::type_mismatch(
1684 anchor_column.data_type.type_name(),
1685 recursive_column.data_type.type_name(),
1686 operation.span,
1687 ));
1688 }
1689 }
1690
1691 let schema = anchor_relation.schema.clone();
1692 let relation = PlannedRelation {
1693 plan: LogicalPlan::RecursiveCte {
1694 name: cte.name.clone(),
1695 anchor: Box::new(anchor_relation.plan),
1696 recursive_term: Box::new(recursive_relation.plan),
1697 union_all: operation.all,
1698 schema: schema.clone(),
1699 limits: RecursiveCteLimits::default(),
1700 },
1701 schema: schema.clone(),
1702 scope: vec![ScopedTable::new(TableMetadata::new(&cte.name, schema), 0)],
1703 };
1704 let mut plans = enclosing_ctes.clone();
1705 plans.insert(cte.name.clone(), relation);
1706 Ok(plans)
1707 }
1708
1709 fn plan_values_relation(
1710 &self,
1711 stmt: &Values,
1712 outer_scope: &[ScopedTable],
1713 enclosing_ctes: &CtePlans,
1714 ) -> Result<PlannedRelation, PlannerError> {
1715 let ctes = self.plan_with_clause(stmt.with.as_ref(), enclosing_ctes)?;
1716 let relation = self.plan_values_core(stmt, outer_scope, &ctes)?;
1717 self.apply_set_operations_and_tail(
1718 relation,
1719 &stmt.set_operations,
1720 &stmt.order_by,
1721 &stmt.limit,
1722 &stmt.offset,
1723 stmt.limit_with_ties,
1724 outer_scope,
1725 &ctes,
1726 )
1727 }
1728
1729 fn plan_values_core(
1730 &self,
1731 stmt: &Values,
1732 outer_scope: &[ScopedTable],
1733 ctes: &CtePlans,
1734 ) -> Result<PlannedRelation, PlannerError> {
1735 if stmt.rows.is_empty() {
1736 let schema = Vec::new();
1737 return Ok(PlannedRelation {
1738 plan: LogicalPlan::Values {
1739 rows: Vec::new(),
1740 schema: schema.clone(),
1741 },
1742 schema: schema.clone(),
1743 scope: vec![ScopedTable::new(
1744 TableMetadata::new(LITERAL_TABLE, schema),
1745 0,
1746 )],
1747 });
1748 }
1749
1750 let width = stmt.rows[0].len();
1751 if width == 0 {
1752 return Err(PlannerError::values_column_count_mismatch(
1753 1, 1, 0, stmt.span,
1754 ));
1755 }
1756
1757 let mut typed_rows = Vec::with_capacity(stmt.rows.len());
1758 for (row_index, row) in stmt.rows.iter().enumerate() {
1759 if row.len() != width {
1760 return Err(PlannerError::values_column_count_mismatch(
1761 row_index + 1,
1762 width,
1763 row.len(),
1764 stmt.span,
1765 ));
1766 }
1767 let mut typed_row = Vec::with_capacity(width);
1768 for expr in row {
1769 if expr_contains_subquery(expr) {
1770 return Err(PlannerError::invalid_expression(
1771 "VALUES expressions cannot contain subqueries".to_string(),
1772 ));
1773 }
1774 let typed = self.infer_expr_with_scope(expr, outer_scope, ctes)?;
1775 if typed_expr_contains_aggregate(&typed) || typed_expr_contains_window(&typed) {
1776 return Err(PlannerError::invalid_expression(
1777 "VALUES expressions must be scalar".to_string(),
1778 ));
1779 }
1780 typed_row.push(typed);
1781 }
1782 typed_rows.push(typed_row);
1783 }
1784
1785 let mut common_types = vec![ResolvedType::Null; width];
1786 for row in &typed_rows {
1787 for (column_index, value) in row.iter().enumerate() {
1788 common_types[column_index] = common_values_type(
1789 &common_types[column_index],
1790 &value.resolved_type,
1791 value.span,
1792 )?;
1793 }
1794 }
1795 for row in &mut typed_rows {
1796 for (value, target) in row.iter_mut().zip(&common_types) {
1797 if value.resolved_type != *target && value.resolved_type != ResolvedType::Null {
1798 *value = TypedExpr::cast(value.clone(), target.clone(), value.span);
1799 }
1800 }
1801 }
1802
1803 let schema = common_types
1804 .into_iter()
1805 .enumerate()
1806 .map(|(index, data_type)| {
1807 ColumnMetadata::new(format!("column{}", index + 1), data_type)
1808 })
1809 .collect::<Vec<_>>();
1810 Ok(PlannedRelation {
1811 plan: LogicalPlan::Values {
1812 rows: typed_rows,
1813 schema: schema.clone(),
1814 },
1815 schema: schema.clone(),
1816 scope: vec![ScopedTable::new(
1817 TableMetadata::new(LITERAL_TABLE, schema),
1818 0,
1819 )],
1820 })
1821 }
1822
1823 #[allow(clippy::too_many_arguments)]
1824 fn apply_set_operations_and_tail(
1825 &self,
1826 mut relation: PlannedRelation,
1827 operations: &[AstSetOperation],
1828 order_by: &[OrderByExpr],
1829 limit: &Option<Expr>,
1830 offset: &Option<Expr>,
1831 limit_with_ties: bool,
1832 outer_scope: &[ScopedTable],
1833 ctes: &CtePlans,
1834 ) -> Result<PlannedRelation, PlannerError> {
1835 for operation in operations {
1836 let right = self.plan_query_body_relation(&operation.right, outer_scope, ctes)?;
1837 if relation.schema.len() != right.schema.len() {
1838 return Err(PlannerError::set_operation_column_count_mismatch(
1839 relation.schema.len(),
1840 right.schema.len(),
1841 operation.span,
1842 ));
1843 }
1844 for (left_column, right_column) in relation.schema.iter().zip(&right.schema) {
1845 if left_column.data_type != right_column.data_type {
1846 return Err(PlannerError::type_mismatch(
1847 left_column.data_type.type_name(),
1848 right_column.data_type.type_name(),
1849 operation.span,
1850 ));
1851 }
1852 }
1853
1854 relation.plan = LogicalPlan::SetOperation {
1855 left: Box::new(relation.plan),
1856 right: Box::new(right.plan),
1857 operator: match operation.operator {
1858 AstSetOperator::Union => SetOperator::Union,
1859 AstSetOperator::Intersect => SetOperator::Intersect,
1860 AstSetOperator::Except => SetOperator::Except,
1861 },
1862 all: operation.all,
1863 };
1864 }
1865
1866 relation.scope = vec![ScopedTable::new(
1867 TableMetadata::new(LITERAL_TABLE, relation.schema.clone()),
1868 0,
1869 )];
1870 if !order_by.is_empty() {
1871 let order_by =
1872 self.build_sort_exprs_with_scope(order_by, &relation.scope, &HashMap::new(), ctes)?;
1873 relation.plan = LogicalPlan::Sort {
1874 input: Box::new(relation.plan),
1875 order_by,
1876 };
1877 }
1878 relation.plan = self.apply_pagination(relation.plan, limit, offset, limit_with_ties)?;
1879 Ok(relation)
1880 }
1881
1882 fn plan_select_relation(
1883 &self,
1884 stmt: &Select,
1885 outer_scope: &[ScopedTable],
1886 enclosing_ctes: &CtePlans,
1887 ) -> Result<PlannedRelation, PlannerError> {
1888 let resolved_stmt = resolve_named_windows(stmt)?;
1889 let stmt = &resolved_stmt;
1890 let ctes = self.plan_ctes(stmt, enclosing_ctes)?;
1891 if !stmt.set_operations.is_empty() {
1892 if !stmt.distinct_on.is_empty() {
1896 return Err(PlannerError::unsupported_feature(
1897 "DISTINCT ON combined with UNION, INTERSECT, or EXCEPT",
1898 "a future version",
1899 stmt.span,
1900 ));
1901 }
1902 let mut left_select = stmt.clone();
1903 left_select.with = None;
1904 left_select.set_operations.clear();
1905 left_select.order_by.clear();
1906 left_select.limit = None;
1907 left_select.offset = None;
1908 left_select.limit_with_ties = false;
1909 let relation = self.plan_select_relation(&left_select, outer_scope, &ctes)?;
1910
1911 return self.apply_set_operations_and_tail(
1912 relation,
1913 &stmt.set_operations,
1914 &stmt.order_by,
1915 &stmt.limit,
1916 &stmt.offset,
1917 stmt.limit_with_ties,
1918 outer_scope,
1919 &ctes,
1920 );
1921 }
1922
1923 let mut relation = self.plan_from_items(&stmt.from, stmt.span, outer_scope, &ctes)?;
1924 let expr_scope = relation
1925 .scope
1926 .iter()
1927 .cloned()
1928 .chain(offset_scope(outer_scope, relation.schema.len()))
1929 .collect::<Vec<_>>();
1930
1931 let has_group_by = stmt
1932 .group_by
1933 .as_ref()
1934 .is_some_and(|items| !items.is_empty());
1935 let has_aggregate = self.select_contains_aggregate(stmt);
1936 let has_window = select_contains_window(stmt);
1937 if stmt.qualify.is_some() && !has_window {
1938 return Err(PlannerError::invalid_expression(
1939 "QUALIFY requires at least one window function in the query block".to_string(),
1940 ));
1941 }
1942 let distinct_only =
1943 stmt.distinct && !has_group_by && !has_aggregate && stmt.having.is_none();
1944
1945 let has_distinct_on = !stmt.distinct_on.is_empty();
1946 if has_distinct_on {
1947 if stmt.distinct {
1950 return Err(PlannerError::invalid_expression(
1951 "DISTINCT and DISTINCT ON cannot be combined".to_string(),
1952 ));
1953 }
1954 for key in &stmt.distinct_on {
1956 if expr_contains_aggregate(key) || expr_contains_window(key) {
1957 return Err(PlannerError::invalid_expression(
1958 "DISTINCT ON expressions cannot contain aggregate or window functions"
1959 .to_string(),
1960 ));
1961 }
1962 if expr_contains_subquery(key) {
1963 return Err(PlannerError::invalid_expression(
1964 "DISTINCT ON expressions cannot contain subqueries".to_string(),
1965 ));
1966 }
1967 }
1968 if has_group_by || has_aggregate || stmt.having.is_some() {
1971 return Err(PlannerError::unsupported_feature(
1972 "DISTINCT ON with GROUP BY, aggregate functions, or HAVING",
1973 "a future version",
1974 stmt.span,
1975 ));
1976 }
1977 if has_window || stmt.qualify.is_some() {
1978 return Err(PlannerError::unsupported_feature(
1979 "DISTINCT ON with window functions or QUALIFY",
1980 "a future version",
1981 stmt.span,
1982 ));
1983 }
1984 }
1985
1986 if stmt.having.as_ref().is_some_and(expr_contains_window) {
1987 return Err(PlannerError::invalid_expression(
1988 "HAVING cannot contain window functions".to_string(),
1989 ));
1990 }
1991 if stmt.group_by.as_ref().is_some_and(|items| {
1992 items
1993 .iter()
1994 .flat_map(GroupByItem::exprs)
1995 .any(expr_contains_window)
1996 }) {
1997 return Err(PlannerError::invalid_expression(
1998 "GROUP BY cannot contain window functions".to_string(),
1999 ));
2000 }
2001 if stmt.group_by.as_ref().is_some_and(|items| {
2003 items
2004 .iter()
2005 .flat_map(GroupByItem::exprs)
2006 .any(expr_contains_grouping)
2007 }) {
2008 return Err(PlannerError::invalid_expression(
2009 "GROUPING is not allowed in GROUP BY".to_string(),
2010 ));
2011 }
2012 if stmt.selection.as_ref().is_some_and(expr_contains_grouping) {
2013 return Err(PlannerError::invalid_expression(
2014 "GROUPING is not allowed in WHERE".to_string(),
2015 ));
2016 }
2017 if !(has_group_by || has_aggregate || stmt.having.is_some()) {
2018 let grouping_present = stmt.projection.iter().any(|item| match item {
2019 SelectItem::Expr { expr, .. } => expr_contains_grouping(expr),
2020 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
2021 }) || stmt
2022 .order_by
2023 .iter()
2024 .any(|order| expr_contains_grouping(&order.expr))
2025 || stmt.qualify.as_ref().is_some_and(expr_contains_grouping);
2026 if grouping_present {
2027 return Err(PlannerError::invalid_expression(
2028 "GROUPING is only allowed in grouped queries".to_string(),
2029 ));
2030 }
2031 }
2032
2033 let projection_aliases = collect_projection_aliases(&stmt.projection);
2037
2038 let final_projection = self.build_projection_with_scope(
2039 &stmt.projection,
2040 &relation.schema,
2041 &expr_scope,
2042 &ctes,
2043 )?;
2044 if !has_window {
2045 install_base_projection(&mut relation.plan, &final_projection);
2046 }
2047 let needs_project_boundary = !matches!(relation.plan, LogicalPlan::Scan { .. });
2048 let base_schema = relation.schema.clone();
2049 let mut plan = relation.plan;
2050
2051 if let Some(ref selection) = stmt.selection {
2053 if expr_contains_window(selection) {
2054 return Err(PlannerError::invalid_expression(
2055 "WHERE cannot contain window functions".to_string(),
2056 ));
2057 }
2058 let predicate = self.infer_expr_with_scope(selection, &expr_scope, &ctes)?;
2059
2060 if predicate.resolved_type != ResolvedType::Boolean {
2062 return Err(PlannerError::type_mismatch(
2063 "Boolean",
2064 predicate.resolved_type.to_string(),
2065 selection.span,
2066 ));
2067 }
2068
2069 plan = LogicalPlan::Filter {
2070 input: Box::new(plan),
2071 predicate,
2072 };
2073 }
2074
2075 if has_window && (has_group_by || has_aggregate || stmt.having.is_some()) {
2076 return self.plan_grouped_window_select(
2077 stmt,
2078 &ctes,
2079 &expr_scope,
2080 &projection_aliases,
2081 plan,
2082 );
2083 }
2084
2085 if has_window {
2086 let mut windows = Vec::new();
2087 let mut window_map = HashMap::new();
2088 if let Projection::Columns(columns) = &final_projection {
2089 for column in columns {
2090 self.collect_windows_from_typed_expr(
2091 &column.expr,
2092 &mut windows,
2093 &mut window_map,
2094 )?;
2095 }
2096 }
2097
2098 let mut outer_order_by = Vec::new();
2099 for order_expr in &stmt.order_by {
2100 let sort_source =
2101 substitute_projection_aliases(&order_expr.expr, &projection_aliases);
2102 let typed = self.infer_expr_with_scope(&sort_source, &expr_scope, &ctes)?;
2103 self.collect_windows_from_typed_expr(&typed, &mut windows, &mut window_map)?;
2104 outer_order_by.push(SortExpr::new(
2105 typed,
2106 order_expr.asc.unwrap_or(true),
2107 order_expr.nulls_first.unwrap_or(false),
2108 ));
2109 }
2110
2111 let qualify = if let Some(qualify) = &stmt.qualify {
2112 let source = substitute_projection_aliases(qualify, &projection_aliases);
2113 let typed = self.infer_expr_with_scope(&source, &expr_scope, &ctes)?;
2114 if typed.resolved_type != ResolvedType::Boolean {
2115 return Err(PlannerError::type_mismatch(
2116 "Boolean",
2117 typed.resolved_type.type_name().to_string(),
2118 typed.span,
2119 ));
2120 }
2121 self.collect_windows_from_typed_expr(&typed, &mut windows, &mut window_map)?;
2122 Some(typed)
2123 } else {
2124 None
2125 };
2126
2127 let window_names = (0..windows.len())
2128 .map(|idx| format!("__window_{idx}"))
2129 .collect::<Vec<_>>();
2130 let mut window_schema = base_schema;
2131 window_schema.extend(windows.iter().enumerate().map(|(idx, window)| {
2132 ColumnMetadata::new(window_names[idx].clone(), window.result_type.clone())
2133 }));
2134
2135 let projection = rewrite_projection_for_windows(
2136 &final_projection,
2137 &window_map,
2138 relation.schema.len(),
2139 &window_names,
2140 )?;
2141 let order_by = outer_order_by
2142 .into_iter()
2143 .map(|sort| {
2144 Ok(SortExpr::new(
2145 rewrite_expr_for_windows(
2146 &sort.expr,
2147 &window_map,
2148 relation.schema.len(),
2149 &window_names,
2150 )?,
2151 sort.asc,
2152 sort.nulls_first,
2153 ))
2154 })
2155 .collect::<Result<Vec<_>, PlannerError>>()?;
2156 let qualify = qualify
2157 .as_ref()
2158 .map(|expr| {
2159 rewrite_expr_for_windows(
2160 expr,
2161 &window_map,
2162 relation.schema.len(),
2163 &window_names,
2164 )
2165 })
2166 .transpose()?;
2167
2168 return self.finish_window_select(
2169 stmt,
2170 WindowSelectStages {
2171 plan,
2172 windows,
2173 qualify,
2174 projection,
2175 order_by,
2176 window_schema,
2177 },
2178 );
2179 }
2180
2181 if has_group_by || has_aggregate || stmt.having.is_some() || stmt.distinct {
2182 if !has_group_by && !has_aggregate && stmt.having.is_some() {
2183 return Err(PlannerError::invalid_expression(
2184 "HAVING requires GROUP BY or aggregate functions".to_string(),
2185 ));
2186 }
2187
2188 let (group_keys, grouping_sets, projected) = if distinct_only {
2189 let projected = self.build_projected_columns_for_distinct_with_scope(
2190 &stmt.projection,
2191 &relation.schema,
2192 &expr_scope,
2193 &ctes,
2194 )?;
2195 let group_keys = projected.iter().map(|col| col.expr.clone()).collect();
2196 (group_keys, None, projected)
2197 } else {
2198 let expanded = self.expand_group_by_items(stmt, &expr_scope, &ctes)?;
2199 let projected = self.build_projected_columns_for_aggregate_with_scope(
2200 &stmt.projection,
2201 &expr_scope,
2202 &ctes,
2203 )?;
2204 (expanded.group_keys, expanded.grouping_sets, projected)
2205 };
2206 let mut aggregates = Vec::new();
2207 let mut agg_map = HashMap::new();
2208
2209 for col in &projected {
2210 self.collect_aggregates_from_typed_expr(&col.expr, &mut aggregates, &mut agg_map)?;
2211 }
2212
2213 let having_typed = if let Some(having) = &stmt.having {
2214 let having = substitute_projection_aliases(having, &projection_aliases);
2215 let typed = self.infer_expr_with_scope(&having, &expr_scope, &ctes)?;
2216 if typed.resolved_type != ResolvedType::Boolean {
2217 return Err(PlannerError::type_mismatch(
2218 "Boolean",
2219 typed.resolved_type.type_name().to_string(),
2220 typed.span,
2221 ));
2222 }
2223 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut agg_map)?;
2224 Some(typed)
2225 } else {
2226 None
2227 };
2228
2229 let mut order_by = Vec::new();
2230 if !stmt.order_by.is_empty() {
2231 for order_expr in &stmt.order_by {
2232 let sort_source =
2233 substitute_projection_aliases(&order_expr.expr, &projection_aliases);
2234 let typed = self.infer_expr_with_scope(&sort_source, &expr_scope, &ctes)?;
2235 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut agg_map)?;
2236 let asc = order_expr.asc.unwrap_or(true);
2237 let nulls_first = order_expr.nulls_first.unwrap_or(false);
2238 order_by.push(SortExpr::new(typed, asc, nulls_first));
2239 }
2240 }
2241
2242 if let Some(ref having) = having_typed {
2243 self.type_checker
2244 .validate_having_expr(having, &group_keys, &aggregates)?;
2245 }
2246
2247 let mut output_schema = build_aggregate_schema(&group_keys, &aggregates);
2248 let output_names: Vec<String> = output_schema.iter().map(|c| c.name.clone()).collect();
2253 let grouping_rewrite = GroupingRewrite::new(&group_keys, &aggregates, &grouping_sets);
2254 if grouping_sets.is_some() {
2255 output_schema.push(ColumnMetadata::new(
2256 GROUPING_ID_COLUMN,
2257 ResolvedType::BigInt,
2258 ));
2259 }
2260
2261 let projection = self.build_aggregate_projection(
2262 projected,
2263 &group_keys,
2264 &aggregates,
2265 &output_names,
2266 Some(&grouping_rewrite),
2267 )?;
2268
2269 let having = if let Some(having) = having_typed {
2270 Some(self.rewrite_expr_for_aggregate(
2271 &having,
2272 &group_keys,
2273 &aggregates,
2274 &output_names,
2275 Some(&grouping_rewrite),
2276 )?)
2277 } else {
2278 None
2279 };
2280
2281 let order_by = order_by
2282 .into_iter()
2283 .map(|expr| {
2284 let rewritten = self.rewrite_expr_for_aggregate(
2285 &expr.expr,
2286 &group_keys,
2287 &aggregates,
2288 &output_names,
2289 Some(&grouping_rewrite),
2290 )?;
2291 Ok(SortExpr::new(rewritten, expr.asc, expr.nulls_first))
2292 })
2293 .collect::<Result<Vec<_>, PlannerError>>()?;
2294
2295 let schema = projection_schema(&projection, &output_schema);
2296 plan = LogicalPlan::Aggregate {
2297 input: Box::new(plan),
2298 group_keys,
2299 aggregates,
2300 having,
2301 projection,
2302 grouping_sets,
2303 };
2304
2305 if !order_by.is_empty() {
2306 plan = LogicalPlan::Sort {
2307 input: Box::new(plan),
2308 order_by,
2309 };
2310 }
2311
2312 plan = self.apply_pagination(plan, &stmt.limit, &stmt.offset, stmt.limit_with_ties)?;
2313
2314 return Ok(PlannedRelation {
2315 plan,
2316 schema: schema.clone(),
2317 scope: vec![ScopedTable::new(
2318 TableMetadata::new(LITERAL_TABLE, schema),
2319 0,
2320 )],
2321 });
2322 }
2323
2324 let mut distinct_on_tie_keys: Option<Vec<SortExpr>> = None;
2326 if has_distinct_on {
2327 let mut key_exprs: Vec<TypedExpr> = Vec::new();
2334 let mut key_signatures = HashSet::new();
2335 for key in &stmt.distinct_on {
2336 let source = substitute_projection_aliases(key, &projection_aliases);
2337 let typed = self.infer_expr_with_scope(&source, &expr_scope, &ctes)?;
2338 if key_signatures.insert(distinct_on_expr_signature(&typed)) {
2339 key_exprs.push(typed);
2340 }
2341 }
2342 let user_order_by = self.build_sort_exprs_with_scope(
2343 &stmt.order_by,
2344 &expr_scope,
2345 &projection_aliases,
2346 &ctes,
2347 )?;
2348 let user_order_by_len = user_order_by.len();
2356 let (key_count, order_by) =
2357 build_distinct_on_sort_spec(key_exprs, user_order_by, &base_schema, stmt.span)?;
2358 distinct_on_tie_keys = Some(order_by[..user_order_by_len.min(order_by.len())].to_vec());
2359 plan = LogicalPlan::DistinctOn {
2360 input: Box::new(plan),
2361 key_count,
2362 order_by,
2363 };
2364 } else if !stmt.order_by.is_empty() {
2365 let order_by = self.build_sort_exprs_with_scope(
2366 &stmt.order_by,
2367 &expr_scope,
2368 &projection_aliases,
2369 &ctes,
2370 )?;
2371 plan = LogicalPlan::Sort {
2372 input: Box::new(plan),
2373 order_by,
2374 };
2375 }
2376
2377 plan = self.apply_pagination_with_tie_keys(
2378 plan,
2379 &stmt.limit,
2380 &stmt.offset,
2381 stmt.limit_with_ties,
2382 distinct_on_tie_keys.as_deref(),
2383 )?;
2384
2385 let output_schema = projection_schema(&final_projection, &relation.schema);
2386 if needs_project_boundary {
2387 plan = LogicalPlan::Project {
2388 input: Box::new(plan),
2389 projection: final_projection,
2390 };
2391 }
2392 Ok(PlannedRelation {
2393 plan,
2394 schema: output_schema.clone(),
2395 scope: vec![ScopedTable::new(
2396 TableMetadata::new(LITERAL_TABLE, output_schema),
2397 0,
2398 )],
2399 })
2400 }
2401
2402 fn plan_grouped_window_select(
2409 &self,
2410 stmt: &Select,
2411 ctes: &CtePlans,
2412 expr_scope: &[ScopedTable],
2413 projection_aliases: &HashMap<String, crate::ast::expr::Expr>,
2414 mut plan: LogicalPlan,
2415 ) -> Result<PlannedRelation, PlannerError> {
2416 let expanded = self.expand_group_by_items(stmt, expr_scope, ctes)?;
2417 let group_keys = expanded.group_keys;
2418 let grouping_sets = expanded.grouping_sets;
2419 let projected = self.build_projected_columns_for_aggregate_with_scope(
2420 &stmt.projection,
2421 expr_scope,
2422 ctes,
2423 )?;
2424 let mut aggregates = Vec::new();
2425 let mut aggregate_map = HashMap::new();
2426 for column in &projected {
2427 self.collect_aggregates_from_typed_expr(
2428 &column.expr,
2429 &mut aggregates,
2430 &mut aggregate_map,
2431 )?;
2432 }
2433
2434 let having_typed = if let Some(having) = &stmt.having {
2435 let having = substitute_projection_aliases(having, projection_aliases);
2436 if expr_contains_window(&having) {
2437 return Err(PlannerError::invalid_expression(
2438 "HAVING cannot contain window functions".to_string(),
2439 ));
2440 }
2441 let typed = self.infer_expr_with_scope(&having, expr_scope, ctes)?;
2442 if typed.resolved_type != ResolvedType::Boolean {
2443 return Err(PlannerError::type_mismatch(
2444 "Boolean",
2445 typed.resolved_type.type_name().to_string(),
2446 typed.span,
2447 ));
2448 }
2449 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut aggregate_map)?;
2450 Some(typed)
2451 } else {
2452 None
2453 };
2454
2455 let qualify_typed = if let Some(qualify) = &stmt.qualify {
2456 let qualify = substitute_projection_aliases(qualify, projection_aliases);
2457 let typed = self.infer_expr_with_scope(&qualify, expr_scope, ctes)?;
2458 if typed.resolved_type != ResolvedType::Boolean {
2459 return Err(PlannerError::type_mismatch(
2460 "Boolean",
2461 typed.resolved_type.type_name().to_string(),
2462 typed.span,
2463 ));
2464 }
2465 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut aggregate_map)?;
2466 Some(typed)
2467 } else {
2468 None
2469 };
2470
2471 let mut outer_order_by = Vec::new();
2472 for order_expr in &stmt.order_by {
2473 let source = substitute_projection_aliases(&order_expr.expr, projection_aliases);
2474 let typed = self.infer_expr_with_scope(&source, expr_scope, ctes)?;
2475 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut aggregate_map)?;
2476 outer_order_by.push(SortExpr::new(
2477 typed,
2478 order_expr.asc.unwrap_or(true),
2479 order_expr.nulls_first.unwrap_or(false),
2480 ));
2481 }
2482
2483 if let Some(having) = &having_typed {
2484 self.type_checker
2485 .validate_having_expr(having, &group_keys, &aggregates)?;
2486 }
2487
2488 let mut aggregate_schema = build_aggregate_schema(&group_keys, &aggregates);
2489 let rewrite_names = aggregate_schema
2492 .iter()
2493 .map(|column| column.name.clone())
2494 .collect::<Vec<_>>();
2495 let grouping_rewrite = GroupingRewrite::new(&group_keys, &aggregates, &grouping_sets);
2496 if grouping_sets.is_some() {
2497 aggregate_schema.push(ColumnMetadata::new(
2498 GROUPING_ID_COLUMN,
2499 ResolvedType::BigInt,
2500 ));
2501 }
2502 let aggregate_names = aggregate_schema
2503 .iter()
2504 .map(|column| column.name.clone())
2505 .collect::<Vec<_>>();
2506 let projection = self.build_aggregate_projection(
2507 projected,
2508 &group_keys,
2509 &aggregates,
2510 &rewrite_names,
2511 Some(&grouping_rewrite),
2512 )?;
2513 let having = having_typed
2514 .as_ref()
2515 .map(|expr| {
2516 self.rewrite_expr_for_aggregate(
2517 expr,
2518 &group_keys,
2519 &aggregates,
2520 &rewrite_names,
2521 Some(&grouping_rewrite),
2522 )
2523 })
2524 .transpose()?;
2525 let outer_order_by = outer_order_by
2526 .into_iter()
2527 .map(|sort| {
2528 Ok(SortExpr::new(
2529 self.rewrite_expr_for_aggregate(
2530 &sort.expr,
2531 &group_keys,
2532 &aggregates,
2533 &rewrite_names,
2534 Some(&grouping_rewrite),
2535 )?,
2536 sort.asc,
2537 sort.nulls_first,
2538 ))
2539 })
2540 .collect::<Result<Vec<_>, PlannerError>>()?;
2541 let qualify = qualify_typed
2542 .as_ref()
2543 .map(|expr| {
2544 self.rewrite_expr_for_aggregate(
2545 expr,
2546 &group_keys,
2547 &aggregates,
2548 &rewrite_names,
2549 Some(&grouping_rewrite),
2550 )
2551 })
2552 .transpose()?;
2553
2554 plan = LogicalPlan::Aggregate {
2555 input: Box::new(plan),
2556 group_keys,
2557 aggregates,
2558 having,
2559 projection: Projection::All(aggregate_names),
2560 grouping_sets,
2561 };
2562
2563 let mut windows = Vec::new();
2564 let mut window_map = HashMap::new();
2565 if let Projection::Columns(columns) = &projection {
2566 for column in columns {
2567 self.collect_windows_from_typed_expr(&column.expr, &mut windows, &mut window_map)?;
2568 }
2569 }
2570 for sort in &outer_order_by {
2571 self.collect_windows_from_typed_expr(&sort.expr, &mut windows, &mut window_map)?;
2572 }
2573 if let Some(qualify) = &qualify {
2574 self.collect_windows_from_typed_expr(qualify, &mut windows, &mut window_map)?;
2575 }
2576
2577 let window_names = (0..windows.len())
2578 .map(|index| format!("__window_{index}"))
2579 .collect::<Vec<_>>();
2580 let mut window_schema = aggregate_schema;
2581 window_schema.extend(windows.iter().enumerate().map(|(index, window)| {
2582 ColumnMetadata::new(window_names[index].clone(), window.result_type.clone())
2583 }));
2584 let projection = rewrite_projection_for_windows(
2585 &projection,
2586 &window_map,
2587 window_schema.len() - windows.len(),
2588 &window_names,
2589 )?;
2590 let outer_order_by = outer_order_by
2591 .into_iter()
2592 .map(|sort| {
2593 Ok(SortExpr::new(
2594 rewrite_expr_for_windows(
2595 &sort.expr,
2596 &window_map,
2597 window_schema.len() - windows.len(),
2598 &window_names,
2599 )?,
2600 sort.asc,
2601 sort.nulls_first,
2602 ))
2603 })
2604 .collect::<Result<Vec<_>, PlannerError>>()?;
2605 let qualify = qualify
2606 .as_ref()
2607 .map(|expr| {
2608 rewrite_expr_for_windows(
2609 expr,
2610 &window_map,
2611 window_schema.len() - windows.len(),
2612 &window_names,
2613 )
2614 })
2615 .transpose()?;
2616
2617 self.finish_window_select(
2618 stmt,
2619 WindowSelectStages {
2620 plan,
2621 windows,
2622 qualify,
2623 projection,
2624 order_by: outer_order_by,
2625 window_schema,
2626 },
2627 )
2628 }
2629
2630 fn finish_window_select(
2632 &self,
2633 stmt: &Select,
2634 stages: WindowSelectStages,
2635 ) -> Result<PlannedRelation, PlannerError> {
2636 let WindowSelectStages {
2637 mut plan,
2638 windows,
2639 qualify,
2640 projection,
2641 order_by,
2642 window_schema,
2643 } = stages;
2644 plan = LogicalPlan::Window {
2645 input: Box::new(plan),
2646 windows,
2647 };
2648 if let Some(predicate) = qualify {
2649 plan = LogicalPlan::Filter {
2650 input: Box::new(plan),
2651 predicate,
2652 };
2653 }
2654
2655 let output_schema = projection_schema(&projection, &window_schema);
2656 let mut hidden_order_keys = Vec::new();
2657 let mut projected_order_by = Vec::with_capacity(order_by.len());
2658 for sort in order_by {
2659 let expr =
2660 match rewrite_expr_for_projected_output(&sort.expr, &projection, &output_schema) {
2661 Ok(expr) => expr,
2662 Err(_) if !stmt.distinct => {
2663 let hidden_index = hidden_order_keys.len();
2664 let name = format!("__order_{hidden_index}");
2665 let output_index = output_schema.len() + hidden_index;
2666 hidden_order_keys.push(ProjectedColumn {
2667 expr: sort.expr.clone(),
2668 alias: Some(name.clone()),
2669 });
2670 TypedExpr::column_ref(
2671 "__project__".to_string(),
2672 name,
2673 output_index,
2674 sort.expr.resolved_type.clone(),
2675 sort.expr.span,
2676 )
2677 }
2678 Err(error) => return Err(error),
2679 };
2680 projected_order_by.push(SortExpr::new(expr, sort.asc, sort.nulls_first));
2681 }
2682 let has_hidden_order_keys = !hidden_order_keys.is_empty();
2683 let projection = if has_hidden_order_keys {
2684 let mut columns = match projection {
2685 Projection::Columns(columns) => columns,
2686 Projection::All(_) => output_schema
2687 .iter()
2688 .enumerate()
2689 .map(|(index, column)| ProjectedColumn {
2690 expr: TypedExpr::column_ref(
2691 "__window__".to_string(),
2692 column.name.clone(),
2693 index,
2694 column.data_type.clone(),
2695 stmt.span,
2696 ),
2697 alias: None,
2698 })
2699 .collect(),
2700 };
2701 columns.extend(hidden_order_keys);
2702 Projection::Columns(columns)
2703 } else {
2704 projection
2705 };
2706 plan = LogicalPlan::Project {
2707 input: Box::new(plan),
2708 projection,
2709 };
2710
2711 if stmt.distinct {
2712 let group_keys = output_schema
2713 .iter()
2714 .enumerate()
2715 .map(|(index, column)| {
2716 TypedExpr::column_ref(
2717 LITERAL_TABLE.to_string(),
2718 column.name.clone(),
2719 index,
2720 column.data_type.clone(),
2721 stmt.span,
2722 )
2723 })
2724 .collect();
2725 plan = LogicalPlan::Aggregate {
2726 input: Box::new(plan),
2727 group_keys,
2728 aggregates: Vec::new(),
2729 having: None,
2730 projection: Projection::All(
2731 output_schema
2732 .iter()
2733 .map(|column| column.name.clone())
2734 .collect(),
2735 ),
2736 grouping_sets: None,
2737 };
2738 }
2739 if !projected_order_by.is_empty() {
2740 plan = LogicalPlan::Sort {
2741 input: Box::new(plan),
2742 order_by: projected_order_by,
2743 };
2744 }
2745 plan = self.apply_pagination(plan, &stmt.limit, &stmt.offset, stmt.limit_with_ties)?;
2746 if has_hidden_order_keys {
2747 plan = LogicalPlan::Project {
2748 input: Box::new(plan),
2749 projection: Projection::Columns(
2750 output_schema
2751 .iter()
2752 .enumerate()
2753 .map(|(index, column)| ProjectedColumn {
2754 expr: TypedExpr::column_ref(
2755 "__ordered__".to_string(),
2756 column.name.clone(),
2757 index,
2758 column.data_type.clone(),
2759 stmt.span,
2760 ),
2761 alias: Some(column.name.clone()),
2762 })
2763 .collect(),
2764 ),
2765 };
2766 }
2767
2768 Ok(PlannedRelation {
2769 plan,
2770 schema: output_schema.clone(),
2771 scope: vec![ScopedTable::new(
2772 TableMetadata::new(LITERAL_TABLE, output_schema),
2773 0,
2774 )],
2775 })
2776 }
2777
2778 fn plan_from_items(
2782 &self,
2783 items: &[FromItem],
2784 select_span: crate::ast::Span,
2785 outer_scope: &[ScopedTable],
2786 ctes: &CtePlans,
2787 ) -> Result<PlannedRelation, PlannerError> {
2788 match items {
2789 [] => {
2790 let schema = Vec::new();
2791 Ok(PlannedRelation {
2792 plan: LogicalPlan::Scan {
2793 table: LITERAL_TABLE.to_string(),
2794 projection: Projection::All(Vec::new()),
2795 },
2796 schema: schema.clone(),
2797 scope: vec![ScopedTable::new(
2798 TableMetadata::new(LITERAL_TABLE, schema),
2799 0,
2800 )],
2801 })
2802 }
2803 [single] => self.plan_from_item(single, 0, outer_scope, outer_scope, ctes),
2804 [first, rest @ ..] => {
2805 let mut relation = self.plan_from_item(first, 0, outer_scope, outer_scope, ctes)?;
2806 for item in rest {
2807 let left_width = relation.schema.len();
2810 let lateral_scope =
2811 lateral_outer_scope(&relation.scope, 0, left_width, outer_scope);
2812 let right =
2813 self.plan_from_item(item, left_width, outer_scope, &lateral_scope, ctes)?;
2814 relation = if from_item_is_lateral(item) {
2815 self.combine_lateral_join_relation(
2816 relation,
2817 right,
2818 JoinType::Cross,
2819 None,
2820 None,
2821 select_span,
2822 )?
2823 } else {
2824 self.combine_join_relation(
2825 relation,
2826 right,
2827 JoinType::Cross,
2828 None,
2829 None,
2830 select_span,
2831 )?
2832 };
2833 }
2834 Ok(relation)
2835 }
2836 }
2837 }
2838
2839 fn plan_from_item(
2840 &self,
2841 item: &FromItem,
2842 start_index: usize,
2843 outer_scope: &[ScopedTable],
2844 lateral_scope: &[ScopedTable],
2845 ctes: &CtePlans,
2846 ) -> Result<PlannedRelation, PlannerError> {
2847 match item {
2848 FromItem::Table {
2849 name,
2850 alias,
2851 columns,
2852 span,
2853 } => {
2854 if let Some(cte) = ctes.get(name) {
2855 let mut relation = cte.clone();
2856 relation.plan = LogicalPlan::Project {
2857 input: Box::new(relation.plan),
2858 projection: Projection::All(
2859 relation.schema.iter().map(|col| col.name.clone()).collect(),
2860 ),
2861 };
2862 let relation_name = alias.clone().unwrap_or_else(|| name.clone());
2863 apply_alias_columns(&relation_name, columns, &mut relation.schema, *span)?;
2864 relation.scope = vec![ScopedTable::new(
2865 TableMetadata::new(relation_name, relation.schema.clone()),
2866 start_index,
2867 )];
2868 return Ok(relation);
2869 }
2870 let table = self.name_resolver.resolve_table(name, *span)?.clone();
2871 let mut scope_table = table.clone();
2872 if let Some(alias) = alias {
2873 scope_table.name = alias.clone();
2874 }
2875 let mut schema = table.columns.clone();
2876 let projection =
2879 Projection::All(schema.iter().map(|col| col.name.clone()).collect());
2880 apply_alias_columns(&scope_table.name, columns, &mut schema, *span)?;
2881 scope_table.columns = schema.clone();
2882 Ok(PlannedRelation {
2883 plan: LogicalPlan::Scan {
2884 table: name.clone(),
2885 projection,
2886 },
2887 schema,
2888 scope: vec![ScopedTable::new(scope_table, start_index)],
2889 })
2890 }
2891 FromItem::Function {
2892 name,
2893 args,
2894 alias,
2895 columns,
2896 with_ordinality,
2897 span,
2898 ..
2899 } => self.plan_table_function(
2900 name,
2901 args,
2902 alias.as_deref(),
2903 columns,
2904 *span,
2905 start_index,
2906 lateral_scope,
2907 ctes,
2908 *with_ordinality,
2909 ),
2910 FromItem::Join {
2911 left,
2912 right,
2913 join_type,
2914 condition,
2915 using,
2916 natural,
2917 span,
2918 } => {
2919 let right_lateral = from_item_is_lateral(right);
2920 if right_lateral {
2921 let unsupported = match join_type {
2924 crate::ast::dml::JoinType::Right => Some("RIGHT"),
2925 crate::ast::dml::JoinType::Full => Some("FULL"),
2926 _ => None,
2927 };
2928 if let Some(kind) = unsupported {
2929 return Err(PlannerError::lateral_join_type_unsupported(kind, *span));
2930 }
2931 }
2932 let left_relation =
2933 self.plan_from_item(left, start_index, outer_scope, lateral_scope, ctes)?;
2934 let right_lateral_scope = right_lateral.then(|| {
2939 lateral_outer_scope(
2940 &left_relation.scope,
2941 start_index,
2942 left_relation.schema.len(),
2943 outer_scope,
2944 )
2945 });
2946 let right_relation = self.plan_from_item(
2947 right,
2948 start_index + left_relation.schema.len(),
2949 outer_scope,
2950 right_lateral_scope.as_deref().unwrap_or(lateral_scope),
2951 ctes,
2952 )?;
2953 let expr_scope = left_relation
2954 .scope
2955 .iter()
2956 .cloned()
2957 .chain(right_relation.scope.iter().cloned())
2958 .chain(offset_scope(
2959 outer_scope,
2960 left_relation.schema.len() + right_relation.schema.len(),
2961 ))
2962 .collect::<Vec<_>>();
2963 let using = if *natural {
2964 Some(natural_join_columns(
2965 &left_relation.schema,
2966 &right_relation.schema,
2967 ))
2968 } else {
2969 using.clone()
2970 };
2971 let typed_condition = if let Some(expr) = condition {
2972 let typed = self.infer_expr_with_scope(expr, &expr_scope, ctes)?;
2973 if typed.resolved_type != ResolvedType::Boolean {
2974 return Err(PlannerError::type_mismatch(
2975 "Boolean",
2976 typed.resolved_type.to_string(),
2977 expr.span,
2978 ));
2979 }
2980 Some(typed)
2981 } else {
2982 self.build_using_condition(
2983 using.as_deref(),
2984 &left_relation,
2985 &right_relation,
2986 *span,
2987 )?
2988 };
2989 if right_lateral {
2990 self.combine_lateral_join_relation(
2991 left_relation,
2992 right_relation,
2993 map_join_type(*join_type),
2994 typed_condition,
2995 using.as_deref(),
2996 *span,
2997 )
2998 } else {
2999 self.combine_join_relation(
3000 left_relation,
3001 right_relation,
3002 map_join_type(*join_type),
3003 typed_condition,
3004 using,
3005 *span,
3006 )
3007 }
3008 }
3009 FromItem::Derived {
3010 subquery,
3011 alias,
3012 columns,
3013 lateral,
3014 span,
3015 } => {
3016 let visible: &[ScopedTable] = if *lateral { lateral_scope } else { &[] };
3022 let mut relation = self.plan_query_body_relation(subquery, visible, ctes)?;
3023 let alias = alias.clone().ok_or_else(|| {
3024 PlannerError::invalid_expression("derived table requires an alias".to_string())
3025 })?;
3026 let source_names = relation
3027 .schema
3028 .iter()
3029 .map(|column| column.name.clone())
3030 .collect();
3031 relation.plan = LogicalPlan::Project {
3032 input: Box::new(relation.plan),
3033 projection: Projection::All(source_names),
3034 };
3035 apply_alias_columns(&alias, columns, &mut relation.schema, *span)?;
3036 relation.scope = vec![ScopedTable::new(
3037 TableMetadata::new(alias, relation.schema.clone()),
3038 start_index,
3039 )];
3040 Ok(relation)
3041 }
3042 }
3043 }
3044
3045 #[allow(clippy::too_many_arguments)]
3053 fn plan_table_function(
3054 &self,
3055 name: &str,
3056 args: &[Expr],
3057 alias: Option<&str>,
3058 columns: &[String],
3059 span: crate::ast::Span,
3060 start_index: usize,
3061 lateral_scope: &[ScopedTable],
3062 ctes: &CtePlans,
3063 with_ordinality: bool,
3064 ) -> Result<PlannedRelation, PlannerError> {
3065 let Some(mut function) = TableFunctionKind::from_name(name) else {
3066 return Err(PlannerError::unknown_table_function(name, span));
3067 };
3068 if with_ordinality {
3069 if function != TableFunctionKind::Unnest {
3070 return Err(PlannerError::invalid_expression(
3071 "WITH ORDINALITY is supported only for UNNEST",
3072 ));
3073 }
3074 function = TableFunctionKind::UnnestWithOrdinality;
3075 }
3076 let (typed_args, mut schema) = match function {
3077 TableFunctionKind::Unnest | TableFunctionKind::UnnestWithOrdinality => {
3078 if args.len() != 1 {
3079 return Err(PlannerError::invalid_expression(format!(
3080 "table function UNNEST takes exactly 1 argument, found {}",
3081 args.len()
3082 )));
3083 }
3084 let typed = self.infer_expr_with_scope(&args[0], lateral_scope, ctes)?;
3085 if !matches!(
3086 typed.resolved_type,
3087 ResolvedType::Vector { .. } | ResolvedType::Array(_) | ResolvedType::Null
3088 ) {
3089 return Err(PlannerError::type_mismatch(
3090 "ARRAY or VECTOR",
3091 typed.resolved_type.to_string(),
3092 args[0].span,
3093 ));
3094 }
3095 let element_type = match &typed.resolved_type {
3096 ResolvedType::Array(element) => (**element).clone(),
3097 ResolvedType::Vector { .. } => ResolvedType::Float,
3098 ResolvedType::Null => ResolvedType::Null,
3099 _ => unreachable!(),
3100 };
3101 let mut schema = vec![ColumnMetadata::new("unnest", element_type)];
3102 if function == TableFunctionKind::UnnestWithOrdinality {
3103 schema.push(ColumnMetadata::new("ordinality", ResolvedType::BigInt));
3104 }
3105 (vec![typed], schema)
3106 }
3107 TableFunctionKind::GenerateSeries => {
3108 if !(2..=3).contains(&args.len()) {
3109 return Err(PlannerError::invalid_expression(format!(
3110 "table function GENERATE_SERIES takes 2 or 3 arguments, found {}",
3111 args.len()
3112 )));
3113 }
3114 let mut typed = Vec::with_capacity(args.len());
3115 for arg in args {
3116 typed.push(self.infer_expr_with_scope(arg, lateral_scope, ctes)?);
3117 }
3118
3119 let timestamp_series = typed.len() == 3
3120 && matches!(
3121 typed[0].resolved_type,
3122 ResolvedType::Timestamp | ResolvedType::Null
3123 )
3124 && matches!(
3125 typed[1].resolved_type,
3126 ResolvedType::Timestamp | ResolvedType::Null
3127 )
3128 && matches!(
3129 typed[2].resolved_type,
3130 ResolvedType::Interval | ResolvedType::Null
3131 )
3132 && typed
3133 .iter()
3134 .any(|arg| !matches!(arg.resolved_type, ResolvedType::Null));
3135 let output_type = if timestamp_series {
3136 ResolvedType::Timestamp
3137 } else {
3138 let mut output_type = ResolvedType::Integer;
3139 for (arg, value) in args.iter().zip(&typed) {
3140 match value.resolved_type {
3141 ResolvedType::BigInt => output_type = ResolvedType::BigInt,
3142 ResolvedType::Integer | ResolvedType::Null => {}
3143 _ => {
3144 return Err(PlannerError::type_mismatch(
3145 "INTEGER/BIGINT or (TIMESTAMP, TIMESTAMP, INTERVAL)",
3146 value.resolved_type.to_string(),
3147 arg.span,
3148 ));
3149 }
3150 }
3151 }
3152 output_type
3153 };
3154 (
3155 typed,
3156 vec![ColumnMetadata::new("generate_series", output_type)],
3157 )
3158 }
3159 TableFunctionKind::JsonEach | TableFunctionKind::JsonTree => {
3160 if !(1..=2).contains(&args.len()) {
3161 return Err(PlannerError::invalid_expression(format!(
3162 "table function {} takes 1 or 2 arguments, found {}",
3163 function.name(),
3164 args.len()
3165 )));
3166 }
3167 let mut typed = Vec::with_capacity(args.len());
3168 for arg in args {
3169 let value = self.infer_expr_with_scope(arg, lateral_scope, ctes)?;
3170 if !matches!(value.resolved_type, ResolvedType::Text | ResolvedType::Null) {
3171 return Err(PlannerError::type_mismatch(
3172 "TEXT",
3173 value.resolved_type.to_string(),
3174 arg.span,
3175 ));
3176 }
3177 typed.push(value);
3178 }
3179 let schema = vec![
3180 ColumnMetadata::new("key", ResolvedType::Text),
3181 ColumnMetadata::new("value", ResolvedType::Text),
3182 ColumnMetadata::new("type", ResolvedType::Text),
3183 ColumnMetadata::new("atom", ResolvedType::Text),
3184 ColumnMetadata::new("id", ResolvedType::BigInt),
3185 ColumnMetadata::new("parent", ResolvedType::BigInt),
3186 ColumnMetadata::new("fullkey", ResolvedType::Text),
3187 ColumnMetadata::new("path", ResolvedType::Text),
3188 ];
3189 (typed, schema)
3190 }
3191 TableFunctionKind::FtsSearch => {
3192 if !(3..=4).contains(&args.len()) {
3193 return Err(PlannerError::invalid_expression(format!(
3194 "table function FTS_SEARCH takes 3 or 4 arguments, found {}",
3195 args.len()
3196 )));
3197 }
3198 let mut typed = Vec::with_capacity(args.len());
3199 for arg in args {
3200 let value = self.infer_expr_with_scope(arg, lateral_scope, ctes)?;
3201 if !matches!(value.resolved_type, ResolvedType::Text | ResolvedType::Null) {
3202 return Err(PlannerError::type_mismatch(
3203 "TEXT",
3204 value.resolved_type.to_string(),
3205 arg.span,
3206 ));
3207 }
3208 typed.push(value);
3209 }
3210 let schema = vec![
3211 ColumnMetadata::new("row_id", ResolvedType::BigInt),
3212 ColumnMetadata::new("document", ResolvedType::Text),
3213 ColumnMetadata::new("rank", ResolvedType::Double),
3214 ColumnMetadata::new("headline", ResolvedType::Text),
3215 ];
3216 (typed, schema)
3217 }
3218 };
3219
3220 let relation_name = alias
3221 .map(str::to_string)
3222 .unwrap_or_else(|| function.default_relation_name().to_string());
3223 apply_alias_columns(&relation_name, columns, &mut schema, span)?;
3224
3225 Ok(PlannedRelation {
3226 plan: LogicalPlan::TableFunction {
3227 function,
3228 args: typed_args,
3229 schema: schema.clone(),
3230 },
3231 schema: schema.clone(),
3232 scope: vec![ScopedTable::new(
3233 TableMetadata::new(relation_name, schema),
3234 start_index,
3235 )],
3236 })
3237 }
3238
3239 fn combine_lateral_join_relation(
3240 &self,
3241 left: PlannedRelation,
3242 right: PlannedRelation,
3243 join_type: JoinType,
3244 condition: Option<TypedExpr>,
3245 using: Option<&[String]>,
3246 _span: crate::ast::Span,
3247 ) -> Result<PlannedRelation, PlannerError> {
3248 let (schema, scope) = combine_join_shape(&left, &right, using);
3253 Ok(PlannedRelation {
3254 plan: LogicalPlan::LateralJoin {
3255 left: Box::new(left.plan),
3256 right: Box::new(right.plan),
3257 join_type,
3258 condition,
3259 right_schema: right.schema,
3260 },
3261 schema,
3262 scope,
3263 })
3264 }
3265
3266 fn combine_join_relation(
3267 &self,
3268 left: PlannedRelation,
3269 right: PlannedRelation,
3270 join_type: JoinType,
3271 condition: Option<TypedExpr>,
3272 using: Option<Vec<String>>,
3273 _span: crate::ast::Span,
3274 ) -> Result<PlannedRelation, PlannerError> {
3275 let (schema, scope) = combine_join_shape(&left, &right, using.as_deref());
3276 Ok(PlannedRelation {
3277 plan: LogicalPlan::Join {
3278 left: Box::new(left.plan),
3279 right: Box::new(right.plan),
3280 join_type,
3281 condition,
3282 using,
3283 },
3284 schema,
3285 scope,
3286 })
3287 }
3288
3289 fn build_using_condition(
3290 &self,
3291 using: Option<&[String]>,
3292 left: &PlannedRelation,
3293 right: &PlannedRelation,
3294 span: crate::ast::Span,
3295 ) -> Result<Option<TypedExpr>, PlannerError> {
3296 let Some(columns) = using else {
3297 return Ok(None);
3298 };
3299 let mut condition = None;
3300 for column in columns {
3301 let left_col = find_scoped_column(&left.scope, column, span)?;
3302 let right_col = find_scoped_column(&right.scope, column, span)?;
3303 let left_expr = merged_scoped_column_expr(&left_col, column, span);
3304 let right_expr = merged_scoped_column_expr(&right_col, column, span);
3305 self.type_checker
3306 .check_comparison_op(&left_col.ty, &right_col.ty, span)?;
3307 let eq = TypedExpr::binary_op(
3308 left_expr,
3309 crate::ast::expr::BinaryOp::Eq,
3310 right_expr,
3311 ResolvedType::Boolean,
3312 span,
3313 );
3314 condition = Some(match condition {
3315 Some(prev) => TypedExpr::binary_op(
3316 prev,
3317 crate::ast::expr::BinaryOp::And,
3318 eq,
3319 ResolvedType::Boolean,
3320 span,
3321 ),
3322 None => eq,
3323 });
3324 }
3325 Ok(condition)
3326 }
3327
3328 fn infer_expr_with_scope(
3329 &self,
3330 expr: &crate::ast::expr::Expr,
3331 scope: &[ScopedTable],
3332 ctes: &CtePlans,
3333 ) -> Result<TypedExpr, PlannerError> {
3334 self.type_checker
3335 .infer_type_with_scope(expr, scope, &|stmt, outer_scope| {
3336 let relation = match &stmt.kind {
3337 StatementKind::Select(select) => {
3338 self.plan_select_relation(select, outer_scope, ctes)?
3339 }
3340 StatementKind::Values(values) => {
3341 self.plan_values_relation(values, outer_scope, ctes)?
3342 }
3343 _ => {
3344 return Err(PlannerError::unsupported_feature(
3345 "non-query subquery",
3346 "a future version",
3347 stmt.span(),
3348 ));
3349 }
3350 };
3351 Ok((relation.plan, relation.schema))
3352 })
3353 }
3354
3355 #[allow(dead_code)]
3356 fn build_projection(
3357 &self,
3358 items: &[SelectItem],
3359 table: &TableMetadata,
3360 ) -> Result<Projection, PlannerError> {
3361 if items.len() == 1 && matches!(&items[0], SelectItem::Wildcard { .. }) {
3363 let columns = self.name_resolver.expand_wildcard(table);
3364 return Ok(Projection::All(columns));
3365 }
3366
3367 let mut projected_columns = Vec::new();
3369 for item in items {
3370 match item {
3371 SelectItem::Wildcard { span } => {
3372 for col in &table.columns {
3374 let column_index = table.get_column_index(&col.name).unwrap();
3375 let typed_expr = TypedExpr::column_ref(
3376 table.name.clone(),
3377 col.name.clone(),
3378 column_index,
3379 col.data_type.clone(),
3380 *span,
3381 );
3382 projected_columns.push(ProjectedColumn::new(typed_expr));
3383 }
3384 }
3385 SelectItem::QualifiedWildcard {
3386 table: qualifier,
3387 span,
3388 } => {
3389 if qualifier != &table.name {
3390 return Err(PlannerError::invalid_expression(format!(
3391 "table '{qualifier}' is not available for wildcard projection"
3392 )));
3393 }
3394 for col in &table.columns {
3395 let column_index = table.get_column_index(&col.name).unwrap();
3396 projected_columns.push(ProjectedColumn::new(TypedExpr::column_ref(
3397 table.name.clone(),
3398 col.name.clone(),
3399 column_index,
3400 col.data_type.clone(),
3401 *span,
3402 )));
3403 }
3404 }
3405 SelectItem::Expr { expr, alias, .. } => {
3406 let typed_expr = self.type_checker.infer_type(expr, table)?;
3407 let projected = if let Some(alias) = alias {
3408 ProjectedColumn::with_alias(typed_expr, alias.clone())
3409 } else {
3410 ProjectedColumn::new(typed_expr)
3411 };
3412 projected_columns.push(projected);
3413 }
3414 }
3415 }
3416
3417 Ok(Projection::Columns(projected_columns))
3418 }
3419
3420 fn build_projection_with_scope(
3421 &self,
3422 items: &[SelectItem],
3423 schema: &[ColumnMetadata],
3424 scope: &[ScopedTable],
3425 ctes: &CtePlans,
3426 ) -> Result<Projection, PlannerError> {
3427 if items.len() == 1 && matches!(&items[0], SelectItem::Wildcard { .. }) {
3428 return Ok(Projection::All(visible_wildcard_columns(schema, scope)));
3429 }
3430
3431 let mut projected_columns = Vec::new();
3432 for item in items {
3433 match item {
3434 SelectItem::Wildcard { span } => {
3435 for scoped in scope {
3436 for (local_idx, col) in scoped.table.columns.iter().enumerate() {
3437 projected_columns.push(ProjectedColumn::new(TypedExpr::column_ref(
3438 scoped.table.name.clone(),
3439 col.name.clone(),
3440 scoped.start_index + local_idx,
3441 col.data_type.clone(),
3442 *span,
3443 )));
3444 }
3445 }
3446 }
3447 SelectItem::QualifiedWildcard { table, span } => {
3448 let scoped = scope
3449 .iter()
3450 .filter(|scoped| scoped.table.name == *table)
3451 .collect::<Vec<_>>();
3452 match scoped.as_slice() {
3453 [] => {
3454 return Err(PlannerError::invalid_expression(format!(
3455 "table '{table}' is not available for wildcard projection"
3456 )));
3457 }
3458 [scoped] => {
3459 for (local_idx, col) in scoped.table.columns.iter().enumerate() {
3460 projected_columns.push(ProjectedColumn::new(
3461 TypedExpr::column_ref(
3462 scoped.table.name.clone(),
3463 col.name.clone(),
3464 scoped.start_index + local_idx,
3465 col.data_type.clone(),
3466 *span,
3467 ),
3468 ));
3469 }
3470 }
3471 _ => {
3472 return Err(PlannerError::ambiguous_column(
3473 table,
3474 scoped
3475 .iter()
3476 .map(|scoped| scoped.table.name.clone())
3477 .collect(),
3478 *span,
3479 ));
3480 }
3481 }
3482 }
3483 SelectItem::Expr { expr, alias, .. } => {
3484 let typed_expr = self.infer_expr_with_scope(expr, scope, ctes)?;
3485 let projected = if let Some(alias) = alias {
3486 ProjectedColumn::with_alias(typed_expr, alias.clone())
3487 } else {
3488 ProjectedColumn::new(typed_expr)
3489 };
3490 projected_columns.push(projected);
3491 }
3492 }
3493 }
3494
3495 Ok(Projection::Columns(projected_columns))
3496 }
3497
3498 #[allow(dead_code)]
3500 fn build_sort_exprs(
3501 &self,
3502 order_by: &[OrderByExpr],
3503 table: &TableMetadata,
3504 ) -> Result<Vec<SortExpr>, PlannerError> {
3505 let mut sort_exprs = Vec::new();
3506
3507 for order_expr in order_by {
3508 let typed_expr = self.type_checker.infer_type(&order_expr.expr, table)?;
3509
3510 let asc = order_expr.asc.unwrap_or(true);
3512
3513 let nulls_first = order_expr.nulls_first.unwrap_or(false);
3515
3516 sort_exprs.push(SortExpr::new(typed_expr, asc, nulls_first));
3517 }
3518
3519 Ok(sort_exprs)
3520 }
3521
3522 fn build_sort_exprs_with_scope(
3523 &self,
3524 order_by: &[OrderByExpr],
3525 scope: &[ScopedTable],
3526 projection_aliases: &HashMap<String, crate::ast::expr::Expr>,
3527 ctes: &CtePlans,
3528 ) -> Result<Vec<SortExpr>, PlannerError> {
3529 let mut sort_exprs = Vec::new();
3530 for order_expr in order_by {
3531 let sort_source = substitute_projection_aliases(&order_expr.expr, projection_aliases);
3532 let typed_expr = self.infer_expr_with_scope(&sort_source, scope, ctes)?;
3533 let asc = order_expr.asc.unwrap_or(true);
3534 let nulls_first = order_expr.nulls_first.unwrap_or(false);
3535 sort_exprs.push(SortExpr::new(typed_expr, asc, nulls_first));
3536 }
3537 Ok(sort_exprs)
3538 }
3539
3540 fn select_contains_aggregate(&self, stmt: &Select) -> bool {
3541 stmt.projection.iter().any(|item| match item {
3542 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
3543 SelectItem::Expr { expr, .. } => expr_contains_aggregate(expr),
3544 }) || stmt
3545 .group_by
3546 .as_ref()
3547 .map(|items| {
3548 items
3549 .iter()
3550 .flat_map(GroupByItem::exprs)
3551 .any(expr_contains_aggregate)
3552 })
3553 .unwrap_or(false)
3554 || stmt
3555 .having
3556 .as_ref()
3557 .map(expr_contains_aggregate)
3558 .unwrap_or(false)
3559 || stmt
3560 .qualify
3561 .as_ref()
3562 .map(expr_contains_aggregate)
3563 .unwrap_or(false)
3564 || stmt
3565 .order_by
3566 .iter()
3567 .any(|order| expr_contains_aggregate(&order.expr))
3568 }
3569
3570 #[allow(dead_code)]
3571 fn build_group_keys(
3572 &self,
3573 stmt: &Select,
3574 table: &TableMetadata,
3575 ) -> Result<Vec<TypedExpr>, PlannerError> {
3576 let mut keys = Vec::new();
3577 if let Some(items) = &stmt.group_by {
3578 for expr in items.iter().flat_map(GroupByItem::exprs) {
3579 let typed = self.type_checker.infer_type(expr, table)?;
3580 if typed_expr_contains_aggregate(&typed) {
3581 return Err(PlannerError::invalid_expression(
3582 "GROUP BY cannot contain aggregate functions".to_string(),
3583 ));
3584 }
3585 if !matches!(typed.kind, TypedExprKind::ColumnRef { .. }) {
3586 return Err(PlannerError::invalid_expression(
3587 "GROUP BY expressions must be column references".to_string(),
3588 ));
3589 }
3590 keys.push(typed);
3591 }
3592 }
3593 Ok(keys)
3594 }
3595
3596 fn type_group_key_with_scope(
3598 &self,
3599 expr: &Expr,
3600 scope: &[ScopedTable],
3601 ctes: &CtePlans,
3602 ) -> Result<TypedExpr, PlannerError> {
3603 let typed = self.infer_expr_with_scope(expr, scope, ctes)?;
3604 if typed_expr_contains_aggregate(&typed) {
3605 return Err(PlannerError::invalid_expression(
3606 "GROUP BY cannot contain aggregate functions".to_string(),
3607 ));
3608 }
3609 if !matches!(typed.kind, TypedExprKind::ColumnRef { .. }) {
3610 return Err(PlannerError::invalid_expression(
3611 "GROUP BY expressions must be column references".to_string(),
3612 ));
3613 }
3614 Ok(typed)
3615 }
3616
3617 fn expand_group_by_items(
3625 &self,
3626 stmt: &Select,
3627 scope: &[ScopedTable],
3628 ctes: &CtePlans,
3629 ) -> Result<ExpandedGroupBy, PlannerError> {
3630 let Some(items) = &stmt.group_by else {
3631 return Ok(ExpandedGroupBy {
3632 group_keys: Vec::new(),
3633 grouping_sets: None,
3634 });
3635 };
3636
3637 if items
3638 .iter()
3639 .all(|item| matches!(item, GroupByItem::Expr { .. }))
3640 {
3641 let mut keys = Vec::new();
3643 for item in items {
3644 if let GroupByItem::Expr { expr } = item {
3645 keys.push(self.type_group_key_with_scope(expr, scope, ctes)?);
3646 }
3647 }
3648 return Ok(ExpandedGroupBy {
3649 group_keys: keys,
3650 grouping_sets: None,
3651 });
3652 }
3653
3654 let mut keys: Vec<TypedExpr> = Vec::new();
3655 let mut key_index: HashMap<String, usize> = HashMap::new();
3656 let mut add_key = |planner: &Self, expr: &Expr| -> Result<usize, PlannerError> {
3657 let typed = planner.type_group_key_with_scope(expr, scope, ctes)?;
3658 let signature = expr_key(&typed);
3659 if let Some(&index) = key_index.get(&signature) {
3660 return Ok(index);
3661 }
3662 let index = keys.len();
3663 keys.push(typed);
3664 key_index.insert(signature, index);
3665 Ok(index)
3666 };
3667
3668 let mut sets: Vec<Vec<usize>> = vec![Vec::new()];
3671 for item in items {
3672 let item_sets: Vec<Vec<usize>> = match item {
3673 GroupByItem::Expr { expr } => vec![vec![add_key(self, expr)?]],
3674 GroupByItem::Rollup { exprs } => {
3675 if exprs.is_empty() {
3676 return Err(PlannerError::invalid_expression(
3677 "ROLLUP requires at least one expression".to_string(),
3678 ));
3679 }
3680 let indexes = exprs
3681 .iter()
3682 .map(|expr| add_key(self, expr))
3683 .collect::<Result<Vec<_>, _>>()?;
3684 (0..=indexes.len())
3685 .rev()
3686 .map(|len| indexes[..len].to_vec())
3687 .collect()
3688 }
3689 GroupByItem::Cube { exprs } => {
3690 if exprs.is_empty() {
3691 return Err(PlannerError::invalid_expression(
3692 "CUBE requires at least one expression".to_string(),
3693 ));
3694 }
3695 if exprs.len() > MAX_CUBE_COLUMNS {
3696 return Err(PlannerError::invalid_expression(format!(
3697 "too many grouping sets (max {MAX_GROUPING_SETS})"
3698 )));
3699 }
3700 let indexes = exprs
3701 .iter()
3702 .map(|expr| add_key(self, expr))
3703 .collect::<Result<Vec<_>, _>>()?;
3704 let n = indexes.len();
3705 (0..(1usize << n))
3706 .rev()
3707 .map(|included| {
3708 indexes
3709 .iter()
3710 .enumerate()
3711 .filter(|(position, _)| (included >> (n - 1 - position)) & 1 == 1)
3712 .map(|(_, &index)| index)
3713 .collect()
3714 })
3715 .collect()
3716 }
3717 GroupByItem::GroupingSets { sets: listed } => {
3718 if listed.is_empty() {
3719 return Err(PlannerError::invalid_expression(
3720 "GROUPING SETS requires at least one grouping set".to_string(),
3721 ));
3722 }
3723 listed
3724 .iter()
3725 .map(|set| {
3726 set.iter()
3727 .map(|expr| add_key(self, expr))
3728 .collect::<Result<Vec<_>, _>>()
3729 })
3730 .collect::<Result<Vec<_>, _>>()?
3731 }
3732 };
3733
3734 let mut combined = Vec::with_capacity(sets.len().saturating_mul(item_sets.len()));
3735 for base in &sets {
3736 for item_set in &item_sets {
3737 if combined.len() >= MAX_GROUPING_SETS {
3738 return Err(PlannerError::invalid_expression(format!(
3739 "too many grouping sets (max {MAX_GROUPING_SETS})"
3740 )));
3741 }
3742 let mut set = base.clone();
3743 set.extend(item_set.iter().copied());
3744 combined.push(set);
3745 }
3746 }
3747 sets = combined;
3748 }
3749
3750 if keys.len() > MAX_GROUPING_KEYS {
3751 return Err(PlannerError::invalid_expression(format!(
3752 "too many grouping columns (max {MAX_GROUPING_KEYS})"
3753 )));
3754 }
3755
3756 let key_count = keys.len();
3757 let full_mask = grouping_full_mask(key_count);
3758 let masks = sets
3759 .iter()
3760 .map(|set| {
3761 let mut mask = full_mask;
3762 for &index in set {
3763 mask &= !(1u64 << (key_count - 1 - index));
3764 }
3765 mask
3766 })
3767 .collect();
3768
3769 Ok(ExpandedGroupBy {
3770 group_keys: keys,
3771 grouping_sets: Some(masks),
3772 })
3773 }
3774
3775 #[allow(dead_code)]
3776 fn build_projected_columns_for_aggregate(
3777 &self,
3778 items: &[SelectItem],
3779 table: &TableMetadata,
3780 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
3781 let mut projected = Vec::new();
3782 for item in items {
3783 match item {
3784 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => {
3785 return Err(PlannerError::invalid_expression(
3786 "wildcard projection not supported with GROUP BY/aggregate".to_string(),
3787 ));
3788 }
3789 SelectItem::Expr { expr, alias, .. } => {
3790 let typed = self.type_checker.infer_type(expr, table)?;
3791 projected.push(ProjectedColumn {
3792 expr: typed,
3793 alias: alias.clone(),
3794 });
3795 }
3796 }
3797 }
3798 Ok(projected)
3799 }
3800
3801 fn build_projected_columns_for_aggregate_with_scope(
3802 &self,
3803 items: &[SelectItem],
3804 scope: &[ScopedTable],
3805 ctes: &CtePlans,
3806 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
3807 let mut projected = Vec::new();
3808 for item in items {
3809 match item {
3810 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => {
3811 return Err(PlannerError::invalid_expression(
3812 "wildcard projection not supported with GROUP BY/aggregate".to_string(),
3813 ));
3814 }
3815 SelectItem::Expr { expr, alias, .. } => {
3816 let typed = self.infer_expr_with_scope(expr, scope, ctes)?;
3817 projected.push(ProjectedColumn {
3818 expr: typed,
3819 alias: alias.clone(),
3820 });
3821 }
3822 }
3823 }
3824 Ok(projected)
3825 }
3826
3827 #[allow(dead_code)]
3828 fn build_projected_columns_for_distinct(
3829 &self,
3830 items: &[SelectItem],
3831 table: &TableMetadata,
3832 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
3833 let projection = self.build_projection(items, table)?;
3834 match projection {
3835 Projection::All(columns) => {
3836 let mut projected = Vec::with_capacity(columns.len());
3837 for column in columns {
3838 let column_index = table.get_column_index(&column).ok_or_else(|| {
3839 PlannerError::invalid_expression(format!(
3840 "column '{column}' not found for DISTINCT projection"
3841 ))
3842 })?;
3843 let column_meta = table.get_column(&column).ok_or_else(|| {
3844 PlannerError::invalid_expression(format!(
3845 "column '{column}' not found for DISTINCT projection"
3846 ))
3847 })?;
3848 let typed_expr = TypedExpr::column_ref(
3849 table.name.clone(),
3850 column.clone(),
3851 column_index,
3852 column_meta.data_type.clone(),
3853 crate::ast::Span::default(),
3854 );
3855 projected.push(ProjectedColumn::new(typed_expr));
3856 }
3857 Ok(projected)
3858 }
3859 Projection::Columns(columns) => Ok(columns),
3860 }
3861 }
3862
3863 fn build_projected_columns_for_distinct_with_scope(
3864 &self,
3865 items: &[SelectItem],
3866 schema: &[ColumnMetadata],
3867 scope: &[ScopedTable],
3868 ctes: &CtePlans,
3869 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
3870 let projection = self.build_projection_with_scope(items, schema, scope, ctes)?;
3871 match projection {
3872 Projection::All(columns) => {
3873 let mut projected = Vec::with_capacity(columns.len());
3874 for (idx, column) in columns.into_iter().enumerate() {
3875 let column_meta = schema.get(idx).ok_or_else(|| {
3876 PlannerError::invalid_expression(format!(
3877 "column '{column}' not found for DISTINCT projection"
3878 ))
3879 })?;
3880 projected.push(ProjectedColumn::new(TypedExpr::column_ref(
3881 LITERAL_TABLE.to_string(),
3882 column,
3883 idx,
3884 column_meta.data_type.clone(),
3885 crate::ast::Span::default(),
3886 )));
3887 }
3888 Ok(projected)
3889 }
3890 Projection::Columns(columns) => Ok(columns),
3891 }
3892 }
3893
3894 fn collect_aggregates_from_typed_expr(
3895 &self,
3896 expr: &TypedExpr,
3897 aggregates: &mut Vec<AggregateExpr>,
3898 aggregate_map: &mut HashMap<AggregateSignature, usize>,
3899 ) -> Result<(), PlannerError> {
3900 match &expr.kind {
3901 TypedExprKind::FunctionCall {
3902 name,
3903 args,
3904 distinct,
3905 star,
3906 filter,
3907 order_by,
3908 over: None,
3909 } if is_aggregate_function(name) => {
3910 if args.iter().any(typed_expr_contains_window) {
3911 return Err(PlannerError::invalid_expression(
3912 "aggregate functions cannot contain window functions".to_string(),
3913 ));
3914 }
3915 for arg in args {
3916 if typed_expr_contains_aggregate(arg) {
3917 return Err(PlannerError::invalid_expression(
3918 "nested aggregate functions are not supported".to_string(),
3919 ));
3920 }
3921 }
3922 if let Some(filter) = filter {
3926 if typed_expr_contains_aggregate(filter) {
3927 return Err(PlannerError::invalid_expression(
3928 "aggregate functions are not allowed in FILTER".to_string(),
3929 ));
3930 }
3931 if typed_expr_contains_window(filter) {
3932 return Err(PlannerError::invalid_expression(
3933 "window functions are not allowed in FILTER".to_string(),
3934 ));
3935 }
3936 }
3937 for sort in order_by {
3938 if typed_expr_contains_aggregate(&sort.expr) {
3939 return Err(PlannerError::invalid_expression(
3940 "aggregate functions are not allowed in aggregate ORDER BY".to_string(),
3941 ));
3942 }
3943 if typed_expr_contains_window(&sort.expr) {
3944 return Err(PlannerError::invalid_expression(
3945 "window functions are not allowed in aggregate ORDER BY".to_string(),
3946 ));
3947 }
3948 }
3949 let (agg, signature) = self.build_aggregate_expr_from_typed(
3950 expr,
3951 name,
3952 args,
3953 *distinct,
3954 *star,
3955 filter.as_deref(),
3956 order_by,
3957 )?;
3958 aggregate_map.entry(signature).or_insert_with(|| {
3959 aggregates.push(agg);
3960 aggregates.len() - 1
3961 });
3962 Ok(())
3963 }
3964 TypedExprKind::BinaryOp { left, right, .. } => {
3965 self.collect_aggregates_from_typed_expr(left, aggregates, aggregate_map)?;
3966 self.collect_aggregates_from_typed_expr(right, aggregates, aggregate_map)?;
3967 Ok(())
3968 }
3969 TypedExprKind::UnaryOp { operand, .. } => {
3970 self.collect_aggregates_from_typed_expr(operand, aggregates, aggregate_map)
3971 }
3972 TypedExprKind::Cast { expr, .. } | TypedExprKind::TryCast { expr, .. } => {
3973 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)
3974 }
3975 TypedExprKind::Case {
3976 operand,
3977 branches,
3978 else_expr,
3979 } => {
3980 if let Some(operand) = operand {
3981 self.collect_aggregates_from_typed_expr(operand, aggregates, aggregate_map)?;
3982 }
3983 for branch in branches {
3984 self.collect_aggregates_from_typed_expr(
3985 &branch.when,
3986 aggregates,
3987 aggregate_map,
3988 )?;
3989 self.collect_aggregates_from_typed_expr(
3990 &branch.then,
3991 aggregates,
3992 aggregate_map,
3993 )?;
3994 }
3995 if let Some(else_expr) = else_expr {
3996 self.collect_aggregates_from_typed_expr(else_expr, aggregates, aggregate_map)?;
3997 }
3998 Ok(())
3999 }
4000 TypedExprKind::FunctionCall { args, over, .. } => {
4001 for arg in args {
4002 self.collect_aggregates_from_typed_expr(arg, aggregates, aggregate_map)?;
4003 }
4004 if let Some(window) = over {
4005 for expr in &window.partition_by {
4006 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
4007 }
4008 for sort in &window.order_by {
4009 self.collect_aggregates_from_typed_expr(
4010 &sort.expr,
4011 aggregates,
4012 aggregate_map,
4013 )?;
4014 }
4015 }
4016 Ok(())
4017 }
4018 TypedExprKind::Between {
4019 expr, low, high, ..
4020 } => {
4021 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
4022 self.collect_aggregates_from_typed_expr(low, aggregates, aggregate_map)?;
4023 self.collect_aggregates_from_typed_expr(high, aggregates, aggregate_map)?;
4024 Ok(())
4025 }
4026 TypedExprKind::Like {
4027 expr,
4028 pattern,
4029 escape,
4030 ..
4031 } => {
4032 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
4033 self.collect_aggregates_from_typed_expr(pattern, aggregates, aggregate_map)?;
4034 if let Some(esc) = escape {
4035 self.collect_aggregates_from_typed_expr(esc, aggregates, aggregate_map)?;
4036 }
4037 Ok(())
4038 }
4039 TypedExprKind::InList { expr, list, .. } => {
4040 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
4041 for item in list {
4042 self.collect_aggregates_from_typed_expr(item, aggregates, aggregate_map)?;
4043 }
4044 Ok(())
4045 }
4046 TypedExprKind::IsNull { expr, .. } => {
4047 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)
4048 }
4049 _ => Ok(()),
4050 }
4051 }
4052
4053 fn collect_windows_from_typed_expr(
4054 &self,
4055 expr: &TypedExpr,
4056 windows: &mut Vec<WindowExpr>,
4057 window_map: &mut HashMap<String, usize>,
4058 ) -> Result<(), PlannerError> {
4059 match &expr.kind {
4060 TypedExprKind::FunctionCall {
4061 name,
4062 args,
4063 distinct,
4064 star,
4065 filter,
4066 order_by,
4067 over: Some(over),
4068 } => {
4069 if filter.is_some() || !order_by.is_empty() {
4070 return Err(PlannerError::invalid_expression(
4074 "FILTER and aggregate ORDER BY cannot be combined with OVER".to_string(),
4075 ));
4076 }
4077 if args.iter().any(typed_expr_contains_window)
4078 || over.partition_by.iter().any(typed_expr_contains_window)
4079 || over
4080 .order_by
4081 .iter()
4082 .any(|sort| typed_expr_contains_window(&sort.expr))
4083 {
4084 return Err(PlannerError::invalid_expression(
4085 "nested window functions are not supported".to_string(),
4086 ));
4087 }
4088
4089 let key = expr_key(expr);
4090 if window_map.contains_key(&key) {
4091 return Ok(());
4092 }
4093 let function = match name.to_ascii_lowercase().as_str() {
4094 "row_number" => WindowFunction::RowNumber,
4095 "rank" => WindowFunction::Rank,
4096 "dense_rank" => WindowFunction::DenseRank,
4097 "percent_rank" => WindowFunction::PercentRank,
4098 "cume_dist" => WindowFunction::CumeDist,
4099 "ntile" => WindowFunction::Ntile(args[0].clone()),
4100 "first_value" => {
4101 WindowFunction::Value(ValueWindowFunction::FirstValue(args[0].clone()))
4102 }
4103 "last_value" => {
4104 WindowFunction::Value(ValueWindowFunction::LastValue(args[0].clone()))
4105 }
4106 "nth_value" => WindowFunction::Value(ValueWindowFunction::NthValue {
4107 value: args[0].clone(),
4108 nth: args[1].clone(),
4109 }),
4110 name if is_aggregate_function(name) => {
4111 let (aggregate, _) = self.build_aggregate_expr_from_typed(
4112 expr, name, args, *distinct, *star, None, order_by,
4113 )?;
4114 WindowFunction::Aggregate(aggregate)
4115 }
4116 "lag" | "lead" => {
4117 let positional = build_offset_window_function(name, args)?;
4118 if name.eq_ignore_ascii_case("lag") {
4119 WindowFunction::Lag(positional)
4120 } else {
4121 WindowFunction::Lead(positional)
4122 }
4123 }
4124 _ => {
4125 return Err(PlannerError::unsupported_feature(
4126 format!("function '{}' with OVER", name),
4127 "future",
4128 expr.span,
4129 ));
4130 }
4131 };
4132 let index = windows.len();
4133 windows.push(WindowExpr {
4134 function,
4135 partition_by: over.partition_by.clone(),
4136 order_by: over.order_by.clone(),
4137 frame: over.frame.clone(),
4138 result_type: expr.resolved_type.clone(),
4139 });
4140 window_map.insert(key, index);
4141 Ok(())
4142 }
4143 TypedExprKind::FunctionCall { args, .. } => {
4144 for arg in args {
4145 self.collect_windows_from_typed_expr(arg, windows, window_map)?;
4146 }
4147 Ok(())
4148 }
4149 TypedExprKind::BinaryOp { left, right, .. } => {
4150 self.collect_windows_from_typed_expr(left, windows, window_map)?;
4151 self.collect_windows_from_typed_expr(right, windows, window_map)
4152 }
4153 TypedExprKind::UnaryOp { operand, .. } => {
4154 self.collect_windows_from_typed_expr(operand, windows, window_map)
4155 }
4156 TypedExprKind::Cast { expr, .. }
4157 | TypedExprKind::TryCast { expr, .. }
4158 | TypedExprKind::IsNull { expr, .. } => {
4159 self.collect_windows_from_typed_expr(expr, windows, window_map)
4160 }
4161 TypedExprKind::Between {
4162 expr, low, high, ..
4163 } => {
4164 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
4165 self.collect_windows_from_typed_expr(low, windows, window_map)?;
4166 self.collect_windows_from_typed_expr(high, windows, window_map)
4167 }
4168 TypedExprKind::Like {
4169 expr,
4170 pattern,
4171 escape,
4172 ..
4173 } => {
4174 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
4175 self.collect_windows_from_typed_expr(pattern, windows, window_map)?;
4176 if let Some(escape) = escape {
4177 self.collect_windows_from_typed_expr(escape, windows, window_map)?;
4178 }
4179 Ok(())
4180 }
4181 TypedExprKind::InList { expr, list, .. } => {
4182 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
4183 for item in list {
4184 self.collect_windows_from_typed_expr(item, windows, window_map)?;
4185 }
4186 Ok(())
4187 }
4188 _ => Ok(()),
4189 }
4190 }
4191
4192 #[allow(clippy::too_many_arguments)]
4193 fn build_aggregate_expr_from_typed(
4194 &self,
4195 expr: &TypedExpr,
4196 name: &str,
4197 args: &[TypedExpr],
4198 distinct: bool,
4199 star: bool,
4200 filter: Option<&TypedExpr>,
4201 order_by: &[SortExpr],
4202 ) -> Result<(AggregateExpr, AggregateSignature), PlannerError> {
4203 let lower = name.to_lowercase();
4204 let filter_owned = filter.cloned();
4205 let retained_order_by: Vec<SortExpr> = if is_order_sensitive_aggregate(&lower) {
4210 order_by.to_vec()
4211 } else {
4212 Vec::new()
4213 };
4214 match lower.as_str() {
4215 "count" => {
4216 if star {
4217 let mut agg = AggregateExpr::count_star();
4218 agg.filter = filter_owned;
4219 let signature = aggregate_signature(
4220 name,
4221 distinct,
4222 star,
4223 None,
4224 None,
4225 expr,
4226 filter,
4227 &retained_order_by,
4228 );
4229 return Ok((agg, signature));
4230 }
4231 if args.len() != 1 {
4232 return Err(PlannerError::type_mismatch(
4233 "1 argument",
4234 format!("{} arguments", args.len()),
4235 expr.span,
4236 ));
4237 }
4238 let agg = AggregateExpr {
4239 function: AggregateFunction::Count,
4240 arg: Some(args[0].clone()),
4241 extra_args: Vec::new(),
4242 distinct,
4243 result_type: ResolvedType::BigInt,
4244 filter: filter_owned,
4245 order_by: retained_order_by.clone(),
4246 };
4247 let signature = aggregate_signature(
4248 name,
4249 distinct,
4250 star,
4251 Some(&args[0]),
4252 None,
4253 expr,
4254 filter,
4255 &retained_order_by,
4256 );
4257 Ok((agg, signature))
4258 }
4259 "sum" => {
4260 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4261 let agg = AggregateExpr {
4262 function: AggregateFunction::Sum,
4263 arg: Some(arg.clone()),
4264 extra_args: Vec::new(),
4265 distinct,
4266 result_type: crate::planner::aggregate_expr::sum_result_type(
4267 &arg.resolved_type,
4268 ),
4269 filter: filter_owned,
4270 order_by: retained_order_by.clone(),
4271 };
4272 let signature = aggregate_signature(
4273 name,
4274 distinct,
4275 star,
4276 Some(arg),
4277 None,
4278 expr,
4279 filter,
4280 &retained_order_by,
4281 );
4282 Ok((agg, signature))
4283 }
4284 "total" => {
4285 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4286 let agg = AggregateExpr {
4287 function: AggregateFunction::Total,
4288 arg: Some(arg.clone()),
4289 extra_args: Vec::new(),
4290 distinct: false,
4291 result_type: ResolvedType::Double,
4292 filter: filter_owned,
4293 order_by: retained_order_by.clone(),
4294 };
4295 let signature = aggregate_signature(
4296 name,
4297 false,
4298 star,
4299 Some(arg),
4300 None,
4301 expr,
4302 filter,
4303 &retained_order_by,
4304 );
4305 Ok((agg, signature))
4306 }
4307 "avg" => {
4308 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4309 let agg = AggregateExpr {
4310 function: AggregateFunction::Avg,
4311 arg: Some(arg.clone()),
4312 extra_args: Vec::new(),
4313 distinct,
4314 result_type: crate::planner::aggregate_expr::avg_result_type(
4315 &arg.resolved_type,
4316 ),
4317 filter: filter_owned,
4318 order_by: retained_order_by.clone(),
4319 };
4320 let signature = aggregate_signature(
4321 name,
4322 distinct,
4323 star,
4324 Some(arg),
4325 None,
4326 expr,
4327 filter,
4328 &retained_order_by,
4329 );
4330 Ok((agg, signature))
4331 }
4332 "min" => {
4333 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4334 let agg = AggregateExpr {
4335 function: AggregateFunction::Min,
4336 arg: Some(arg.clone()),
4337 extra_args: Vec::new(),
4338 distinct,
4339 result_type: arg.resolved_type.clone(),
4340 filter: filter_owned,
4341 order_by: retained_order_by.clone(),
4342 };
4343 let signature = aggregate_signature(
4344 name,
4345 distinct,
4346 star,
4347 Some(arg),
4348 None,
4349 expr,
4350 filter,
4351 &retained_order_by,
4352 );
4353 Ok((agg, signature))
4354 }
4355 "max" => {
4356 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4357 let agg = AggregateExpr {
4358 function: AggregateFunction::Max,
4359 arg: Some(arg.clone()),
4360 extra_args: Vec::new(),
4361 distinct,
4362 result_type: arg.resolved_type.clone(),
4363 filter: filter_owned,
4364 order_by: retained_order_by.clone(),
4365 };
4366 let signature = aggregate_signature(
4367 name,
4368 distinct,
4369 star,
4370 Some(arg),
4371 None,
4372 expr,
4373 filter,
4374 &retained_order_by,
4375 );
4376 Ok((agg, signature))
4377 }
4378 "group_concat" => {
4379 if args.is_empty() || args.len() > 2 {
4380 return Err(PlannerError::type_mismatch(
4381 "1 or 2 arguments",
4382 format!("{} arguments", args.len()),
4383 expr.span,
4384 ));
4385 }
4386 let arg = &args[0];
4387 let mut separator = None;
4388 if args.len() == 2 {
4389 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
4390 separator = Some(value.clone());
4391 } else {
4392 return Err(PlannerError::invalid_expression(
4393 "GROUP_CONCAT separator must be a string literal".to_string(),
4394 ));
4395 }
4396 }
4397 let agg = AggregateExpr {
4398 function: AggregateFunction::GroupConcat { separator },
4399 arg: Some(arg.clone()),
4400 extra_args: Vec::new(),
4401 distinct,
4402 result_type: ResolvedType::Text,
4403 filter: filter_owned,
4404 order_by: retained_order_by.clone(),
4405 };
4406 let signature = aggregate_signature(
4407 name,
4408 distinct,
4409 star,
4410 Some(arg),
4411 match &agg.function {
4412 AggregateFunction::GroupConcat { separator } => separator.as_ref(),
4413 _ => None,
4414 },
4415 expr,
4416 filter,
4417 &retained_order_by,
4418 );
4419 Ok((agg, signature))
4420 }
4421 "string_agg" => {
4422 if args.len() != 2 {
4423 return Err(PlannerError::type_mismatch(
4424 "2 arguments",
4425 format!("{} arguments", args.len()),
4426 expr.span,
4427 ));
4428 }
4429 let arg = &args[0];
4430 let separator =
4431 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
4432 Some(value.clone())
4433 } else {
4434 return Err(PlannerError::invalid_expression(
4435 "STRING_AGG separator must be a string literal".to_string(),
4436 ));
4437 };
4438 let agg = AggregateExpr {
4439 function: AggregateFunction::StringAgg { separator },
4440 arg: Some(arg.clone()),
4441 extra_args: Vec::new(),
4442 distinct,
4443 result_type: ResolvedType::Text,
4444 filter: filter_owned,
4445 order_by: retained_order_by.clone(),
4446 };
4447 let signature = aggregate_signature(
4448 name,
4449 distinct,
4450 star,
4451 Some(arg),
4452 match &agg.function {
4453 AggregateFunction::StringAgg { separator } => separator.as_ref(),
4454 _ => None,
4455 },
4456 expr,
4457 filter,
4458 &retained_order_by,
4459 );
4460 Ok((agg, signature))
4461 }
4462 "json_group_array" => {
4463 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4464 let agg = AggregateExpr {
4465 function: AggregateFunction::JsonGroupArray,
4466 arg: Some(arg.clone()),
4467 extra_args: Vec::new(),
4468 distinct,
4469 result_type: ResolvedType::Text,
4470 filter: filter_owned,
4471 order_by: retained_order_by.clone(),
4472 };
4473 let signature = aggregate_signature(
4474 name,
4475 distinct,
4476 star,
4477 Some(arg),
4478 None,
4479 expr,
4480 filter,
4481 &retained_order_by,
4482 );
4483 Ok((agg, signature))
4484 }
4485 "array_agg" => {
4486 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4487 let agg = AggregateExpr {
4488 function: AggregateFunction::ArrayAgg,
4489 arg: Some(arg.clone()),
4490 extra_args: Vec::new(),
4491 distinct,
4492 result_type: ResolvedType::Array(Box::new(arg.resolved_type.clone())),
4493 filter: filter_owned,
4494 order_by: retained_order_by.clone(),
4495 };
4496 let signature = aggregate_signature(
4497 name,
4498 distinct,
4499 star,
4500 Some(arg),
4501 None,
4502 expr,
4503 filter,
4504 &retained_order_by,
4505 );
4506 Ok((agg, signature))
4507 }
4508 "json_group_object" => {
4509 if args.len() != 2 {
4510 return Err(PlannerError::type_mismatch(
4511 "2 arguments",
4512 format!("{} arguments", args.len()),
4513 expr.span,
4514 ));
4515 }
4516 let agg = AggregateExpr {
4517 function: AggregateFunction::JsonGroupObject,
4518 arg: Some(args[0].clone()),
4519 extra_args: vec![args[1].clone()],
4520 distinct,
4521 result_type: ResolvedType::Text,
4522 filter: filter_owned,
4523 order_by: retained_order_by.clone(),
4524 };
4525 let signature = aggregate_signature(
4526 name,
4527 distinct,
4528 star,
4529 Some(&args[0]),
4530 None,
4531 expr,
4532 filter,
4533 &retained_order_by,
4534 );
4535 Ok((agg, signature))
4536 }
4537 "jsonb_agg" => {
4538 let arg = self.require_single_aggregate_arg(args, expr.span)?;
4539 let agg = AggregateExpr {
4540 function: AggregateFunction::JsonbAgg,
4541 arg: Some(arg.clone()),
4542 extra_args: Vec::new(),
4543 distinct,
4544 result_type: ResolvedType::Json,
4545 filter: filter_owned,
4546 order_by: retained_order_by.clone(),
4547 };
4548 let signature = aggregate_signature(
4549 name,
4550 distinct,
4551 star,
4552 Some(arg),
4553 None,
4554 expr,
4555 filter,
4556 &retained_order_by,
4557 );
4558 Ok((agg, signature))
4559 }
4560 "jsonb_object_agg" => {
4561 if args.len() != 2 {
4562 return Err(PlannerError::type_mismatch(
4563 "2 arguments",
4564 format!("{} arguments", args.len()),
4565 expr.span,
4566 ));
4567 }
4568 let agg = AggregateExpr {
4569 function: AggregateFunction::JsonbObjectAgg,
4570 arg: Some(args[0].clone()),
4571 extra_args: vec![args[1].clone()],
4572 distinct,
4573 result_type: ResolvedType::Json,
4574 filter: filter_owned,
4575 order_by: retained_order_by.clone(),
4576 };
4577 let signature = aggregate_signature(
4578 name,
4579 distinct,
4580 star,
4581 Some(&args[0]),
4582 None,
4583 expr,
4584 filter,
4585 &retained_order_by,
4586 );
4587 Ok((agg, signature))
4588 }
4589 "percentile_disc" => {
4590 if args.len() != 1 {
4591 return Err(PlannerError::type_mismatch(
4592 "1 argument",
4593 format!("{} arguments", args.len()),
4594 expr.span,
4595 ));
4596 }
4597 let fraction = type_checker::percentile_fraction(&args[0])?;
4598 if retained_order_by.len() != 1 {
4599 return Err(PlannerError::invalid_expression(
4600 "PERCENTILE_DISC requires WITHIN GROUP (ORDER BY ...) with exactly \
4601 one sort expression"
4602 .to_string(),
4603 ));
4604 }
4605 let sort = &retained_order_by[0];
4606 let agg = AggregateExpr {
4607 function: AggregateFunction::PercentileDisc { fraction },
4608 arg: Some(sort.expr.clone()),
4609 extra_args: Vec::new(),
4610 distinct: false,
4611 result_type: sort.expr.resolved_type.clone(),
4612 filter: filter_owned,
4613 order_by: retained_order_by.clone(),
4614 };
4615 let fraction_key = format!("{fraction:?}");
4618 let signature = aggregate_signature(
4619 name,
4620 false,
4621 star,
4622 None,
4623 Some(&fraction_key),
4624 expr,
4625 filter,
4626 &retained_order_by,
4627 );
4628 Ok((agg, signature))
4629 }
4630 "percentile_cont" => {
4631 let fraction = type_checker::percentile_fraction_named(name, &args[0])?;
4632 let sort = retained_order_by.first().ok_or_else(|| {
4633 PlannerError::invalid_expression(
4634 "PERCENTILE_CONT requires WITHIN GROUP (ORDER BY ...)".to_string(),
4635 )
4636 })?;
4637 let agg = AggregateExpr {
4638 function: AggregateFunction::PercentileCont { fraction },
4639 arg: Some(sort.expr.clone()),
4640 extra_args: Vec::new(),
4641 distinct: false,
4642 result_type: ResolvedType::Double,
4643 filter: filter_owned,
4644 order_by: retained_order_by.clone(),
4645 };
4646 let fraction_key = format!("{fraction:?}");
4647 let signature = aggregate_signature(
4648 name,
4649 false,
4650 star,
4651 None,
4652 Some(&fraction_key),
4653 expr,
4654 filter,
4655 &retained_order_by,
4656 );
4657 Ok((agg, signature))
4658 }
4659 "mode" if args.is_empty() => {
4660 let sort = retained_order_by.first().ok_or_else(|| {
4661 PlannerError::invalid_expression(
4662 "MODE requires WITHIN GROUP (ORDER BY ...)".to_string(),
4663 )
4664 })?;
4665 let agg = AggregateExpr {
4666 function: AggregateFunction::Mode,
4667 arg: Some(sort.expr.clone()),
4668 extra_args: Vec::new(),
4669 distinct: false,
4670 result_type: sort.expr.resolved_type.clone(),
4671 filter: filter_owned,
4672 order_by: retained_order_by.clone(),
4673 };
4674 let signature = aggregate_signature(
4675 name,
4676 false,
4677 star,
4678 None,
4679 None,
4680 expr,
4681 filter,
4682 &retained_order_by,
4683 );
4684 Ok((agg, signature))
4685 }
4686 name if type_checker::is_portable_aggregate_name(name) => {
4687 let function = match name {
4688 "variance" | "var_samp" => AggregateFunction::Variance { sample: true },
4689 "var_pop" => AggregateFunction::Variance { sample: false },
4690 "stddev" | "stddev_samp" => AggregateFunction::Stddev { sample: true },
4691 "stddev_pop" => AggregateFunction::Stddev { sample: false },
4692 "covar_samp" => AggregateFunction::Covariance { sample: true },
4693 "covar_pop" => AggregateFunction::Covariance { sample: false },
4694 "corr" => AggregateFunction::Corr,
4695 "median" => AggregateFunction::Median,
4696 "mode" => AggregateFunction::Mode,
4697 "quantile_cont" => AggregateFunction::QuantileCont {
4698 fraction: type_checker::percentile_fraction_named(name, &args[1])?,
4699 },
4700 "regr_count" => AggregateFunction::RegrCount,
4701 "regr_avgx" => AggregateFunction::RegrAvgX,
4702 "regr_avgy" => AggregateFunction::RegrAvgY,
4703 "regr_sxx" => AggregateFunction::RegrSxx,
4704 "regr_syy" => AggregateFunction::RegrSyy,
4705 "regr_sxy" => AggregateFunction::RegrSxy,
4706 "regr_slope" => AggregateFunction::RegrSlope,
4707 "regr_intercept" => AggregateFunction::RegrIntercept,
4708 "regr_r2" => AggregateFunction::RegrR2,
4709 "any_value" => AggregateFunction::AnyValue,
4710 "first" => AggregateFunction::First,
4711 "last" => AggregateFunction::Last,
4712 "arg_min" | "min_by" => AggregateFunction::ArgMin,
4713 "arg_max" | "max_by" => AggregateFunction::ArgMax,
4714 "bit_and" => AggregateFunction::BitAnd,
4715 "bit_or" => AggregateFunction::BitOr,
4716 "bit_xor" => AggregateFunction::BitXor,
4717 "bool_and" => AggregateFunction::BoolAnd,
4718 "bool_or" => AggregateFunction::BoolOr,
4719 _ => unreachable!(),
4720 };
4721 let primary = args[0].clone();
4722 let extra_args = if matches!(name, "quantile_cont") {
4723 Vec::new()
4724 } else {
4725 args[1..].to_vec()
4726 };
4727 let config = match &function {
4728 AggregateFunction::PercentileCont { fraction }
4729 | AggregateFunction::QuantileCont { fraction } => Some(format!("{fraction:?}")),
4730 _ => None,
4731 };
4732 let agg = AggregateExpr {
4733 function,
4734 arg: Some(primary.clone()),
4735 extra_args: extra_args.clone(),
4736 distinct: false,
4737 result_type: expr.resolved_type.clone(),
4738 filter: filter_owned,
4739 order_by: retained_order_by.clone(),
4740 };
4741 let mut signature = aggregate_signature(
4742 name,
4743 false,
4744 star,
4745 Some(&primary),
4746 config.as_ref(),
4747 expr,
4748 filter,
4749 &retained_order_by,
4750 );
4751 signature.extra_arg_keys = extra_args.iter().map(expr_key).collect();
4752 Ok((agg, signature))
4753 }
4754 _ => Err(PlannerError::unsupported_feature(
4755 format!("function '{}'", name),
4756 "future",
4757 expr.span,
4758 )),
4759 }
4760 }
4761 fn require_single_aggregate_arg<'b>(
4762 &self,
4763 args: &'b [TypedExpr],
4764 span: crate::ast::Span,
4765 ) -> Result<&'b TypedExpr, PlannerError> {
4766 if args.len() != 1 {
4767 return Err(PlannerError::type_mismatch(
4768 "1 argument",
4769 format!("{} arguments", args.len()),
4770 span,
4771 ));
4772 }
4773 Ok(&args[0])
4774 }
4775
4776 fn build_aggregate_projection(
4777 &self,
4778 projected: Vec<ProjectedColumn>,
4779 group_keys: &[TypedExpr],
4780 aggregates: &[AggregateExpr],
4781 output_names: &[String],
4782 grouping: Option<&GroupingRewrite>,
4783 ) -> Result<Projection, PlannerError> {
4784 let mut columns = Vec::new();
4785 for col in projected {
4786 let rewritten = self.rewrite_expr_for_aggregate(
4787 &col.expr,
4788 group_keys,
4789 aggregates,
4790 output_names,
4791 grouping,
4792 )?;
4793 columns.push(ProjectedColumn {
4794 expr: rewritten,
4795 alias: col.alias,
4796 });
4797 }
4798 Ok(Projection::Columns(columns))
4799 }
4800
4801 fn rewrite_expr_for_aggregate(
4809 &self,
4810 expr: &TypedExpr,
4811 group_keys: &[TypedExpr],
4812 aggregates: &[AggregateExpr],
4813 output_names: &[String],
4814 grouping: Option<&GroupingRewrite>,
4815 ) -> Result<TypedExpr, PlannerError> {
4816 let group_key_map = build_group_key_map(group_keys);
4817 let aggregate_map = build_aggregate_map(aggregates);
4818
4819 let expr = match grouping {
4820 Some(context) => rewrite_grouping_calls(expr, context)?,
4821 None => expr.clone(),
4822 };
4823 rewrite_expr_with_maps(&expr, &group_key_map, &aggregate_map, output_names)
4824 }
4825
4826 fn resolve_pagination_count(
4837 &self,
4838 expr: &Option<crate::ast::expr::Expr>,
4839 clause: &str,
4840 ) -> Result<Option<u64>, PlannerError> {
4841 let Some(expr) = expr else {
4842 return Ok(None);
4843 };
4844 if expr_contains_subquery(expr) {
4845 return Err(PlannerError::unsupported_feature(
4846 format!("subquery in {clause}"),
4847 "a future version",
4848 expr.span,
4849 ));
4850 }
4851 let typed = self
4853 .type_checker
4854 .infer_type_with_scope(expr, &[], &|stmt, _outer| {
4855 Err(PlannerError::unsupported_feature(
4856 format!("subquery in {clause}"),
4857 "a future version",
4858 stmt.span(),
4859 ))
4860 })?;
4861 if typed_expr_contains_aggregate(&typed) {
4862 return Err(PlannerError::invalid_expression(format!(
4863 "aggregate functions are not allowed in {clause}"
4864 )));
4865 }
4866 if typed_expr_contains_window(&typed) {
4867 return Err(PlannerError::invalid_expression(format!(
4868 "window functions are not allowed in {clause}"
4869 )));
4870 }
4871 match typed.resolved_type {
4872 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null => {}
4873 ref other => {
4874 return Err(PlannerError::type_mismatch(
4875 "BIGINT",
4876 other.type_name().to_string(),
4877 expr.span,
4878 ));
4879 }
4880 }
4881 let context = crate::executor::evaluator::EvalContext::new(&[]);
4884 let value = crate::executor::evaluator::evaluate(&typed, &context).map_err(|error| {
4885 PlannerError::invalid_expression(format!("{clause} expression is invalid: {error}"))
4886 })?;
4887 let count = match value {
4888 crate::storage::SqlValue::Null => return Ok(None),
4889 crate::storage::SqlValue::Integer(value) => i64::from(value),
4890 crate::storage::SqlValue::BigInt(value) => value,
4891 other => {
4892 return Err(PlannerError::type_mismatch(
4893 "BIGINT",
4894 format!("{other:?}"),
4895 expr.span,
4896 ));
4897 }
4898 };
4899 if count < 0 {
4900 return Err(PlannerError::invalid_expression(format!(
4901 "{clause} must not be negative"
4902 )));
4903 }
4904 Ok(Some(count as u64))
4905 }
4906
4907 fn apply_pagination(
4912 &self,
4913 plan: LogicalPlan,
4914 limit: &Option<Expr>,
4915 offset: &Option<Expr>,
4916 with_ties: bool,
4917 ) -> Result<LogicalPlan, PlannerError> {
4918 self.apply_pagination_with_tie_keys(plan, limit, offset, with_ties, None)
4919 }
4920
4921 fn apply_pagination_with_tie_keys(
4929 &self,
4930 plan: LogicalPlan,
4931 limit: &Option<Expr>,
4932 offset: &Option<Expr>,
4933 with_ties: bool,
4934 tie_keys: Option<&[SortExpr]>,
4935 ) -> Result<LogicalPlan, PlannerError> {
4936 if limit.is_none() && offset.is_none() && !with_ties {
4937 return Ok(plan);
4938 }
4939 let ties = if with_ties {
4940 let keys = match tie_keys {
4941 Some(keys) => keys.to_vec(),
4942 None => match &plan {
4943 LogicalPlan::Sort { order_by, .. } => order_by.clone(),
4944 _ => Vec::new(),
4945 },
4946 };
4947 if keys.is_empty() {
4948 return Err(PlannerError::invalid_expression(
4949 "FETCH ... WITH TIES requires ORDER BY".to_string(),
4950 ));
4951 }
4952 Some(keys)
4953 } else {
4954 None
4955 };
4956 Ok(LogicalPlan::Limit {
4957 input: Box::new(plan),
4958 limit: self.resolve_pagination_count(limit, "LIMIT")?,
4959 offset: self.resolve_pagination_count(offset, "OFFSET")?,
4960 ties,
4961 })
4962 }
4963
4964 fn plan_insert(&self, stmt: &Insert) -> Result<LogicalPlan, PlannerError> {
4969 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
4971
4972 let columns: Vec<String> = if let Some(ref cols) = stmt.columns {
4974 for col in cols {
4976 self.name_resolver.resolve_column(table, col, stmt.span)?;
4977 }
4978 cols.clone()
4979 } else {
4980 table.column_names().into_iter().map(String::from).collect()
4982 };
4983
4984 match &stmt.source {
4985 InsertSource::Values { values } => {
4986 let mut typed_values: Vec<Vec<TypedExpr>> = Vec::new();
4987
4988 for row in values {
4989 if row.len() != columns.len() {
4990 return Err(PlannerError::column_value_count_mismatch(
4991 columns.len(),
4992 row.len(),
4993 stmt.span,
4994 ));
4995 }
4996
4997 typed_values.push(self.type_check_insert_values(row, &columns, table)?);
4998 }
4999
5000 Ok(LogicalPlan::Insert {
5001 table: table.name.clone(),
5002 columns,
5003 values: typed_values,
5004 })
5005 }
5006 InsertSource::Select { select } => {
5007 let source = self.plan_select_relation(select, &[], &CtePlans::new())?;
5008 self.finish_insert_query(stmt, table, columns, source)
5009 }
5010 InsertSource::Query { query } => {
5011 let source = self.plan_query_body_relation(query, &[], &CtePlans::new())?;
5012 self.finish_insert_query(stmt, table, columns, source)
5013 }
5014 }
5015 }
5016
5017 fn finish_insert_query(
5018 &self,
5019 stmt: &Insert,
5020 table: &TableMetadata,
5021 columns: Vec<String>,
5022 source: PlannedRelation,
5023 ) -> Result<LogicalPlan, PlannerError> {
5024 if source.schema.len() != columns.len() {
5025 return Err(PlannerError::column_value_count_mismatch(
5026 columns.len(),
5027 source.schema.len(),
5028 stmt.span,
5029 ));
5030 }
5031
5032 for (source_column, target_column) in source.schema.iter().zip(&columns) {
5033 let target = table
5034 .get_column(target_column)
5035 .expect("validated target column");
5036 if target.not_null && source_column.data_type == ResolvedType::Null {
5037 return Err(PlannerError::null_constraint_violation(
5038 target_column,
5039 stmt.span,
5040 ));
5041 }
5042 self.validate_resolved_type_assignment(
5043 &source_column.data_type,
5044 &target.data_type,
5045 stmt.span,
5046 )?;
5047 }
5048
5049 Ok(LogicalPlan::InsertSelect {
5050 table: table.name.clone(),
5051 columns,
5052 source: Box::new(source.plan),
5053 })
5054 }
5055
5056 fn type_check_insert_values(
5058 &self,
5059 values: &[crate::ast::expr::Expr],
5060 columns: &[String],
5061 table: &TableMetadata,
5062 ) -> Result<Vec<TypedExpr>, PlannerError> {
5063 let mut typed_values = Vec::new();
5064
5065 for (i, value) in values.iter().enumerate() {
5066 let column_name = &columns[i];
5067 let column_meta = table.get_column(column_name).ok_or_else(|| {
5068 PlannerError::column_not_found(column_name, &table.name, value.span)
5069 })?;
5070
5071 let typed_value = self.type_checker.infer_type(value, table)?;
5073
5074 if column_meta.not_null
5076 && matches!(&typed_value.kind, TypedExprKind::Literal(Literal::Null))
5077 {
5078 return Err(PlannerError::null_constraint_violation(
5079 column_name,
5080 value.span,
5081 ));
5082 }
5083
5084 self.validate_type_assignment(&typed_value, &column_meta.data_type, value.span)?;
5086
5087 let typed_value =
5088 self.coerce_assignment_value(typed_value, &column_meta.data_type, value.span);
5089
5090 typed_values.push(typed_value);
5091 }
5092
5093 Ok(typed_values)
5094 }
5095
5096 fn validate_type_assignment(
5098 &self,
5099 value: &TypedExpr,
5100 target_type: &ResolvedType,
5101 span: crate::ast::Span,
5102 ) -> Result<(), PlannerError> {
5103 self.validate_resolved_type_assignment(&value.resolved_type, target_type, span)
5104 }
5105
5106 fn validate_resolved_type_assignment(
5107 &self,
5108 source_type: &ResolvedType,
5109 target_type: &ResolvedType,
5110 span: crate::ast::Span,
5111 ) -> Result<(), PlannerError> {
5112 if *source_type == ResolvedType::Null {
5114 return Ok(());
5115 }
5116
5117 if self.types_compatible(source_type, target_type) {
5119 return Ok(());
5120 }
5121
5122 Err(PlannerError::type_mismatch(
5123 target_type.to_string(),
5124 source_type.to_string(),
5125 span,
5126 ))
5127 }
5128
5129 fn types_compatible(&self, source: &ResolvedType, target: &ResolvedType) -> bool {
5131 use ResolvedType::*;
5132
5133 if source == target {
5135 return true;
5136 }
5137
5138 match (source, target) {
5140 (Integer, BigInt) | (Integer, Float) | (Integer, Double) => true,
5142 (BigInt, Float) | (BigInt, Double) => true,
5144 (Float, Double) => true,
5146 (Double, Float) => true,
5149 (Integer | BigInt | Float | Double | Text | Decimal { .. }, Decimal { .. }) => true,
5150 (Text | Integer | BigInt | Float | Double, Timestamp) => true,
5153 (Text, Date | Time | Interval) => true,
5154 (Vector { dimension: d1, .. }, Vector { dimension: d2, .. }) => d1 == d2,
5156 _ => false,
5157 }
5158 }
5159
5160 fn coerce_assignment_value(
5161 &self,
5162 value: TypedExpr,
5163 target_type: &ResolvedType,
5164 span: crate::ast::Span,
5165 ) -> TypedExpr {
5166 if value.resolved_type != *target_type
5167 && value.resolved_type != ResolvedType::Null
5168 && matches!(
5169 target_type,
5170 ResolvedType::Integer
5171 | ResolvedType::BigInt
5172 | ResolvedType::Float
5173 | ResolvedType::Double
5174 | ResolvedType::Timestamp
5175 | ResolvedType::Date
5176 | ResolvedType::Time
5177 | ResolvedType::Interval
5178 | ResolvedType::Decimal { .. }
5179 )
5180 {
5181 TypedExpr::cast(value, target_type.clone(), span)
5182 } else {
5183 value
5184 }
5185 }
5186
5187 fn plan_update(&self, stmt: &Update) -> Result<LogicalPlan, PlannerError> {
5191 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
5193
5194 let mut typed_assignments = Vec::new();
5196
5197 for assignment in &stmt.assignments {
5198 let column_meta =
5200 self.name_resolver
5201 .resolve_column(table, &assignment.column, assignment.span)?;
5202 let column_index = table.get_column_index(&assignment.column).unwrap();
5203
5204 let typed_value = self.type_checker.infer_type(&assignment.value, table)?;
5206
5207 if column_meta.not_null
5209 && matches!(&typed_value.kind, TypedExprKind::Literal(Literal::Null))
5210 {
5211 return Err(PlannerError::null_constraint_violation(
5212 &assignment.column,
5213 assignment.value.span,
5214 ));
5215 }
5216
5217 self.validate_type_assignment(
5219 &typed_value,
5220 &column_meta.data_type,
5221 assignment.value.span,
5222 )?;
5223
5224 let typed_value = self.coerce_assignment_value(
5225 typed_value,
5226 &column_meta.data_type,
5227 assignment.value.span,
5228 );
5229
5230 typed_assignments.push(TypedAssignment::new(
5231 assignment.column.clone(),
5232 column_index,
5233 typed_value,
5234 ));
5235 }
5236
5237 let filter = if let Some(ref selection) = stmt.selection {
5239 let predicate = self.type_checker.infer_type(selection, table)?;
5240
5241 if predicate.resolved_type != ResolvedType::Boolean {
5243 return Err(PlannerError::type_mismatch(
5244 "Boolean",
5245 predicate.resolved_type.to_string(),
5246 selection.span,
5247 ));
5248 }
5249
5250 Some(predicate)
5251 } else {
5252 None
5253 };
5254
5255 Ok(LogicalPlan::Update {
5256 table: table.name.clone(),
5257 assignments: typed_assignments,
5258 filter,
5259 })
5260 }
5261
5262 fn plan_delete(&self, stmt: &Delete) -> Result<LogicalPlan, PlannerError> {
5266 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
5268
5269 let filter = if let Some(ref selection) = stmt.selection {
5271 let predicate = self.type_checker.infer_type(selection, table)?;
5272
5273 if predicate.resolved_type != ResolvedType::Boolean {
5275 return Err(PlannerError::type_mismatch(
5276 "Boolean",
5277 predicate.resolved_type.to_string(),
5278 selection.span,
5279 ));
5280 }
5281
5282 Some(predicate)
5283 } else {
5284 None
5285 };
5286
5287 Ok(LogicalPlan::Delete {
5288 table: table.name.clone(),
5289 filter,
5290 })
5291 }
5292}
5293
5294#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5295struct AggregateSignature {
5296 name: String,
5297 distinct: bool,
5298 star: bool,
5299 arg_key: Option<String>,
5300 extra_arg_keys: Vec<String>,
5301 separator: Option<String>,
5302 filter_key: Option<String>,
5305 order_key: Option<String>,
5309}
5310
5311fn collect_projection_aliases(items: &[SelectItem]) -> HashMap<String, crate::ast::expr::Expr> {
5321 let mut aliases = HashMap::new();
5322 for item in items {
5323 if let SelectItem::Expr {
5324 expr,
5325 alias: Some(alias),
5326 ..
5327 } = item
5328 {
5329 aliases.entry(alias.clone()).or_insert_with(|| expr.clone());
5330 }
5331 }
5332 aliases
5333}
5334
5335fn substitute_projection_aliases(
5353 expr: &crate::ast::expr::Expr,
5354 aliases: &HashMap<String, crate::ast::expr::Expr>,
5355) -> crate::ast::expr::Expr {
5356 use crate::ast::expr::ExprKind;
5357
5358 if aliases.is_empty() {
5359 return expr.clone();
5360 }
5361
5362 let recurse = |e: &crate::ast::expr::Expr| substitute_projection_aliases(e, aliases);
5363
5364 let kind = match &expr.kind {
5365 ExprKind::ColumnRef {
5366 table: None,
5367 column,
5368 } => match aliases.get(column) {
5369 Some(source) => {
5370 let mut replacement = source.clone();
5371 replacement.span = expr.span;
5372 return replacement;
5373 }
5374 None => return expr.clone(),
5375 },
5376 ExprKind::BinaryOp { left, op, right } => ExprKind::BinaryOp {
5377 left: Box::new(recurse(left)),
5378 op: *op,
5379 right: Box::new(recurse(right)),
5380 },
5381 ExprKind::UnaryOp { op, operand } => ExprKind::UnaryOp {
5382 op: *op,
5383 operand: Box::new(recurse(operand)),
5384 },
5385 ExprKind::FunctionCall {
5386 name,
5387 args,
5388 distinct,
5389 star,
5390 order_by,
5391 within_group,
5392 filter,
5393 over,
5394 } => ExprKind::FunctionCall {
5395 name: name.clone(),
5396 args: args.iter().map(recurse).collect(),
5397 distinct: *distinct,
5398 star: *star,
5399 order_by: order_by
5400 .iter()
5401 .map(|order| OrderByExpr {
5402 expr: recurse(&order.expr),
5403 asc: order.asc,
5404 nulls_first: order.nulls_first,
5405 span: order.span,
5406 })
5407 .collect(),
5408 within_group: within_group
5409 .iter()
5410 .map(|order| OrderByExpr {
5411 expr: recurse(&order.expr),
5412 asc: order.asc,
5413 nulls_first: order.nulls_first,
5414 span: order.span,
5415 })
5416 .collect(),
5417 filter: filter
5418 .as_deref()
5419 .map(|predicate| Box::new(recurse(predicate))),
5420 over: over.as_ref().map(|window| crate::ast::expr::WindowSpec {
5421 base: window.base.clone(),
5422 partition_by: window.partition_by.iter().map(recurse).collect(),
5423 order_by: window
5424 .order_by
5425 .iter()
5426 .map(|order| OrderByExpr {
5427 expr: recurse(&order.expr),
5428 asc: order.asc,
5429 nulls_first: order.nulls_first,
5430 span: order.span,
5431 })
5432 .collect(),
5433 frame: window.frame.clone(),
5434 }),
5435 },
5436 ExprKind::Case {
5437 operand,
5438 branches,
5439 else_expr,
5440 } => ExprKind::Case {
5441 operand: operand.as_deref().map(|e| Box::new(recurse(e))),
5442 branches: branches
5443 .iter()
5444 .map(|branch| crate::ast::expr::CaseWhen {
5445 when: recurse(&branch.when),
5446 then: recurse(&branch.then),
5447 })
5448 .collect(),
5449 else_expr: else_expr.as_deref().map(|e| Box::new(recurse(e))),
5450 },
5451 ExprKind::Cast { expr, target_type } => ExprKind::Cast {
5452 expr: Box::new(recurse(expr)),
5453 target_type: target_type.clone(),
5454 },
5455 ExprKind::TryCast { expr, target_type } => ExprKind::TryCast {
5456 expr: Box::new(recurse(expr)),
5457 target_type: target_type.clone(),
5458 },
5459 ExprKind::Between {
5460 expr,
5461 low,
5462 high,
5463 negated,
5464 } => ExprKind::Between {
5465 expr: Box::new(recurse(expr)),
5466 low: Box::new(recurse(low)),
5467 high: Box::new(recurse(high)),
5468 negated: *negated,
5469 },
5470 ExprKind::Like {
5471 expr,
5472 pattern,
5473 escape,
5474 negated,
5475 kind,
5476 } => ExprKind::Like {
5477 expr: Box::new(recurse(expr)),
5478 pattern: Box::new(recurse(pattern)),
5479 escape: escape.as_deref().map(|e| Box::new(recurse(e))),
5480 negated: *negated,
5481 kind: *kind,
5482 },
5483 ExprKind::InList {
5484 expr,
5485 list,
5486 negated,
5487 } => ExprKind::InList {
5488 expr: Box::new(recurse(expr)),
5489 list: list.iter().map(recurse).collect(),
5490 negated: *negated,
5491 },
5492 ExprKind::IsNull { expr, negated } => ExprKind::IsNull {
5493 expr: Box::new(recurse(expr)),
5494 negated: *negated,
5495 },
5496 ExprKind::TruthPredicate {
5497 expr,
5498 value,
5499 negated,
5500 } => ExprKind::TruthPredicate {
5501 expr: Box::new(recurse(expr)),
5502 value: *value,
5503 negated: *negated,
5504 },
5505 ExprKind::IsDistinctFrom {
5506 left,
5507 right,
5508 negated,
5509 } => ExprKind::IsDistinctFrom {
5510 left: Box::new(recurse(left)),
5511 right: Box::new(recurse(right)),
5512 negated: *negated,
5513 },
5514 ExprKind::Row { items } => ExprKind::Row {
5515 items: items.iter().map(recurse).collect(),
5516 },
5517 ExprKind::ColumnRef { .. }
5520 | ExprKind::Literal { .. }
5521 | ExprKind::VectorLiteral { .. }
5522 | ExprKind::ScalarSubquery { .. }
5523 | ExprKind::InSubquery { .. }
5524 | ExprKind::Exists { .. }
5525 | ExprKind::Quantified { .. } => return expr.clone(),
5526 };
5527
5528 crate::ast::expr::Expr {
5529 kind,
5530 span: expr.span,
5531 }
5532}
5533
5534fn build_offset_window_function(
5535 name: &str,
5536 args: &[TypedExpr],
5537) -> Result<OffsetWindowFunction, PlannerError> {
5538 let value = args.first().cloned().ok_or_else(|| {
5539 PlannerError::invalid_expression(format!(
5540 "{}() window function expects 1 to 3 arguments",
5541 name.to_ascii_uppercase()
5542 ))
5543 })?;
5544 if args.len() > 3 {
5545 return Err(PlannerError::invalid_expression(format!(
5546 "{}() window function expects 1 to 3 arguments",
5547 name.to_ascii_uppercase()
5548 )));
5549 }
5550 Ok(OffsetWindowFunction {
5551 value,
5552 offset: args.get(1).cloned(),
5553 default: args.get(2).cloned(),
5554 })
5555}
5556
5557fn expr_contains_aggregate(expr: &crate::ast::expr::Expr) -> bool {
5558 use crate::ast::expr::ExprKind;
5559
5560 match &expr.kind {
5561 ExprKind::FunctionCall {
5562 name,
5563 args,
5564 order_by,
5565 within_group,
5566 filter,
5567 over,
5568 ..
5569 } => {
5570 if over.is_none() && is_aggregate_function(name) {
5571 return true;
5572 }
5573 args.iter().any(expr_contains_aggregate)
5574 || order_by
5575 .iter()
5576 .any(|sort| expr_contains_aggregate(&sort.expr))
5577 || within_group
5578 .iter()
5579 .any(|sort| expr_contains_aggregate(&sort.expr))
5580 || filter.as_deref().is_some_and(expr_contains_aggregate)
5581 || over.as_ref().is_some_and(|window| {
5582 window.partition_by.iter().any(expr_contains_aggregate)
5583 || window
5584 .order_by
5585 .iter()
5586 .any(|sort| expr_contains_aggregate(&sort.expr))
5587 })
5588 }
5589 ExprKind::BinaryOp { left, right, .. } => {
5590 expr_contains_aggregate(left) || expr_contains_aggregate(right)
5591 }
5592 ExprKind::UnaryOp { operand, .. } => expr_contains_aggregate(operand),
5593 ExprKind::TruthPredicate { expr, .. } => expr_contains_aggregate(expr),
5594 ExprKind::IsDistinctFrom { left, right, .. } => {
5595 expr_contains_aggregate(left) || expr_contains_aggregate(right)
5596 }
5597 ExprKind::Row { items } => items.iter().any(expr_contains_aggregate),
5598 ExprKind::Case {
5599 operand,
5600 branches,
5601 else_expr,
5602 } => {
5603 operand.as_deref().is_some_and(expr_contains_aggregate)
5604 || branches.iter().any(|branch| {
5605 expr_contains_aggregate(&branch.when) || expr_contains_aggregate(&branch.then)
5606 })
5607 || else_expr.as_deref().is_some_and(expr_contains_aggregate)
5608 }
5609 ExprKind::Cast { expr, .. } | ExprKind::TryCast { expr, .. } => {
5610 expr_contains_aggregate(expr)
5611 }
5612 ExprKind::Between {
5613 expr, low, high, ..
5614 } => {
5615 expr_contains_aggregate(expr)
5616 || expr_contains_aggregate(low)
5617 || expr_contains_aggregate(high)
5618 }
5619 ExprKind::Like {
5620 expr,
5621 pattern,
5622 escape,
5623 ..
5624 } => {
5625 expr_contains_aggregate(expr)
5626 || expr_contains_aggregate(pattern)
5627 || escape.as_deref().is_some_and(expr_contains_aggregate)
5628 }
5629 ExprKind::InList { expr, list, .. } => {
5630 expr_contains_aggregate(expr) || list.iter().any(expr_contains_aggregate)
5631 }
5632 ExprKind::IsNull { expr, .. } => expr_contains_aggregate(expr),
5633 ExprKind::ScalarSubquery { .. }
5634 | ExprKind::InSubquery { .. }
5635 | ExprKind::Exists { .. }
5636 | ExprKind::Quantified { .. }
5637 | ExprKind::Literal { .. }
5638 | ExprKind::VectorLiteral { .. }
5639 | ExprKind::ColumnRef { .. } => false,
5640 }
5641}
5642
5643fn typed_expr_contains_aggregate(expr: &TypedExpr) -> bool {
5644 match &expr.kind {
5645 TypedExprKind::FunctionCall {
5646 name,
5647 args,
5648 filter,
5649 order_by,
5650 over,
5651 ..
5652 } => {
5653 if over.is_none() && is_aggregate_function(name) {
5654 return true;
5655 }
5656 args.iter().any(typed_expr_contains_aggregate)
5657 || filter.as_deref().is_some_and(typed_expr_contains_aggregate)
5658 || order_by
5659 .iter()
5660 .any(|sort| typed_expr_contains_aggregate(&sort.expr))
5661 || over.as_ref().is_some_and(|window| {
5662 window
5663 .partition_by
5664 .iter()
5665 .any(typed_expr_contains_aggregate)
5666 || window
5667 .order_by
5668 .iter()
5669 .any(|sort| typed_expr_contains_aggregate(&sort.expr))
5670 })
5671 }
5672 TypedExprKind::BinaryOp { left, right, .. } => {
5673 typed_expr_contains_aggregate(left) || typed_expr_contains_aggregate(right)
5674 }
5675 TypedExprKind::UnaryOp { operand, .. } => typed_expr_contains_aggregate(operand),
5676 TypedExprKind::Cast { expr, .. } | TypedExprKind::TryCast { expr, .. } => {
5677 typed_expr_contains_aggregate(expr)
5678 }
5679 TypedExprKind::Case {
5680 operand,
5681 branches,
5682 else_expr,
5683 } => {
5684 operand
5685 .as_deref()
5686 .is_some_and(typed_expr_contains_aggregate)
5687 || branches.iter().any(|branch| {
5688 typed_expr_contains_aggregate(&branch.when)
5689 || typed_expr_contains_aggregate(&branch.then)
5690 })
5691 || else_expr
5692 .as_deref()
5693 .is_some_and(typed_expr_contains_aggregate)
5694 }
5695 TypedExprKind::Between {
5696 expr, low, high, ..
5697 } => {
5698 typed_expr_contains_aggregate(expr)
5699 || typed_expr_contains_aggregate(low)
5700 || typed_expr_contains_aggregate(high)
5701 }
5702 TypedExprKind::Like {
5703 expr,
5704 pattern,
5705 escape,
5706 ..
5707 } => {
5708 typed_expr_contains_aggregate(expr)
5709 || typed_expr_contains_aggregate(pattern)
5710 || escape
5711 .as_ref()
5712 .is_some_and(|inner| typed_expr_contains_aggregate(inner))
5713 }
5714 TypedExprKind::InList { expr, list, .. } => {
5715 typed_expr_contains_aggregate(expr) || list.iter().any(typed_expr_contains_aggregate)
5716 }
5717 TypedExprKind::IsNull { expr, .. } => typed_expr_contains_aggregate(expr),
5718 TypedExprKind::InSubquery { expr, .. } => typed_expr_contains_aggregate(expr),
5719 TypedExprKind::Quantified { expr, .. } => typed_expr_contains_aggregate(expr),
5720 TypedExprKind::ScalarSubquery(_) | TypedExprKind::Exists { .. } => false,
5721 _ => false,
5722 }
5723}
5724
5725fn select_contains_window(stmt: &Select) -> bool {
5726 stmt.projection.iter().any(|item| match item {
5727 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
5728 SelectItem::Expr { expr, .. } => expr_contains_window(expr),
5729 }) || stmt.qualify.as_ref().is_some_and(expr_contains_window)
5730 || stmt
5731 .order_by
5732 .iter()
5733 .any(|order| expr_contains_window(&order.expr))
5734}
5735
5736fn expr_contains_window(expr: &crate::ast::expr::Expr) -> bool {
5737 match &expr.kind {
5738 crate::ast::expr::ExprKind::FunctionCall {
5739 args,
5740 order_by,
5741 within_group,
5742 filter,
5743 over,
5744 ..
5745 } => {
5746 over.is_some()
5747 || args.iter().any(expr_contains_window)
5748 || order_by.iter().any(|sort| expr_contains_window(&sort.expr))
5749 || within_group
5750 .iter()
5751 .any(|sort| expr_contains_window(&sort.expr))
5752 || filter.as_deref().is_some_and(expr_contains_window)
5753 }
5754 crate::ast::expr::ExprKind::BinaryOp { left, right, .. } => {
5755 expr_contains_window(left) || expr_contains_window(right)
5756 }
5757 crate::ast::expr::ExprKind::UnaryOp { operand, .. }
5758 | crate::ast::expr::ExprKind::Cast { expr: operand, .. }
5759 | crate::ast::expr::ExprKind::TryCast { expr: operand, .. }
5760 | crate::ast::expr::ExprKind::IsNull { expr: operand, .. } => expr_contains_window(operand),
5761 crate::ast::expr::ExprKind::Between {
5762 expr, low, high, ..
5763 } => expr_contains_window(expr) || expr_contains_window(low) || expr_contains_window(high),
5764 crate::ast::expr::ExprKind::Like {
5765 expr,
5766 pattern,
5767 escape,
5768 ..
5769 } => {
5770 expr_contains_window(expr)
5771 || expr_contains_window(pattern)
5772 || escape.as_deref().is_some_and(expr_contains_window)
5773 }
5774 crate::ast::expr::ExprKind::InList { expr, list, .. } => {
5775 expr_contains_window(expr) || list.iter().any(expr_contains_window)
5776 }
5777 _ => false,
5778 }
5779}
5780
5781fn typed_expr_contains_window(expr: &TypedExpr) -> bool {
5782 match &expr.kind {
5783 TypedExprKind::FunctionCall {
5784 args,
5785 filter,
5786 order_by,
5787 over,
5788 ..
5789 } => {
5790 over.is_some()
5791 || args.iter().any(typed_expr_contains_window)
5792 || filter.as_deref().is_some_and(typed_expr_contains_window)
5793 || order_by
5794 .iter()
5795 .any(|sort| typed_expr_contains_window(&sort.expr))
5796 }
5797 TypedExprKind::BinaryOp { left, right, .. } => {
5798 typed_expr_contains_window(left) || typed_expr_contains_window(right)
5799 }
5800 TypedExprKind::UnaryOp { operand, .. }
5801 | TypedExprKind::Cast { expr: operand, .. }
5802 | TypedExprKind::TryCast { expr: operand, .. }
5803 | TypedExprKind::IsNull { expr: operand, .. } => typed_expr_contains_window(operand),
5804 TypedExprKind::Between {
5805 expr, low, high, ..
5806 } => {
5807 typed_expr_contains_window(expr)
5808 || typed_expr_contains_window(low)
5809 || typed_expr_contains_window(high)
5810 }
5811 TypedExprKind::Like {
5812 expr,
5813 pattern,
5814 escape,
5815 ..
5816 } => {
5817 typed_expr_contains_window(expr)
5818 || typed_expr_contains_window(pattern)
5819 || escape.as_deref().is_some_and(typed_expr_contains_window)
5820 }
5821 TypedExprKind::InList { expr, list, .. } => {
5822 typed_expr_contains_window(expr) || list.iter().any(typed_expr_contains_window)
5823 }
5824 _ => false,
5825 }
5826}
5827
5828fn rewrite_projection_for_windows(
5829 projection: &Projection,
5830 window_map: &HashMap<String, usize>,
5831 base_width: usize,
5832 window_names: &[String],
5833) -> Result<Projection, PlannerError> {
5834 match projection {
5835 Projection::All(names) => Ok(Projection::All(names.clone())),
5836 Projection::Columns(columns) => Ok(Projection::Columns(
5837 columns
5838 .iter()
5839 .map(|column| {
5840 Ok(ProjectedColumn {
5841 expr: rewrite_expr_for_windows(
5842 &column.expr,
5843 window_map,
5844 base_width,
5845 window_names,
5846 )?,
5847 alias: column.alias.clone(),
5848 })
5849 })
5850 .collect::<Result<Vec<_>, PlannerError>>()?,
5851 )),
5852 }
5853}
5854
5855fn rewrite_expr_for_windows(
5856 expr: &TypedExpr,
5857 window_map: &HashMap<String, usize>,
5858 base_width: usize,
5859 window_names: &[String],
5860) -> Result<TypedExpr, PlannerError> {
5861 if let Some(index) = window_map.get(&expr_key(expr)) {
5862 return Ok(TypedExpr::column_ref(
5863 "__window__".to_string(),
5864 window_names
5865 .get(*index)
5866 .cloned()
5867 .unwrap_or_else(|| format!("__window_{index}")),
5868 base_width + index,
5869 expr.resolved_type.clone(),
5870 expr.span,
5871 ));
5872 }
5873
5874 let rewrite =
5875 |inner: &TypedExpr| rewrite_expr_for_windows(inner, window_map, base_width, window_names);
5876 let kind = match &expr.kind {
5877 TypedExprKind::FunctionCall {
5878 name,
5879 args,
5880 distinct,
5881 star,
5882 filter,
5883 order_by,
5884 over,
5885 } => {
5886 if over.is_some() {
5887 return Err(PlannerError::invalid_expression(
5888 "window expression is not part of the window plan".to_string(),
5889 ));
5890 }
5891 TypedExprKind::FunctionCall {
5892 name: name.clone(),
5893 args: args.iter().map(rewrite).collect::<Result<Vec<_>, _>>()?,
5894 distinct: *distinct,
5895 star: *star,
5896 filter: filter.as_deref().map(rewrite).transpose()?.map(Box::new),
5897 order_by: order_by
5898 .iter()
5899 .map(|sort| {
5900 Ok(SortExpr::new(
5901 rewrite(&sort.expr)?,
5902 sort.asc,
5903 sort.nulls_first,
5904 ))
5905 })
5906 .collect::<Result<Vec<_>, PlannerError>>()?,
5907 over: None,
5908 }
5909 }
5910 TypedExprKind::BinaryOp { left, op, right } => TypedExprKind::BinaryOp {
5911 left: Box::new(rewrite(left)?),
5912 op: *op,
5913 right: Box::new(rewrite(right)?),
5914 },
5915 TypedExprKind::UnaryOp { op, operand } => TypedExprKind::UnaryOp {
5916 op: *op,
5917 operand: Box::new(rewrite(operand)?),
5918 },
5919 TypedExprKind::Cast {
5920 expr: inner,
5921 target_type,
5922 } => TypedExprKind::Cast {
5923 expr: Box::new(rewrite(inner)?),
5924 target_type: target_type.clone(),
5925 },
5926 TypedExprKind::TryCast {
5927 expr: inner,
5928 target_type,
5929 } => TypedExprKind::TryCast {
5930 expr: Box::new(rewrite(inner)?),
5931 target_type: target_type.clone(),
5932 },
5933 TypedExprKind::Between {
5934 expr: inner,
5935 low,
5936 high,
5937 negated,
5938 } => TypedExprKind::Between {
5939 expr: Box::new(rewrite(inner)?),
5940 low: Box::new(rewrite(low)?),
5941 high: Box::new(rewrite(high)?),
5942 negated: *negated,
5943 },
5944 TypedExprKind::Like {
5945 expr: inner,
5946 pattern,
5947 escape,
5948 negated,
5949 kind,
5950 } => TypedExprKind::Like {
5951 expr: Box::new(rewrite(inner)?),
5952 pattern: Box::new(rewrite(pattern)?),
5953 escape: escape.as_deref().map(rewrite).transpose()?.map(Box::new),
5954 negated: *negated,
5955 kind: *kind,
5956 },
5957 TypedExprKind::InList {
5958 expr: inner,
5959 list,
5960 negated,
5961 } => TypedExprKind::InList {
5962 expr: Box::new(rewrite(inner)?),
5963 list: list.iter().map(rewrite).collect::<Result<Vec<_>, _>>()?,
5964 negated: *negated,
5965 },
5966 TypedExprKind::IsNull {
5967 expr: inner,
5968 negated,
5969 } => TypedExprKind::IsNull {
5970 expr: Box::new(rewrite(inner)?),
5971 negated: *negated,
5972 },
5973 _ => return Ok(expr.clone()),
5974 };
5975 Ok(TypedExpr {
5976 kind,
5977 resolved_type: expr.resolved_type.clone(),
5978 span: expr.span,
5979 })
5980}
5981
5982fn rewrite_expr_for_projected_output(
5988 expr: &TypedExpr,
5989 projection: &Projection,
5990 output_schema: &[ColumnMetadata],
5991) -> Result<TypedExpr, PlannerError> {
5992 let index = match projection {
5993 Projection::Columns(columns) => columns
5994 .iter()
5995 .position(|column| expr_key(&column.expr) == expr_key(expr)),
5996 Projection::All(_) => match &expr.kind {
5997 TypedExprKind::ColumnRef { column_index, .. }
5998 if *column_index < output_schema.len() =>
5999 {
6000 Some(*column_index)
6001 }
6002 _ => None,
6003 },
6004 };
6005 let Some(index) = index else {
6006 return Err(PlannerError::invalid_expression(
6007 "ORDER BY expression must appear in the SELECT projection for window queries"
6008 .to_string(),
6009 ));
6010 };
6011 let column = output_schema.get(index).ok_or_else(|| {
6012 PlannerError::invalid_expression(
6013 "ORDER BY projection index is outside the output schema".to_string(),
6014 )
6015 })?;
6016 Ok(TypedExpr::column_ref(
6017 "__project__".to_string(),
6018 column.name.clone(),
6019 index,
6020 column.data_type.clone(),
6021 expr.span,
6022 ))
6023}
6024
6025fn map_join_type(join_type: crate::ast::dml::JoinType) -> JoinType {
6026 match join_type {
6027 crate::ast::dml::JoinType::Inner => JoinType::Inner,
6028 crate::ast::dml::JoinType::Left => JoinType::Left,
6029 crate::ast::dml::JoinType::Right => JoinType::Right,
6030 crate::ast::dml::JoinType::Full => JoinType::Full,
6031 crate::ast::dml::JoinType::Cross => JoinType::Cross,
6032 }
6033}
6034
6035struct FoundScopedColumn {
6036 table: String,
6037 index: usize,
6038 ty: ResolvedType,
6039 partner_indices: Vec<usize>,
6040}
6041
6042fn find_scoped_column(
6043 scope: &[ScopedTable],
6044 column: &str,
6045 span: crate::ast::Span,
6046) -> Result<FoundScopedColumn, PlannerError> {
6047 let mut matches = Vec::new();
6048 for table in scope {
6049 if table.hidden_unqualified_columns.contains(column) {
6050 continue;
6051 }
6052 if let Some(local_idx) = table.table.get_column_index(column) {
6053 let meta = &table.table.columns[local_idx];
6054 matches.push(FoundScopedColumn {
6055 table: table.table.name.clone(),
6056 index: table.start_index + local_idx,
6057 ty: meta.data_type.clone(),
6058 partner_indices: table
6059 .merged_column_partners
6060 .get(column)
6061 .cloned()
6062 .unwrap_or_default(),
6063 });
6064 }
6065 }
6066 match matches.len() {
6067 0 => Err(PlannerError::column_not_found(column, "JOIN input", span)),
6068 1 => Ok(matches.remove(0)),
6069 _ => Err(PlannerError::ambiguous_column(
6070 column,
6071 scope.iter().map(|s| s.table.name.clone()).collect(),
6072 span,
6073 )),
6074 }
6075}
6076
6077fn merged_scoped_column_expr(
6078 found: &FoundScopedColumn,
6079 column: &str,
6080 span: crate::ast::Span,
6081) -> TypedExpr {
6082 let own = TypedExpr::column_ref(
6083 found.table.clone(),
6084 column.to_string(),
6085 found.index,
6086 found.ty.clone(),
6087 span,
6088 );
6089 if found.partner_indices.is_empty() {
6090 return own;
6091 }
6092
6093 let mut args = Vec::with_capacity(found.partner_indices.len() + 1);
6094 args.push(own);
6095 args.extend(found.partner_indices.iter().map(|&index| {
6096 TypedExpr::column_ref(
6097 found.table.clone(),
6098 column.to_string(),
6099 index,
6100 found.ty.clone(),
6101 span,
6102 )
6103 }));
6104 TypedExpr {
6105 kind: TypedExprKind::FunctionCall {
6106 name: "coalesce".to_string(),
6107 args,
6108 distinct: false,
6109 star: false,
6110 filter: None,
6111 order_by: Vec::new(),
6112 over: None,
6113 },
6114 resolved_type: found.ty.clone(),
6115 span,
6116 }
6117}
6118
6119fn projection_schema(
6120 projection: &Projection,
6121 input_schema: &[ColumnMetadata],
6122) -> Vec<ColumnMetadata> {
6123 match projection {
6124 Projection::All(names) => names
6125 .iter()
6126 .enumerate()
6127 .map(|(idx, name)| {
6128 let ty = (names.len() == input_schema.len())
6129 .then(|| input_schema.get(idx))
6130 .flatten()
6131 .or_else(|| input_schema.iter().find(|col| &col.name == name))
6132 .map(|col| col.data_type.clone())
6133 .unwrap_or(ResolvedType::Null);
6134 ColumnMetadata::new(name.clone(), ty)
6135 })
6136 .collect(),
6137 Projection::Columns(columns) => columns
6138 .iter()
6139 .enumerate()
6140 .map(|(idx, col)| {
6141 let name = col
6142 .alias
6143 .clone()
6144 .or_else(|| match &col.expr.kind {
6145 TypedExprKind::ColumnRef { column, .. } => Some(column.clone()),
6146 TypedExprKind::FunctionCall { name, args, .. }
6150 if name == "coalesce" && !args.is_empty() =>
6151 {
6152 let first_column = match &args[0].kind {
6153 TypedExprKind::ColumnRef { column, .. } => Some(column),
6154 _ => None,
6155 };
6156 first_column
6157 .filter(|column| {
6158 args.iter().all(|arg| {
6159 matches!(
6160 &arg.kind,
6161 TypedExprKind::ColumnRef { column: other, .. }
6162 if other == *column
6163 )
6164 })
6165 })
6166 .cloned()
6167 }
6168 _ => None,
6169 })
6170 .unwrap_or_else(|| format!("col_{idx}"));
6171 ColumnMetadata::new(name, col.expr.resolved_type.clone())
6172 })
6173 .collect(),
6174 }
6175}
6176
6177fn visible_wildcard_columns(schema: &[ColumnMetadata], scope: &[ScopedTable]) -> Vec<String> {
6178 schema
6179 .iter()
6180 .enumerate()
6181 .filter(|(index, column)| {
6182 !scope.iter().any(|table| {
6183 *index >= table.start_index
6184 && *index < table.start_index + table.table.columns.len()
6185 && table.hidden_unqualified_columns.contains(&column.name)
6186 })
6187 })
6188 .map(|(_, column)| column.name.clone())
6189 .collect()
6190}
6191
6192fn combine_join_shape(
6197 left: &PlannedRelation,
6198 right: &PlannedRelation,
6199 using: Option<&[String]>,
6200) -> (Vec<ColumnMetadata>, Vec<ScopedTable>) {
6201 let mut schema = left.schema.clone();
6202 schema.extend(right.schema.clone());
6203 let mut scope = left.scope.clone();
6204 let mut right_scope = right.scope.clone();
6205 if let Some(columns) = using {
6206 for column in columns {
6210 let right_index = right_scope.iter().find_map(|table| {
6211 table
6212 .table
6213 .get_column_index(column)
6214 .map(|index| table.start_index + index)
6215 });
6216 let Some(right_index) = right_index else {
6217 continue;
6218 };
6219 for table in &mut scope {
6220 if table.table.get_column_index(column).is_some() {
6221 table.merge_column_with(column, right_index);
6222 }
6223 }
6224 }
6225 for table in &mut right_scope {
6226 table.hide_unqualified_columns(columns);
6227 }
6228 }
6229 scope.extend(right_scope);
6230 (schema, scope)
6231}
6232
6233fn from_item_is_lateral(item: &FromItem) -> bool {
6238 match item {
6239 FromItem::Derived { lateral, .. } => *lateral,
6240 FromItem::Function { .. } => true,
6241 FromItem::Table { .. } | FromItem::Join { .. } => false,
6242 }
6243}
6244
6245fn lateral_outer_scope(
6253 left_scope: &[ScopedTable],
6254 base: usize,
6255 left_width: usize,
6256 outer_scope: &[ScopedTable],
6257) -> Vec<ScopedTable> {
6258 debug_assert!(
6259 left_scope.iter().all(|table| table.start_index >= base),
6260 "a FROM item's left sibling scope must start at the join's own base"
6261 );
6262 left_scope
6263 .iter()
6264 .cloned()
6265 .map(|mut table| {
6266 table.start_index -= base;
6267 table
6268 })
6269 .chain(outer_scope.iter().cloned().map(|mut table| {
6270 table.start_index += left_width;
6271 table
6272 }))
6273 .collect()
6274}
6275
6276fn apply_alias_columns(
6281 alias: &str,
6282 columns: &[String],
6283 schema: &mut [ColumnMetadata],
6284 span: crate::ast::Span,
6285) -> Result<(), PlannerError> {
6286 if columns.is_empty() {
6287 return Ok(());
6288 }
6289 if columns.len() != schema.len() {
6290 return Err(PlannerError::table_alias_column_count_mismatch(
6291 alias,
6292 columns.len(),
6293 schema.len(),
6294 span,
6295 ));
6296 }
6297 let mut names = HashSet::new();
6298 for name in columns {
6299 if !names.insert(name) {
6300 return Err(PlannerError::invalid_expression(format!(
6301 "relation alias '{alias}' declares column '{name}' more than once"
6302 )));
6303 }
6304 }
6305 for (column, name) in schema.iter_mut().zip(columns) {
6306 column.name.clone_from(name);
6307 }
6308 Ok(())
6309}
6310
6311fn offset_scope(scope: &[ScopedTable], offset: usize) -> Vec<ScopedTable> {
6312 scope
6313 .iter()
6314 .cloned()
6315 .map(|mut table| {
6316 table.start_index += offset;
6317 table.scope_level += 1;
6318 table
6319 })
6320 .collect()
6321}
6322
6323fn natural_join_columns(
6324 left_schema: &[ColumnMetadata],
6325 right_schema: &[ColumnMetadata],
6326) -> Vec<String> {
6327 let right_names = right_schema
6331 .iter()
6332 .map(|column| column.name.as_str())
6333 .collect::<HashSet<_>>();
6334 left_schema
6335 .iter()
6336 .filter(|left| right_names.contains(left.name.as_str()))
6337 .map(|column| column.name.clone())
6338 .collect()
6339}
6340
6341fn install_base_projection(plan: &mut LogicalPlan, projection: &Projection) {
6342 match plan {
6343 LogicalPlan::Scan {
6344 projection: scan_projection,
6345 ..
6346 } => *scan_projection = projection.clone(),
6347 LogicalPlan::Filter { input, .. } => install_base_projection(input, projection),
6348 _ => {}
6349 }
6350}
6351
6352fn is_aggregate_function(name: &str) -> bool {
6353 matches!(
6354 name.to_ascii_lowercase().as_str(),
6355 "count"
6356 | "sum"
6357 | "total"
6358 | "avg"
6359 | "min"
6360 | "max"
6361 | "group_concat"
6362 | "string_agg"
6363 | "json_group_array"
6364 | "array_agg"
6365 | "json_group_object"
6366 | "jsonb_agg"
6367 | "jsonb_object_agg"
6368 | "percentile_disc"
6369 | "percentile_cont"
6370 ) || type_checker::is_portable_aggregate_name(&name.to_ascii_lowercase())
6371}
6372
6373fn expr_key(expr: &TypedExpr) -> String {
6374 format!("{:?}", expr.kind)
6375}
6376
6377fn distinct_on_expr_signature(expr: &TypedExpr) -> String {
6387 const MARKER: &str = "span: Span { start: Location { line: ";
6388 let rendered = format!("{:?}", expr.kind);
6389 let mut result = String::with_capacity(rendered.len());
6390 let mut rest = rendered.as_str();
6391 while let Some(position) = rest.find(MARKER) {
6392 let after = &rest[position + MARKER.len()..];
6393 match debug_span_tail_length(after) {
6394 Some(consumed) => {
6395 result.push_str(&rest[..position]);
6396 result.push_str("span: _");
6397 rest = &after[consumed..];
6398 }
6399 None => {
6400 let keep = position + MARKER.len();
6401 result.push_str(&rest[..keep]);
6402 rest = &rest[keep..];
6403 }
6404 }
6405 }
6406 result.push_str(rest);
6407 result
6408}
6409
6410fn debug_span_tail_length(input: &str) -> Option<usize> {
6414 fn digits(input: &str, offset: &mut usize) -> bool {
6415 let start = *offset;
6416 while input
6417 .as_bytes()
6418 .get(*offset)
6419 .is_some_and(u8::is_ascii_digit)
6420 {
6421 *offset += 1;
6422 }
6423 *offset > start
6424 }
6425 fn literal(input: &str, offset: &mut usize, expected: &str) -> bool {
6426 if input[*offset..].starts_with(expected) {
6427 *offset += expected.len();
6428 true
6429 } else {
6430 false
6431 }
6432 }
6433 let mut offset = 0;
6434 (digits(input, &mut offset)
6435 && literal(input, &mut offset, ", column: ")
6436 && digits(input, &mut offset)
6437 && literal(input, &mut offset, " }, end: Location { line: ")
6438 && digits(input, &mut offset)
6439 && literal(input, &mut offset, ", column: ")
6440 && digits(input, &mut offset)
6441 && literal(input, &mut offset, " } }"))
6442 .then_some(offset)
6443}
6444
6445fn build_distinct_on_sort_spec(
6462 key_exprs: Vec<TypedExpr>,
6463 user_order_by: Vec<SortExpr>,
6464 base_schema: &[ColumnMetadata],
6465 fallback_span: crate::ast::Span,
6466) -> Result<(usize, Vec<SortExpr>), PlannerError> {
6467 let key_signatures: Vec<String> = key_exprs.iter().map(distinct_on_expr_signature).collect();
6468 let mut consumed = vec![false; key_exprs.len()];
6469 let mut prefix: Vec<SortExpr> = Vec::new();
6470 let mut tail: Vec<SortExpr> = Vec::new();
6471 let mut prefix_ended = false;
6472 for sort in user_order_by {
6473 let signature = distinct_on_expr_signature(&sort.expr);
6474 if let Some(index) = key_signatures
6475 .iter()
6476 .position(|candidate| candidate == &signature)
6477 {
6478 if prefix_ended {
6479 return Err(PlannerError::distinct_on_order_by_mismatch(sort.expr.span));
6482 }
6483 consumed[index] = true;
6484 prefix.push(sort);
6485 } else {
6486 prefix_ended = true;
6487 tail.push(sort);
6488 }
6489 }
6490 let mut implicit: Vec<SortExpr> = Vec::new();
6491 for (index, key) in key_exprs.into_iter().enumerate() {
6492 if consumed[index] {
6493 continue;
6494 }
6495 if prefix_ended {
6496 return Err(PlannerError::distinct_on_order_by_mismatch(key.span));
6499 }
6500 implicit.push(SortExpr::new(key, true, false));
6501 }
6502 let key_count = prefix.len() + implicit.len();
6503 let mut order_by = prefix;
6504 order_by.append(&mut implicit);
6505 order_by.append(&mut tail);
6506 let mut seen_columns: HashSet<usize> = order_by
6507 .iter()
6508 .filter_map(|sort| match &sort.expr.kind {
6509 TypedExprKind::ColumnRef { column_index, .. } => Some(*column_index),
6510 _ => None,
6511 })
6512 .collect();
6513 for (index, column) in base_schema.iter().enumerate() {
6514 if seen_columns.insert(index) {
6515 order_by.push(SortExpr::new(
6516 TypedExpr::column_ref(
6517 String::new(),
6518 column.name.clone(),
6519 index,
6520 column.data_type.clone(),
6521 fallback_span,
6522 ),
6523 true,
6524 false,
6525 ));
6526 }
6527 }
6528 Ok((key_count, order_by))
6529}
6530
6531fn is_order_sensitive_aggregate(name: &str) -> bool {
6533 matches!(
6534 name.to_ascii_lowercase().as_str(),
6535 "group_concat"
6536 | "string_agg"
6537 | "percentile_disc"
6538 | "percentile_cont"
6539 | "mode"
6540 | "first"
6541 | "last"
6542 )
6543}
6544
6545fn sort_exprs_key(order_by: &[SortExpr]) -> Option<String> {
6546 if order_by.is_empty() {
6547 return None;
6548 }
6549 Some(
6550 order_by
6551 .iter()
6552 .map(|sort| format!("{}|{}|{}", expr_key(&sort.expr), sort.asc, sort.nulls_first))
6553 .collect::<Vec<_>>()
6554 .join(","),
6555 )
6556}
6557
6558#[allow(clippy::too_many_arguments)]
6559fn aggregate_signature(
6560 name: &str,
6561 distinct: bool,
6562 star: bool,
6563 arg: Option<&TypedExpr>,
6564 separator: Option<&String>,
6565 _expr: &TypedExpr,
6566 filter: Option<&TypedExpr>,
6567 order_by: &[SortExpr],
6568) -> AggregateSignature {
6569 AggregateSignature {
6570 name: canonical_aggregate_name(name),
6571 distinct,
6572 star,
6573 arg_key: arg.map(expr_key),
6574 extra_arg_keys: Vec::new(),
6575 separator: separator.cloned(),
6576 filter_key: filter.map(expr_key),
6577 order_key: sort_exprs_key(order_by),
6578 }
6579}
6580
6581fn canonical_aggregate_name(name: &str) -> String {
6582 match name.to_ascii_lowercase().as_str() {
6583 "variance" | "var_samp" => "var_samp".into(),
6584 "stddev" | "stddev_samp" => "stddev_samp".into(),
6585 "min_by" => "arg_min".into(),
6586 "max_by" => "arg_max".into(),
6587 lower => lower.into(),
6588 }
6589}
6590
6591fn build_group_key_map(group_keys: &[TypedExpr]) -> HashMap<String, usize> {
6592 let mut map = HashMap::new();
6593 for (idx, key) in group_keys.iter().enumerate() {
6594 map.insert(expr_key(key), idx);
6595 }
6596 map
6597}
6598
6599fn build_aggregate_map(aggregates: &[AggregateExpr]) -> HashMap<AggregateSignature, usize> {
6600 let mut map = HashMap::new();
6601 for (idx, agg) in aggregates.iter().enumerate() {
6602 let (name, separator, star, arg) = match &agg.function {
6603 AggregateFunction::Count => (
6604 "count".to_string(),
6605 None,
6606 agg.arg.is_none(),
6607 agg.arg.as_ref(),
6608 ),
6609 AggregateFunction::Sum => ("sum".to_string(), None, false, agg.arg.as_ref()),
6610 AggregateFunction::Total => ("total".to_string(), None, false, agg.arg.as_ref()),
6611 AggregateFunction::Avg => ("avg".to_string(), None, false, agg.arg.as_ref()),
6612 AggregateFunction::Min => ("min".to_string(), None, false, agg.arg.as_ref()),
6613 AggregateFunction::Max => ("max".to_string(), None, false, agg.arg.as_ref()),
6614 AggregateFunction::GroupConcat { separator } => (
6615 "group_concat".to_string(),
6616 separator.clone(),
6617 false,
6618 agg.arg.as_ref(),
6619 ),
6620 AggregateFunction::StringAgg { separator } => (
6621 "string_agg".to_string(),
6622 separator.clone(),
6623 false,
6624 agg.arg.as_ref(),
6625 ),
6626 AggregateFunction::ArrayAgg => ("array_agg".to_string(), None, false, agg.arg.as_ref()),
6627 AggregateFunction::JsonGroupArray => (
6628 "json_group_array".to_string(),
6629 None,
6630 false,
6631 agg.arg.as_ref(),
6632 ),
6633 AggregateFunction::JsonGroupObject => (
6634 "json_group_object".to_string(),
6635 None,
6636 false,
6637 agg.arg.as_ref(),
6638 ),
6639 AggregateFunction::JsonbAgg => ("jsonb_agg".to_string(), None, false, agg.arg.as_ref()),
6640 AggregateFunction::JsonbObjectAgg => (
6641 "jsonb_object_agg".to_string(),
6642 None,
6643 false,
6644 agg.arg.as_ref(),
6645 ),
6646 AggregateFunction::PercentileDisc { fraction } => (
6647 "percentile_disc".to_string(),
6648 Some(format!("{fraction:?}")),
6649 false,
6650 None,
6651 ),
6652 AggregateFunction::PercentileCont { fraction } => (
6653 "percentile_cont".to_string(),
6654 Some(format!("{fraction:?}")),
6655 false,
6656 None,
6657 ),
6658 AggregateFunction::QuantileCont { fraction } => (
6659 "quantile_cont".to_string(),
6660 Some(format!("{fraction:?}")),
6661 false,
6662 agg.arg.as_ref(),
6663 ),
6664 AggregateFunction::Variance { sample } => (
6665 if *sample { "var_samp" } else { "var_pop" }.to_string(),
6666 None,
6667 false,
6668 agg.arg.as_ref(),
6669 ),
6670 AggregateFunction::Stddev { sample } => (
6671 if *sample { "stddev_samp" } else { "stddev_pop" }.to_string(),
6672 None,
6673 false,
6674 agg.arg.as_ref(),
6675 ),
6676 AggregateFunction::Covariance { sample } => (
6677 if *sample { "covar_samp" } else { "covar_pop" }.to_string(),
6678 None,
6679 false,
6680 agg.arg.as_ref(),
6681 ),
6682 AggregateFunction::Corr => ("corr".into(), None, false, agg.arg.as_ref()),
6683 AggregateFunction::Median => ("median".into(), None, false, agg.arg.as_ref()),
6684 AggregateFunction::Mode => (
6685 "mode".into(),
6686 None,
6687 false,
6688 agg.order_by.is_empty().then_some(()).and(agg.arg.as_ref()),
6689 ),
6690 AggregateFunction::RegrCount => ("regr_count".into(), None, false, agg.arg.as_ref()),
6691 AggregateFunction::RegrAvgX => ("regr_avgx".into(), None, false, agg.arg.as_ref()),
6692 AggregateFunction::RegrAvgY => ("regr_avgy".into(), None, false, agg.arg.as_ref()),
6693 AggregateFunction::RegrSxx => ("regr_sxx".into(), None, false, agg.arg.as_ref()),
6694 AggregateFunction::RegrSyy => ("regr_syy".into(), None, false, agg.arg.as_ref()),
6695 AggregateFunction::RegrSxy => ("regr_sxy".into(), None, false, agg.arg.as_ref()),
6696 AggregateFunction::RegrSlope => ("regr_slope".into(), None, false, agg.arg.as_ref()),
6697 AggregateFunction::RegrIntercept => {
6698 ("regr_intercept".into(), None, false, agg.arg.as_ref())
6699 }
6700 AggregateFunction::RegrR2 => ("regr_r2".into(), None, false, agg.arg.as_ref()),
6701 AggregateFunction::AnyValue => ("any_value".into(), None, false, agg.arg.as_ref()),
6702 AggregateFunction::First => ("first".into(), None, false, agg.arg.as_ref()),
6703 AggregateFunction::Last => ("last".into(), None, false, agg.arg.as_ref()),
6704 AggregateFunction::ArgMin => ("arg_min".into(), None, false, agg.arg.as_ref()),
6705 AggregateFunction::ArgMax => ("arg_max".into(), None, false, agg.arg.as_ref()),
6706 AggregateFunction::BitAnd => ("bit_and".into(), None, false, agg.arg.as_ref()),
6707 AggregateFunction::BitOr => ("bit_or".into(), None, false, agg.arg.as_ref()),
6708 AggregateFunction::BitXor => ("bit_xor".into(), None, false, agg.arg.as_ref()),
6709 AggregateFunction::BoolAnd => ("bool_and".into(), None, false, agg.arg.as_ref()),
6710 AggregateFunction::BoolOr => ("bool_or".into(), None, false, agg.arg.as_ref()),
6711 };
6712 let signature = AggregateSignature {
6713 name,
6714 distinct: agg.distinct,
6715 star,
6716 arg_key: arg.map(expr_key),
6717 extra_arg_keys: agg.extra_args.iter().map(expr_key).collect(),
6718 separator,
6719 filter_key: agg.filter.as_ref().map(expr_key),
6720 order_key: sort_exprs_key(&agg.order_by),
6721 };
6722 map.insert(signature, idx);
6723 }
6724 map
6725}
6726
6727pub(crate) const GROUPING_ID_COLUMN: &str = "__grouping_id";
6729const MAX_GROUPING_SETS: usize = 4096;
6731const MAX_CUBE_COLUMNS: usize = 12;
6733const MAX_GROUPING_KEYS: usize = 63;
6735
6736struct ExpandedGroupBy {
6738 group_keys: Vec<TypedExpr>,
6739 grouping_sets: Option<Vec<u64>>,
6740}
6741
6742fn grouping_full_mask(key_count: usize) -> u64 {
6743 if key_count == 0 {
6744 0
6745 } else {
6746 (1u64 << key_count) - 1
6747 }
6748}
6749
6750fn is_grouping_function(name: &str) -> bool {
6751 name.eq_ignore_ascii_case("grouping") || name.eq_ignore_ascii_case("grouping_id")
6752}
6753
6754struct GroupingRewrite {
6756 key_index: HashMap<String, usize>,
6758 key_count: usize,
6759 gid_index: usize,
6761 sets_present: bool,
6764}
6765
6766impl GroupingRewrite {
6767 fn new(
6768 group_keys: &[TypedExpr],
6769 aggregates: &[AggregateExpr],
6770 grouping_sets: &Option<Vec<u64>>,
6771 ) -> Self {
6772 let key_index = group_keys
6773 .iter()
6774 .enumerate()
6775 .map(|(index, key)| (expr_key(key), index))
6776 .collect();
6777 Self {
6778 key_index,
6779 key_count: group_keys.len(),
6780 gid_index: group_keys.len() + aggregates.len(),
6781 sets_present: grouping_sets.is_some(),
6782 }
6783 }
6784}
6785
6786fn expr_contains_grouping(expr: &crate::ast::expr::Expr) -> bool {
6788 use crate::ast::expr::ExprKind;
6789
6790 match &expr.kind {
6791 ExprKind::FunctionCall {
6792 name,
6793 args,
6794 order_by,
6795 within_group,
6796 filter,
6797 over,
6798 ..
6799 } => {
6800 is_grouping_function(name)
6801 || args.iter().any(expr_contains_grouping)
6802 || order_by
6803 .iter()
6804 .any(|sort| expr_contains_grouping(&sort.expr))
6805 || within_group
6806 .iter()
6807 .any(|sort| expr_contains_grouping(&sort.expr))
6808 || filter.as_deref().is_some_and(expr_contains_grouping)
6809 || over.as_ref().is_some_and(|window| {
6810 window.partition_by.iter().any(expr_contains_grouping)
6811 || window
6812 .order_by
6813 .iter()
6814 .any(|sort| expr_contains_grouping(&sort.expr))
6815 })
6816 }
6817 ExprKind::BinaryOp { left, right, .. } => {
6818 expr_contains_grouping(left) || expr_contains_grouping(right)
6819 }
6820 ExprKind::UnaryOp { operand, .. } => expr_contains_grouping(operand),
6821 ExprKind::TruthPredicate { expr, .. } => expr_contains_grouping(expr),
6822 ExprKind::IsDistinctFrom { left, right, .. } => {
6823 expr_contains_grouping(left) || expr_contains_grouping(right)
6824 }
6825 ExprKind::Row { items } => items.iter().any(expr_contains_grouping),
6826 ExprKind::Case {
6827 operand,
6828 branches,
6829 else_expr,
6830 } => {
6831 operand.as_deref().is_some_and(expr_contains_grouping)
6832 || branches.iter().any(|branch| {
6833 expr_contains_grouping(&branch.when) || expr_contains_grouping(&branch.then)
6834 })
6835 || else_expr.as_deref().is_some_and(expr_contains_grouping)
6836 }
6837 ExprKind::Cast { expr, .. } | ExprKind::TryCast { expr, .. } => {
6838 expr_contains_grouping(expr)
6839 }
6840 ExprKind::Between {
6841 expr, low, high, ..
6842 } => {
6843 expr_contains_grouping(expr)
6844 || expr_contains_grouping(low)
6845 || expr_contains_grouping(high)
6846 }
6847 ExprKind::Like {
6848 expr,
6849 pattern,
6850 escape,
6851 ..
6852 } => {
6853 expr_contains_grouping(expr)
6854 || expr_contains_grouping(pattern)
6855 || escape.as_deref().is_some_and(expr_contains_grouping)
6856 }
6857 ExprKind::InList { expr, list, .. } => {
6858 expr_contains_grouping(expr) || list.iter().any(expr_contains_grouping)
6859 }
6860 ExprKind::IsNull { expr, .. } => expr_contains_grouping(expr),
6861 ExprKind::ScalarSubquery { .. }
6862 | ExprKind::InSubquery { .. }
6863 | ExprKind::Exists { .. }
6864 | ExprKind::Quantified { .. }
6865 | ExprKind::Literal { .. }
6866 | ExprKind::VectorLiteral { .. }
6867 | ExprKind::ColumnRef { .. } => false,
6868 }
6869}
6870
6871fn typed_expr_contains_grouping(expr: &TypedExpr) -> bool {
6872 match &expr.kind {
6873 TypedExprKind::FunctionCall {
6874 name,
6875 args,
6876 filter,
6877 order_by,
6878 over,
6879 ..
6880 } => {
6881 is_grouping_function(name)
6882 || args.iter().any(typed_expr_contains_grouping)
6883 || filter.as_deref().is_some_and(typed_expr_contains_grouping)
6884 || order_by
6885 .iter()
6886 .any(|sort| typed_expr_contains_grouping(&sort.expr))
6887 || over.as_ref().is_some_and(|window| {
6888 window.partition_by.iter().any(typed_expr_contains_grouping)
6889 || window
6890 .order_by
6891 .iter()
6892 .any(|sort| typed_expr_contains_grouping(&sort.expr))
6893 })
6894 }
6895 TypedExprKind::BinaryOp { left, right, .. } => {
6896 typed_expr_contains_grouping(left) || typed_expr_contains_grouping(right)
6897 }
6898 TypedExprKind::UnaryOp { operand, .. } => typed_expr_contains_grouping(operand),
6899 TypedExprKind::Cast { expr, .. } | TypedExprKind::TryCast { expr, .. } => {
6900 typed_expr_contains_grouping(expr)
6901 }
6902 TypedExprKind::Case {
6903 operand,
6904 branches,
6905 else_expr,
6906 } => {
6907 operand.as_deref().is_some_and(typed_expr_contains_grouping)
6908 || branches.iter().any(|branch| {
6909 typed_expr_contains_grouping(&branch.when)
6910 || typed_expr_contains_grouping(&branch.then)
6911 })
6912 || else_expr
6913 .as_deref()
6914 .is_some_and(typed_expr_contains_grouping)
6915 }
6916 TypedExprKind::Between {
6917 expr, low, high, ..
6918 } => {
6919 typed_expr_contains_grouping(expr)
6920 || typed_expr_contains_grouping(low)
6921 || typed_expr_contains_grouping(high)
6922 }
6923 TypedExprKind::Like {
6924 expr,
6925 pattern,
6926 escape,
6927 ..
6928 } => {
6929 typed_expr_contains_grouping(expr)
6930 || typed_expr_contains_grouping(pattern)
6931 || escape
6932 .as_ref()
6933 .is_some_and(|inner| typed_expr_contains_grouping(inner))
6934 }
6935 TypedExprKind::InList { expr, list, .. } => {
6936 typed_expr_contains_grouping(expr) || list.iter().any(typed_expr_contains_grouping)
6937 }
6938 TypedExprKind::IsNull { expr, .. } => typed_expr_contains_grouping(expr),
6939 TypedExprKind::InSubquery { expr, .. } => typed_expr_contains_grouping(expr),
6940 TypedExprKind::Quantified { expr, .. } => typed_expr_contains_grouping(expr),
6941 TypedExprKind::Literal(_)
6942 | TypedExprKind::VectorLiteral(_)
6943 | TypedExprKind::ColumnRef { .. }
6944 | TypedExprKind::ScalarSubquery(_)
6945 | TypedExprKind::Exists { .. } => false,
6946 }
6947}
6948
6949fn bigint_literal(value: u64, span: crate::ast::Span) -> TypedExpr {
6950 TypedExpr {
6951 kind: TypedExprKind::Literal(Literal::Number(value.to_string())),
6952 resolved_type: ResolvedType::BigInt,
6953 span,
6954 }
6955}
6956
6957fn bigint_binary_op(
6958 left: TypedExpr,
6959 op: crate::ast::expr::BinaryOp,
6960 right: TypedExpr,
6961 span: crate::ast::Span,
6962) -> TypedExpr {
6963 TypedExpr {
6964 kind: TypedExprKind::BinaryOp {
6965 left: Box::new(left),
6966 op,
6967 right: Box::new(right),
6968 },
6969 resolved_type: ResolvedType::BigInt,
6970 span,
6971 }
6972}
6973
6974fn lower_grouping_call(
6982 args: &[TypedExpr],
6983 context: &GroupingRewrite,
6984 span: crate::ast::Span,
6985) -> Result<TypedExpr, PlannerError> {
6986 use crate::ast::expr::BinaryOp as AstBinaryOp;
6987
6988 if args.is_empty() {
6989 return Err(PlannerError::invalid_expression(
6990 "GROUPING requires at least one argument".to_string(),
6991 ));
6992 }
6993 if args.len() > MAX_GROUPING_KEYS {
6994 return Err(PlannerError::invalid_expression(format!(
6995 "GROUPING accepts at most {MAX_GROUPING_KEYS} arguments"
6996 )));
6997 }
6998 let mut key_positions = Vec::with_capacity(args.len());
6999 for arg in args {
7000 let Some(&position) = context.key_index.get(&expr_key(arg)) else {
7001 return Err(PlannerError::invalid_expression(
7002 "arguments to GROUPING must be grouping expressions of the query".to_string(),
7003 ));
7004 };
7005 key_positions.push(position);
7006 }
7007
7008 if !context.sets_present {
7009 return Ok(bigint_literal(0, span));
7011 }
7012
7013 let argument_count = key_positions.len();
7014 let mut sum: Option<TypedExpr> = None;
7015 for (argument, key_position) in key_positions.into_iter().enumerate() {
7016 let gid_ref = TypedExpr::column_ref(
7017 "__agg__".to_string(),
7018 GROUPING_ID_COLUMN.to_string(),
7019 context.gid_index,
7020 ResolvedType::BigInt,
7021 span,
7022 );
7023 let excluded_shift = (context.key_count - 1 - key_position) as u32;
7024 let bit = bigint_binary_op(
7025 bigint_binary_op(
7026 gid_ref,
7027 AstBinaryOp::Div,
7028 bigint_literal(1u64 << excluded_shift, span),
7029 span,
7030 ),
7031 AstBinaryOp::Mod,
7032 bigint_literal(2, span),
7033 span,
7034 );
7035 let weight = 1u64 << (argument_count - 1 - argument);
7036 let term = if weight == 1 {
7037 bit
7038 } else {
7039 bigint_binary_op(bit, AstBinaryOp::Mul, bigint_literal(weight, span), span)
7040 };
7041 sum = Some(match sum {
7042 None => term,
7043 Some(current) => bigint_binary_op(current, AstBinaryOp::Add, term, span),
7044 });
7045 }
7046 Ok(sum.expect("GROUPING argument list is non-empty"))
7047}
7048
7049fn rewrite_grouping_calls(
7056 expr: &TypedExpr,
7057 context: &GroupingRewrite,
7058) -> Result<TypedExpr, PlannerError> {
7059 let rebuild = |inner: &TypedExpr| rewrite_grouping_calls(inner, context);
7060 let rebuild_box = |inner: &TypedExpr| -> Result<Box<TypedExpr>, PlannerError> {
7061 Ok(Box::new(rewrite_grouping_calls(inner, context)?))
7062 };
7063 let kind = match &expr.kind {
7064 TypedExprKind::FunctionCall {
7065 name,
7066 args,
7067 distinct,
7068 star,
7069 filter,
7070 order_by,
7071 over,
7072 } => {
7073 if is_grouping_function(name) {
7074 if over.is_some() {
7075 return Err(PlannerError::invalid_expression(
7076 "GROUPING cannot be used as a window function".to_string(),
7077 ));
7078 }
7079 return lower_grouping_call(args, context, expr.span);
7080 }
7081 if over.is_none() && is_aggregate_function(name) {
7082 if args.iter().any(typed_expr_contains_grouping)
7083 || filter.as_deref().is_some_and(typed_expr_contains_grouping)
7084 || order_by
7085 .iter()
7086 .any(|sort| typed_expr_contains_grouping(&sort.expr))
7087 {
7088 return Err(PlannerError::invalid_expression(
7089 "GROUPING cannot appear inside aggregate function arguments".to_string(),
7090 ));
7091 }
7092 return Ok(expr.clone());
7093 }
7094 TypedExprKind::FunctionCall {
7095 name: name.clone(),
7096 args: args.iter().map(rebuild).collect::<Result<Vec<_>, _>>()?,
7097 distinct: *distinct,
7098 star: *star,
7099 filter: filter.as_deref().map(rebuild_box).transpose()?,
7100 order_by: order_by
7101 .iter()
7102 .map(|sort| {
7103 Ok(SortExpr::new(
7104 rebuild(&sort.expr)?,
7105 sort.asc,
7106 sort.nulls_first,
7107 ))
7108 })
7109 .collect::<Result<Vec<_>, PlannerError>>()?,
7110 over: over
7111 .as_ref()
7112 .map(|window| {
7113 Ok(typed_expr::TypedWindowSpec {
7114 partition_by: window
7115 .partition_by
7116 .iter()
7117 .map(rebuild)
7118 .collect::<Result<Vec<_>, _>>()?,
7119 order_by: window
7120 .order_by
7121 .iter()
7122 .map(|sort| {
7123 Ok(SortExpr::new(
7124 rebuild(&sort.expr)?,
7125 sort.asc,
7126 sort.nulls_first,
7127 ))
7128 })
7129 .collect::<Result<Vec<_>, PlannerError>>()?,
7130 frame: window.frame.clone(),
7131 })
7132 })
7133 .transpose()
7134 .map_err(|error: PlannerError| error)?,
7135 }
7136 }
7137 TypedExprKind::BinaryOp { left, op, right } => TypedExprKind::BinaryOp {
7138 left: rebuild_box(left)?,
7139 op: *op,
7140 right: rebuild_box(right)?,
7141 },
7142 TypedExprKind::UnaryOp { op, operand } => TypedExprKind::UnaryOp {
7143 op: *op,
7144 operand: rebuild_box(operand)?,
7145 },
7146 TypedExprKind::Case {
7147 operand,
7148 branches,
7149 else_expr,
7150 } => TypedExprKind::Case {
7151 operand: operand.as_deref().map(rebuild_box).transpose()?,
7152 branches: branches
7153 .iter()
7154 .map(|branch| {
7155 Ok(TypedCaseWhen {
7156 when: rebuild(&branch.when)?,
7157 then: rebuild(&branch.then)?,
7158 })
7159 })
7160 .collect::<Result<Vec<_>, PlannerError>>()?,
7161 else_expr: else_expr.as_deref().map(rebuild_box).transpose()?,
7162 },
7163 TypedExprKind::Cast {
7164 expr: inner,
7165 target_type,
7166 } => TypedExprKind::Cast {
7167 expr: rebuild_box(inner)?,
7168 target_type: target_type.clone(),
7169 },
7170 TypedExprKind::TryCast {
7171 expr: inner,
7172 target_type,
7173 } => TypedExprKind::TryCast {
7174 expr: rebuild_box(inner)?,
7175 target_type: target_type.clone(),
7176 },
7177 TypedExprKind::Between {
7178 expr: inner,
7179 low,
7180 high,
7181 negated,
7182 } => TypedExprKind::Between {
7183 expr: rebuild_box(inner)?,
7184 low: rebuild_box(low)?,
7185 high: rebuild_box(high)?,
7186 negated: *negated,
7187 },
7188 TypedExprKind::Like {
7189 expr: inner,
7190 pattern,
7191 escape,
7192 negated,
7193 kind,
7194 } => TypedExprKind::Like {
7195 expr: rebuild_box(inner)?,
7196 pattern: rebuild_box(pattern)?,
7197 escape: escape.as_deref().map(rebuild_box).transpose()?,
7198 negated: *negated,
7199 kind: *kind,
7200 },
7201 TypedExprKind::InList {
7202 expr: inner,
7203 list,
7204 negated,
7205 } => TypedExprKind::InList {
7206 expr: rebuild_box(inner)?,
7207 list: list.iter().map(rebuild).collect::<Result<Vec<_>, _>>()?,
7208 negated: *negated,
7209 },
7210 TypedExprKind::IsNull {
7211 expr: inner,
7212 negated,
7213 } => TypedExprKind::IsNull {
7214 expr: rebuild_box(inner)?,
7215 negated: *negated,
7216 },
7217 TypedExprKind::InSubquery {
7218 expr: inner,
7219 subquery,
7220 negated,
7221 } => TypedExprKind::InSubquery {
7222 expr: rebuild_box(inner)?,
7223 subquery: subquery.clone(),
7224 negated: *negated,
7225 },
7226 TypedExprKind::Quantified {
7227 expr: inner,
7228 op,
7229 quantifier,
7230 subquery,
7231 } => TypedExprKind::Quantified {
7232 expr: rebuild_box(inner)?,
7233 op: *op,
7234 quantifier: *quantifier,
7235 subquery: subquery.clone(),
7236 },
7237 TypedExprKind::Literal(_)
7238 | TypedExprKind::VectorLiteral(_)
7239 | TypedExprKind::ColumnRef { .. }
7240 | TypedExprKind::ScalarSubquery(_)
7241 | TypedExprKind::Exists { .. } => return Ok(expr.clone()),
7242 };
7243 Ok(TypedExpr {
7244 kind,
7245 resolved_type: expr.resolved_type.clone(),
7246 span: expr.span,
7247 })
7248}
7249
7250fn build_aggregate_schema(
7251 group_keys: &[TypedExpr],
7252 aggregates: &[AggregateExpr],
7253) -> Vec<ColumnMetadata> {
7254 let mut schema = Vec::new();
7255 for (idx, key) in group_keys.iter().enumerate() {
7256 let name = match &key.kind {
7257 TypedExprKind::ColumnRef { column, .. } => column.clone(),
7258 _ => format!("group_{idx}"),
7259 };
7260 schema.push(ColumnMetadata::new(name, key.resolved_type.clone()));
7261 }
7262 for (idx, agg) in aggregates.iter().enumerate() {
7263 let name = match &agg.function {
7264 AggregateFunction::Count => format!("count_{idx}"),
7265 AggregateFunction::Sum => format!("sum_{idx}"),
7266 AggregateFunction::Total => format!("total_{idx}"),
7267 AggregateFunction::Avg => format!("avg_{idx}"),
7268 AggregateFunction::Min => format!("min_{idx}"),
7269 AggregateFunction::Max => format!("max_{idx}"),
7270 AggregateFunction::GroupConcat { .. } => format!("group_concat_{idx}"),
7271 AggregateFunction::StringAgg { .. } => format!("string_agg_{idx}"),
7272 AggregateFunction::JsonGroupArray => format!("json_group_array_{idx}"),
7273 AggregateFunction::JsonGroupObject => format!("json_group_object_{idx}"),
7274 AggregateFunction::JsonbAgg => format!("jsonb_agg_{idx}"),
7275 AggregateFunction::JsonbObjectAgg => format!("jsonb_object_agg_{idx}"),
7276 AggregateFunction::PercentileDisc { .. } => format!("percentile_disc_{idx}"),
7277 _ => format!("aggregate_{idx}"),
7278 };
7279 schema.push(ColumnMetadata::new(name, agg.result_type.clone()));
7280 }
7281 schema
7282}
7283
7284fn rewrite_expr_with_maps(
7285 expr: &TypedExpr,
7286 group_key_map: &HashMap<String, usize>,
7287 aggregate_map: &HashMap<AggregateSignature, usize>,
7288 output_names: &[String],
7289) -> Result<TypedExpr, PlannerError> {
7290 let group_key_count = output_names.len().saturating_sub(aggregate_map.len());
7291 let key = expr_key(expr);
7292 if let Some(idx) = group_key_map.get(&key) {
7293 return Ok(make_output_column_ref(
7294 *idx,
7295 output_names,
7296 expr.resolved_type.clone(),
7297 expr.span,
7298 ));
7299 }
7300
7301 match &expr.kind {
7302 TypedExprKind::FunctionCall {
7303 name,
7304 args,
7305 distinct,
7306 star,
7307 filter,
7308 order_by,
7309 over: None,
7310 } if is_aggregate_function(name) => {
7311 let lower = name.to_ascii_lowercase();
7312 let is_percentile = matches!(lower.as_str(), "percentile_disc" | "percentile_cont");
7313 let separator = if name.eq_ignore_ascii_case("group_concat") && args.len() == 2 {
7314 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
7315 Some(value.clone())
7316 } else {
7317 return Err(PlannerError::invalid_expression(
7318 "GROUP_CONCAT separator must be a string literal".to_string(),
7319 ));
7320 }
7321 } else if name.eq_ignore_ascii_case("string_agg") && args.len() == 2 {
7322 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
7323 Some(value.clone())
7324 } else {
7325 return Err(PlannerError::invalid_expression(
7326 "STRING_AGG separator must be a string literal".to_string(),
7327 ));
7328 }
7329 } else if is_percentile && args.len() == 1 {
7330 Some(format!(
7331 "{:?}",
7332 type_checker::percentile_fraction_named(&lower, &args[0])?
7333 ))
7334 } else if lower == "quantile_cont" && args.len() == 2 {
7335 Some(format!(
7336 "{:?}",
7337 type_checker::percentile_fraction_named(&lower, &args[1])?
7338 ))
7339 } else {
7340 None
7341 };
7342 let signature = AggregateSignature {
7343 name: canonical_aggregate_name(name),
7344 distinct: *distinct,
7345 star: *star,
7346 arg_key: if is_percentile {
7347 None
7348 } else {
7349 args.first().map(expr_key)
7350 },
7351 extra_arg_keys: if matches!(
7352 lower.as_str(),
7353 "group_concat"
7354 | "string_agg"
7355 | "percentile_disc"
7356 | "percentile_cont"
7357 | "quantile_cont"
7358 ) {
7359 Vec::new()
7360 } else {
7361 args.iter().skip(1).map(expr_key).collect()
7362 },
7363 separator,
7364 filter_key: filter.as_deref().map(expr_key),
7365 order_key: if is_order_sensitive_aggregate(name) {
7366 sort_exprs_key(order_by)
7367 } else {
7368 None
7369 },
7370 };
7371 let idx = aggregate_map.get(&signature).ok_or_else(|| {
7372 PlannerError::invalid_expression(
7373 "aggregate in expression is not part of plan".to_string(),
7374 )
7375 })?;
7376 let output_index = group_key_count + idx;
7377 Ok(make_output_column_ref(
7378 output_index,
7379 output_names,
7380 expr.resolved_type.clone(),
7381 expr.span,
7382 ))
7383 }
7384 TypedExprKind::FunctionCall {
7385 name,
7386 args,
7387 distinct,
7388 star,
7389 filter,
7390 order_by,
7391 over,
7392 } => {
7393 if over.is_none() && (*distinct || *star) {
7394 return Err(PlannerError::invalid_expression(
7395 "DISTINCT/STAR modifiers are only supported for aggregates".to_string(),
7396 ));
7397 }
7398 if filter.is_some() || !order_by.is_empty() {
7399 return Err(PlannerError::invalid_expression(
7403 "aggregate in expression is not part of plan".to_string(),
7404 ));
7405 }
7406 let mut rewritten_args = Vec::with_capacity(args.len());
7407 for arg in args {
7408 rewritten_args.push(rewrite_expr_with_maps(
7409 arg,
7410 group_key_map,
7411 aggregate_map,
7412 output_names,
7413 )?);
7414 }
7415 let over = over
7416 .as_ref()
7417 .map(|window| {
7418 let partition_by = window
7419 .partition_by
7420 .iter()
7421 .map(|expr| {
7422 rewrite_expr_with_maps(expr, group_key_map, aggregate_map, output_names)
7423 })
7424 .collect::<Result<Vec<_>, PlannerError>>()?;
7425 let order_by = window
7426 .order_by
7427 .iter()
7428 .map(|sort| {
7429 Ok(SortExpr::new(
7430 rewrite_expr_with_maps(
7431 &sort.expr,
7432 group_key_map,
7433 aggregate_map,
7434 output_names,
7435 )?,
7436 sort.asc,
7437 sort.nulls_first,
7438 ))
7439 })
7440 .collect::<Result<Vec<_>, PlannerError>>()?;
7441 Ok(crate::planner::typed_expr::TypedWindowSpec {
7442 partition_by,
7443 order_by,
7444 frame: window.frame.clone(),
7445 })
7446 })
7447 .transpose()?;
7448 Ok(TypedExpr {
7449 kind: TypedExprKind::FunctionCall {
7450 name: name.clone(),
7451 args: rewritten_args,
7452 distinct: *distinct,
7453 star: *star,
7454 filter: None,
7455 order_by: Vec::new(),
7456 over,
7457 },
7458 resolved_type: expr.resolved_type.clone(),
7459 span: expr.span,
7460 })
7461 }
7462 TypedExprKind::BinaryOp { left, op, right } => {
7463 let left = rewrite_expr_with_maps(left, group_key_map, aggregate_map, output_names)?;
7464 let right = rewrite_expr_with_maps(right, group_key_map, aggregate_map, output_names)?;
7465 Ok(TypedExpr {
7466 kind: TypedExprKind::BinaryOp {
7467 left: Box::new(left),
7468 op: *op,
7469 right: Box::new(right),
7470 },
7471 resolved_type: expr.resolved_type.clone(),
7472 span: expr.span,
7473 })
7474 }
7475 TypedExprKind::UnaryOp { op, operand } => {
7476 let operand =
7477 rewrite_expr_with_maps(operand, group_key_map, aggregate_map, output_names)?;
7478 Ok(TypedExpr {
7479 kind: TypedExprKind::UnaryOp {
7480 op: *op,
7481 operand: Box::new(operand),
7482 },
7483 resolved_type: expr.resolved_type.clone(),
7484 span: expr.span,
7485 })
7486 }
7487 TypedExprKind::Case {
7488 operand,
7489 branches,
7490 else_expr,
7491 } => {
7492 let operand = operand
7493 .as_deref()
7494 .map(|operand| {
7495 rewrite_expr_with_maps(operand, group_key_map, aggregate_map, output_names)
7496 .map(Box::new)
7497 })
7498 .transpose()?;
7499 let mut rewritten_branches = Vec::with_capacity(branches.len());
7500 for branch in branches {
7501 rewritten_branches.push(TypedCaseWhen {
7502 when: rewrite_expr_with_maps(
7503 &branch.when,
7504 group_key_map,
7505 aggregate_map,
7506 output_names,
7507 )?,
7508 then: rewrite_expr_with_maps(
7509 &branch.then,
7510 group_key_map,
7511 aggregate_map,
7512 output_names,
7513 )?,
7514 });
7515 }
7516 let else_expr = else_expr
7517 .as_deref()
7518 .map(|else_expr| {
7519 rewrite_expr_with_maps(else_expr, group_key_map, aggregate_map, output_names)
7520 .map(Box::new)
7521 })
7522 .transpose()?;
7523 Ok(TypedExpr {
7524 kind: TypedExprKind::Case {
7525 operand,
7526 branches: rewritten_branches,
7527 else_expr,
7528 },
7529 resolved_type: expr.resolved_type.clone(),
7530 span: expr.span,
7531 })
7532 }
7533 TypedExprKind::Between {
7534 expr: inner,
7535 low,
7536 high,
7537 negated,
7538 } => {
7539 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7540 let low = rewrite_expr_with_maps(low, group_key_map, aggregate_map, output_names)?;
7541 let high = rewrite_expr_with_maps(high, group_key_map, aggregate_map, output_names)?;
7542 Ok(TypedExpr {
7543 kind: TypedExprKind::Between {
7544 expr: Box::new(inner),
7545 low: Box::new(low),
7546 high: Box::new(high),
7547 negated: *negated,
7548 },
7549 resolved_type: expr.resolved_type.clone(),
7550 span: expr.span,
7551 })
7552 }
7553 TypedExprKind::Like {
7554 expr: inner,
7555 pattern,
7556 escape,
7557 negated,
7558 kind,
7559 } => {
7560 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7561 let pattern =
7562 rewrite_expr_with_maps(pattern, group_key_map, aggregate_map, output_names)?;
7563 let escape = if let Some(esc) = escape {
7564 Some(Box::new(rewrite_expr_with_maps(
7565 esc,
7566 group_key_map,
7567 aggregate_map,
7568 output_names,
7569 )?))
7570 } else {
7571 None
7572 };
7573 Ok(TypedExpr {
7574 kind: TypedExprKind::Like {
7575 expr: Box::new(inner),
7576 pattern: Box::new(pattern),
7577 escape,
7578 negated: *negated,
7579 kind: *kind,
7580 },
7581 resolved_type: expr.resolved_type.clone(),
7582 span: expr.span,
7583 })
7584 }
7585 TypedExprKind::InList {
7586 expr: inner,
7587 list,
7588 negated,
7589 } => {
7590 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7591 let mut rewritten_list = Vec::with_capacity(list.len());
7592 for item in list {
7593 rewritten_list.push(rewrite_expr_with_maps(
7594 item,
7595 group_key_map,
7596 aggregate_map,
7597 output_names,
7598 )?);
7599 }
7600 Ok(TypedExpr {
7601 kind: TypedExprKind::InList {
7602 expr: Box::new(inner),
7603 list: rewritten_list,
7604 negated: *negated,
7605 },
7606 resolved_type: expr.resolved_type.clone(),
7607 span: expr.span,
7608 })
7609 }
7610 TypedExprKind::IsNull {
7611 expr: inner,
7612 negated,
7613 } => {
7614 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7615 Ok(TypedExpr {
7616 kind: TypedExprKind::IsNull {
7617 expr: Box::new(inner),
7618 negated: *negated,
7619 },
7620 resolved_type: expr.resolved_type.clone(),
7621 span: expr.span,
7622 })
7623 }
7624 TypedExprKind::Literal(_) | TypedExprKind::VectorLiteral(_) => Ok(expr.clone()),
7625 TypedExprKind::ColumnRef { table, .. } if table == "__agg__" => Ok(expr.clone()),
7628 TypedExprKind::ColumnRef { .. } => Err(PlannerError::invalid_expression(
7629 "column reference must appear in GROUP BY or be aggregated".to_string(),
7630 )),
7631 TypedExprKind::Cast {
7632 expr: inner,
7633 target_type,
7634 } => {
7635 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7636 Ok(TypedExpr {
7637 kind: TypedExprKind::Cast {
7638 expr: Box::new(inner),
7639 target_type: target_type.clone(),
7640 },
7641 resolved_type: expr.resolved_type.clone(),
7642 span: expr.span,
7643 })
7644 }
7645 TypedExprKind::TryCast {
7646 expr: inner,
7647 target_type,
7648 } => {
7649 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
7650 Ok(TypedExpr {
7651 kind: TypedExprKind::TryCast {
7652 expr: Box::new(inner),
7653 target_type: target_type.clone(),
7654 },
7655 resolved_type: expr.resolved_type.clone(),
7656 span: expr.span,
7657 })
7658 }
7659 TypedExprKind::ScalarSubquery(_)
7660 | TypedExprKind::InSubquery { .. }
7661 | TypedExprKind::Exists { .. }
7662 | TypedExprKind::Quantified { .. } => Ok(expr.clone()),
7663 }
7664}
7665
7666fn make_output_column_ref(
7667 index: usize,
7668 output_names: &[String],
7669 resolved_type: ResolvedType,
7670 span: crate::ast::Span,
7671) -> TypedExpr {
7672 let name = output_names
7673 .get(index)
7674 .cloned()
7675 .unwrap_or_else(|| format!("col_{index}"));
7676 TypedExpr::column_ref("__agg__".to_string(), name, index, resolved_type, span)
7677}