1pub mod aggregate_expr;
13mod error;
14pub mod knn_optimizer;
15pub mod logical_plan;
16pub mod name_resolver;
17pub mod type_checker;
18pub mod typed_expr;
19pub mod types;
20
21#[cfg(test)]
22mod planner_tests;
23
24pub use aggregate_expr::{AggregateExpr, AggregateFunction};
25pub use error::PlannerError;
26pub use knn_optimizer::{KnnPattern, SortDirection, detect_knn_pattern};
27pub use logical_plan::{JoinType, LogicalPlan, SetOperator, WindowExpr, WindowFunction};
28pub use name_resolver::{NameResolver, ResolvedColumn};
29pub use type_checker::{ScopedTable, TypeChecker};
30pub use typed_expr::{
31 ProjectedColumn, Projection, SortExpr, TypedAssignment, TypedCaseWhen, TypedExpr, TypedExprKind,
32};
33pub use types::ResolvedType;
34
35use crate::ast::ddl::{
36 ColumnConstraint, ColumnDef, CreateIndex, CreateTable, DropIndex, DropTable,
37};
38use crate::ast::dml::{
39 Delete, FromItem, Insert, InsertSource, LITERAL_TABLE, OrderByExpr, Select, SelectItem,
40 SetOperator as AstSetOperator, Update,
41};
42use crate::ast::expr::Literal;
43use crate::ast::{PragmaValue, Spanned, Statement, StatementKind};
44use crate::catalog::{Catalog, ColumnMetadata, IndexMetadata, TableMetadata};
45use crate::{AlopexDialect, DataSourceFormat, Parser, SqlError, TableType};
46use std::collections::{HashMap, HashSet};
47
48#[derive(Clone)]
49struct PlannedRelation {
50 plan: LogicalPlan,
51 schema: Vec<ColumnMetadata>,
52 scope: Vec<ScopedTable>,
53}
54
55type CtePlans = HashMap<String, PlannedRelation>;
56
57#[derive(Debug, Clone)]
63pub struct PlannedStatement {
64 pub plan: LogicalPlan,
66 pub routing_input: RoutingInput,
68}
69
70impl PlannedStatement {
71 pub fn statement_kind(&self) -> &StatementKind {
73 &self.routing_input.statement_kind
74 }
75
76 pub fn table_references(&self) -> &[TableReference] {
78 &self.routing_input.table_references
79 }
80
81 pub fn diagnostics(&self) -> &[PlanningDiagnostic] {
84 &self.routing_input.diagnostics
85 }
86}
87
88#[derive(Debug, Clone)]
90pub struct RoutingInput {
91 pub statement_kind: StatementKind,
94 pub table_references: Vec<TableReference>,
96 pub diagnostics: Vec<PlanningDiagnostic>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct TableReference {
103 pub table_name: String,
105 pub access: TableReferenceAccess,
107 pub source: TableReferenceSource,
109}
110
111impl TableReference {
112 pub fn new(
113 table_name: impl Into<String>,
114 access: TableReferenceAccess,
115 source: TableReferenceSource,
116 ) -> Self {
117 Self {
118 table_name: table_name.into(),
119 access,
120 source,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum TableReferenceAccess {
128 Read,
130 Write,
132 Create,
134 Drop,
136 Metadata,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum TableReferenceSource {
143 TopLevelPlanTableName,
145 LogicalPlanScan,
147 LogicalPlanMutationTarget,
149 LogicalPlanDdlTarget,
151 LogicalPlanIndexTarget,
153 TypedExprSubquery,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum PlanningDiagnosticSeverity {
160 Info,
161 Warning,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct PlanningDiagnostic {
167 pub code: &'static str,
169 pub severity: PlanningDiagnosticSeverity,
171 pub message: String,
173}
174
175impl PlanningDiagnostic {
176 pub fn info(code: &'static str, message: impl Into<String>) -> Self {
177 Self {
178 code,
179 severity: PlanningDiagnosticSeverity::Info,
180 message: message.into(),
181 }
182 }
183
184 pub fn warning(code: &'static str, message: impl Into<String>) -> Self {
185 Self {
186 code,
187 severity: PlanningDiagnosticSeverity::Warning,
188 message: message.into(),
189 }
190 }
191}
192
193pub fn plan_sql_for_routing<C: Catalog + ?Sized>(
195 catalog: &C,
196 sql: &str,
197) -> Result<Vec<PlannedStatement>, SqlError> {
198 let statements = Parser::parse_sql(&AlopexDialect, sql).map_err(SqlError::from)?;
199 statements
200 .iter()
201 .map(|statement| plan_statement_for_routing(catalog, statement).map_err(SqlError::from))
202 .collect()
203}
204
205pub fn plan_statement_for_routing<C: Catalog + ?Sized>(
207 catalog: &C,
208 statement: &Statement,
209) -> Result<PlannedStatement, PlannerError> {
210 let planner = Planner::new(catalog);
211 let plan = planner.plan(statement)?;
212 let routing_input = routing_input_for_plan(statement, &plan)?;
213 Ok(PlannedStatement {
214 plan,
215 routing_input,
216 })
217}
218
219fn routing_input_for_plan(
220 statement: &Statement,
221 plan: &LogicalPlan,
222) -> Result<RoutingInput, PlannerError> {
223 let mut diagnostics = Vec::new();
224 let extractor = TableReferenceExtractor::new();
225 let table_references = extractor.extract_from_logical_plan(
226 plan,
227 table_reference_access(statement)?,
228 &mut diagnostics,
229 );
230
231 Ok(RoutingInput {
232 statement_kind: statement.kind.clone(),
233 table_references,
234 diagnostics,
235 })
236}
237
238#[derive(Debug, Default, Clone, Copy)]
240pub struct TableReferenceExtractor;
241
242impl TableReferenceExtractor {
243 pub fn new() -> Self {
244 Self
245 }
246
247 pub fn extract_from_logical_plan(
250 &self,
251 plan: &LogicalPlan,
252 root_access: TableReferenceAccess,
253 diagnostics: &mut Vec<PlanningDiagnostic>,
254 ) -> Vec<TableReference> {
255 let mut references = Vec::new();
256 self.extract_plan(
257 plan,
258 root_access,
259 TableReferenceSource::LogicalPlanScan,
260 diagnostics,
261 &mut references,
262 );
263 if references.is_empty() {
264 diagnostics.push(PlanningDiagnostic::info(
265 "ALOPEX-PLAN-ROUTE-001",
266 "statement has no physical table reference",
267 ));
268 }
269 references
270 }
271
272 pub fn extract_from_subquery_context(
274 &self,
275 plan: &LogicalPlan,
276 diagnostics: &mut Vec<PlanningDiagnostic>,
277 ) -> Vec<TableReference> {
278 let mut references = Vec::new();
279 self.extract_plan(
280 plan,
281 TableReferenceAccess::Read,
282 TableReferenceSource::TypedExprSubquery,
283 diagnostics,
284 &mut references,
285 );
286 references
287 }
288
289 fn extract_plan(
290 &self,
291 plan: &LogicalPlan,
292 root_access: TableReferenceAccess,
293 scan_source: TableReferenceSource,
294 diagnostics: &mut Vec<PlanningDiagnostic>,
295 references: &mut Vec<TableReference>,
296 ) {
297 match plan {
298 LogicalPlan::Scan { table, projection } => {
299 if table != LITERAL_TABLE {
300 push_table_reference(
301 references,
302 table,
303 TableReferenceAccess::Read,
304 scan_source,
305 );
306 }
307 self.extract_projection(projection, diagnostics, references);
308 }
309 LogicalPlan::Filter { input, predicate } => {
310 self.extract_plan(input, root_access, scan_source, diagnostics, references);
311 self.extract_typed_expr(predicate, diagnostics, references);
312 }
313 LogicalPlan::Project { input, projection } => {
314 self.extract_plan(input, root_access, scan_source, diagnostics, references);
315 self.extract_projection(projection, diagnostics, references);
316 }
317 LogicalPlan::Join {
318 left,
319 right,
320 condition,
321 ..
322 } => {
323 self.extract_plan(
324 left,
325 TableReferenceAccess::Read,
326 scan_source,
327 diagnostics,
328 references,
329 );
330 self.extract_plan(
331 right,
332 TableReferenceAccess::Read,
333 scan_source,
334 diagnostics,
335 references,
336 );
337 if let Some(condition) = condition {
338 self.extract_typed_expr(condition, diagnostics, references);
339 }
340 }
341 LogicalPlan::Aggregate {
342 input,
343 group_keys,
344 aggregates,
345 having,
346 projection,
347 } => {
348 self.extract_plan(input, root_access, scan_source, diagnostics, references);
349 for expr in group_keys {
350 self.extract_typed_expr(expr, diagnostics, references);
351 }
352 for aggregate in aggregates {
353 if let Some(arg) = &aggregate.arg {
354 self.extract_typed_expr(arg, diagnostics, references);
355 }
356 }
357 if let Some(having) = having {
358 self.extract_typed_expr(having, diagnostics, references);
359 }
360 self.extract_projection(projection, diagnostics, references);
361 }
362 LogicalPlan::Window { input, windows } => {
363 self.extract_plan(input, root_access, scan_source, diagnostics, references);
364 for window in windows {
365 for expr in &window.partition_by {
366 self.extract_typed_expr(expr, diagnostics, references);
367 }
368 for sort_expr in &window.order_by {
369 self.extract_typed_expr(&sort_expr.expr, diagnostics, references);
370 }
371 if let WindowFunction::Aggregate(aggregate) = &window.function
372 && let Some(arg) = &aggregate.arg
373 {
374 self.extract_typed_expr(arg, diagnostics, references);
375 }
376 }
377 }
378 LogicalPlan::SetOperation { left, right, .. } => {
379 self.extract_plan(left, root_access, scan_source, diagnostics, references);
380 self.extract_plan(right, root_access, scan_source, diagnostics, references);
381 }
382 LogicalPlan::Sort { input, order_by } => {
383 self.extract_plan(input, root_access, scan_source, diagnostics, references);
384 for sort_expr in order_by {
385 self.extract_typed_expr(&sort_expr.expr, diagnostics, references);
386 }
387 }
388 LogicalPlan::Limit { input, .. } => {
389 self.extract_plan(input, root_access, scan_source, diagnostics, references);
390 }
391 LogicalPlan::Insert { table, values, .. } => {
392 push_table_reference(
393 references,
394 table,
395 root_access,
396 TableReferenceSource::LogicalPlanMutationTarget,
397 );
398 for row in values {
399 for value in row {
400 self.extract_typed_expr(value, diagnostics, references);
401 }
402 }
403 }
404 LogicalPlan::InsertSelect { table, source, .. } => {
405 push_table_reference(
406 references,
407 table,
408 root_access,
409 TableReferenceSource::LogicalPlanMutationTarget,
410 );
411 self.extract_plan(
412 source,
413 TableReferenceAccess::Read,
414 scan_source,
415 diagnostics,
416 references,
417 );
418 }
419 LogicalPlan::Update {
420 table,
421 assignments,
422 filter,
423 } => {
424 push_table_reference(
425 references,
426 table,
427 root_access,
428 TableReferenceSource::LogicalPlanMutationTarget,
429 );
430 for assignment in assignments {
431 self.extract_typed_expr(&assignment.value, diagnostics, references);
432 }
433 if let Some(filter) = filter {
434 self.extract_typed_expr(filter, diagnostics, references);
435 }
436 }
437 LogicalPlan::Delete { table, filter } => {
438 push_table_reference(
439 references,
440 table,
441 root_access,
442 TableReferenceSource::LogicalPlanMutationTarget,
443 );
444 if let Some(filter) = filter {
445 self.extract_typed_expr(filter, diagnostics, references);
446 }
447 }
448 LogicalPlan::CreateTable { table, .. } => push_table_reference(
449 references,
450 &table.name,
451 root_access,
452 TableReferenceSource::LogicalPlanDdlTarget,
453 ),
454 LogicalPlan::DropTable { name, .. } => push_table_reference(
455 references,
456 name,
457 root_access,
458 TableReferenceSource::LogicalPlanDdlTarget,
459 ),
460 LogicalPlan::CreateIndex { index, .. } => push_table_reference(
461 references,
462 &index.table,
463 root_access,
464 TableReferenceSource::LogicalPlanIndexTarget,
465 ),
466 LogicalPlan::DropIndex { name, .. } => diagnostics.push(PlanningDiagnostic::warning(
467 "ALOPEX-PLAN-ROUTE-003",
468 format!(
469 "DROP INDEX {name} does not expose a target table in the current logical plan"
470 ),
471 )),
472 LogicalPlan::Pragma { .. } => {}
473 }
474 }
475
476 fn extract_projection(
477 &self,
478 projection: &Projection,
479 diagnostics: &mut Vec<PlanningDiagnostic>,
480 references: &mut Vec<TableReference>,
481 ) {
482 if let Projection::Columns(columns) = projection {
483 for column in columns {
484 self.extract_typed_expr(&column.expr, diagnostics, references);
485 }
486 }
487 }
488
489 fn extract_typed_expr(
490 &self,
491 expr: &TypedExpr,
492 diagnostics: &mut Vec<PlanningDiagnostic>,
493 references: &mut Vec<TableReference>,
494 ) {
495 match &expr.kind {
496 TypedExprKind::Literal(_)
497 | TypedExprKind::ColumnRef { .. }
498 | TypedExprKind::VectorLiteral(_) => {}
499 TypedExprKind::BinaryOp { left, right, .. } => {
500 self.extract_typed_expr(left, diagnostics, references);
501 self.extract_typed_expr(right, diagnostics, references);
502 }
503 TypedExprKind::UnaryOp { operand, .. }
504 | TypedExprKind::Cast { expr: operand, .. }
505 | TypedExprKind::IsNull { expr: operand, .. } => {
506 self.extract_typed_expr(operand, diagnostics, references);
507 }
508 TypedExprKind::Case {
509 operand,
510 branches,
511 else_expr,
512 } => {
513 if let Some(operand) = operand {
514 self.extract_typed_expr(operand, diagnostics, references);
515 }
516 for branch in branches {
517 self.extract_typed_expr(&branch.when, diagnostics, references);
518 self.extract_typed_expr(&branch.then, diagnostics, references);
519 }
520 if let Some(else_expr) = else_expr {
521 self.extract_typed_expr(else_expr, diagnostics, references);
522 }
523 }
524 TypedExprKind::FunctionCall { args, .. } => {
525 for arg in args {
526 self.extract_typed_expr(arg, diagnostics, references);
527 }
528 }
529 TypedExprKind::Between {
530 expr, low, high, ..
531 } => {
532 self.extract_typed_expr(expr, diagnostics, references);
533 self.extract_typed_expr(low, diagnostics, references);
534 self.extract_typed_expr(high, diagnostics, references);
535 }
536 TypedExprKind::Like {
537 expr,
538 pattern,
539 escape,
540 ..
541 } => {
542 self.extract_typed_expr(expr, diagnostics, references);
543 self.extract_typed_expr(pattern, diagnostics, references);
544 if let Some(escape) = escape {
545 self.extract_typed_expr(escape, diagnostics, references);
546 }
547 }
548 TypedExprKind::InList { expr, list, .. } => {
549 self.extract_typed_expr(expr, diagnostics, references);
550 for item in list {
551 self.extract_typed_expr(item, diagnostics, references);
552 }
553 }
554 TypedExprKind::ScalarSubquery(subquery) => self.extract_plan(
555 subquery,
556 TableReferenceAccess::Read,
557 TableReferenceSource::TypedExprSubquery,
558 diagnostics,
559 references,
560 ),
561 TypedExprKind::InSubquery { expr, subquery, .. } => {
562 self.extract_typed_expr(expr, diagnostics, references);
563 self.extract_plan(
564 subquery,
565 TableReferenceAccess::Read,
566 TableReferenceSource::TypedExprSubquery,
567 diagnostics,
568 references,
569 );
570 }
571 TypedExprKind::Exists { subquery, .. } => self.extract_plan(
572 subquery,
573 TableReferenceAccess::Read,
574 TableReferenceSource::TypedExprSubquery,
575 diagnostics,
576 references,
577 ),
578 TypedExprKind::Quantified { expr, subquery, .. } => {
579 self.extract_typed_expr(expr, diagnostics, references);
580 self.extract_plan(
581 subquery,
582 TableReferenceAccess::Read,
583 TableReferenceSource::TypedExprSubquery,
584 diagnostics,
585 references,
586 );
587 }
588 }
589 }
590}
591
592fn push_table_reference(
593 references: &mut Vec<TableReference>,
594 table_name: &str,
595 access: TableReferenceAccess,
596 source: TableReferenceSource,
597) {
598 if !references.iter().any(|reference| {
599 reference.table_name == table_name
600 && reference.access == access
601 && reference.source == source
602 }) {
603 references.push(TableReference::new(table_name, access, source));
604 }
605}
606
607#[derive(Debug)]
608enum GenericHostStatement<'a> {
609 CreateTable(&'a CreateTable),
610 DropTable(&'a DropTable),
611 CreateIndex(&'a CreateIndex),
612 DropIndex(&'a DropIndex),
613 Pragma {
614 name: &'a str,
615 value: &'a Option<PragmaValue>,
616 },
617 Select(&'a Select),
618 Insert(&'a Insert),
619 Update(&'a Update),
620 Delete(&'a Delete),
621 Unsupported,
622}
623
624fn classify_generic_host_statement(statement_kind: &StatementKind) -> GenericHostStatement<'_> {
625 #[allow(unreachable_patterns)]
628 match statement_kind {
629 StatementKind::CreateTable(statement) => GenericHostStatement::CreateTable(statement),
630 StatementKind::DropTable(statement) => GenericHostStatement::DropTable(statement),
631 StatementKind::CreateIndex(statement) => GenericHostStatement::CreateIndex(statement),
632 StatementKind::DropIndex(statement) => GenericHostStatement::DropIndex(statement),
633 StatementKind::Pragma { name, value } => GenericHostStatement::Pragma { name, value },
634 StatementKind::Select(statement) => GenericHostStatement::Select(statement),
635 StatementKind::Insert(statement) => GenericHostStatement::Insert(statement),
636 StatementKind::Update(statement) => GenericHostStatement::Update(statement),
637 StatementKind::Delete(statement) => GenericHostStatement::Delete(statement),
638 _ => GenericHostStatement::Unsupported,
639 }
640}
641
642fn unsupported_generic_statement(statement: &Statement) -> PlannerError {
643 PlannerError::unsupported_feature(
644 "statement kind for the generic SQL planner",
645 "a statement-specific planner",
646 statement.span,
647 )
648}
649
650fn table_reference_access(statement: &Statement) -> Result<TableReferenceAccess, PlannerError> {
651 table_reference_access_for_classified(
652 statement,
653 classify_generic_host_statement(&statement.kind),
654 )
655}
656
657fn table_reference_access_for_classified(
658 statement: &Statement,
659 classified: GenericHostStatement<'_>,
660) -> Result<TableReferenceAccess, PlannerError> {
661 match classified {
662 GenericHostStatement::Select(_) => Ok(TableReferenceAccess::Read),
663 GenericHostStatement::Insert(_)
664 | GenericHostStatement::Update(_)
665 | GenericHostStatement::Delete(_) => Ok(TableReferenceAccess::Write),
666 GenericHostStatement::CreateTable(_) => Ok(TableReferenceAccess::Create),
667 GenericHostStatement::DropTable(_) => Ok(TableReferenceAccess::Drop),
668 GenericHostStatement::CreateIndex(_)
669 | GenericHostStatement::DropIndex(_)
670 | GenericHostStatement::Pragma { .. } => Ok(TableReferenceAccess::Metadata),
671 GenericHostStatement::Unsupported => Err(unsupported_generic_statement(statement)),
672 }
673}
674
675pub struct Planner<'a, C: Catalog + ?Sized> {
702 catalog: &'a C,
703 name_resolver: NameResolver<'a, C>,
704 type_checker: TypeChecker<'a, C>,
705}
706
707impl<'a, C: Catalog + ?Sized> Planner<'a, C> {
708 pub fn new(catalog: &'a C) -> Self {
710 Self {
711 catalog,
712 name_resolver: NameResolver::new(catalog),
713 type_checker: TypeChecker::new(catalog),
714 }
715 }
716
717 pub fn plan(&self, stmt: &Statement) -> Result<LogicalPlan, PlannerError> {
728 self.plan_classified_statement(stmt, classify_generic_host_statement(&stmt.kind))
729 }
730
731 fn plan_classified_statement(
732 &self,
733 stmt: &Statement,
734 classified: GenericHostStatement<'_>,
735 ) -> Result<LogicalPlan, PlannerError> {
736 match classified {
737 GenericHostStatement::CreateTable(statement) => self.plan_create_table(statement),
739 GenericHostStatement::DropTable(statement) => self.plan_drop_table(statement),
740 GenericHostStatement::CreateIndex(statement) => self.plan_create_index(statement),
741 GenericHostStatement::DropIndex(statement) => self.plan_drop_index(statement),
742 GenericHostStatement::Pragma { name, value } => self.plan_pragma(name, value),
743
744 GenericHostStatement::Select(statement) => self.plan_select(statement),
746 GenericHostStatement::Insert(statement) => self.plan_insert(statement),
747 GenericHostStatement::Update(statement) => self.plan_update(statement),
748 GenericHostStatement::Delete(statement) => self.plan_delete(statement),
749 GenericHostStatement::Unsupported => Err(unsupported_generic_statement(stmt)),
750 }
751 }
752
753 fn plan_pragma(
754 &self,
755 raw_name: &str,
756 value: &Option<PragmaValue>,
757 ) -> Result<LogicalPlan, PlannerError> {
758 let name = raw_name.to_ascii_lowercase();
759 if !matches!(name.as_str(), "cache_size" | "memory_limit" | "io_stats") {
760 return Err(PlannerError::InvalidPragma {
761 name,
762 reason: "supported names are cache_size, memory_limit, and io_stats".to_string(),
763 });
764 }
765 match name.as_str() {
766 "cache_size" => match value {
767 Some(PragmaValue::Int(v)) if *v > 0 => {}
768 Some(PragmaValue::Int(_)) => {
769 return Err(PlannerError::InvalidPragma {
770 name,
771 reason: "cache_size must be a positive page count".to_string(),
772 });
773 }
774 Some(PragmaValue::Text(_)) => {
775 return Err(PlannerError::InvalidPragma {
776 name,
777 reason: "cache_size requires an integer page count".to_string(),
778 });
779 }
780 None => {}
781 },
782 "memory_limit" => {
783 if let Some(PragmaValue::Int(v)) = value
784 && *v < 0
785 {
786 return Err(PlannerError::InvalidPragma {
787 name,
788 reason: "memory_limit cannot be negative".to_string(),
789 });
790 }
791 }
792 "io_stats" => {
793 if value.is_some() {
794 return Err(PlannerError::InvalidPragma {
795 name,
796 reason: "io_stats does not accept a value".to_string(),
797 });
798 }
799 }
800 _ => unreachable!(),
801 }
802 Ok(LogicalPlan::Pragma {
803 name,
804 value: value.clone(),
805 })
806 }
807
808 fn plan_create_table(&self, stmt: &CreateTable) -> Result<LogicalPlan, PlannerError> {
817 if !stmt.if_not_exists && self.catalog.table_exists(&stmt.name) {
819 return Err(PlannerError::table_already_exists(&stmt.name));
820 }
821
822 let columns: Vec<ColumnMetadata> = stmt
824 .columns
825 .iter()
826 .map(|col| self.convert_column_def(col))
827 .collect();
828
829 let primary_key = Self::extract_primary_key(stmt);
831
832 let mut table = TableMetadata::new(stmt.name.clone(), columns);
835 if let Some(pk) = primary_key {
836 table = table.with_primary_key(pk);
837 }
838 table.catalog_name = "default".to_string();
839 table.namespace_name = "default".to_string();
840 table.table_type = TableType::Managed;
841 table.data_source_format = DataSourceFormat::Alopex;
842 table.properties = HashMap::new();
843
844 Ok(LogicalPlan::CreateTable {
845 table,
846 if_not_exists: stmt.if_not_exists,
847 with_options: stmt
848 .with_options
849 .iter()
850 .map(|opt| (opt.key.clone(), opt.value.clone()))
851 .collect(),
852 })
853 }
854
855 fn convert_column_def(&self, col: &ColumnDef) -> ColumnMetadata {
857 let data_type = ResolvedType::from_ast(&col.data_type);
858 let mut meta = ColumnMetadata::new(col.name.clone(), data_type);
859
860 for constraint in &col.constraints {
862 meta = Self::apply_column_constraint(meta, constraint);
863 }
864
865 meta
866 }
867
868 fn apply_column_constraint(
870 mut meta: ColumnMetadata,
871 constraint: &ColumnConstraint,
872 ) -> ColumnMetadata {
873 match constraint {
874 ColumnConstraint::NotNull { .. } => {
875 meta.not_null = true;
876 }
877 ColumnConstraint::PrimaryKey { .. } => {
878 meta.primary_key = true;
879 meta.not_null = true; }
881 ColumnConstraint::Unique { .. } => {
882 meta.unique = true;
883 }
884 ColumnConstraint::Default { value: expr, .. } => {
885 meta.default = Some(expr.clone());
886 }
887 }
888 meta
889 }
890
891 fn extract_primary_key(stmt: &CreateTable) -> Option<Vec<String>> {
893 use crate::ast::ddl::TableConstraint;
894
895 if let Some(TableConstraint::PrimaryKey { columns, .. }) = stmt.constraints.first() {
899 return Some(columns.clone());
900 }
901
902 let pk_columns: Vec<String> = stmt
904 .columns
905 .iter()
906 .filter(|col| col.constraints.iter().any(Self::is_primary_key_constraint))
907 .map(|col| col.name.clone())
908 .collect();
909
910 if pk_columns.is_empty() {
911 None
912 } else {
913 Some(pk_columns)
914 }
915 }
916
917 fn is_primary_key_constraint(constraint: &ColumnConstraint) -> bool {
919 matches!(constraint, ColumnConstraint::PrimaryKey { .. })
920 }
921
922 fn plan_drop_table(&self, stmt: &DropTable) -> Result<LogicalPlan, PlannerError> {
926 if !stmt.if_exists && !self.table_exists_in_default(&stmt.name) {
928 return Err(PlannerError::TableNotFound {
929 name: stmt.name.clone(),
930 line: stmt.span.start.line,
931 column: stmt.span.start.column,
932 });
933 }
934
935 Ok(LogicalPlan::DropTable {
936 name: stmt.name.clone(),
937 if_exists: stmt.if_exists,
938 })
939 }
940
941 fn table_exists_in_default(&self, name: &str) -> bool {
942 match self.catalog.get_table(name) {
943 Some(table) => table.catalog_name == "default" && table.namespace_name == "default",
944 None => false,
945 }
946 }
947
948 fn plan_create_index(&self, stmt: &CreateIndex) -> Result<LogicalPlan, PlannerError> {
955 if !stmt.if_not_exists && self.catalog.index_exists(&stmt.name) {
957 return Err(PlannerError::index_already_exists(&stmt.name));
958 }
959
960 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
962
963 self.name_resolver
965 .resolve_column(table, &stmt.column, stmt.span)?;
966
967 let mut index = IndexMetadata::new(
971 0,
972 stmt.name.clone(),
973 stmt.table.clone(),
974 vec![stmt.column.clone()],
975 );
976
977 if let Some(method) = stmt.method {
978 index = index.with_method(method);
979 }
980
981 let options: Vec<(String, String)> = stmt
982 .options
983 .iter()
984 .map(|opt| (opt.key.clone(), opt.value.clone()))
985 .collect();
986 if !options.is_empty() {
987 index = index.with_options(options);
988 }
989
990 Ok(LogicalPlan::CreateIndex {
991 index,
992 if_not_exists: stmt.if_not_exists,
993 })
994 }
995
996 fn plan_drop_index(&self, stmt: &DropIndex) -> Result<LogicalPlan, PlannerError> {
1000 if !stmt.if_exists && !self.index_exists_in_default(&stmt.name) {
1002 return Err(PlannerError::index_not_found(&stmt.name));
1003 }
1004
1005 Ok(LogicalPlan::DropIndex {
1006 name: stmt.name.clone(),
1007 if_exists: stmt.if_exists,
1008 })
1009 }
1010
1011 fn index_exists_in_default(&self, name: &str) -> bool {
1012 match self.catalog.get_index(name) {
1013 Some(index) => index.catalog_name == "default" && index.namespace_name == "default",
1014 None => false,
1015 }
1016 }
1017
1018 fn plan_select(&self, stmt: &Select) -> Result<LogicalPlan, PlannerError> {
1027 self.plan_select_relation(stmt, &[], &CtePlans::new())
1028 .map(|relation| relation.plan)
1029 }
1030
1031 fn plan_ctes(
1032 &self,
1033 stmt: &Select,
1034 enclosing_ctes: &CtePlans,
1035 ) -> Result<CtePlans, PlannerError> {
1036 let Some(with) = &stmt.with else {
1037 return Ok(enclosing_ctes.clone());
1038 };
1039 if with.recursive {
1040 return Err(PlannerError::unsupported_feature(
1041 "recursive common table expressions",
1042 "a future version",
1043 with.span,
1044 ));
1045 }
1046
1047 let mut plans = enclosing_ctes.clone();
1048 let mut local_names = HashSet::new();
1049 for cte in &with.ctes {
1050 if !local_names.insert(cte.name.clone()) {
1051 return Err(PlannerError::invalid_expression(format!(
1052 "common table expression '{}' is defined more than once",
1053 cte.name
1054 )));
1055 }
1056 let StatementKind::Select(select) = &cte.query.kind else {
1057 return Err(PlannerError::unsupported_feature(
1058 "non-SELECT common table expression",
1059 "a future version",
1060 cte.span,
1061 ));
1062 };
1063 let relation = self.plan_select_relation(select, &[], &plans)?;
1064 plans.insert(cte.name.clone(), relation);
1065 }
1066 Ok(plans)
1067 }
1068
1069 fn plan_select_relation(
1070 &self,
1071 stmt: &Select,
1072 outer_scope: &[ScopedTable],
1073 enclosing_ctes: &CtePlans,
1074 ) -> Result<PlannedRelation, PlannerError> {
1075 let ctes = self.plan_ctes(stmt, enclosing_ctes)?;
1076 if !stmt.set_operations.is_empty() {
1077 let mut left_select = stmt.clone();
1078 left_select.with = None;
1079 left_select.set_operations.clear();
1080 left_select.order_by.clear();
1081 left_select.limit = None;
1082 left_select.offset = None;
1083 let mut relation = self.plan_select_relation(&left_select, outer_scope, &ctes)?;
1084
1085 for operation in &stmt.set_operations {
1086 let right = self.plan_select_relation(&operation.right, outer_scope, &ctes)?;
1087 if relation.schema.len() != right.schema.len() {
1088 return Err(PlannerError::set_operation_column_count_mismatch(
1089 relation.schema.len(),
1090 right.schema.len(),
1091 operation.span,
1092 ));
1093 }
1094 for (left_column, right_column) in relation.schema.iter().zip(&right.schema) {
1095 if left_column.data_type != right_column.data_type {
1096 return Err(PlannerError::type_mismatch(
1097 left_column.data_type.type_name(),
1098 right_column.data_type.type_name(),
1099 operation.span,
1100 ));
1101 }
1102 }
1103
1104 relation.plan = LogicalPlan::SetOperation {
1105 left: Box::new(relation.plan),
1106 right: Box::new(right.plan),
1107 operator: match operation.operator {
1108 AstSetOperator::Union => SetOperator::Union,
1109 AstSetOperator::Intersect => SetOperator::Intersect,
1110 AstSetOperator::Except => SetOperator::Except,
1111 },
1112 all: operation.all,
1113 };
1114 }
1115
1116 relation.scope = vec![ScopedTable::new(
1117 TableMetadata::new(LITERAL_TABLE, relation.schema.clone()),
1118 0,
1119 )];
1120 if !stmt.order_by.is_empty() {
1121 let order_by = self.build_sort_exprs_with_scope(
1125 &stmt.order_by,
1126 &relation.scope,
1127 &HashMap::new(),
1128 &ctes,
1129 )?;
1130 relation.plan = LogicalPlan::Sort {
1131 input: Box::new(relation.plan),
1132 order_by,
1133 };
1134 }
1135 if stmt.limit.is_some() || stmt.offset.is_some() {
1136 relation.plan = LogicalPlan::Limit {
1137 input: Box::new(relation.plan),
1138 limit: self.extract_limit_value(&stmt.limit, stmt.span)?,
1139 offset: self.extract_limit_value(&stmt.offset, stmt.span)?,
1140 };
1141 }
1142 return Ok(relation);
1143 }
1144
1145 let mut relation = self.plan_from_items(&stmt.from, stmt.span, outer_scope, &ctes)?;
1146 let expr_scope = relation
1147 .scope
1148 .iter()
1149 .cloned()
1150 .chain(offset_scope(outer_scope, relation.schema.len()))
1151 .collect::<Vec<_>>();
1152
1153 let has_group_by = stmt
1154 .group_by
1155 .as_ref()
1156 .is_some_and(|items| !items.is_empty());
1157 let has_aggregate = self.select_contains_aggregate(stmt);
1158 let has_window = select_contains_window(stmt);
1159 let distinct_only =
1160 stmt.distinct && !has_group_by && !has_aggregate && stmt.having.is_none();
1161
1162 if has_window && (has_group_by || has_aggregate || stmt.having.is_some() || stmt.distinct) {
1163 return Err(PlannerError::unsupported_feature(
1164 "window functions combined with GROUP BY, ordinary aggregates, HAVING, or DISTINCT",
1165 "future",
1166 stmt.span,
1167 ));
1168 }
1169
1170 let projection_aliases = collect_projection_aliases(&stmt.projection);
1174
1175 let final_projection = self.build_projection_with_scope(
1176 &stmt.projection,
1177 &relation.schema,
1178 &expr_scope,
1179 &ctes,
1180 )?;
1181 if !has_window {
1182 install_base_projection(&mut relation.plan, &final_projection);
1183 }
1184 let needs_project_boundary = !matches!(relation.plan, LogicalPlan::Scan { .. });
1185 let base_schema = relation.schema.clone();
1186 let mut plan = relation.plan;
1187
1188 if let Some(ref selection) = stmt.selection {
1190 let predicate = self.infer_expr_with_scope(selection, &expr_scope, &ctes)?;
1191
1192 if predicate.resolved_type != ResolvedType::Boolean {
1194 return Err(PlannerError::type_mismatch(
1195 "Boolean",
1196 predicate.resolved_type.to_string(),
1197 selection.span,
1198 ));
1199 }
1200
1201 plan = LogicalPlan::Filter {
1202 input: Box::new(plan),
1203 predicate,
1204 };
1205 }
1206
1207 if has_window {
1208 let mut windows = Vec::new();
1209 let mut window_map = HashMap::new();
1210 if let Projection::Columns(columns) = &final_projection {
1211 for column in columns {
1212 self.collect_windows_from_typed_expr(
1213 &column.expr,
1214 &mut windows,
1215 &mut window_map,
1216 )?;
1217 }
1218 }
1219
1220 let mut outer_order_by = Vec::new();
1221 for order_expr in &stmt.order_by {
1222 let sort_source =
1223 substitute_projection_aliases(&order_expr.expr, &projection_aliases);
1224 let typed = self.infer_expr_with_scope(&sort_source, &expr_scope, &ctes)?;
1225 self.collect_windows_from_typed_expr(&typed, &mut windows, &mut window_map)?;
1226 outer_order_by.push(SortExpr::new(
1227 typed,
1228 order_expr.asc.unwrap_or(true),
1229 order_expr.nulls_first.unwrap_or(false),
1230 ));
1231 }
1232
1233 let window_names = (0..windows.len())
1234 .map(|idx| format!("__window_{idx}"))
1235 .collect::<Vec<_>>();
1236 let mut window_schema = base_schema;
1237 window_schema.extend(windows.iter().enumerate().map(|(idx, window)| {
1238 ColumnMetadata::new(window_names[idx].clone(), window.result_type.clone())
1239 }));
1240
1241 let projection = rewrite_projection_for_windows(
1242 &final_projection,
1243 &window_map,
1244 relation.schema.len(),
1245 &window_names,
1246 )?;
1247 let order_by = outer_order_by
1248 .into_iter()
1249 .map(|sort| {
1250 Ok(SortExpr::new(
1251 rewrite_expr_for_windows(
1252 &sort.expr,
1253 &window_map,
1254 relation.schema.len(),
1255 &window_names,
1256 )?,
1257 sort.asc,
1258 sort.nulls_first,
1259 ))
1260 })
1261 .collect::<Result<Vec<_>, PlannerError>>()?;
1262
1263 plan = LogicalPlan::Window {
1264 input: Box::new(plan),
1265 windows,
1266 };
1267 if !order_by.is_empty() {
1268 plan = LogicalPlan::Sort {
1269 input: Box::new(plan),
1270 order_by,
1271 };
1272 }
1273 if stmt.limit.is_some() || stmt.offset.is_some() {
1274 plan = LogicalPlan::Limit {
1275 input: Box::new(plan),
1276 limit: self.extract_limit_value(&stmt.limit, stmt.span)?,
1277 offset: self.extract_limit_value(&stmt.offset, stmt.span)?,
1278 };
1279 }
1280 let output_schema = projection_schema(&projection, &window_schema);
1281 plan = LogicalPlan::Project {
1282 input: Box::new(plan),
1283 projection,
1284 };
1285 return Ok(PlannedRelation {
1286 plan,
1287 schema: output_schema.clone(),
1288 scope: vec![ScopedTable::new(
1289 TableMetadata::new(LITERAL_TABLE, output_schema),
1290 0,
1291 )],
1292 });
1293 }
1294
1295 if has_group_by || has_aggregate || stmt.having.is_some() || stmt.distinct {
1296 if !has_group_by && !has_aggregate && stmt.having.is_some() {
1297 return Err(PlannerError::invalid_expression(
1298 "HAVING requires GROUP BY or aggregate functions".to_string(),
1299 ));
1300 }
1301
1302 let (group_keys, projected) = if distinct_only {
1303 let projected = self.build_projected_columns_for_distinct_with_scope(
1304 &stmt.projection,
1305 &relation.schema,
1306 &expr_scope,
1307 &ctes,
1308 )?;
1309 let group_keys = projected.iter().map(|col| col.expr.clone()).collect();
1310 (group_keys, projected)
1311 } else {
1312 let group_keys = self.build_group_keys_with_scope(stmt, &expr_scope, &ctes)?;
1313 let projected = self.build_projected_columns_for_aggregate_with_scope(
1314 &stmt.projection,
1315 &expr_scope,
1316 &ctes,
1317 )?;
1318 (group_keys, projected)
1319 };
1320 let mut aggregates = Vec::new();
1321 let mut agg_map = HashMap::new();
1322
1323 for col in &projected {
1324 self.collect_aggregates_from_typed_expr(&col.expr, &mut aggregates, &mut agg_map)?;
1325 }
1326
1327 let having_typed = if let Some(having) = &stmt.having {
1328 let having = substitute_projection_aliases(having, &projection_aliases);
1329 let typed = self.infer_expr_with_scope(&having, &expr_scope, &ctes)?;
1330 if typed.resolved_type != ResolvedType::Boolean {
1331 return Err(PlannerError::type_mismatch(
1332 "Boolean",
1333 typed.resolved_type.type_name().to_string(),
1334 typed.span,
1335 ));
1336 }
1337 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut agg_map)?;
1338 Some(typed)
1339 } else {
1340 None
1341 };
1342
1343 let mut order_by = Vec::new();
1344 if !stmt.order_by.is_empty() {
1345 for order_expr in &stmt.order_by {
1346 let sort_source =
1347 substitute_projection_aliases(&order_expr.expr, &projection_aliases);
1348 let typed = self.infer_expr_with_scope(&sort_source, &expr_scope, &ctes)?;
1349 self.collect_aggregates_from_typed_expr(&typed, &mut aggregates, &mut agg_map)?;
1350 let asc = order_expr.asc.unwrap_or(true);
1351 let nulls_first = order_expr.nulls_first.unwrap_or(false);
1352 order_by.push(SortExpr::new(typed, asc, nulls_first));
1353 }
1354 }
1355
1356 if let Some(ref having) = having_typed {
1357 self.type_checker
1358 .validate_having_expr(having, &group_keys, &aggregates)?;
1359 }
1360
1361 let output_schema = build_aggregate_schema(&group_keys, &aggregates);
1362 let output_names: Vec<String> = output_schema.iter().map(|c| c.name.clone()).collect();
1363
1364 let projection = self.build_aggregate_projection(
1365 projected,
1366 &group_keys,
1367 &aggregates,
1368 &output_names,
1369 )?;
1370
1371 let having = if let Some(having) = having_typed {
1372 Some(self.rewrite_expr_for_aggregate(
1373 &having,
1374 &group_keys,
1375 &aggregates,
1376 &output_names,
1377 )?)
1378 } else {
1379 None
1380 };
1381
1382 let order_by = order_by
1383 .into_iter()
1384 .map(|expr| {
1385 let rewritten = self.rewrite_expr_for_aggregate(
1386 &expr.expr,
1387 &group_keys,
1388 &aggregates,
1389 &output_names,
1390 )?;
1391 Ok(SortExpr::new(rewritten, expr.asc, expr.nulls_first))
1392 })
1393 .collect::<Result<Vec<_>, PlannerError>>()?;
1394
1395 let schema = projection_schema(&projection, &output_schema);
1396 plan = LogicalPlan::Aggregate {
1397 input: Box::new(plan),
1398 group_keys,
1399 aggregates,
1400 having,
1401 projection,
1402 };
1403
1404 if !order_by.is_empty() {
1405 plan = LogicalPlan::Sort {
1406 input: Box::new(plan),
1407 order_by,
1408 };
1409 }
1410
1411 if stmt.limit.is_some() || stmt.offset.is_some() {
1412 let limit = self.extract_limit_value(&stmt.limit, stmt.span)?;
1413 let offset = self.extract_limit_value(&stmt.offset, stmt.span)?;
1414 plan = LogicalPlan::Limit {
1415 input: Box::new(plan),
1416 limit,
1417 offset,
1418 };
1419 }
1420
1421 return Ok(PlannedRelation {
1422 plan,
1423 schema: schema.clone(),
1424 scope: vec![ScopedTable::new(
1425 TableMetadata::new(LITERAL_TABLE, schema),
1426 0,
1427 )],
1428 });
1429 }
1430
1431 if !stmt.order_by.is_empty() {
1433 let order_by = self.build_sort_exprs_with_scope(
1434 &stmt.order_by,
1435 &expr_scope,
1436 &projection_aliases,
1437 &ctes,
1438 )?;
1439 plan = LogicalPlan::Sort {
1440 input: Box::new(plan),
1441 order_by,
1442 };
1443 }
1444
1445 if stmt.limit.is_some() || stmt.offset.is_some() {
1446 let limit = self.extract_limit_value(&stmt.limit, stmt.span)?;
1447 let offset = self.extract_limit_value(&stmt.offset, stmt.span)?;
1448 plan = LogicalPlan::Limit {
1449 input: Box::new(plan),
1450 limit,
1451 offset,
1452 };
1453 }
1454
1455 let output_schema = projection_schema(&final_projection, &relation.schema);
1456 if needs_project_boundary {
1457 plan = LogicalPlan::Project {
1458 input: Box::new(plan),
1459 projection: final_projection,
1460 };
1461 }
1462 Ok(PlannedRelation {
1463 plan,
1464 schema: output_schema.clone(),
1465 scope: vec![ScopedTable::new(
1466 TableMetadata::new(LITERAL_TABLE, output_schema),
1467 0,
1468 )],
1469 })
1470 }
1471
1472 fn plan_from_items(
1476 &self,
1477 items: &[FromItem],
1478 select_span: crate::ast::Span,
1479 outer_scope: &[ScopedTable],
1480 ctes: &CtePlans,
1481 ) -> Result<PlannedRelation, PlannerError> {
1482 match items {
1483 [] => {
1484 let schema = Vec::new();
1485 Ok(PlannedRelation {
1486 plan: LogicalPlan::Scan {
1487 table: LITERAL_TABLE.to_string(),
1488 projection: Projection::All(Vec::new()),
1489 },
1490 schema: schema.clone(),
1491 scope: vec![ScopedTable::new(
1492 TableMetadata::new(LITERAL_TABLE, schema),
1493 0,
1494 )],
1495 })
1496 }
1497 [single] => self.plan_from_item(single, 0, outer_scope, ctes),
1498 [first, rest @ ..] => {
1499 let mut relation = self.plan_from_item(first, 0, outer_scope, ctes)?;
1500 for item in rest {
1501 let right =
1502 self.plan_from_item(item, relation.schema.len(), outer_scope, ctes)?;
1503 relation = self.combine_join_relation(
1504 relation,
1505 right,
1506 JoinType::Cross,
1507 None,
1508 None,
1509 select_span,
1510 )?;
1511 }
1512 Ok(relation)
1513 }
1514 }
1515 }
1516
1517 fn plan_from_item(
1518 &self,
1519 item: &FromItem,
1520 start_index: usize,
1521 outer_scope: &[ScopedTable],
1522 ctes: &CtePlans,
1523 ) -> Result<PlannedRelation, PlannerError> {
1524 match item {
1525 FromItem::Table { name, alias, span } => {
1526 if let Some(cte) = ctes.get(name) {
1527 let mut relation = cte.clone();
1528 relation.plan = LogicalPlan::Project {
1529 input: Box::new(relation.plan),
1530 projection: Projection::All(
1531 relation.schema.iter().map(|col| col.name.clone()).collect(),
1532 ),
1533 };
1534 relation.scope = vec![ScopedTable::new(
1535 TableMetadata::new(
1536 alias.clone().unwrap_or_else(|| name.clone()),
1537 relation.schema.clone(),
1538 ),
1539 start_index,
1540 )];
1541 return Ok(relation);
1542 }
1543 let table = self.name_resolver.resolve_table(name, *span)?.clone();
1544 let mut scope_table = table.clone();
1545 if let Some(alias) = alias {
1546 scope_table.name = alias.clone();
1547 }
1548 let schema = table.columns.clone();
1549 Ok(PlannedRelation {
1550 plan: LogicalPlan::Scan {
1551 table: name.clone(),
1552 projection: Projection::All(
1553 schema.iter().map(|col| col.name.clone()).collect(),
1554 ),
1555 },
1556 schema,
1557 scope: vec![ScopedTable::new(scope_table, start_index)],
1558 })
1559 }
1560 FromItem::Join {
1561 left,
1562 right,
1563 join_type,
1564 condition,
1565 using,
1566 natural,
1567 span,
1568 } => {
1569 let left_relation = self.plan_from_item(left, start_index, outer_scope, ctes)?;
1570 let right_relation = self.plan_from_item(
1571 right,
1572 start_index + left_relation.schema.len(),
1573 outer_scope,
1574 ctes,
1575 )?;
1576 let expr_scope = left_relation
1577 .scope
1578 .iter()
1579 .cloned()
1580 .chain(right_relation.scope.iter().cloned())
1581 .chain(offset_scope(
1582 outer_scope,
1583 left_relation.schema.len() + right_relation.schema.len(),
1584 ))
1585 .collect::<Vec<_>>();
1586 let using = if *natural {
1587 Some(natural_join_columns(
1588 &left_relation.schema,
1589 &right_relation.schema,
1590 ))
1591 } else {
1592 using.clone()
1593 };
1594 let typed_condition = if let Some(expr) = condition {
1595 let typed = self.infer_expr_with_scope(expr, &expr_scope, ctes)?;
1596 if typed.resolved_type != ResolvedType::Boolean {
1597 return Err(PlannerError::type_mismatch(
1598 "Boolean",
1599 typed.resolved_type.to_string(),
1600 expr.span,
1601 ));
1602 }
1603 Some(typed)
1604 } else {
1605 self.build_using_condition(
1606 using.as_deref(),
1607 &left_relation,
1608 &right_relation,
1609 *span,
1610 )?
1611 };
1612 self.combine_join_relation(
1613 left_relation,
1614 right_relation,
1615 map_join_type(*join_type),
1616 typed_condition,
1617 using,
1618 *span,
1619 )
1620 }
1621 FromItem::Derived {
1622 subquery,
1623 alias,
1624 span,
1625 } => {
1626 let crate::ast::StatementKind::Select(select) = &subquery.kind else {
1627 return Err(PlannerError::unsupported_feature(
1628 "non-SELECT derived table",
1629 "v0.6.0-subquery Phase 6",
1630 *span,
1631 ));
1632 };
1633 let mut relation = self.plan_select_relation(select, &[], ctes)?;
1640 let alias = alias.clone().ok_or_else(|| {
1641 PlannerError::invalid_expression("derived table requires an alias".to_string())
1642 })?;
1643 relation.plan = LogicalPlan::Project {
1644 input: Box::new(relation.plan),
1645 projection: Projection::All(
1646 relation.schema.iter().map(|col| col.name.clone()).collect(),
1647 ),
1648 };
1649 relation.scope = vec![ScopedTable::new(
1650 TableMetadata::new(alias, relation.schema.clone()),
1651 start_index,
1652 )];
1653 Ok(relation)
1654 }
1655 }
1656 }
1657
1658 fn combine_join_relation(
1659 &self,
1660 left: PlannedRelation,
1661 right: PlannedRelation,
1662 join_type: JoinType,
1663 condition: Option<TypedExpr>,
1664 using: Option<Vec<String>>,
1665 _span: crate::ast::Span,
1666 ) -> Result<PlannedRelation, PlannerError> {
1667 let mut schema = left.schema.clone();
1668 schema.extend(right.schema.clone());
1669 let mut scope = left.scope.clone();
1670 let mut right_scope = right.scope.clone();
1671 if let Some(columns) = &using {
1672 for column in columns {
1676 let right_index = right_scope.iter().find_map(|table| {
1677 table
1678 .table
1679 .get_column_index(column)
1680 .map(|index| table.start_index + index)
1681 });
1682 let Some(right_index) = right_index else {
1683 continue;
1684 };
1685 for table in &mut scope {
1686 if table.table.get_column_index(column).is_some() {
1687 table.merge_column_with(column, right_index);
1688 }
1689 }
1690 }
1691 for table in &mut right_scope {
1692 table.hide_unqualified_columns(columns);
1693 }
1694 }
1695 scope.extend(right_scope);
1696 Ok(PlannedRelation {
1697 plan: LogicalPlan::Join {
1698 left: Box::new(left.plan),
1699 right: Box::new(right.plan),
1700 join_type,
1701 condition,
1702 using,
1703 },
1704 schema,
1705 scope,
1706 })
1707 }
1708
1709 fn build_using_condition(
1710 &self,
1711 using: Option<&[String]>,
1712 left: &PlannedRelation,
1713 right: &PlannedRelation,
1714 span: crate::ast::Span,
1715 ) -> Result<Option<TypedExpr>, PlannerError> {
1716 let Some(columns) = using else {
1717 return Ok(None);
1718 };
1719 let mut condition = None;
1720 for column in columns {
1721 let left_col = find_scoped_column(&left.scope, column, span)?;
1722 let right_col = find_scoped_column(&right.scope, column, span)?;
1723 let left_expr = merged_scoped_column_expr(&left_col, column, span);
1724 let right_expr = merged_scoped_column_expr(&right_col, column, span);
1725 self.type_checker
1726 .check_comparison_op(&left_col.ty, &right_col.ty, span)?;
1727 let eq = TypedExpr::binary_op(
1728 left_expr,
1729 crate::ast::expr::BinaryOp::Eq,
1730 right_expr,
1731 ResolvedType::Boolean,
1732 span,
1733 );
1734 condition = Some(match condition {
1735 Some(prev) => TypedExpr::binary_op(
1736 prev,
1737 crate::ast::expr::BinaryOp::And,
1738 eq,
1739 ResolvedType::Boolean,
1740 span,
1741 ),
1742 None => eq,
1743 });
1744 }
1745 Ok(condition)
1746 }
1747
1748 fn infer_expr_with_scope(
1749 &self,
1750 expr: &crate::ast::expr::Expr,
1751 scope: &[ScopedTable],
1752 ctes: &CtePlans,
1753 ) -> Result<TypedExpr, PlannerError> {
1754 self.type_checker
1755 .infer_type_with_scope(expr, scope, &|stmt, outer_scope| {
1756 let crate::ast::StatementKind::Select(select) = &stmt.kind else {
1757 return Err(PlannerError::unsupported_feature(
1758 "non-SELECT subquery",
1759 "v0.6.0-subquery Phase 6",
1760 stmt.span(),
1761 ));
1762 };
1763 let relation = self.plan_select_relation(select, outer_scope, ctes)?;
1764 Ok((relation.plan, relation.schema))
1765 })
1766 }
1767
1768 #[allow(dead_code)]
1769 fn build_projection(
1770 &self,
1771 items: &[SelectItem],
1772 table: &TableMetadata,
1773 ) -> Result<Projection, PlannerError> {
1774 if items.len() == 1 && matches!(&items[0], SelectItem::Wildcard { .. }) {
1776 let columns = self.name_resolver.expand_wildcard(table);
1777 return Ok(Projection::All(columns));
1778 }
1779
1780 let mut projected_columns = Vec::new();
1782 for item in items {
1783 match item {
1784 SelectItem::Wildcard { span } => {
1785 for col in &table.columns {
1787 let column_index = table.get_column_index(&col.name).unwrap();
1788 let typed_expr = TypedExpr::column_ref(
1789 table.name.clone(),
1790 col.name.clone(),
1791 column_index,
1792 col.data_type.clone(),
1793 *span,
1794 );
1795 projected_columns.push(ProjectedColumn::new(typed_expr));
1796 }
1797 }
1798 SelectItem::QualifiedWildcard {
1799 table: qualifier,
1800 span,
1801 } => {
1802 if qualifier != &table.name {
1803 return Err(PlannerError::invalid_expression(format!(
1804 "table '{qualifier}' is not available for wildcard projection"
1805 )));
1806 }
1807 for col in &table.columns {
1808 let column_index = table.get_column_index(&col.name).unwrap();
1809 projected_columns.push(ProjectedColumn::new(TypedExpr::column_ref(
1810 table.name.clone(),
1811 col.name.clone(),
1812 column_index,
1813 col.data_type.clone(),
1814 *span,
1815 )));
1816 }
1817 }
1818 SelectItem::Expr { expr, alias, .. } => {
1819 let typed_expr = self.type_checker.infer_type(expr, table)?;
1820 let projected = if let Some(alias) = alias {
1821 ProjectedColumn::with_alias(typed_expr, alias.clone())
1822 } else {
1823 ProjectedColumn::new(typed_expr)
1824 };
1825 projected_columns.push(projected);
1826 }
1827 }
1828 }
1829
1830 Ok(Projection::Columns(projected_columns))
1831 }
1832
1833 fn build_projection_with_scope(
1834 &self,
1835 items: &[SelectItem],
1836 schema: &[ColumnMetadata],
1837 scope: &[ScopedTable],
1838 ctes: &CtePlans,
1839 ) -> Result<Projection, PlannerError> {
1840 if items.len() == 1 && matches!(&items[0], SelectItem::Wildcard { .. }) {
1841 return Ok(Projection::All(visible_wildcard_columns(schema, scope)));
1842 }
1843
1844 let mut projected_columns = Vec::new();
1845 for item in items {
1846 match item {
1847 SelectItem::Wildcard { span } => {
1848 for scoped in scope {
1849 for (local_idx, col) in scoped.table.columns.iter().enumerate() {
1850 projected_columns.push(ProjectedColumn::new(TypedExpr::column_ref(
1851 scoped.table.name.clone(),
1852 col.name.clone(),
1853 scoped.start_index + local_idx,
1854 col.data_type.clone(),
1855 *span,
1856 )));
1857 }
1858 }
1859 }
1860 SelectItem::QualifiedWildcard { table, span } => {
1861 let scoped = scope
1862 .iter()
1863 .filter(|scoped| scoped.table.name == *table)
1864 .collect::<Vec<_>>();
1865 match scoped.as_slice() {
1866 [] => {
1867 return Err(PlannerError::invalid_expression(format!(
1868 "table '{table}' is not available for wildcard projection"
1869 )));
1870 }
1871 [scoped] => {
1872 for (local_idx, col) in scoped.table.columns.iter().enumerate() {
1873 projected_columns.push(ProjectedColumn::new(
1874 TypedExpr::column_ref(
1875 scoped.table.name.clone(),
1876 col.name.clone(),
1877 scoped.start_index + local_idx,
1878 col.data_type.clone(),
1879 *span,
1880 ),
1881 ));
1882 }
1883 }
1884 _ => {
1885 return Err(PlannerError::ambiguous_column(
1886 table,
1887 scoped
1888 .iter()
1889 .map(|scoped| scoped.table.name.clone())
1890 .collect(),
1891 *span,
1892 ));
1893 }
1894 }
1895 }
1896 SelectItem::Expr { expr, alias, .. } => {
1897 let typed_expr = self.infer_expr_with_scope(expr, scope, ctes)?;
1898 let projected = if let Some(alias) = alias {
1899 ProjectedColumn::with_alias(typed_expr, alias.clone())
1900 } else {
1901 ProjectedColumn::new(typed_expr)
1902 };
1903 projected_columns.push(projected);
1904 }
1905 }
1906 }
1907
1908 Ok(Projection::Columns(projected_columns))
1909 }
1910
1911 #[allow(dead_code)]
1913 fn build_sort_exprs(
1914 &self,
1915 order_by: &[OrderByExpr],
1916 table: &TableMetadata,
1917 ) -> Result<Vec<SortExpr>, PlannerError> {
1918 let mut sort_exprs = Vec::new();
1919
1920 for order_expr in order_by {
1921 let typed_expr = self.type_checker.infer_type(&order_expr.expr, table)?;
1922
1923 let asc = order_expr.asc.unwrap_or(true);
1925
1926 let nulls_first = order_expr.nulls_first.unwrap_or(false);
1928
1929 sort_exprs.push(SortExpr::new(typed_expr, asc, nulls_first));
1930 }
1931
1932 Ok(sort_exprs)
1933 }
1934
1935 fn build_sort_exprs_with_scope(
1936 &self,
1937 order_by: &[OrderByExpr],
1938 scope: &[ScopedTable],
1939 projection_aliases: &HashMap<String, crate::ast::expr::Expr>,
1940 ctes: &CtePlans,
1941 ) -> Result<Vec<SortExpr>, PlannerError> {
1942 let mut sort_exprs = Vec::new();
1943 for order_expr in order_by {
1944 let sort_source = substitute_projection_aliases(&order_expr.expr, projection_aliases);
1945 let typed_expr = self.infer_expr_with_scope(&sort_source, scope, ctes)?;
1946 let asc = order_expr.asc.unwrap_or(true);
1947 let nulls_first = order_expr.nulls_first.unwrap_or(false);
1948 sort_exprs.push(SortExpr::new(typed_expr, asc, nulls_first));
1949 }
1950 Ok(sort_exprs)
1951 }
1952
1953 fn select_contains_aggregate(&self, stmt: &Select) -> bool {
1954 stmt.projection.iter().any(|item| match item {
1955 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
1956 SelectItem::Expr { expr, .. } => expr_contains_aggregate(expr),
1957 }) || stmt
1958 .group_by
1959 .as_ref()
1960 .map(|items| items.iter().any(expr_contains_aggregate))
1961 .unwrap_or(false)
1962 || stmt
1963 .having
1964 .as_ref()
1965 .map(expr_contains_aggregate)
1966 .unwrap_or(false)
1967 || stmt
1968 .order_by
1969 .iter()
1970 .any(|order| expr_contains_aggregate(&order.expr))
1971 }
1972
1973 #[allow(dead_code)]
1974 fn build_group_keys(
1975 &self,
1976 stmt: &Select,
1977 table: &TableMetadata,
1978 ) -> Result<Vec<TypedExpr>, PlannerError> {
1979 let mut keys = Vec::new();
1980 if let Some(items) = &stmt.group_by {
1981 for expr in items {
1982 let typed = self.type_checker.infer_type(expr, table)?;
1983 if typed_expr_contains_aggregate(&typed) {
1984 return Err(PlannerError::invalid_expression(
1985 "GROUP BY cannot contain aggregate functions".to_string(),
1986 ));
1987 }
1988 if !matches!(typed.kind, TypedExprKind::ColumnRef { .. }) {
1989 return Err(PlannerError::invalid_expression(
1990 "GROUP BY expressions must be column references".to_string(),
1991 ));
1992 }
1993 keys.push(typed);
1994 }
1995 }
1996 Ok(keys)
1997 }
1998
1999 fn build_group_keys_with_scope(
2000 &self,
2001 stmt: &Select,
2002 scope: &[ScopedTable],
2003 ctes: &CtePlans,
2004 ) -> Result<Vec<TypedExpr>, PlannerError> {
2005 let mut keys = Vec::new();
2006 if let Some(items) = &stmt.group_by {
2007 for expr in items {
2008 let typed = self.infer_expr_with_scope(expr, scope, ctes)?;
2009 if typed_expr_contains_aggregate(&typed) {
2010 return Err(PlannerError::invalid_expression(
2011 "GROUP BY cannot contain aggregate functions".to_string(),
2012 ));
2013 }
2014 if !matches!(typed.kind, TypedExprKind::ColumnRef { .. }) {
2015 return Err(PlannerError::invalid_expression(
2016 "GROUP BY expressions must be column references".to_string(),
2017 ));
2018 }
2019 keys.push(typed);
2020 }
2021 }
2022 Ok(keys)
2023 }
2024
2025 #[allow(dead_code)]
2026 fn build_projected_columns_for_aggregate(
2027 &self,
2028 items: &[SelectItem],
2029 table: &TableMetadata,
2030 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
2031 let mut projected = Vec::new();
2032 for item in items {
2033 match item {
2034 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => {
2035 return Err(PlannerError::invalid_expression(
2036 "wildcard projection not supported with GROUP BY/aggregate".to_string(),
2037 ));
2038 }
2039 SelectItem::Expr { expr, alias, .. } => {
2040 let typed = self.type_checker.infer_type(expr, table)?;
2041 projected.push(ProjectedColumn {
2042 expr: typed,
2043 alias: alias.clone(),
2044 });
2045 }
2046 }
2047 }
2048 Ok(projected)
2049 }
2050
2051 fn build_projected_columns_for_aggregate_with_scope(
2052 &self,
2053 items: &[SelectItem],
2054 scope: &[ScopedTable],
2055 ctes: &CtePlans,
2056 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
2057 let mut projected = Vec::new();
2058 for item in items {
2059 match item {
2060 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => {
2061 return Err(PlannerError::invalid_expression(
2062 "wildcard projection not supported with GROUP BY/aggregate".to_string(),
2063 ));
2064 }
2065 SelectItem::Expr { expr, alias, .. } => {
2066 let typed = self.infer_expr_with_scope(expr, scope, ctes)?;
2067 projected.push(ProjectedColumn {
2068 expr: typed,
2069 alias: alias.clone(),
2070 });
2071 }
2072 }
2073 }
2074 Ok(projected)
2075 }
2076
2077 #[allow(dead_code)]
2078 fn build_projected_columns_for_distinct(
2079 &self,
2080 items: &[SelectItem],
2081 table: &TableMetadata,
2082 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
2083 let projection = self.build_projection(items, table)?;
2084 match projection {
2085 Projection::All(columns) => {
2086 let mut projected = Vec::with_capacity(columns.len());
2087 for column in columns {
2088 let column_index = table.get_column_index(&column).ok_or_else(|| {
2089 PlannerError::invalid_expression(format!(
2090 "column '{column}' not found for DISTINCT projection"
2091 ))
2092 })?;
2093 let column_meta = table.get_column(&column).ok_or_else(|| {
2094 PlannerError::invalid_expression(format!(
2095 "column '{column}' not found for DISTINCT projection"
2096 ))
2097 })?;
2098 let typed_expr = TypedExpr::column_ref(
2099 table.name.clone(),
2100 column.clone(),
2101 column_index,
2102 column_meta.data_type.clone(),
2103 crate::ast::Span::default(),
2104 );
2105 projected.push(ProjectedColumn::new(typed_expr));
2106 }
2107 Ok(projected)
2108 }
2109 Projection::Columns(columns) => Ok(columns),
2110 }
2111 }
2112
2113 fn build_projected_columns_for_distinct_with_scope(
2114 &self,
2115 items: &[SelectItem],
2116 schema: &[ColumnMetadata],
2117 scope: &[ScopedTable],
2118 ctes: &CtePlans,
2119 ) -> Result<Vec<ProjectedColumn>, PlannerError> {
2120 let projection = self.build_projection_with_scope(items, schema, scope, ctes)?;
2121 match projection {
2122 Projection::All(columns) => {
2123 let mut projected = Vec::with_capacity(columns.len());
2124 for (idx, column) in columns.into_iter().enumerate() {
2125 let column_meta = schema.get(idx).ok_or_else(|| {
2126 PlannerError::invalid_expression(format!(
2127 "column '{column}' not found for DISTINCT projection"
2128 ))
2129 })?;
2130 projected.push(ProjectedColumn::new(TypedExpr::column_ref(
2131 LITERAL_TABLE.to_string(),
2132 column,
2133 idx,
2134 column_meta.data_type.clone(),
2135 crate::ast::Span::default(),
2136 )));
2137 }
2138 Ok(projected)
2139 }
2140 Projection::Columns(columns) => Ok(columns),
2141 }
2142 }
2143
2144 fn collect_aggregates_from_typed_expr(
2145 &self,
2146 expr: &TypedExpr,
2147 aggregates: &mut Vec<AggregateExpr>,
2148 aggregate_map: &mut HashMap<AggregateSignature, usize>,
2149 ) -> Result<(), PlannerError> {
2150 match &expr.kind {
2151 TypedExprKind::FunctionCall {
2152 name,
2153 args,
2154 distinct,
2155 star,
2156 over: None,
2157 } if is_aggregate_function(name) => {
2158 for arg in args {
2159 if typed_expr_contains_aggregate(arg) {
2160 return Err(PlannerError::invalid_expression(
2161 "nested aggregate functions are not supported".to_string(),
2162 ));
2163 }
2164 }
2165 let (agg, signature) =
2166 self.build_aggregate_expr_from_typed(expr, name, args, *distinct, *star)?;
2167 aggregate_map.entry(signature).or_insert_with(|| {
2168 aggregates.push(agg);
2169 aggregates.len() - 1
2170 });
2171 Ok(())
2172 }
2173 TypedExprKind::BinaryOp { left, right, .. } => {
2174 self.collect_aggregates_from_typed_expr(left, aggregates, aggregate_map)?;
2175 self.collect_aggregates_from_typed_expr(right, aggregates, aggregate_map)?;
2176 Ok(())
2177 }
2178 TypedExprKind::UnaryOp { operand, .. } => {
2179 self.collect_aggregates_from_typed_expr(operand, aggregates, aggregate_map)
2180 }
2181 TypedExprKind::Cast { expr, .. } => {
2182 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)
2183 }
2184 TypedExprKind::Case {
2185 operand,
2186 branches,
2187 else_expr,
2188 } => {
2189 if let Some(operand) = operand {
2190 self.collect_aggregates_from_typed_expr(operand, aggregates, aggregate_map)?;
2191 }
2192 for branch in branches {
2193 self.collect_aggregates_from_typed_expr(
2194 &branch.when,
2195 aggregates,
2196 aggregate_map,
2197 )?;
2198 self.collect_aggregates_from_typed_expr(
2199 &branch.then,
2200 aggregates,
2201 aggregate_map,
2202 )?;
2203 }
2204 if let Some(else_expr) = else_expr {
2205 self.collect_aggregates_from_typed_expr(else_expr, aggregates, aggregate_map)?;
2206 }
2207 Ok(())
2208 }
2209 TypedExprKind::FunctionCall { args, .. } => {
2210 for arg in args {
2211 self.collect_aggregates_from_typed_expr(arg, aggregates, aggregate_map)?;
2212 }
2213 Ok(())
2214 }
2215 TypedExprKind::Between {
2216 expr, low, high, ..
2217 } => {
2218 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
2219 self.collect_aggregates_from_typed_expr(low, aggregates, aggregate_map)?;
2220 self.collect_aggregates_from_typed_expr(high, aggregates, aggregate_map)?;
2221 Ok(())
2222 }
2223 TypedExprKind::Like {
2224 expr,
2225 pattern,
2226 escape,
2227 ..
2228 } => {
2229 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
2230 self.collect_aggregates_from_typed_expr(pattern, aggregates, aggregate_map)?;
2231 if let Some(esc) = escape {
2232 self.collect_aggregates_from_typed_expr(esc, aggregates, aggregate_map)?;
2233 }
2234 Ok(())
2235 }
2236 TypedExprKind::InList { expr, list, .. } => {
2237 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)?;
2238 for item in list {
2239 self.collect_aggregates_from_typed_expr(item, aggregates, aggregate_map)?;
2240 }
2241 Ok(())
2242 }
2243 TypedExprKind::IsNull { expr, .. } => {
2244 self.collect_aggregates_from_typed_expr(expr, aggregates, aggregate_map)
2245 }
2246 _ => Ok(()),
2247 }
2248 }
2249
2250 fn collect_windows_from_typed_expr(
2251 &self,
2252 expr: &TypedExpr,
2253 windows: &mut Vec<WindowExpr>,
2254 window_map: &mut HashMap<String, usize>,
2255 ) -> Result<(), PlannerError> {
2256 match &expr.kind {
2257 TypedExprKind::FunctionCall {
2258 name,
2259 args,
2260 distinct,
2261 star,
2262 over: Some(over),
2263 } => {
2264 if args.iter().any(typed_expr_contains_window)
2265 || over.partition_by.iter().any(typed_expr_contains_window)
2266 || over
2267 .order_by
2268 .iter()
2269 .any(|sort| typed_expr_contains_window(&sort.expr))
2270 {
2271 return Err(PlannerError::invalid_expression(
2272 "nested window functions are not supported".to_string(),
2273 ));
2274 }
2275
2276 let key = expr_key(expr);
2277 if window_map.contains_key(&key) {
2278 return Ok(());
2279 }
2280 let function = match name.to_ascii_lowercase().as_str() {
2281 "row_number" => WindowFunction::RowNumber,
2282 "rank" => WindowFunction::Rank,
2283 "dense_rank" => WindowFunction::DenseRank,
2284 "sum" | "count" | "avg" | "min" | "max" => {
2285 let (aggregate, _) = self
2286 .build_aggregate_expr_from_typed(expr, name, args, *distinct, *star)?;
2287 WindowFunction::Aggregate(aggregate)
2288 }
2289 "lag" | "lead" => {
2290 return Err(PlannerError::unsupported_feature(
2291 format!("{} window function", name.to_ascii_uppercase()),
2292 "future",
2293 expr.span,
2294 ));
2295 }
2296 _ => {
2297 return Err(PlannerError::unsupported_feature(
2298 format!("function '{}' with OVER", name),
2299 "future",
2300 expr.span,
2301 ));
2302 }
2303 };
2304 let index = windows.len();
2305 windows.push(WindowExpr {
2306 function,
2307 partition_by: over.partition_by.clone(),
2308 order_by: over.order_by.clone(),
2309 result_type: expr.resolved_type.clone(),
2310 });
2311 window_map.insert(key, index);
2312 Ok(())
2313 }
2314 TypedExprKind::FunctionCall { args, .. } => {
2315 for arg in args {
2316 self.collect_windows_from_typed_expr(arg, windows, window_map)?;
2317 }
2318 Ok(())
2319 }
2320 TypedExprKind::BinaryOp { left, right, .. } => {
2321 self.collect_windows_from_typed_expr(left, windows, window_map)?;
2322 self.collect_windows_from_typed_expr(right, windows, window_map)
2323 }
2324 TypedExprKind::UnaryOp { operand, .. } => {
2325 self.collect_windows_from_typed_expr(operand, windows, window_map)
2326 }
2327 TypedExprKind::Cast { expr, .. } | TypedExprKind::IsNull { expr, .. } => {
2328 self.collect_windows_from_typed_expr(expr, windows, window_map)
2329 }
2330 TypedExprKind::Between {
2331 expr, low, high, ..
2332 } => {
2333 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
2334 self.collect_windows_from_typed_expr(low, windows, window_map)?;
2335 self.collect_windows_from_typed_expr(high, windows, window_map)
2336 }
2337 TypedExprKind::Like {
2338 expr,
2339 pattern,
2340 escape,
2341 ..
2342 } => {
2343 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
2344 self.collect_windows_from_typed_expr(pattern, windows, window_map)?;
2345 if let Some(escape) = escape {
2346 self.collect_windows_from_typed_expr(escape, windows, window_map)?;
2347 }
2348 Ok(())
2349 }
2350 TypedExprKind::InList { expr, list, .. } => {
2351 self.collect_windows_from_typed_expr(expr, windows, window_map)?;
2352 for item in list {
2353 self.collect_windows_from_typed_expr(item, windows, window_map)?;
2354 }
2355 Ok(())
2356 }
2357 _ => Ok(()),
2358 }
2359 }
2360
2361 fn build_aggregate_expr_from_typed(
2362 &self,
2363 expr: &TypedExpr,
2364 name: &str,
2365 args: &[TypedExpr],
2366 distinct: bool,
2367 star: bool,
2368 ) -> Result<(AggregateExpr, AggregateSignature), PlannerError> {
2369 let lower = name.to_lowercase();
2370 match lower.as_str() {
2371 "count" => {
2372 if star {
2373 let agg = AggregateExpr::count_star();
2374 let signature = aggregate_signature(name, distinct, star, None, None, expr);
2375 return Ok((agg, signature));
2376 }
2377 if args.len() != 1 {
2378 return Err(PlannerError::type_mismatch(
2379 "1 argument",
2380 format!("{} arguments", args.len()),
2381 expr.span,
2382 ));
2383 }
2384 let agg = AggregateExpr {
2385 function: AggregateFunction::Count,
2386 arg: Some(args[0].clone()),
2387 distinct,
2388 result_type: ResolvedType::BigInt,
2389 };
2390 let signature =
2391 aggregate_signature(name, distinct, star, Some(&args[0]), None, expr);
2392 Ok((agg, signature))
2393 }
2394 "sum" => {
2395 let arg = self.require_single_aggregate_arg(args, expr.span)?;
2396 let agg = AggregateExpr {
2397 function: AggregateFunction::Sum,
2398 arg: Some(arg.clone()),
2399 distinct,
2400 result_type: crate::planner::aggregate_expr::sum_result_type(
2401 &arg.resolved_type,
2402 ),
2403 };
2404 let signature = aggregate_signature(name, distinct, star, Some(arg), None, expr);
2405 Ok((agg, signature))
2406 }
2407 "total" => {
2408 let arg = self.require_single_aggregate_arg(args, expr.span)?;
2409 let agg = AggregateExpr {
2410 function: AggregateFunction::Total,
2411 arg: Some(arg.clone()),
2412 distinct: false,
2413 result_type: ResolvedType::Double,
2414 };
2415 let signature = aggregate_signature(name, false, star, Some(arg), None, expr);
2416 Ok((agg, signature))
2417 }
2418 "avg" => {
2419 let arg = self.require_single_aggregate_arg(args, expr.span)?;
2420 let agg = AggregateExpr {
2421 function: AggregateFunction::Avg,
2422 arg: Some(arg.clone()),
2423 distinct,
2424 result_type: ResolvedType::Double,
2425 };
2426 let signature = aggregate_signature(name, distinct, star, Some(arg), None, expr);
2427 Ok((agg, signature))
2428 }
2429 "min" => {
2430 let arg = self.require_single_aggregate_arg(args, expr.span)?;
2431 let agg = AggregateExpr {
2432 function: AggregateFunction::Min,
2433 arg: Some(arg.clone()),
2434 distinct,
2435 result_type: arg.resolved_type.clone(),
2436 };
2437 let signature = aggregate_signature(name, distinct, star, Some(arg), None, expr);
2438 Ok((agg, signature))
2439 }
2440 "max" => {
2441 let arg = self.require_single_aggregate_arg(args, expr.span)?;
2442 let agg = AggregateExpr {
2443 function: AggregateFunction::Max,
2444 arg: Some(arg.clone()),
2445 distinct,
2446 result_type: arg.resolved_type.clone(),
2447 };
2448 let signature = aggregate_signature(name, distinct, star, Some(arg), None, expr);
2449 Ok((agg, signature))
2450 }
2451 "group_concat" => {
2452 if args.is_empty() || args.len() > 2 {
2453 return Err(PlannerError::type_mismatch(
2454 "1 or 2 arguments",
2455 format!("{} arguments", args.len()),
2456 expr.span,
2457 ));
2458 }
2459 let arg = &args[0];
2460 let mut separator = None;
2461 if args.len() == 2 {
2462 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
2463 separator = Some(value.clone());
2464 } else {
2465 return Err(PlannerError::invalid_expression(
2466 "GROUP_CONCAT separator must be a string literal".to_string(),
2467 ));
2468 }
2469 }
2470 let agg = AggregateExpr {
2471 function: AggregateFunction::GroupConcat { separator },
2472 arg: Some(arg.clone()),
2473 distinct,
2474 result_type: ResolvedType::Text,
2475 };
2476 let signature = aggregate_signature(
2477 name,
2478 distinct,
2479 star,
2480 Some(arg),
2481 match &agg.function {
2482 AggregateFunction::GroupConcat { separator } => separator.as_ref(),
2483 _ => None,
2484 },
2485 expr,
2486 );
2487 Ok((agg, signature))
2488 }
2489 "string_agg" => {
2490 if args.len() != 2 {
2491 return Err(PlannerError::type_mismatch(
2492 "2 arguments",
2493 format!("{} arguments", args.len()),
2494 expr.span,
2495 ));
2496 }
2497 let arg = &args[0];
2498 let separator =
2499 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
2500 Some(value.clone())
2501 } else {
2502 return Err(PlannerError::invalid_expression(
2503 "STRING_AGG separator must be a string literal".to_string(),
2504 ));
2505 };
2506 let agg = AggregateExpr {
2507 function: AggregateFunction::StringAgg { separator },
2508 arg: Some(arg.clone()),
2509 distinct,
2510 result_type: ResolvedType::Text,
2511 };
2512 let signature = aggregate_signature(
2513 name,
2514 distinct,
2515 star,
2516 Some(arg),
2517 match &agg.function {
2518 AggregateFunction::StringAgg { separator } => separator.as_ref(),
2519 _ => None,
2520 },
2521 expr,
2522 );
2523 Ok((agg, signature))
2524 }
2525 _ => Err(PlannerError::unsupported_feature(
2526 format!("function '{}'", name),
2527 "future",
2528 expr.span,
2529 )),
2530 }
2531 }
2532
2533 fn require_single_aggregate_arg<'b>(
2534 &self,
2535 args: &'b [TypedExpr],
2536 span: crate::ast::Span,
2537 ) -> Result<&'b TypedExpr, PlannerError> {
2538 if args.len() != 1 {
2539 return Err(PlannerError::type_mismatch(
2540 "1 argument",
2541 format!("{} arguments", args.len()),
2542 span,
2543 ));
2544 }
2545 Ok(&args[0])
2546 }
2547
2548 fn build_aggregate_projection(
2549 &self,
2550 projected: Vec<ProjectedColumn>,
2551 group_keys: &[TypedExpr],
2552 aggregates: &[AggregateExpr],
2553 output_names: &[String],
2554 ) -> Result<Projection, PlannerError> {
2555 let mut columns = Vec::new();
2556 for col in projected {
2557 let rewritten =
2558 self.rewrite_expr_for_aggregate(&col.expr, group_keys, aggregates, output_names)?;
2559 columns.push(ProjectedColumn {
2560 expr: rewritten,
2561 alias: col.alias,
2562 });
2563 }
2564 Ok(Projection::Columns(columns))
2565 }
2566
2567 fn rewrite_expr_for_aggregate(
2568 &self,
2569 expr: &TypedExpr,
2570 group_keys: &[TypedExpr],
2571 aggregates: &[AggregateExpr],
2572 output_names: &[String],
2573 ) -> Result<TypedExpr, PlannerError> {
2574 let group_key_map = build_group_key_map(group_keys);
2575 let aggregate_map = build_aggregate_map(aggregates);
2576
2577 rewrite_expr_with_maps(expr, &group_key_map, &aggregate_map, output_names)
2578 }
2579
2580 fn extract_limit_value(
2584 &self,
2585 expr: &Option<crate::ast::expr::Expr>,
2586 stmt_span: crate::ast::Span,
2587 ) -> Result<Option<u64>, PlannerError> {
2588 match expr {
2589 None => Ok(None),
2590 Some(e) => {
2591 if let crate::ast::expr::ExprKind::Literal {
2593 literal: Literal::Number(s),
2594 } = &e.kind
2595 {
2596 s.parse::<u64>().map(Some).map_err(|_| {
2597 PlannerError::type_mismatch("unsigned integer", s.clone(), e.span)
2598 })
2599 } else {
2600 Err(PlannerError::unsupported_feature(
2601 "non-literal LIMIT/OFFSET",
2602 "v0.3.0+",
2603 stmt_span,
2604 ))
2605 }
2606 }
2607 }
2608 }
2609
2610 fn plan_insert(&self, stmt: &Insert) -> Result<LogicalPlan, PlannerError> {
2615 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
2617
2618 let columns: Vec<String> = if let Some(ref cols) = stmt.columns {
2620 for col in cols {
2622 self.name_resolver.resolve_column(table, col, stmt.span)?;
2623 }
2624 cols.clone()
2625 } else {
2626 table.column_names().into_iter().map(String::from).collect()
2628 };
2629
2630 match &stmt.source {
2631 InsertSource::Values { values } => {
2632 let mut typed_values: Vec<Vec<TypedExpr>> = Vec::new();
2633
2634 for row in values {
2635 if row.len() != columns.len() {
2636 return Err(PlannerError::column_value_count_mismatch(
2637 columns.len(),
2638 row.len(),
2639 stmt.span,
2640 ));
2641 }
2642
2643 typed_values.push(self.type_check_insert_values(row, &columns, table)?);
2644 }
2645
2646 Ok(LogicalPlan::Insert {
2647 table: table.name.clone(),
2648 columns,
2649 values: typed_values,
2650 })
2651 }
2652 InsertSource::Select { select } => {
2653 let source = self.plan_select_relation(select, &[], &CtePlans::new())?;
2654 if source.schema.len() != columns.len() {
2655 return Err(PlannerError::column_value_count_mismatch(
2656 columns.len(),
2657 source.schema.len(),
2658 stmt.span,
2659 ));
2660 }
2661
2662 for (source_column, target_column) in source.schema.iter().zip(&columns) {
2663 let target = table
2664 .get_column(target_column)
2665 .expect("validated target column");
2666 if target.not_null && source_column.data_type == ResolvedType::Null {
2667 return Err(PlannerError::null_constraint_violation(
2668 target_column,
2669 stmt.span,
2670 ));
2671 }
2672 self.validate_resolved_type_assignment(
2673 &source_column.data_type,
2674 &target.data_type,
2675 stmt.span,
2676 )?;
2677 }
2678
2679 Ok(LogicalPlan::InsertSelect {
2680 table: table.name.clone(),
2681 columns,
2682 source: Box::new(source.plan),
2683 })
2684 }
2685 }
2686 }
2687
2688 fn type_check_insert_values(
2690 &self,
2691 values: &[crate::ast::expr::Expr],
2692 columns: &[String],
2693 table: &TableMetadata,
2694 ) -> Result<Vec<TypedExpr>, PlannerError> {
2695 let mut typed_values = Vec::new();
2696
2697 for (i, value) in values.iter().enumerate() {
2698 let column_name = &columns[i];
2699 let column_meta = table.get_column(column_name).ok_or_else(|| {
2700 PlannerError::column_not_found(column_name, &table.name, value.span)
2701 })?;
2702
2703 let typed_value = self.type_checker.infer_type(value, table)?;
2705
2706 if column_meta.not_null
2708 && matches!(&typed_value.kind, TypedExprKind::Literal(Literal::Null))
2709 {
2710 return Err(PlannerError::null_constraint_violation(
2711 column_name,
2712 value.span,
2713 ));
2714 }
2715
2716 self.validate_type_assignment(&typed_value, &column_meta.data_type, value.span)?;
2718
2719 let typed_value =
2720 self.coerce_assignment_value(typed_value, &column_meta.data_type, value.span);
2721
2722 typed_values.push(typed_value);
2723 }
2724
2725 Ok(typed_values)
2726 }
2727
2728 fn validate_type_assignment(
2730 &self,
2731 value: &TypedExpr,
2732 target_type: &ResolvedType,
2733 span: crate::ast::Span,
2734 ) -> Result<(), PlannerError> {
2735 self.validate_resolved_type_assignment(&value.resolved_type, target_type, span)
2736 }
2737
2738 fn validate_resolved_type_assignment(
2739 &self,
2740 source_type: &ResolvedType,
2741 target_type: &ResolvedType,
2742 span: crate::ast::Span,
2743 ) -> Result<(), PlannerError> {
2744 if *source_type == ResolvedType::Null {
2746 return Ok(());
2747 }
2748
2749 if self.types_compatible(source_type, target_type) {
2751 return Ok(());
2752 }
2753
2754 Err(PlannerError::type_mismatch(
2755 target_type.to_string(),
2756 source_type.to_string(),
2757 span,
2758 ))
2759 }
2760
2761 fn types_compatible(&self, source: &ResolvedType, target: &ResolvedType) -> bool {
2763 use ResolvedType::*;
2764
2765 if source == target {
2767 return true;
2768 }
2769
2770 match (source, target) {
2772 (Integer, BigInt) | (Integer, Float) | (Integer, Double) => true,
2774 (BigInt, Float) | (BigInt, Double) => true,
2776 (Float, Double) => true,
2778 (Double, Float) => true,
2781 (Text | Integer | BigInt | Float | Double, Timestamp) => true,
2784 (Vector { dimension: d1, .. }, Vector { dimension: d2, .. }) => d1 == d2,
2786 _ => false,
2787 }
2788 }
2789
2790 fn coerce_assignment_value(
2791 &self,
2792 value: TypedExpr,
2793 target_type: &ResolvedType,
2794 span: crate::ast::Span,
2795 ) -> TypedExpr {
2796 if value.resolved_type != *target_type
2797 && value.resolved_type != ResolvedType::Null
2798 && matches!(
2799 target_type,
2800 ResolvedType::Integer
2801 | ResolvedType::BigInt
2802 | ResolvedType::Float
2803 | ResolvedType::Double
2804 | ResolvedType::Timestamp
2805 )
2806 {
2807 TypedExpr::cast(value, target_type.clone(), span)
2808 } else {
2809 value
2810 }
2811 }
2812
2813 fn plan_update(&self, stmt: &Update) -> Result<LogicalPlan, PlannerError> {
2817 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
2819
2820 let mut typed_assignments = Vec::new();
2822
2823 for assignment in &stmt.assignments {
2824 let column_meta =
2826 self.name_resolver
2827 .resolve_column(table, &assignment.column, assignment.span)?;
2828 let column_index = table.get_column_index(&assignment.column).unwrap();
2829
2830 let typed_value = self.type_checker.infer_type(&assignment.value, table)?;
2832
2833 if column_meta.not_null
2835 && matches!(&typed_value.kind, TypedExprKind::Literal(Literal::Null))
2836 {
2837 return Err(PlannerError::null_constraint_violation(
2838 &assignment.column,
2839 assignment.value.span,
2840 ));
2841 }
2842
2843 self.validate_type_assignment(
2845 &typed_value,
2846 &column_meta.data_type,
2847 assignment.value.span,
2848 )?;
2849
2850 let typed_value = self.coerce_assignment_value(
2851 typed_value,
2852 &column_meta.data_type,
2853 assignment.value.span,
2854 );
2855
2856 typed_assignments.push(TypedAssignment::new(
2857 assignment.column.clone(),
2858 column_index,
2859 typed_value,
2860 ));
2861 }
2862
2863 let filter = if let Some(ref selection) = stmt.selection {
2865 let predicate = self.type_checker.infer_type(selection, table)?;
2866
2867 if predicate.resolved_type != ResolvedType::Boolean {
2869 return Err(PlannerError::type_mismatch(
2870 "Boolean",
2871 predicate.resolved_type.to_string(),
2872 selection.span,
2873 ));
2874 }
2875
2876 Some(predicate)
2877 } else {
2878 None
2879 };
2880
2881 Ok(LogicalPlan::Update {
2882 table: table.name.clone(),
2883 assignments: typed_assignments,
2884 filter,
2885 })
2886 }
2887
2888 fn plan_delete(&self, stmt: &Delete) -> Result<LogicalPlan, PlannerError> {
2892 let table = self.name_resolver.resolve_table(&stmt.table, stmt.span)?;
2894
2895 let filter = if let Some(ref selection) = stmt.selection {
2897 let predicate = self.type_checker.infer_type(selection, table)?;
2898
2899 if predicate.resolved_type != ResolvedType::Boolean {
2901 return Err(PlannerError::type_mismatch(
2902 "Boolean",
2903 predicate.resolved_type.to_string(),
2904 selection.span,
2905 ));
2906 }
2907
2908 Some(predicate)
2909 } else {
2910 None
2911 };
2912
2913 Ok(LogicalPlan::Delete {
2914 table: table.name.clone(),
2915 filter,
2916 })
2917 }
2918}
2919
2920#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2921struct AggregateSignature {
2922 name: String,
2923 distinct: bool,
2924 star: bool,
2925 arg_key: Option<String>,
2926 separator: Option<String>,
2927}
2928
2929fn collect_projection_aliases(items: &[SelectItem]) -> HashMap<String, crate::ast::expr::Expr> {
2939 let mut aliases = HashMap::new();
2940 for item in items {
2941 if let SelectItem::Expr {
2942 expr,
2943 alias: Some(alias),
2944 ..
2945 } = item
2946 {
2947 aliases.entry(alias.clone()).or_insert_with(|| expr.clone());
2948 }
2949 }
2950 aliases
2951}
2952
2953fn substitute_projection_aliases(
2971 expr: &crate::ast::expr::Expr,
2972 aliases: &HashMap<String, crate::ast::expr::Expr>,
2973) -> crate::ast::expr::Expr {
2974 use crate::ast::expr::ExprKind;
2975
2976 if aliases.is_empty() {
2977 return expr.clone();
2978 }
2979
2980 let recurse = |e: &crate::ast::expr::Expr| substitute_projection_aliases(e, aliases);
2981
2982 let kind = match &expr.kind {
2983 ExprKind::ColumnRef {
2984 table: None,
2985 column,
2986 } => match aliases.get(column) {
2987 Some(source) => {
2988 let mut replacement = source.clone();
2989 replacement.span = expr.span;
2990 return replacement;
2991 }
2992 None => return expr.clone(),
2993 },
2994 ExprKind::BinaryOp { left, op, right } => ExprKind::BinaryOp {
2995 left: Box::new(recurse(left)),
2996 op: *op,
2997 right: Box::new(recurse(right)),
2998 },
2999 ExprKind::UnaryOp { op, operand } => ExprKind::UnaryOp {
3000 op: *op,
3001 operand: Box::new(recurse(operand)),
3002 },
3003 ExprKind::FunctionCall {
3004 name,
3005 args,
3006 distinct,
3007 star,
3008 over,
3009 } => ExprKind::FunctionCall {
3010 name: name.clone(),
3011 args: args.iter().map(recurse).collect(),
3012 distinct: *distinct,
3013 star: *star,
3014 over: over.as_ref().map(|window| crate::ast::expr::WindowSpec {
3015 partition_by: window.partition_by.iter().map(recurse).collect(),
3016 order_by: window
3017 .order_by
3018 .iter()
3019 .map(|order| OrderByExpr {
3020 expr: recurse(&order.expr),
3021 asc: order.asc,
3022 nulls_first: order.nulls_first,
3023 span: order.span,
3024 })
3025 .collect(),
3026 }),
3027 },
3028 ExprKind::Case {
3029 operand,
3030 branches,
3031 else_expr,
3032 } => ExprKind::Case {
3033 operand: operand.as_deref().map(|e| Box::new(recurse(e))),
3034 branches: branches
3035 .iter()
3036 .map(|branch| crate::ast::expr::CaseWhen {
3037 when: recurse(&branch.when),
3038 then: recurse(&branch.then),
3039 })
3040 .collect(),
3041 else_expr: else_expr.as_deref().map(|e| Box::new(recurse(e))),
3042 },
3043 ExprKind::Cast { expr, target_type } => ExprKind::Cast {
3044 expr: Box::new(recurse(expr)),
3045 target_type: target_type.clone(),
3046 },
3047 ExprKind::Between {
3048 expr,
3049 low,
3050 high,
3051 negated,
3052 } => ExprKind::Between {
3053 expr: Box::new(recurse(expr)),
3054 low: Box::new(recurse(low)),
3055 high: Box::new(recurse(high)),
3056 negated: *negated,
3057 },
3058 ExprKind::Like {
3059 expr,
3060 pattern,
3061 escape,
3062 negated,
3063 kind,
3064 } => ExprKind::Like {
3065 expr: Box::new(recurse(expr)),
3066 pattern: Box::new(recurse(pattern)),
3067 escape: escape.as_deref().map(|e| Box::new(recurse(e))),
3068 negated: *negated,
3069 kind: *kind,
3070 },
3071 ExprKind::InList {
3072 expr,
3073 list,
3074 negated,
3075 } => ExprKind::InList {
3076 expr: Box::new(recurse(expr)),
3077 list: list.iter().map(recurse).collect(),
3078 negated: *negated,
3079 },
3080 ExprKind::IsNull { expr, negated } => ExprKind::IsNull {
3081 expr: Box::new(recurse(expr)),
3082 negated: *negated,
3083 },
3084 ExprKind::ColumnRef { .. }
3087 | ExprKind::Literal { .. }
3088 | ExprKind::VectorLiteral { .. }
3089 | ExprKind::ScalarSubquery { .. }
3090 | ExprKind::InSubquery { .. }
3091 | ExprKind::Exists { .. }
3092 | ExprKind::Quantified { .. } => return expr.clone(),
3093 };
3094
3095 crate::ast::expr::Expr {
3096 kind,
3097 span: expr.span,
3098 }
3099}
3100
3101fn expr_contains_aggregate(expr: &crate::ast::expr::Expr) -> bool {
3102 use crate::ast::expr::ExprKind;
3103
3104 match &expr.kind {
3105 ExprKind::FunctionCall {
3106 name, args, over, ..
3107 } => {
3108 if over.is_none() && is_aggregate_function(name) {
3109 return true;
3110 }
3111 args.iter().any(expr_contains_aggregate)
3112 || over.as_ref().is_some_and(|window| {
3113 window.partition_by.iter().any(expr_contains_aggregate)
3114 || window
3115 .order_by
3116 .iter()
3117 .any(|sort| expr_contains_aggregate(&sort.expr))
3118 })
3119 }
3120 ExprKind::BinaryOp { left, right, .. } => {
3121 expr_contains_aggregate(left) || expr_contains_aggregate(right)
3122 }
3123 ExprKind::UnaryOp { operand, .. } => expr_contains_aggregate(operand),
3124 ExprKind::Case {
3125 operand,
3126 branches,
3127 else_expr,
3128 } => {
3129 operand.as_deref().is_some_and(expr_contains_aggregate)
3130 || branches.iter().any(|branch| {
3131 expr_contains_aggregate(&branch.when) || expr_contains_aggregate(&branch.then)
3132 })
3133 || else_expr.as_deref().is_some_and(expr_contains_aggregate)
3134 }
3135 ExprKind::Cast { expr, .. } => expr_contains_aggregate(expr),
3136 ExprKind::Between {
3137 expr, low, high, ..
3138 } => {
3139 expr_contains_aggregate(expr)
3140 || expr_contains_aggregate(low)
3141 || expr_contains_aggregate(high)
3142 }
3143 ExprKind::Like {
3144 expr,
3145 pattern,
3146 escape,
3147 ..
3148 } => {
3149 expr_contains_aggregate(expr)
3150 || expr_contains_aggregate(pattern)
3151 || escape.as_deref().is_some_and(expr_contains_aggregate)
3152 }
3153 ExprKind::InList { expr, list, .. } => {
3154 expr_contains_aggregate(expr) || list.iter().any(expr_contains_aggregate)
3155 }
3156 ExprKind::IsNull { expr, .. } => expr_contains_aggregate(expr),
3157 ExprKind::ScalarSubquery { .. }
3158 | ExprKind::InSubquery { .. }
3159 | ExprKind::Exists { .. }
3160 | ExprKind::Quantified { .. }
3161 | ExprKind::Literal { .. }
3162 | ExprKind::VectorLiteral { .. }
3163 | ExprKind::ColumnRef { .. } => false,
3164 }
3165}
3166
3167fn typed_expr_contains_aggregate(expr: &TypedExpr) -> bool {
3168 match &expr.kind {
3169 TypedExprKind::FunctionCall {
3170 name, args, over, ..
3171 } => {
3172 if over.is_none() && is_aggregate_function(name) {
3173 return true;
3174 }
3175 args.iter().any(typed_expr_contains_aggregate)
3176 || over.as_ref().is_some_and(|window| {
3177 window
3178 .partition_by
3179 .iter()
3180 .any(typed_expr_contains_aggregate)
3181 || window
3182 .order_by
3183 .iter()
3184 .any(|sort| typed_expr_contains_aggregate(&sort.expr))
3185 })
3186 }
3187 TypedExprKind::BinaryOp { left, right, .. } => {
3188 typed_expr_contains_aggregate(left) || typed_expr_contains_aggregate(right)
3189 }
3190 TypedExprKind::UnaryOp { operand, .. } => typed_expr_contains_aggregate(operand),
3191 TypedExprKind::Cast { expr, .. } => typed_expr_contains_aggregate(expr),
3192 TypedExprKind::Case {
3193 operand,
3194 branches,
3195 else_expr,
3196 } => {
3197 operand
3198 .as_deref()
3199 .is_some_and(typed_expr_contains_aggregate)
3200 || branches.iter().any(|branch| {
3201 typed_expr_contains_aggregate(&branch.when)
3202 || typed_expr_contains_aggregate(&branch.then)
3203 })
3204 || else_expr
3205 .as_deref()
3206 .is_some_and(typed_expr_contains_aggregate)
3207 }
3208 TypedExprKind::Between {
3209 expr, low, high, ..
3210 } => {
3211 typed_expr_contains_aggregate(expr)
3212 || typed_expr_contains_aggregate(low)
3213 || typed_expr_contains_aggregate(high)
3214 }
3215 TypedExprKind::Like {
3216 expr,
3217 pattern,
3218 escape,
3219 ..
3220 } => {
3221 typed_expr_contains_aggregate(expr)
3222 || typed_expr_contains_aggregate(pattern)
3223 || escape
3224 .as_ref()
3225 .is_some_and(|inner| typed_expr_contains_aggregate(inner))
3226 }
3227 TypedExprKind::InList { expr, list, .. } => {
3228 typed_expr_contains_aggregate(expr) || list.iter().any(typed_expr_contains_aggregate)
3229 }
3230 TypedExprKind::IsNull { expr, .. } => typed_expr_contains_aggregate(expr),
3231 TypedExprKind::InSubquery { expr, .. } => typed_expr_contains_aggregate(expr),
3232 TypedExprKind::Quantified { expr, .. } => typed_expr_contains_aggregate(expr),
3233 TypedExprKind::ScalarSubquery(_) | TypedExprKind::Exists { .. } => false,
3234 _ => false,
3235 }
3236}
3237
3238fn select_contains_window(stmt: &Select) -> bool {
3239 stmt.projection.iter().any(|item| match item {
3240 SelectItem::Wildcard { .. } | SelectItem::QualifiedWildcard { .. } => false,
3241 SelectItem::Expr { expr, .. } => expr_contains_window(expr),
3242 }) || stmt
3243 .order_by
3244 .iter()
3245 .any(|order| expr_contains_window(&order.expr))
3246}
3247
3248fn expr_contains_window(expr: &crate::ast::expr::Expr) -> bool {
3249 match &expr.kind {
3250 crate::ast::expr::ExprKind::FunctionCall { args, over, .. } => {
3251 over.is_some() || args.iter().any(expr_contains_window)
3252 }
3253 crate::ast::expr::ExprKind::BinaryOp { left, right, .. } => {
3254 expr_contains_window(left) || expr_contains_window(right)
3255 }
3256 crate::ast::expr::ExprKind::UnaryOp { operand, .. }
3257 | crate::ast::expr::ExprKind::Cast { expr: operand, .. }
3258 | crate::ast::expr::ExprKind::IsNull { expr: operand, .. } => expr_contains_window(operand),
3259 crate::ast::expr::ExprKind::Between {
3260 expr, low, high, ..
3261 } => expr_contains_window(expr) || expr_contains_window(low) || expr_contains_window(high),
3262 crate::ast::expr::ExprKind::Like {
3263 expr,
3264 pattern,
3265 escape,
3266 ..
3267 } => {
3268 expr_contains_window(expr)
3269 || expr_contains_window(pattern)
3270 || escape.as_deref().is_some_and(expr_contains_window)
3271 }
3272 crate::ast::expr::ExprKind::InList { expr, list, .. } => {
3273 expr_contains_window(expr) || list.iter().any(expr_contains_window)
3274 }
3275 _ => false,
3276 }
3277}
3278
3279fn typed_expr_contains_window(expr: &TypedExpr) -> bool {
3280 match &expr.kind {
3281 TypedExprKind::FunctionCall { args, over, .. } => {
3282 over.is_some() || args.iter().any(typed_expr_contains_window)
3283 }
3284 TypedExprKind::BinaryOp { left, right, .. } => {
3285 typed_expr_contains_window(left) || typed_expr_contains_window(right)
3286 }
3287 TypedExprKind::UnaryOp { operand, .. }
3288 | TypedExprKind::Cast { expr: operand, .. }
3289 | TypedExprKind::IsNull { expr: operand, .. } => typed_expr_contains_window(operand),
3290 TypedExprKind::Between {
3291 expr, low, high, ..
3292 } => {
3293 typed_expr_contains_window(expr)
3294 || typed_expr_contains_window(low)
3295 || typed_expr_contains_window(high)
3296 }
3297 TypedExprKind::Like {
3298 expr,
3299 pattern,
3300 escape,
3301 ..
3302 } => {
3303 typed_expr_contains_window(expr)
3304 || typed_expr_contains_window(pattern)
3305 || escape.as_deref().is_some_and(typed_expr_contains_window)
3306 }
3307 TypedExprKind::InList { expr, list, .. } => {
3308 typed_expr_contains_window(expr) || list.iter().any(typed_expr_contains_window)
3309 }
3310 _ => false,
3311 }
3312}
3313
3314fn rewrite_projection_for_windows(
3315 projection: &Projection,
3316 window_map: &HashMap<String, usize>,
3317 base_width: usize,
3318 window_names: &[String],
3319) -> Result<Projection, PlannerError> {
3320 match projection {
3321 Projection::All(names) => Ok(Projection::All(names.clone())),
3322 Projection::Columns(columns) => Ok(Projection::Columns(
3323 columns
3324 .iter()
3325 .map(|column| {
3326 Ok(ProjectedColumn {
3327 expr: rewrite_expr_for_windows(
3328 &column.expr,
3329 window_map,
3330 base_width,
3331 window_names,
3332 )?,
3333 alias: column.alias.clone(),
3334 })
3335 })
3336 .collect::<Result<Vec<_>, PlannerError>>()?,
3337 )),
3338 }
3339}
3340
3341fn rewrite_expr_for_windows(
3342 expr: &TypedExpr,
3343 window_map: &HashMap<String, usize>,
3344 base_width: usize,
3345 window_names: &[String],
3346) -> Result<TypedExpr, PlannerError> {
3347 if let Some(index) = window_map.get(&expr_key(expr)) {
3348 return Ok(TypedExpr::column_ref(
3349 "__window__".to_string(),
3350 window_names
3351 .get(*index)
3352 .cloned()
3353 .unwrap_or_else(|| format!("__window_{index}")),
3354 base_width + index,
3355 expr.resolved_type.clone(),
3356 expr.span,
3357 ));
3358 }
3359
3360 let rewrite =
3361 |inner: &TypedExpr| rewrite_expr_for_windows(inner, window_map, base_width, window_names);
3362 let kind = match &expr.kind {
3363 TypedExprKind::FunctionCall {
3364 name,
3365 args,
3366 distinct,
3367 star,
3368 over,
3369 } => {
3370 if over.is_some() {
3371 return Err(PlannerError::invalid_expression(
3372 "window expression is not part of the window plan".to_string(),
3373 ));
3374 }
3375 TypedExprKind::FunctionCall {
3376 name: name.clone(),
3377 args: args.iter().map(rewrite).collect::<Result<Vec<_>, _>>()?,
3378 distinct: *distinct,
3379 star: *star,
3380 over: None,
3381 }
3382 }
3383 TypedExprKind::BinaryOp { left, op, right } => TypedExprKind::BinaryOp {
3384 left: Box::new(rewrite(left)?),
3385 op: *op,
3386 right: Box::new(rewrite(right)?),
3387 },
3388 TypedExprKind::UnaryOp { op, operand } => TypedExprKind::UnaryOp {
3389 op: *op,
3390 operand: Box::new(rewrite(operand)?),
3391 },
3392 TypedExprKind::Cast {
3393 expr: inner,
3394 target_type,
3395 } => TypedExprKind::Cast {
3396 expr: Box::new(rewrite(inner)?),
3397 target_type: target_type.clone(),
3398 },
3399 TypedExprKind::Between {
3400 expr: inner,
3401 low,
3402 high,
3403 negated,
3404 } => TypedExprKind::Between {
3405 expr: Box::new(rewrite(inner)?),
3406 low: Box::new(rewrite(low)?),
3407 high: Box::new(rewrite(high)?),
3408 negated: *negated,
3409 },
3410 TypedExprKind::Like {
3411 expr: inner,
3412 pattern,
3413 escape,
3414 negated,
3415 kind,
3416 } => TypedExprKind::Like {
3417 expr: Box::new(rewrite(inner)?),
3418 pattern: Box::new(rewrite(pattern)?),
3419 escape: escape.as_deref().map(rewrite).transpose()?.map(Box::new),
3420 negated: *negated,
3421 kind: *kind,
3422 },
3423 TypedExprKind::InList {
3424 expr: inner,
3425 list,
3426 negated,
3427 } => TypedExprKind::InList {
3428 expr: Box::new(rewrite(inner)?),
3429 list: list.iter().map(rewrite).collect::<Result<Vec<_>, _>>()?,
3430 negated: *negated,
3431 },
3432 TypedExprKind::IsNull {
3433 expr: inner,
3434 negated,
3435 } => TypedExprKind::IsNull {
3436 expr: Box::new(rewrite(inner)?),
3437 negated: *negated,
3438 },
3439 _ => return Ok(expr.clone()),
3440 };
3441 Ok(TypedExpr {
3442 kind,
3443 resolved_type: expr.resolved_type.clone(),
3444 span: expr.span,
3445 })
3446}
3447
3448fn map_join_type(join_type: crate::ast::dml::JoinType) -> JoinType {
3449 match join_type {
3450 crate::ast::dml::JoinType::Inner => JoinType::Inner,
3451 crate::ast::dml::JoinType::Left => JoinType::Left,
3452 crate::ast::dml::JoinType::Right => JoinType::Right,
3453 crate::ast::dml::JoinType::Full => JoinType::Full,
3454 crate::ast::dml::JoinType::Cross => JoinType::Cross,
3455 }
3456}
3457
3458struct FoundScopedColumn {
3459 table: String,
3460 index: usize,
3461 ty: ResolvedType,
3462 partner_indices: Vec<usize>,
3463}
3464
3465fn find_scoped_column(
3466 scope: &[ScopedTable],
3467 column: &str,
3468 span: crate::ast::Span,
3469) -> Result<FoundScopedColumn, PlannerError> {
3470 let mut matches = Vec::new();
3471 for table in scope {
3472 if table.hidden_unqualified_columns.contains(column) {
3473 continue;
3474 }
3475 if let Some(local_idx) = table.table.get_column_index(column) {
3476 let meta = &table.table.columns[local_idx];
3477 matches.push(FoundScopedColumn {
3478 table: table.table.name.clone(),
3479 index: table.start_index + local_idx,
3480 ty: meta.data_type.clone(),
3481 partner_indices: table
3482 .merged_column_partners
3483 .get(column)
3484 .cloned()
3485 .unwrap_or_default(),
3486 });
3487 }
3488 }
3489 match matches.len() {
3490 0 => Err(PlannerError::column_not_found(column, "JOIN input", span)),
3491 1 => Ok(matches.remove(0)),
3492 _ => Err(PlannerError::ambiguous_column(
3493 column,
3494 scope.iter().map(|s| s.table.name.clone()).collect(),
3495 span,
3496 )),
3497 }
3498}
3499
3500fn merged_scoped_column_expr(
3501 found: &FoundScopedColumn,
3502 column: &str,
3503 span: crate::ast::Span,
3504) -> TypedExpr {
3505 let own = TypedExpr::column_ref(
3506 found.table.clone(),
3507 column.to_string(),
3508 found.index,
3509 found.ty.clone(),
3510 span,
3511 );
3512 if found.partner_indices.is_empty() {
3513 return own;
3514 }
3515
3516 let mut args = Vec::with_capacity(found.partner_indices.len() + 1);
3517 args.push(own);
3518 args.extend(found.partner_indices.iter().map(|&index| {
3519 TypedExpr::column_ref(
3520 found.table.clone(),
3521 column.to_string(),
3522 index,
3523 found.ty.clone(),
3524 span,
3525 )
3526 }));
3527 TypedExpr {
3528 kind: TypedExprKind::FunctionCall {
3529 name: "coalesce".to_string(),
3530 args,
3531 distinct: false,
3532 star: false,
3533 over: None,
3534 },
3535 resolved_type: found.ty.clone(),
3536 span,
3537 }
3538}
3539
3540fn projection_schema(
3541 projection: &Projection,
3542 input_schema: &[ColumnMetadata],
3543) -> Vec<ColumnMetadata> {
3544 match projection {
3545 Projection::All(names) => names
3546 .iter()
3547 .enumerate()
3548 .map(|(idx, name)| {
3549 let ty = (names.len() == input_schema.len())
3550 .then(|| input_schema.get(idx))
3551 .flatten()
3552 .or_else(|| input_schema.iter().find(|col| &col.name == name))
3553 .map(|col| col.data_type.clone())
3554 .unwrap_or(ResolvedType::Null);
3555 ColumnMetadata::new(name.clone(), ty)
3556 })
3557 .collect(),
3558 Projection::Columns(columns) => columns
3559 .iter()
3560 .enumerate()
3561 .map(|(idx, col)| {
3562 let name = col
3563 .alias
3564 .clone()
3565 .or_else(|| match &col.expr.kind {
3566 TypedExprKind::ColumnRef { column, .. } => Some(column.clone()),
3567 TypedExprKind::FunctionCall { name, args, .. }
3571 if name == "coalesce" && !args.is_empty() =>
3572 {
3573 let first_column = match &args[0].kind {
3574 TypedExprKind::ColumnRef { column, .. } => Some(column),
3575 _ => None,
3576 };
3577 first_column
3578 .filter(|column| {
3579 args.iter().all(|arg| {
3580 matches!(
3581 &arg.kind,
3582 TypedExprKind::ColumnRef { column: other, .. }
3583 if other == *column
3584 )
3585 })
3586 })
3587 .cloned()
3588 }
3589 _ => None,
3590 })
3591 .unwrap_or_else(|| format!("col_{idx}"));
3592 ColumnMetadata::new(name, col.expr.resolved_type.clone())
3593 })
3594 .collect(),
3595 }
3596}
3597
3598fn visible_wildcard_columns(schema: &[ColumnMetadata], scope: &[ScopedTable]) -> Vec<String> {
3599 schema
3600 .iter()
3601 .enumerate()
3602 .filter(|(index, column)| {
3603 !scope.iter().any(|table| {
3604 *index >= table.start_index
3605 && *index < table.start_index + table.table.columns.len()
3606 && table.hidden_unqualified_columns.contains(&column.name)
3607 })
3608 })
3609 .map(|(_, column)| column.name.clone())
3610 .collect()
3611}
3612
3613fn offset_scope(scope: &[ScopedTable], offset: usize) -> Vec<ScopedTable> {
3614 scope
3615 .iter()
3616 .cloned()
3617 .map(|mut table| {
3618 table.start_index += offset;
3619 table.scope_level += 1;
3620 table
3621 })
3622 .collect()
3623}
3624
3625fn natural_join_columns(
3626 left_schema: &[ColumnMetadata],
3627 right_schema: &[ColumnMetadata],
3628) -> Vec<String> {
3629 let right_names = right_schema
3633 .iter()
3634 .map(|column| column.name.as_str())
3635 .collect::<HashSet<_>>();
3636 left_schema
3637 .iter()
3638 .filter(|left| right_names.contains(left.name.as_str()))
3639 .map(|column| column.name.clone())
3640 .collect()
3641}
3642
3643fn install_base_projection(plan: &mut LogicalPlan, projection: &Projection) {
3644 match plan {
3645 LogicalPlan::Scan {
3646 projection: scan_projection,
3647 ..
3648 } => *scan_projection = projection.clone(),
3649 LogicalPlan::Filter { input, .. } => install_base_projection(input, projection),
3650 _ => {}
3651 }
3652}
3653
3654fn is_aggregate_function(name: &str) -> bool {
3655 matches!(
3656 name.to_ascii_lowercase().as_str(),
3657 "count" | "sum" | "total" | "avg" | "min" | "max" | "group_concat" | "string_agg"
3658 )
3659}
3660
3661fn expr_key(expr: &TypedExpr) -> String {
3662 format!("{:?}", expr.kind)
3663}
3664
3665fn aggregate_signature(
3666 name: &str,
3667 distinct: bool,
3668 star: bool,
3669 arg: Option<&TypedExpr>,
3670 separator: Option<&String>,
3671 _expr: &TypedExpr,
3672) -> AggregateSignature {
3673 AggregateSignature {
3674 name: name.to_ascii_lowercase(),
3675 distinct,
3676 star,
3677 arg_key: arg.map(expr_key),
3678 separator: separator.cloned(),
3679 }
3680}
3681
3682fn build_group_key_map(group_keys: &[TypedExpr]) -> HashMap<String, usize> {
3683 let mut map = HashMap::new();
3684 for (idx, key) in group_keys.iter().enumerate() {
3685 map.insert(expr_key(key), idx);
3686 }
3687 map
3688}
3689
3690fn build_aggregate_map(aggregates: &[AggregateExpr]) -> HashMap<AggregateSignature, usize> {
3691 let mut map = HashMap::new();
3692 for (idx, agg) in aggregates.iter().enumerate() {
3693 let (name, separator, star, arg) = match &agg.function {
3694 AggregateFunction::Count => (
3695 "count".to_string(),
3696 None,
3697 agg.arg.is_none(),
3698 agg.arg.as_ref(),
3699 ),
3700 AggregateFunction::Sum => ("sum".to_string(), None, false, agg.arg.as_ref()),
3701 AggregateFunction::Total => ("total".to_string(), None, false, agg.arg.as_ref()),
3702 AggregateFunction::Avg => ("avg".to_string(), None, false, agg.arg.as_ref()),
3703 AggregateFunction::Min => ("min".to_string(), None, false, agg.arg.as_ref()),
3704 AggregateFunction::Max => ("max".to_string(), None, false, agg.arg.as_ref()),
3705 AggregateFunction::GroupConcat { separator } => (
3706 "group_concat".to_string(),
3707 separator.clone(),
3708 false,
3709 agg.arg.as_ref(),
3710 ),
3711 AggregateFunction::StringAgg { separator } => (
3712 "string_agg".to_string(),
3713 separator.clone(),
3714 false,
3715 agg.arg.as_ref(),
3716 ),
3717 };
3718 let signature = AggregateSignature {
3719 name,
3720 distinct: agg.distinct,
3721 star,
3722 arg_key: arg.map(expr_key),
3723 separator,
3724 };
3725 map.insert(signature, idx);
3726 }
3727 map
3728}
3729
3730fn build_aggregate_schema(
3731 group_keys: &[TypedExpr],
3732 aggregates: &[AggregateExpr],
3733) -> Vec<ColumnMetadata> {
3734 let mut schema = Vec::new();
3735 for (idx, key) in group_keys.iter().enumerate() {
3736 let name = match &key.kind {
3737 TypedExprKind::ColumnRef { column, .. } => column.clone(),
3738 _ => format!("group_{idx}"),
3739 };
3740 schema.push(ColumnMetadata::new(name, key.resolved_type.clone()));
3741 }
3742 for (idx, agg) in aggregates.iter().enumerate() {
3743 let name = match &agg.function {
3744 AggregateFunction::Count => format!("count_{idx}"),
3745 AggregateFunction::Sum => format!("sum_{idx}"),
3746 AggregateFunction::Total => format!("total_{idx}"),
3747 AggregateFunction::Avg => format!("avg_{idx}"),
3748 AggregateFunction::Min => format!("min_{idx}"),
3749 AggregateFunction::Max => format!("max_{idx}"),
3750 AggregateFunction::GroupConcat { .. } => format!("group_concat_{idx}"),
3751 AggregateFunction::StringAgg { .. } => format!("string_agg_{idx}"),
3752 };
3753 schema.push(ColumnMetadata::new(name, agg.result_type.clone()));
3754 }
3755 schema
3756}
3757
3758fn rewrite_expr_with_maps(
3759 expr: &TypedExpr,
3760 group_key_map: &HashMap<String, usize>,
3761 aggregate_map: &HashMap<AggregateSignature, usize>,
3762 output_names: &[String],
3763) -> Result<TypedExpr, PlannerError> {
3764 let group_key_count = output_names.len().saturating_sub(aggregate_map.len());
3765 let key = expr_key(expr);
3766 if let Some(idx) = group_key_map.get(&key) {
3767 return Ok(make_output_column_ref(
3768 *idx,
3769 output_names,
3770 expr.resolved_type.clone(),
3771 expr.span,
3772 ));
3773 }
3774
3775 match &expr.kind {
3776 TypedExprKind::FunctionCall {
3777 name,
3778 args,
3779 distinct,
3780 star,
3781 over: None,
3782 } if is_aggregate_function(name) => {
3783 let separator = if name.eq_ignore_ascii_case("group_concat") && args.len() == 2 {
3784 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3785 Some(value.clone())
3786 } else {
3787 return Err(PlannerError::invalid_expression(
3788 "GROUP_CONCAT separator must be a string literal".to_string(),
3789 ));
3790 }
3791 } else if name.eq_ignore_ascii_case("string_agg") && args.len() == 2 {
3792 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3793 Some(value.clone())
3794 } else {
3795 return Err(PlannerError::invalid_expression(
3796 "STRING_AGG separator must be a string literal".to_string(),
3797 ));
3798 }
3799 } else {
3800 None
3801 };
3802 let signature = AggregateSignature {
3803 name: name.to_ascii_lowercase(),
3804 distinct: *distinct,
3805 star: *star,
3806 arg_key: args.first().map(expr_key),
3807 separator,
3808 };
3809 let idx = aggregate_map.get(&signature).ok_or_else(|| {
3810 PlannerError::invalid_expression(
3811 "aggregate in expression is not part of plan".to_string(),
3812 )
3813 })?;
3814 let output_index = group_key_count + idx;
3815 Ok(make_output_column_ref(
3816 output_index,
3817 output_names,
3818 expr.resolved_type.clone(),
3819 expr.span,
3820 ))
3821 }
3822 TypedExprKind::FunctionCall {
3823 name,
3824 args,
3825 distinct,
3826 star,
3827 over,
3828 } => {
3829 if over.is_some() {
3830 return Err(PlannerError::invalid_expression(
3831 "window function reached aggregate expression rewrite".to_string(),
3832 ));
3833 }
3834 if *distinct || *star {
3835 return Err(PlannerError::invalid_expression(
3836 "DISTINCT/STAR modifiers are only supported for aggregates".to_string(),
3837 ));
3838 }
3839 let mut rewritten_args = Vec::with_capacity(args.len());
3840 for arg in args {
3841 rewritten_args.push(rewrite_expr_with_maps(
3842 arg,
3843 group_key_map,
3844 aggregate_map,
3845 output_names,
3846 )?);
3847 }
3848 Ok(TypedExpr {
3849 kind: TypedExprKind::FunctionCall {
3850 name: name.clone(),
3851 args: rewritten_args,
3852 distinct: false,
3853 star: false,
3854 over: None,
3855 },
3856 resolved_type: expr.resolved_type.clone(),
3857 span: expr.span,
3858 })
3859 }
3860 TypedExprKind::BinaryOp { left, op, right } => {
3861 let left = rewrite_expr_with_maps(left, group_key_map, aggregate_map, output_names)?;
3862 let right = rewrite_expr_with_maps(right, group_key_map, aggregate_map, output_names)?;
3863 Ok(TypedExpr {
3864 kind: TypedExprKind::BinaryOp {
3865 left: Box::new(left),
3866 op: *op,
3867 right: Box::new(right),
3868 },
3869 resolved_type: expr.resolved_type.clone(),
3870 span: expr.span,
3871 })
3872 }
3873 TypedExprKind::UnaryOp { op, operand } => {
3874 let operand =
3875 rewrite_expr_with_maps(operand, group_key_map, aggregate_map, output_names)?;
3876 Ok(TypedExpr {
3877 kind: TypedExprKind::UnaryOp {
3878 op: *op,
3879 operand: Box::new(operand),
3880 },
3881 resolved_type: expr.resolved_type.clone(),
3882 span: expr.span,
3883 })
3884 }
3885 TypedExprKind::Case {
3886 operand,
3887 branches,
3888 else_expr,
3889 } => {
3890 let operand = operand
3891 .as_deref()
3892 .map(|operand| {
3893 rewrite_expr_with_maps(operand, group_key_map, aggregate_map, output_names)
3894 .map(Box::new)
3895 })
3896 .transpose()?;
3897 let mut rewritten_branches = Vec::with_capacity(branches.len());
3898 for branch in branches {
3899 rewritten_branches.push(TypedCaseWhen {
3900 when: rewrite_expr_with_maps(
3901 &branch.when,
3902 group_key_map,
3903 aggregate_map,
3904 output_names,
3905 )?,
3906 then: rewrite_expr_with_maps(
3907 &branch.then,
3908 group_key_map,
3909 aggregate_map,
3910 output_names,
3911 )?,
3912 });
3913 }
3914 let else_expr = else_expr
3915 .as_deref()
3916 .map(|else_expr| {
3917 rewrite_expr_with_maps(else_expr, group_key_map, aggregate_map, output_names)
3918 .map(Box::new)
3919 })
3920 .transpose()?;
3921 Ok(TypedExpr {
3922 kind: TypedExprKind::Case {
3923 operand,
3924 branches: rewritten_branches,
3925 else_expr,
3926 },
3927 resolved_type: expr.resolved_type.clone(),
3928 span: expr.span,
3929 })
3930 }
3931 TypedExprKind::Between {
3932 expr: inner,
3933 low,
3934 high,
3935 negated,
3936 } => {
3937 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
3938 let low = rewrite_expr_with_maps(low, group_key_map, aggregate_map, output_names)?;
3939 let high = rewrite_expr_with_maps(high, group_key_map, aggregate_map, output_names)?;
3940 Ok(TypedExpr {
3941 kind: TypedExprKind::Between {
3942 expr: Box::new(inner),
3943 low: Box::new(low),
3944 high: Box::new(high),
3945 negated: *negated,
3946 },
3947 resolved_type: expr.resolved_type.clone(),
3948 span: expr.span,
3949 })
3950 }
3951 TypedExprKind::Like {
3952 expr: inner,
3953 pattern,
3954 escape,
3955 negated,
3956 kind,
3957 } => {
3958 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
3959 let pattern =
3960 rewrite_expr_with_maps(pattern, group_key_map, aggregate_map, output_names)?;
3961 let escape = if let Some(esc) = escape {
3962 Some(Box::new(rewrite_expr_with_maps(
3963 esc,
3964 group_key_map,
3965 aggregate_map,
3966 output_names,
3967 )?))
3968 } else {
3969 None
3970 };
3971 Ok(TypedExpr {
3972 kind: TypedExprKind::Like {
3973 expr: Box::new(inner),
3974 pattern: Box::new(pattern),
3975 escape,
3976 negated: *negated,
3977 kind: *kind,
3978 },
3979 resolved_type: expr.resolved_type.clone(),
3980 span: expr.span,
3981 })
3982 }
3983 TypedExprKind::InList {
3984 expr: inner,
3985 list,
3986 negated,
3987 } => {
3988 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
3989 let mut rewritten_list = Vec::with_capacity(list.len());
3990 for item in list {
3991 rewritten_list.push(rewrite_expr_with_maps(
3992 item,
3993 group_key_map,
3994 aggregate_map,
3995 output_names,
3996 )?);
3997 }
3998 Ok(TypedExpr {
3999 kind: TypedExprKind::InList {
4000 expr: Box::new(inner),
4001 list: rewritten_list,
4002 negated: *negated,
4003 },
4004 resolved_type: expr.resolved_type.clone(),
4005 span: expr.span,
4006 })
4007 }
4008 TypedExprKind::IsNull {
4009 expr: inner,
4010 negated,
4011 } => {
4012 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
4013 Ok(TypedExpr {
4014 kind: TypedExprKind::IsNull {
4015 expr: Box::new(inner),
4016 negated: *negated,
4017 },
4018 resolved_type: expr.resolved_type.clone(),
4019 span: expr.span,
4020 })
4021 }
4022 TypedExprKind::Literal(_) | TypedExprKind::VectorLiteral(_) => Ok(expr.clone()),
4023 TypedExprKind::ColumnRef { .. } => Err(PlannerError::invalid_expression(
4024 "column reference must appear in GROUP BY or be aggregated".to_string(),
4025 )),
4026 TypedExprKind::Cast {
4027 expr: inner,
4028 target_type,
4029 } => {
4030 let inner = rewrite_expr_with_maps(inner, group_key_map, aggregate_map, output_names)?;
4031 Ok(TypedExpr {
4032 kind: TypedExprKind::Cast {
4033 expr: Box::new(inner),
4034 target_type: target_type.clone(),
4035 },
4036 resolved_type: expr.resolved_type.clone(),
4037 span: expr.span,
4038 })
4039 }
4040 TypedExprKind::ScalarSubquery(_)
4041 | TypedExprKind::InSubquery { .. }
4042 | TypedExprKind::Exists { .. }
4043 | TypedExprKind::Quantified { .. } => Ok(expr.clone()),
4044 }
4045}
4046
4047fn make_output_column_ref(
4048 index: usize,
4049 output_names: &[String],
4050 resolved_type: ResolvedType,
4051 span: crate::ast::Span,
4052) -> TypedExpr {
4053 let name = output_names
4054 .get(index)
4055 .cloned()
4056 .unwrap_or_else(|| format!("col_{index}"));
4057 TypedExpr::column_ref("__agg__".to_string(), name, index, resolved_type, span)
4058}