1use crate::ast::Span;
8use crate::ast::Statement;
9use crate::ast::ddl::VectorMetric;
10use crate::ast::expr::{
11 BinaryOp, Expr, ExprKind, Literal, PatternMatchKind, Quantifier as AstQuantifier, TruthValue,
12 UnaryOp, WindowFrame, WindowFrameBound, WindowFrameUnits, WindowSpec,
13};
14use crate::ast::expr::{
15 INTERNAL_ROW_BETWEEN, INTERNAL_ROW_DISTINCT, INTERNAL_ROW_EQ, INTERNAL_ROW_GT,
16 INTERNAL_ROW_GTEQ, INTERNAL_ROW_IN, INTERNAL_ROW_LT, INTERNAL_ROW_LTEQ, INTERNAL_ROW_NEQ,
17 INTERNAL_TRUTH_FALSE, INTERNAL_TRUTH_TRUE, INTERNAL_TRUTH_UNKNOWN,
18};
19use crate::catalog::{Catalog, ColumnMetadata, TableMetadata};
20use crate::planner::aggregate_expr::{AggregateExpr, AggregateFunction};
21use crate::planner::error::PlannerError;
22use crate::planner::logical_plan::LogicalPlan;
23use crate::planner::typed_expr::{
24 Quantifier, SortExpr, TypedCaseWhen, TypedExpr, TypedExprKind, TypedWindowSpec,
25};
26use crate::planner::types::ResolvedType;
27use std::collections::{BTreeSet, HashMap, HashSet};
28use std::sync::Arc;
29
30#[derive(Debug, Clone)]
36pub struct ScopedTable {
37 pub table: Arc<TableMetadata>,
38 pub start_index: usize,
39 pub scope_level: usize,
42 pub hidden_unqualified_columns: HashSet<String>,
47 pub merged_column_partners: HashMap<String, Vec<usize>>,
52 column_index: Option<Arc<HashMap<String, usize>>>,
60}
61
62const COLUMN_INDEX_THRESHOLD: usize = 32;
68
69impl ScopedTable {
70 pub fn new(table: impl Into<Arc<TableMetadata>>, start_index: usize) -> Self {
71 let table = table.into();
72 let column_index = (table.columns.len() > COLUMN_INDEX_THRESHOLD).then(|| {
73 let mut index = HashMap::with_capacity(table.columns.len());
76 for (position, column) in table.columns.iter().enumerate() {
77 index.entry(column.name.clone()).or_insert(position);
78 }
79 Arc::new(index)
80 });
81 Self {
82 table,
83 start_index,
84 scope_level: 0,
85 hidden_unqualified_columns: HashSet::new(),
86 merged_column_partners: HashMap::new(),
87 column_index,
88 }
89 }
90
91 pub fn column_position(&self, column: &str) -> Option<usize> {
93 match &self.column_index {
94 Some(index) => index.get(column).copied(),
95 None => self.table.get_column_index(column),
96 }
97 }
98
99 pub fn hide_unqualified_columns(&mut self, columns: &[String]) {
100 self.hidden_unqualified_columns
101 .extend(columns.iter().cloned());
102 }
103
104 pub fn merge_column_with(&mut self, column: &str, partner_index: usize) {
106 let partners = self
107 .merged_column_partners
108 .entry(column.to_string())
109 .or_default();
110 if !partners.contains(&partner_index) {
111 partners.push(partner_index);
112 }
113 }
114}
115
116pub type SubqueryPlanner<'p> = dyn Fn(&Statement, &[ScopedTable]) -> Result<(LogicalPlan, Vec<ColumnMetadata>), PlannerError>
117 + 'p;
118
119pub struct TypeChecker<'a, C: Catalog + ?Sized> {
134 catalog: &'a C,
135}
136
137impl<'a, C: Catalog + ?Sized> TypeChecker<'a, C> {
138 pub fn new(catalog: &'a C) -> Self {
140 Self { catalog }
141 }
142
143 pub fn catalog(&self) -> &'a C {
145 self.catalog
146 }
147
148 pub fn infer_type(
160 &self,
161 expr: &Expr,
162 table: &TableMetadata,
163 ) -> Result<TypedExpr, PlannerError> {
164 let scope = [ScopedTable::new(table.clone(), 0)];
165 self.infer_type_with_scope(expr, &scope, &|stmt, _outer| {
166 let planner = crate::planner::Planner::new(self.catalog);
167 let plan = planner.plan(stmt)?;
168 Ok((plan, Vec::new()))
169 })
170 }
171
172 pub fn infer_type_with_scope(
173 &self,
174 expr: &Expr,
175 scope: &[ScopedTable],
176 plan_subquery: &SubqueryPlanner<'_>,
177 ) -> Result<TypedExpr, PlannerError> {
178 let span = expr.span;
179 match &expr.kind {
180 ExprKind::Literal { literal: lit } => self.infer_literal_type(lit, span),
181
182 ExprKind::ColumnRef {
183 table: table_qualifier,
184 column,
185 } => self.infer_column_ref_type_with_scope(
186 scope,
187 table_qualifier.as_deref(),
188 column,
189 span,
190 ),
191
192 ExprKind::BinaryOp { left, op, right } => {
193 self.infer_binary_op_type_with_scope(left, *op, right, scope, plan_subquery, span)
194 }
195
196 ExprKind::UnaryOp { op, operand } => {
197 self.infer_unary_op_type_with_scope(*op, operand, scope, plan_subquery, span)
198 }
199
200 ExprKind::Case {
201 operand,
202 branches,
203 else_expr,
204 } => self.infer_case_type_with_scope(
205 operand.as_deref(),
206 branches,
207 else_expr.as_deref(),
208 scope,
209 plan_subquery,
210 span,
211 ),
212
213 ExprKind::FunctionCall {
214 name,
215 args,
216 distinct,
217 star,
218 order_by,
219 within_group,
220 filter,
221 over,
222 } => self.infer_function_call_type_with_scope(
223 name,
224 args,
225 *distinct,
226 *star,
227 order_by,
228 within_group,
229 filter.as_deref(),
230 over.as_ref(),
231 scope,
232 plan_subquery,
233 span,
234 ),
235
236 ExprKind::Cast { expr, target_type } => {
237 let typed_expr = self.infer_type_with_scope(expr, scope, plan_subquery)?;
238 Ok(TypedExpr::cast(
239 typed_expr,
240 ResolvedType::from_ast(target_type),
241 span,
242 ))
243 }
244
245 ExprKind::TryCast { expr, target_type } => {
246 let typed_expr = self.infer_type_with_scope(expr, scope, plan_subquery)?;
247 Ok(TypedExpr::try_cast(
248 typed_expr,
249 ResolvedType::from_ast(target_type),
250 span,
251 ))
252 }
253
254 ExprKind::Between {
255 expr,
256 low,
257 high,
258 negated,
259 } => self.infer_between_type_with_scope(
260 expr,
261 low,
262 high,
263 *negated,
264 scope,
265 plan_subquery,
266 span,
267 ),
268
269 ExprKind::Like {
270 expr,
271 pattern,
272 escape,
273 negated,
274 kind,
275 } => self.infer_like_type_with_scope(
276 expr,
277 pattern,
278 escape.as_deref(),
279 *negated,
280 *kind,
281 scope,
282 plan_subquery,
283 span,
284 ),
285
286 ExprKind::InList {
287 expr,
288 list,
289 negated,
290 } => {
291 self.infer_in_list_type_with_scope(expr, list, *negated, scope, plan_subquery, span)
292 }
293
294 ExprKind::IsNull { expr, negated } => {
295 self.infer_is_null_type_with_scope(expr, *negated, scope, plan_subquery, span)
296 }
297
298 ExprKind::Row { .. } => Err(PlannerError::unsupported_feature(
299 "standalone row constructor",
300 "v0.8.8 predicate context",
301 span,
302 )),
303
304 ExprKind::TruthPredicate {
305 expr,
306 value,
307 negated,
308 } => self.infer_truth_predicate_with_scope(
309 expr,
310 *value,
311 *negated,
312 scope,
313 plan_subquery,
314 span,
315 ),
316
317 ExprKind::IsDistinctFrom {
318 left,
319 right,
320 negated,
321 } => self.infer_distinct_predicate_with_scope(
322 left,
323 right,
324 *negated,
325 scope,
326 plan_subquery,
327 span,
328 ),
329
330 ExprKind::VectorLiteral { values } => self.infer_vector_literal_type(values, span),
331
332 ExprKind::ScalarSubquery { subquery } => {
333 let (plan, schema) = plan_subquery(subquery, scope)?;
334 let value_type = single_column_type(&schema, span)?;
335 Ok(TypedExpr {
336 kind: TypedExprKind::ScalarSubquery(Box::new(plan)),
337 resolved_type: value_type,
338 span,
339 })
340 }
341 ExprKind::InSubquery {
342 expr,
343 subquery,
344 negated,
345 } => {
346 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
347 let (plan, schema) = plan_subquery(subquery, scope)?;
348 let value_type = single_column_type(&schema, span)?;
349 self.check_comparison_op(&expr_typed.resolved_type, &value_type, span)?;
350 Ok(TypedExpr {
351 kind: TypedExprKind::InSubquery {
352 expr: Box::new(expr_typed),
353 subquery: Box::new(plan),
354 negated: *negated,
355 },
356 resolved_type: ResolvedType::Boolean,
357 span,
358 })
359 }
360 ExprKind::Exists { subquery, negated } => {
361 let (plan, _schema) = plan_subquery(subquery, scope)?;
362 Ok(TypedExpr {
363 kind: TypedExprKind::Exists {
364 subquery: Box::new(plan),
365 negated: *negated,
366 },
367 resolved_type: ResolvedType::Boolean,
368 span,
369 })
370 }
371 ExprKind::Quantified {
372 expr,
373 op,
374 quantifier,
375 subquery,
376 } => {
377 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
378 let (plan, schema) = plan_subquery(subquery, scope)?;
379 let value_type = single_column_type(&schema, span)?;
380 self.check_binary_op(*op, &expr_typed.resolved_type, &value_type, span)?;
381 Ok(TypedExpr {
382 kind: TypedExprKind::Quantified {
383 expr: Box::new(expr_typed),
384 op: *op,
385 quantifier: match quantifier {
386 AstQuantifier::Any => Quantifier::Any,
387 AstQuantifier::All => Quantifier::All,
388 },
389 subquery: Box::new(plan),
390 },
391 resolved_type: ResolvedType::Boolean,
392 span,
393 })
394 }
395 }
396 }
397
398 fn infer_literal_type(&self, lit: &Literal, span: Span) -> Result<TypedExpr, PlannerError> {
400 let (kind, resolved_type) = match lit {
401 Literal::Number(s) => {
402 let resolved_type = if s.contains('.') || s.contains('e') || s.contains('E') {
404 ResolvedType::Double
405 } else {
406 if s.parse::<i32>().is_ok() {
408 ResolvedType::Integer
409 } else {
410 ResolvedType::BigInt
411 }
412 };
413 (TypedExprKind::Literal(lit.clone()), resolved_type)
414 }
415 Literal::String(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Text),
416 Literal::Interval(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Interval),
417 Literal::Boolean(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Boolean),
418 Literal::Null => (TypedExprKind::Literal(lit.clone()), ResolvedType::Null),
419 };
420
421 Ok(TypedExpr {
422 kind,
423 resolved_type,
424 span,
425 })
426 }
427
428 #[allow(dead_code)]
430 fn infer_column_ref_type(
431 &self,
432 table: &TableMetadata,
433 column_name: &str,
434 span: Span,
435 ) -> Result<TypedExpr, PlannerError> {
436 let (column_index, column) = table
438 .columns
439 .iter()
440 .enumerate()
441 .find(|(_, c)| c.name == column_name)
442 .ok_or_else(|| PlannerError::ColumnNotFound {
443 column: column_name.to_string(),
444 table: table.name.clone(),
445 line: span.start.line,
446 col: span.start.column,
447 })?;
448
449 Ok(TypedExpr {
450 kind: TypedExprKind::ColumnRef {
451 table: table.name.clone(),
452 column: column_name.to_string(),
453 column_index,
454 },
455 resolved_type: column.data_type.clone(),
456 span,
457 })
458 }
459
460 fn infer_column_ref_type_with_scope(
461 &self,
462 scope: &[ScopedTable],
463 table_qualifier: Option<&str>,
464 column_name: &str,
465 span: Span,
466 ) -> Result<TypedExpr, PlannerError> {
467 let levels = scope
468 .iter()
469 .map(|table| table.scope_level)
470 .collect::<BTreeSet<_>>();
471 let mut qualifier_found = false;
472
473 for level in levels {
474 let candidates = scope
475 .iter()
476 .filter(|table| table.scope_level == level)
477 .filter(|table| {
478 table_qualifier.is_some()
479 || !table.hidden_unqualified_columns.contains(column_name)
480 })
481 .collect::<Vec<_>>();
482 if candidates.is_empty() {
483 continue;
484 }
485 if let Some(qualifier) = table_qualifier {
486 let qualified = candidates
487 .iter()
488 .filter(|table| table.table.name == qualifier)
489 .collect::<Vec<_>>();
490 match qualified.len() {
491 0 => continue,
492 1 => qualifier_found = true,
493 _ => {
494 return Err(PlannerError::ambiguous_column(
495 column_name,
496 qualified
497 .iter()
498 .map(|table| table.table.name.clone())
499 .collect(),
500 span,
501 ));
502 }
503 }
504 }
505
506 let mut matches = candidates.iter().filter(|table| {
510 table_qualifier.is_none_or(|qualifier| table.table.name == qualifier)
511 && table.column_position(column_name).is_some()
512 });
513 let found = matches.next();
514 let second = matches.next();
515
516 match (found, second) {
517 (Some(_), Some(_)) => {
518 return Err(PlannerError::ambiguous_column(
519 column_name,
520 candidates
521 .iter()
522 .filter(|table| table.column_position(column_name).is_some())
523 .map(|table| table.table.name.clone())
524 .collect(),
525 span,
526 ));
527 }
528 (None, _) => {
529 if table_qualifier.is_some() {
530 return Err(PlannerError::column_not_found(
533 column_name,
534 candidates
535 .first()
536 .map(|table| table.table.name.as_str())
537 .unwrap_or("unknown"),
538 span,
539 ));
540 }
541 continue;
544 }
545 (Some(scoped), None) => {
546 let column_index = scoped
547 .column_position(column_name)
548 .expect("filtered on the column being present");
549 let column = &scoped.table.columns[column_index];
550 let own_ref = TypedExpr {
551 kind: TypedExprKind::ColumnRef {
552 table: scoped.table.name.clone(),
553 column: column_name.to_string(),
554 column_index: scoped.start_index + column_index,
555 },
556 resolved_type: column.data_type.clone(),
557 span,
558 };
559
560 if table_qualifier.is_none()
565 && let Some(partner_indices) =
566 scoped.merged_column_partners.get(column_name)
567 {
568 let mut args = Vec::with_capacity(partner_indices.len() + 1);
569 args.push(own_ref);
570 args.extend(partner_indices.iter().map(|&partner_index| TypedExpr {
571 kind: TypedExprKind::ColumnRef {
572 table: scoped.table.name.clone(),
573 column: column_name.to_string(),
574 column_index: partner_index,
575 },
576 resolved_type: column.data_type.clone(),
577 span,
578 }));
579 return Ok(TypedExpr {
580 kind: TypedExprKind::FunctionCall {
581 name: "coalesce".to_string(),
582 args,
583 distinct: false,
584 star: false,
585 filter: None,
586 order_by: Vec::new(),
587 over: None,
588 },
589 resolved_type: column.data_type.clone(),
590 span,
591 });
592 }
593
594 return Ok(own_ref);
595 }
596 }
597 }
598
599 let table = scope
600 .iter()
601 .min_by_key(|table| table.scope_level)
602 .map(|table| table.table.name.clone())
603 .unwrap_or_else(|| "unknown".to_string());
604 if let Some(qualifier) = table_qualifier
605 && !qualifier_found
606 {
607 return Err(PlannerError::table_not_found(qualifier, span));
608 }
609 Err(PlannerError::column_not_found(column_name, table, span))
610 }
611
612 #[allow(dead_code)]
614 fn infer_binary_op_type(
615 &self,
616 left: &Expr,
617 op: BinaryOp,
618 right: &Expr,
619 table: &TableMetadata,
620 span: Span,
621 ) -> Result<TypedExpr, PlannerError> {
622 let left_typed = self.infer_type(left, table)?;
623 let right_typed = self.infer_type(right, table)?;
624
625 let result_type = self.check_binary_op(
626 op,
627 &left_typed.resolved_type,
628 &right_typed.resolved_type,
629 span,
630 )?;
631
632 if let Some(folded) =
633 fold_integral_binary(&left_typed, op, &right_typed, &result_type, span)
634 {
635 return Ok(folded);
636 }
637
638 Ok(TypedExpr {
639 kind: TypedExprKind::BinaryOp {
640 left: Box::new(left_typed),
641 op,
642 right: Box::new(right_typed),
643 },
644 resolved_type: result_type,
645 span,
646 })
647 }
648
649 fn infer_binary_op_type_with_scope(
650 &self,
651 left: &Expr,
652 op: BinaryOp,
653 right: &Expr,
654 scope: &[ScopedTable],
655 plan_subquery: &SubqueryPlanner<'_>,
656 span: Span,
657 ) -> Result<TypedExpr, PlannerError> {
658 if row_items(left).is_some() || row_items(right).is_some() {
659 let internal = match op {
660 BinaryOp::Eq => INTERNAL_ROW_EQ,
661 BinaryOp::Neq => INTERNAL_ROW_NEQ,
662 BinaryOp::Lt => INTERNAL_ROW_LT,
663 BinaryOp::LtEq => INTERNAL_ROW_LTEQ,
664 BinaryOp::Gt => INTERNAL_ROW_GT,
665 BinaryOp::GtEq => INTERNAL_ROW_GTEQ,
666 _ => {
667 return Err(PlannerError::invalid_operator(
668 format!("{op:?}"),
669 "Row",
670 span,
671 ));
672 }
673 };
674 let (mut left, right, width) =
675 self.infer_row_pair_with_scope(left, right, scope, plan_subquery, span)?;
676 left.extend(right);
677 return Ok(internal_predicate(
678 format!("{internal}:{width}"),
679 left,
680 span,
681 ));
682 }
683
684 let left_typed = self.infer_type_with_scope(left, scope, plan_subquery)?;
685 let right_typed = self.infer_type_with_scope(right, scope, plan_subquery)?;
686
687 let result_type = self.check_binary_op(
688 op,
689 &left_typed.resolved_type,
690 &right_typed.resolved_type,
691 span,
692 )?;
693
694 if let Some(folded) =
695 fold_integral_binary(&left_typed, op, &right_typed, &result_type, span)
696 {
697 return Ok(folded);
698 }
699
700 Ok(TypedExpr {
701 kind: TypedExprKind::BinaryOp {
702 left: Box::new(left_typed),
703 op,
704 right: Box::new(right_typed),
705 },
706 resolved_type: result_type,
707 span,
708 })
709 }
710
711 fn infer_case_type_with_scope(
712 &self,
713 operand: Option<&Expr>,
714 branches: &[crate::ast::expr::CaseWhen],
715 else_expr: Option<&Expr>,
716 scope: &[ScopedTable],
717 plan_subquery: &SubqueryPlanner<'_>,
718 span: Span,
719 ) -> Result<TypedExpr, PlannerError> {
720 if branches.is_empty() {
721 return Err(PlannerError::invalid_expression(
722 "CASE expression requires at least one WHEN branch",
723 ));
724 }
725 let typed_operand = operand
726 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
727 .transpose()?;
728 let mut typed_branches = Vec::with_capacity(branches.len());
729 let mut result_type = ResolvedType::Null;
730
731 for branch in branches {
732 let condition = self.infer_type_with_scope(&branch.when, scope, plan_subquery)?;
733 if let Some(operand) = &typed_operand {
734 self.check_comparison_op(
735 &operand.resolved_type,
736 &condition.resolved_type,
737 condition.span,
738 )?;
739 } else if !matches!(
740 condition.resolved_type,
741 ResolvedType::Boolean | ResolvedType::Null
742 ) {
743 return Err(PlannerError::type_mismatch(
744 "Boolean",
745 condition.resolved_type.type_name(),
746 condition.span,
747 ));
748 }
749
750 let result = self.infer_type_with_scope(&branch.then, scope, plan_subquery)?;
751 result_type =
752 self.common_case_result_type(&result_type, &result.resolved_type, result.span)?;
753 typed_branches.push(TypedCaseWhen {
754 when: condition,
755 then: result,
756 });
757 }
758
759 let mut typed_else = else_expr
760 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
761 .transpose()?;
762 if let Some(else_expr) = &typed_else {
763 result_type = self.common_case_result_type(
764 &result_type,
765 &else_expr.resolved_type,
766 else_expr.span,
767 )?;
768 }
769
770 for branch in &mut typed_branches {
771 coerce_case_result(&mut branch.then, &result_type);
772 }
773 if let Some(else_expr) = &mut typed_else {
774 coerce_case_result(else_expr, &result_type);
775 }
776
777 Ok(TypedExpr {
778 kind: TypedExprKind::Case {
779 operand: typed_operand.map(Box::new),
780 branches: typed_branches,
781 else_expr: typed_else.map(Box::new),
782 },
783 resolved_type: result_type,
784 span,
785 })
786 }
787
788 fn common_case_result_type(
789 &self,
790 current: &ResolvedType,
791 next: &ResolvedType,
792 span: Span,
793 ) -> Result<ResolvedType, PlannerError> {
794 if matches!(current, ResolvedType::Null) {
795 return Ok(next.clone());
796 }
797 if matches!(next, ResolvedType::Null) || current == next {
798 return Ok(current.clone());
799 }
800 if is_numeric_type(current) && is_numeric_type(next) {
801 return self.check_arithmetic_op(current, next, span);
802 }
803 Err(PlannerError::type_mismatch(
804 current.type_name(),
805 next.type_name(),
806 span,
807 ))
808 }
809
810 pub fn check_binary_op(
822 &self,
823 op: BinaryOp,
824 left: &ResolvedType,
825 right: &ResolvedType,
826 span: Span,
827 ) -> Result<ResolvedType, PlannerError> {
828 use BinaryOp::*;
829 use ResolvedType::*;
830
831 match op {
832 Add | Sub | Mul | Div => {
834 if let Some(result) = Self::temporal_arithmetic_type(op, left, right) {
835 return Ok(result);
836 }
837 if let Some(result) = decimal_arithmetic_type(op, left, right) {
838 return Ok(result);
839 }
840 let result = self.check_arithmetic_op(left, right, span)?;
841 Ok(result)
842 }
843
844 Mod => self.check_modulo_op(left, right, span),
846
847 BitAnd | BitOr | BitXor | ShiftLeft | ShiftRight => {
848 self.check_integral_op(left, right, span)
849 }
850
851 Eq | Neq | Lt | Gt | LtEq | GtEq => {
853 self.check_comparison_op(left, right, span)?;
854 Ok(Boolean)
855 }
856
857 And | Or => {
859 self.check_logical_op(left, right, span)?;
860 Ok(Boolean)
861 }
862
863 StringConcat => {
865 self.check_string_concat_op(left, right, span)?;
866 Ok(Text)
867 }
868 }
869 }
870
871 fn check_arithmetic_op(
873 &self,
874 left: &ResolvedType,
875 right: &ResolvedType,
876 span: Span,
877 ) -> Result<ResolvedType, PlannerError> {
878 use ResolvedType::*;
879
880 if matches!(left, Null) || matches!(right, Null) {
882 return Ok(Null);
883 }
884
885 match (left, right) {
887 (Integer, Integer) => Ok(Integer),
889 (Integer, BigInt) | (BigInt, Integer) | (BigInt, BigInt) => Ok(BigInt),
890 (Float, Float) => Ok(Float),
891 (Integer, Float)
894 | (Float, Integer)
895 | (Integer, Double)
896 | (Double, Integer)
897 | (BigInt, Float)
898 | (Float, BigInt)
899 | (BigInt, Double)
900 | (Double, BigInt)
901 | (Float, Double)
902 | (Double, Float)
903 | (Double, Double) => Ok(Double),
904
905 _ => Err(PlannerError::InvalidOperator {
906 op: "arithmetic".to_string(),
907 type_name: format!("{} and {}", left.type_name(), right.type_name()),
908 line: span.start.line,
909 column: span.start.column,
910 }),
911 }
912 }
913
914 fn temporal_arithmetic_type(
915 op: BinaryOp,
916 left: &ResolvedType,
917 right: &ResolvedType,
918 ) -> Option<ResolvedType> {
919 use ResolvedType::*;
920 match (op, left, right) {
921 (BinaryOp::Add | BinaryOp::Sub, Date, Interval) => Some(Date),
922 (BinaryOp::Add | BinaryOp::Sub, Timestamp, Interval) => Some(Timestamp),
923 (BinaryOp::Add | BinaryOp::Sub, Time, Interval) => Some(Time),
924 (BinaryOp::Add, Interval, Date) => Some(Date),
925 (BinaryOp::Add, Interval, Timestamp) => Some(Timestamp),
926 (BinaryOp::Add, Interval, Time) => Some(Time),
927 (BinaryOp::Sub, Date, Date)
928 | (BinaryOp::Sub, Timestamp, Timestamp)
929 | (BinaryOp::Sub, Time, Time)
930 | (BinaryOp::Add | BinaryOp::Sub, Interval, Interval) => Some(Interval),
931 _ => None,
932 }
933 }
934
935 fn check_modulo_op(
937 &self,
938 left: &ResolvedType,
939 right: &ResolvedType,
940 span: Span,
941 ) -> Result<ResolvedType, PlannerError> {
942 use ResolvedType::*;
943
944 if matches!(left, Null) || matches!(right, Null) {
945 return Ok(Null);
946 }
947
948 match (left, right) {
949 (Integer, Integer) => Ok(Integer),
950 (Integer, BigInt) | (BigInt, Integer) | (BigInt, BigInt) => Ok(BigInt),
951 _ => Err(PlannerError::InvalidOperator {
952 op: "modulo".to_string(),
953 type_name: format!("{} and {}", left.type_name(), right.type_name()),
954 line: span.start.line,
955 column: span.start.column,
956 }),
957 }
958 }
959
960 fn check_integral_op(
961 &self,
962 left: &ResolvedType,
963 right: &ResolvedType,
964 span: Span,
965 ) -> Result<ResolvedType, PlannerError> {
966 use ResolvedType::*;
967 if matches!(left, Null) || matches!(right, Null) {
968 return Ok(Null);
969 }
970 match (left, right) {
971 (Integer, Integer) => Ok(Integer),
972 (Integer | BigInt, Integer | BigInt) => Ok(BigInt),
973 _ => Err(PlannerError::invalid_operator(
974 "bitwise",
975 format!("{} and {}", left.type_name(), right.type_name()),
976 span,
977 )),
978 }
979 }
980
981 pub(crate) fn check_comparison_op(
983 &self,
984 left: &ResolvedType,
985 right: &ResolvedType,
986 span: Span,
987 ) -> Result<(), PlannerError> {
988 use ResolvedType::*;
989
990 if matches!(left, Null) || matches!(right, Null) {
992 return Ok(());
993 }
994
995 let compatible = match (left, right) {
997 (a, b) if a == b => true,
999
1000 (
1002 Integer | BigInt | Float | Double | Decimal { .. },
1003 Integer | BigInt | Float | Double | Decimal { .. },
1004 ) => true,
1005
1006 (Text, Text) => true,
1008
1009 (Boolean, Boolean) => true,
1011
1012 (Timestamp, Timestamp) => true,
1014 (Date, Date) | (Time, Time) | (Interval, Interval) => true,
1015
1016 (Vector { dimension: d1, .. }, Vector { dimension: d2, .. }) => d1 == d2,
1018
1019 _ => false,
1020 };
1021
1022 if compatible {
1023 Ok(())
1024 } else {
1025 Err(PlannerError::TypeMismatch {
1026 expected: left.type_name().to_string(),
1027 found: right.type_name().to_string(),
1028 line: span.start.line,
1029 column: span.start.column,
1030 })
1031 }
1032 }
1033
1034 fn check_logical_op(
1036 &self,
1037 left: &ResolvedType,
1038 right: &ResolvedType,
1039 span: Span,
1040 ) -> Result<(), PlannerError> {
1041 use ResolvedType::*;
1042
1043 let left_ok = matches!(left, Boolean | Null);
1045 let right_ok = matches!(right, Boolean | Null);
1046
1047 if !left_ok {
1048 return Err(PlannerError::TypeMismatch {
1049 expected: "Boolean".to_string(),
1050 found: left.type_name().to_string(),
1051 line: span.start.line,
1052 column: span.start.column,
1053 });
1054 }
1055
1056 if !right_ok {
1057 return Err(PlannerError::TypeMismatch {
1058 expected: "Boolean".to_string(),
1059 found: right.type_name().to_string(),
1060 line: span.start.line,
1061 column: span.start.column,
1062 });
1063 }
1064
1065 Ok(())
1066 }
1067
1068 fn check_string_concat_op(
1070 &self,
1071 left: &ResolvedType,
1072 right: &ResolvedType,
1073 span: Span,
1074 ) -> Result<(), PlannerError> {
1075 use ResolvedType::*;
1076
1077 let left_ok = matches!(left, Text | Null);
1079 let right_ok = matches!(right, Text | Null);
1080
1081 if !left_ok {
1082 return Err(PlannerError::TypeMismatch {
1083 expected: "Text".to_string(),
1084 found: left.type_name().to_string(),
1085 line: span.start.line,
1086 column: span.start.column,
1087 });
1088 }
1089
1090 if !right_ok {
1091 return Err(PlannerError::TypeMismatch {
1092 expected: "Text".to_string(),
1093 found: right.type_name().to_string(),
1094 line: span.start.line,
1095 column: span.start.column,
1096 });
1097 }
1098
1099 Ok(())
1100 }
1101
1102 #[allow(dead_code)]
1104 fn infer_unary_op_type(
1105 &self,
1106 op: UnaryOp,
1107 operand: &Expr,
1108 table: &TableMetadata,
1109 span: Span,
1110 ) -> Result<TypedExpr, PlannerError> {
1111 let operand_typed = self.infer_type(operand, table)?;
1112
1113 let result_type = match op {
1114 UnaryOp::Not => {
1115 if !matches!(
1117 operand_typed.resolved_type,
1118 ResolvedType::Boolean | ResolvedType::Null
1119 ) {
1120 return Err(PlannerError::TypeMismatch {
1121 expected: "Boolean".to_string(),
1122 found: operand_typed.resolved_type.type_name().to_string(),
1123 line: span.start.line,
1124 column: span.start.column,
1125 });
1126 }
1127 ResolvedType::Boolean
1128 }
1129 UnaryOp::Minus => {
1130 match &operand_typed.resolved_type {
1132 ResolvedType::Integer => ResolvedType::Integer,
1133 ResolvedType::BigInt => ResolvedType::BigInt,
1134 ResolvedType::Float => ResolvedType::Float,
1135 ResolvedType::Double => ResolvedType::Double,
1136 ResolvedType::Decimal { precision, scale } => ResolvedType::Decimal {
1137 precision: *precision,
1138 scale: *scale,
1139 },
1140 ResolvedType::Null => ResolvedType::Null,
1141 other => {
1142 return Err(PlannerError::InvalidOperator {
1143 op: "unary minus".to_string(),
1144 type_name: other.type_name().to_string(),
1145 line: span.start.line,
1146 column: span.start.column,
1147 });
1148 }
1149 }
1150 }
1151 UnaryOp::BitNot => match &operand_typed.resolved_type {
1152 ResolvedType::Integer => ResolvedType::Integer,
1153 ResolvedType::BigInt => ResolvedType::BigInt,
1154 ResolvedType::Null => ResolvedType::Null,
1155 other => {
1156 return Err(PlannerError::InvalidOperator {
1157 op: "bitwise not".to_string(),
1158 type_name: other.type_name().to_string(),
1159 line: span.start.line,
1160 column: span.start.column,
1161 });
1162 }
1163 },
1164 };
1165
1166 Ok(TypedExpr {
1167 kind: TypedExprKind::UnaryOp {
1168 op,
1169 operand: Box::new(operand_typed),
1170 },
1171 resolved_type: result_type,
1172 span,
1173 })
1174 }
1175
1176 fn infer_unary_op_type_with_scope(
1177 &self,
1178 op: UnaryOp,
1179 operand: &Expr,
1180 scope: &[ScopedTable],
1181 plan_subquery: &SubqueryPlanner<'_>,
1182 span: Span,
1183 ) -> Result<TypedExpr, PlannerError> {
1184 let operand_typed = self.infer_type_with_scope(operand, scope, plan_subquery)?;
1185
1186 let result_type = match op {
1187 UnaryOp::Not => {
1188 if !matches!(
1189 operand_typed.resolved_type,
1190 ResolvedType::Boolean | ResolvedType::Null
1191 ) {
1192 return Err(PlannerError::TypeMismatch {
1193 expected: "Boolean".to_string(),
1194 found: operand_typed.resolved_type.type_name().to_string(),
1195 line: span.start.line,
1196 column: span.start.column,
1197 });
1198 }
1199 ResolvedType::Boolean
1200 }
1201 UnaryOp::Minus => match &operand_typed.resolved_type {
1202 ResolvedType::Integer => ResolvedType::Integer,
1203 ResolvedType::BigInt => ResolvedType::BigInt,
1204 ResolvedType::Float => ResolvedType::Float,
1205 ResolvedType::Double => ResolvedType::Double,
1206 ResolvedType::Decimal { precision, scale } => ResolvedType::Decimal {
1207 precision: *precision,
1208 scale: *scale,
1209 },
1210 ResolvedType::Null => ResolvedType::Null,
1211 other => {
1212 return Err(PlannerError::InvalidOperator {
1213 op: "unary minus".to_string(),
1214 type_name: other.type_name().to_string(),
1215 line: span.start.line,
1216 column: span.start.column,
1217 });
1218 }
1219 },
1220 UnaryOp::BitNot => match &operand_typed.resolved_type {
1221 ResolvedType::Integer => ResolvedType::Integer,
1222 ResolvedType::BigInt => ResolvedType::BigInt,
1223 ResolvedType::Null => ResolvedType::Null,
1224 other => {
1225 return Err(PlannerError::InvalidOperator {
1226 op: "bitwise not".to_string(),
1227 type_name: other.type_name().to_string(),
1228 line: span.start.line,
1229 column: span.start.column,
1230 });
1231 }
1232 },
1233 };
1234
1235 Ok(TypedExpr {
1236 kind: TypedExprKind::UnaryOp {
1237 op,
1238 operand: Box::new(operand_typed),
1239 },
1240 resolved_type: result_type,
1241 span,
1242 })
1243 }
1244
1245 #[allow(dead_code)]
1247 fn infer_function_call_type(
1248 &self,
1249 name: &str,
1250 args: &[Expr],
1251 distinct: bool,
1252 star: bool,
1253 table: &TableMetadata,
1254 span: Span,
1255 ) -> Result<TypedExpr, PlannerError> {
1256 let typed_args: Vec<TypedExpr> = args
1258 .iter()
1259 .map(|arg| self.infer_type(arg, table))
1260 .collect::<Result<Vec<_>, _>>()?;
1261
1262 let result_type = self.check_function_call(name, &typed_args, distinct, star, span)?;
1264
1265 Ok(TypedExpr {
1266 kind: TypedExprKind::FunctionCall {
1267 name: name.to_string(),
1268 args: typed_args,
1269 distinct,
1270 star,
1271 filter: None,
1272 order_by: Vec::new(),
1273 over: None,
1274 },
1275 resolved_type: result_type,
1276 span,
1277 })
1278 }
1279
1280 #[allow(clippy::too_many_arguments)]
1281 fn infer_function_call_type_with_scope(
1282 &self,
1283 name: &str,
1284 args: &[Expr],
1285 distinct: bool,
1286 star: bool,
1287 order_by: &[crate::ast::dml::OrderByExpr],
1288 within_group: &[crate::ast::dml::OrderByExpr],
1289 filter: Option<&Expr>,
1290 over: Option<&WindowSpec>,
1291 scope: &[ScopedTable],
1292 plan_subquery: &SubqueryPlanner<'_>,
1293 span: Span,
1294 ) -> Result<TypedExpr, PlannerError> {
1295 let lower_name = name.to_ascii_lowercase();
1296 self.validate_aggregate_clause_placement(
1297 &lower_name,
1298 distinct,
1299 order_by,
1300 within_group,
1301 filter,
1302 over.is_some(),
1303 span,
1304 )?;
1305 if over.is_some() {
1306 match lower_name.as_str() {
1307 "lag" | "lead" => {
1308 validate_offset_window_call(name, args.len(), distinct, star)?;
1309 }
1310 "first_value" | "last_value" | "ntile" => {
1311 validate_exact_window_call(name, args.len(), 1, distinct, star)?;
1312 }
1313 "nth_value" => {
1314 validate_exact_window_call(name, args.len(), 2, distinct, star)?;
1315 }
1316 "percent_rank" | "cume_dist" => {
1317 validate_exact_window_call(name, args.len(), 0, distinct, star)?;
1318 }
1319 _ => {}
1320 }
1321 }
1322
1323 let mut typed_args: Vec<TypedExpr> = args
1324 .iter()
1325 .map(|arg| self.infer_type_with_scope(arg, scope, plan_subquery))
1326 .collect::<Result<Vec<_>, _>>()?;
1327
1328 let order_by_source = if within_group.is_empty() {
1331 order_by
1332 } else {
1333 within_group
1334 };
1335 let typed_order_by = order_by_source
1336 .iter()
1337 .map(|order| {
1338 let expr = self.infer_type_with_scope(&order.expr, scope, plan_subquery)?;
1339 if super::typed_expr_contains_aggregate(&expr) {
1340 return Err(PlannerError::invalid_expression(
1341 "aggregate functions are not allowed in aggregate ORDER BY".to_string(),
1342 ));
1343 }
1344 if super::typed_expr_contains_window(&expr) {
1345 return Err(PlannerError::invalid_expression(
1346 "window functions are not allowed in aggregate ORDER BY".to_string(),
1347 ));
1348 }
1349 Ok(SortExpr::new(
1350 expr,
1351 order.asc.unwrap_or(true),
1352 order.nulls_first.unwrap_or(false),
1353 ))
1354 })
1355 .collect::<Result<Vec<_>, PlannerError>>()?;
1356
1357 let typed_filter = filter
1358 .map(|predicate| {
1359 let typed = self.infer_type_with_scope(predicate, scope, plan_subquery)?;
1360 if super::typed_expr_contains_aggregate(&typed) {
1361 return Err(PlannerError::invalid_expression(
1362 "aggregate functions are not allowed in FILTER".to_string(),
1363 ));
1364 }
1365 if super::typed_expr_contains_window(&typed) {
1366 return Err(PlannerError::invalid_expression(
1367 "window functions are not allowed in FILTER".to_string(),
1368 ));
1369 }
1370 if !matches!(
1371 typed.resolved_type,
1372 ResolvedType::Boolean | ResolvedType::Null
1373 ) {
1374 return Err(PlannerError::type_mismatch(
1375 "BOOLEAN FILTER predicate",
1376 typed.resolved_type.type_name(),
1377 typed.span,
1378 ));
1379 }
1380 Ok(Box::new(typed))
1381 })
1382 .transpose()?;
1383
1384 if distinct && !typed_order_by.is_empty() {
1388 for sort in &typed_order_by {
1389 let key = super::distinct_on_expr_signature(&sort.expr);
1390 let appears = typed_args
1391 .iter()
1392 .any(|arg| super::distinct_on_expr_signature(arg) == key);
1393 if !appears {
1394 return Err(PlannerError::invalid_expression(
1395 "in an aggregate with DISTINCT, ORDER BY expressions must appear in \
1396 the argument list"
1397 .to_string(),
1398 ));
1399 }
1400 }
1401 }
1402
1403 let result_type = if over.is_some() {
1404 match lower_name.as_str() {
1405 "lag" | "lead" => self.infer_offset_window_result_type(name, &mut typed_args)?,
1406 "first_value" | "last_value" => typed_args[0].resolved_type.clone(),
1407 "nth_value" => {
1408 validate_positive_integer_argument(name, &typed_args[1])?;
1409 typed_args[0].resolved_type.clone()
1410 }
1411 "ntile" => {
1412 validate_positive_integer_argument(name, &typed_args[0])?;
1413 ResolvedType::BigInt
1414 }
1415 "percent_rank" | "cume_dist" => ResolvedType::Double,
1416 "row_number" | "rank" | "dense_rank" => {
1417 if !typed_args.is_empty() || distinct || star {
1418 return Err(PlannerError::invalid_expression(format!(
1419 "{}() window function takes no arguments",
1420 name.to_ascii_uppercase()
1421 )));
1422 }
1423 ResolvedType::BigInt
1424 }
1425 name if is_aggregate_name(name) => {
1426 self.check_function_call(name, &typed_args, distinct, star, span)?
1427 }
1428 _ => {
1429 return Err(PlannerError::unsupported_feature(
1430 format!("function '{}' with OVER", name),
1431 "future",
1432 span,
1433 ));
1434 }
1435 }
1436 } else if matches!(lower_name.as_str(), "percentile_disc" | "percentile_cont") {
1437 self.check_percentile(&lower_name, &typed_args, &typed_order_by, span)?
1438 } else if lower_name == "mode" && !within_group.is_empty() {
1439 if !typed_args.is_empty() || typed_order_by.len() != 1 {
1440 return Err(PlannerError::invalid_expression(
1441 "MODE requires no arguments and exactly one WITHIN GROUP sort expression"
1442 .to_string(),
1443 ));
1444 }
1445 typed_order_by[0].expr.resolved_type.clone()
1446 } else {
1447 self.check_function_call(name, &typed_args, distinct, star, span)?
1448 };
1449
1450 let typed_over = over
1451 .map(|window| {
1452 if let Some(base) = &window.base {
1453 return Err(PlannerError::invalid_expression(format!(
1454 "named window '{base}' was not resolved in its query block"
1455 )));
1456 }
1457 let partition_by = window
1458 .partition_by
1459 .iter()
1460 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
1461 .collect::<Result<Vec<_>, _>>()?;
1462 let order_by = window
1463 .order_by
1464 .iter()
1465 .map(|order| {
1466 let expr = self.infer_type_with_scope(&order.expr, scope, plan_subquery)?;
1467 Ok(SortExpr::new(
1468 expr,
1469 order.asc.unwrap_or(true),
1470 order.nulls_first.unwrap_or(false),
1471 ))
1472 })
1473 .collect::<Result<Vec<_>, PlannerError>>()?;
1474 if let Some(frame) = &window.frame {
1475 validate_window_frame(&lower_name, frame, &order_by)?;
1476 }
1477 Ok(TypedWindowSpec {
1478 partition_by,
1479 order_by,
1480 frame: window.frame.clone(),
1481 })
1482 })
1483 .transpose()?;
1484
1485 Ok(TypedExpr {
1486 kind: TypedExprKind::FunctionCall {
1487 name: name.to_string(),
1488 args: typed_args,
1489 distinct,
1490 star,
1491 filter: typed_filter,
1492 order_by: typed_order_by,
1493 over: typed_over,
1494 },
1495 resolved_type: result_type,
1496 span,
1497 })
1498 }
1499
1500 #[allow(clippy::too_many_arguments)]
1503 fn validate_aggregate_clause_placement(
1504 &self,
1505 lower_name: &str,
1506 distinct: bool,
1507 order_by: &[crate::ast::dml::OrderByExpr],
1508 within_group: &[crate::ast::dml::OrderByExpr],
1509 filter: Option<&Expr>,
1510 has_over: bool,
1511 span: Span,
1512 ) -> Result<(), PlannerError> {
1513 let is_ordered_set = is_ordered_set_aggregate_name(lower_name);
1514 let is_aggregate = is_aggregate_name(lower_name);
1515
1516 if let Some(filter) = filter {
1517 if has_over {
1518 return Err(PlannerError::unsupported_feature(
1522 "FILTER on a window function call",
1523 "future",
1524 span,
1525 ));
1526 }
1527 if !is_aggregate {
1528 return Err(PlannerError::invalid_expression(format!(
1529 "FILTER (WHERE ...) is only valid for aggregate functions, not '{lower_name}'"
1530 )));
1531 }
1532 if super::expr_contains_subquery(filter) {
1533 return Err(PlannerError::unsupported_feature(
1534 "subquery in aggregate FILTER",
1535 "future",
1536 filter.span,
1537 ));
1538 }
1539 }
1540
1541 if !within_group.is_empty() {
1542 if has_over {
1543 return Err(PlannerError::invalid_expression(
1545 "WITHIN GROUP cannot be combined with OVER".to_string(),
1546 ));
1547 }
1548 if !is_ordered_set {
1549 return Err(PlannerError::invalid_expression(format!(
1550 "WITHIN GROUP is only valid for ordered-set aggregate functions, \
1551 not '{lower_name}'"
1552 )));
1553 }
1554 if distinct {
1555 return Err(PlannerError::invalid_expression(
1556 "DISTINCT is not supported with WITHIN GROUP".to_string(),
1557 ));
1558 }
1559 if within_group
1560 .iter()
1561 .any(|order| super::expr_contains_subquery(&order.expr))
1562 {
1563 return Err(PlannerError::unsupported_feature(
1564 "subquery in aggregate ORDER BY",
1565 "future",
1566 span,
1567 ));
1568 }
1569 }
1570
1571 if !order_by.is_empty() {
1572 if has_over {
1573 return Err(PlannerError::invalid_expression(
1576 "aggregate ORDER BY cannot be combined with OVER".to_string(),
1577 ));
1578 }
1579 if !is_aggregate || is_ordered_set {
1580 return Err(PlannerError::invalid_expression(format!(
1581 "ORDER BY in the argument list is only valid for aggregate functions, \
1582 not '{lower_name}'"
1583 )));
1584 }
1585 if order_by
1586 .iter()
1587 .any(|order| super::expr_contains_subquery(&order.expr))
1588 {
1589 return Err(PlannerError::unsupported_feature(
1590 "subquery in aggregate ORDER BY",
1591 "future",
1592 span,
1593 ));
1594 }
1595 }
1596
1597 if matches!(lower_name, "percentile_disc" | "percentile_cont") && within_group.is_empty() {
1598 return Err(PlannerError::invalid_expression(format!(
1599 "WITHIN GROUP (ORDER BY ...) is required for {}",
1600 lower_name.to_ascii_uppercase()
1601 )));
1602 }
1603
1604 Ok(())
1605 }
1606
1607 fn check_percentile(
1611 &self,
1612 name: &str,
1613 args: &[TypedExpr],
1614 order_by: &[SortExpr],
1615 span: Span,
1616 ) -> Result<ResolvedType, PlannerError> {
1617 if args.len() != 1 {
1618 return Err(PlannerError::type_mismatch(
1619 "1 argument",
1620 format!("{} arguments", args.len()),
1621 span,
1622 ));
1623 }
1624 let _ = percentile_fraction_named(name, &args[0])?;
1625 if order_by.len() != 1 {
1626 return Err(PlannerError::invalid_expression(format!(
1627 "{} requires WITHIN GROUP (ORDER BY ...) with exactly one sort expression",
1628 name.to_ascii_uppercase()
1629 )));
1630 }
1631 Ok(order_by[0].expr.resolved_type.clone())
1632 }
1633
1634 fn infer_offset_window_result_type(
1635 &self,
1636 name: &str,
1637 args: &mut [TypedExpr],
1638 ) -> Result<ResolvedType, PlannerError> {
1639 if let Some(offset) = args.get(1)
1640 && !matches!(
1641 offset.resolved_type,
1642 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
1643 )
1644 {
1645 return Err(PlannerError::type_mismatch(
1646 "INTEGER offset",
1647 offset.resolved_type.type_name(),
1648 offset.span,
1649 ));
1650 }
1651
1652 let value_type = args
1653 .first()
1654 .map(|arg| arg.resolved_type.clone())
1655 .ok_or_else(|| {
1656 PlannerError::invalid_expression(format!(
1657 "{}() window function expects 1 to 3 arguments",
1658 name.to_ascii_uppercase()
1659 ))
1660 })?;
1661 let result_type = if let Some(default) = args.get(2) {
1662 self.common_compatible_result_type(&value_type, &default.resolved_type, default.span)?
1663 } else {
1664 value_type
1665 };
1666
1667 coerce_compatible_result(&mut args[0], &result_type);
1668 if let Some(default) = args.get_mut(2) {
1669 coerce_compatible_result(default, &result_type);
1670 }
1671
1672 Ok(result_type)
1673 }
1674
1675 fn common_compatible_result_type(
1676 &self,
1677 current: &ResolvedType,
1678 next: &ResolvedType,
1679 span: Span,
1680 ) -> Result<ResolvedType, PlannerError> {
1681 if matches!(current, ResolvedType::Null) {
1682 return Ok(next.clone());
1683 }
1684 if matches!(next, ResolvedType::Null) || current == next {
1685 return Ok(current.clone());
1686 }
1687 if is_numeric_type(current) && is_numeric_type(next) {
1688 return self.check_arithmetic_op(current, next, span);
1689 }
1690 if next.can_cast_to(current) {
1691 return Ok(current.clone());
1692 }
1693 if current.can_cast_to(next) {
1694 return Ok(next.clone());
1695 }
1696 Err(PlannerError::type_mismatch(
1697 current.type_name(),
1698 next.type_name(),
1699 span,
1700 ))
1701 }
1702
1703 #[allow(dead_code)]
1705 fn infer_between_type(
1706 &self,
1707 expr: &Expr,
1708 low: &Expr,
1709 high: &Expr,
1710 negated: bool,
1711 table: &TableMetadata,
1712 span: Span,
1713 ) -> Result<TypedExpr, PlannerError> {
1714 let expr_typed = self.infer_type(expr, table)?;
1715 let low_typed = self.infer_type(low, table)?;
1716 let high_typed = self.infer_type(high, table)?;
1717
1718 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
1720 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
1721
1722 Ok(TypedExpr {
1723 kind: TypedExprKind::Between {
1724 expr: Box::new(expr_typed),
1725 low: Box::new(low_typed),
1726 high: Box::new(high_typed),
1727 negated,
1728 },
1729 resolved_type: ResolvedType::Boolean,
1730 span,
1731 })
1732 }
1733
1734 #[allow(clippy::too_many_arguments)]
1735 fn infer_between_type_with_scope(
1736 &self,
1737 expr: &Expr,
1738 low: &Expr,
1739 high: &Expr,
1740 negated: bool,
1741 scope: &[ScopedTable],
1742 plan_subquery: &SubqueryPlanner<'_>,
1743 span: Span,
1744 ) -> Result<TypedExpr, PlannerError> {
1745 if row_items(expr).is_some() || row_items(low).is_some() || row_items(high).is_some() {
1746 let expr_typed = self.infer_row_operand_with_scope(expr, scope, plan_subquery)?;
1747 let low_typed = self.infer_row_operand_with_scope(low, scope, plan_subquery)?;
1748 let high_typed = self.infer_row_operand_with_scope(high, scope, plan_subquery)?;
1749 let width = expr_typed.len();
1750 self.check_row_arity(width, low_typed.len(), span)?;
1751 self.check_row_arity(width, high_typed.len(), span)?;
1752 self.check_row_types(&expr_typed, &low_typed, span)?;
1753 self.check_row_types(&expr_typed, &high_typed, span)?;
1754 let mut args = expr_typed;
1755 args.extend(low_typed);
1756 args.extend(high_typed);
1757 return Ok(internal_predicate(
1758 format!("{INTERNAL_ROW_BETWEEN}:{width}:{}", u8::from(negated)),
1759 args,
1760 span,
1761 ));
1762 }
1763
1764 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1765 let low_typed = self.infer_type_with_scope(low, scope, plan_subquery)?;
1766 let high_typed = self.infer_type_with_scope(high, scope, plan_subquery)?;
1767 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
1768 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
1769
1770 Ok(TypedExpr {
1771 kind: TypedExprKind::Between {
1772 expr: Box::new(expr_typed),
1773 low: Box::new(low_typed),
1774 high: Box::new(high_typed),
1775 negated,
1776 },
1777 resolved_type: ResolvedType::Boolean,
1778 span,
1779 })
1780 }
1781
1782 #[allow(dead_code)]
1784 #[allow(clippy::too_many_arguments)]
1785 fn infer_like_type(
1786 &self,
1787 expr: &Expr,
1788 pattern: &Expr,
1789 escape: Option<&Expr>,
1790 negated: bool,
1791 kind: PatternMatchKind,
1792 table: &TableMetadata,
1793 span: Span,
1794 ) -> Result<TypedExpr, PlannerError> {
1795 let expr_typed = self.infer_type(expr, table)?;
1796 let pattern_typed = self.infer_type(pattern, table)?;
1797
1798 if !matches!(
1800 expr_typed.resolved_type,
1801 ResolvedType::Text | ResolvedType::Null
1802 ) {
1803 return Err(PlannerError::TypeMismatch {
1804 expected: "Text".to_string(),
1805 found: expr_typed.resolved_type.type_name().to_string(),
1806 line: expr.span.start.line,
1807 column: expr.span.start.column,
1808 });
1809 }
1810
1811 if !matches!(
1813 pattern_typed.resolved_type,
1814 ResolvedType::Text | ResolvedType::Null
1815 ) {
1816 return Err(PlannerError::TypeMismatch {
1817 expected: "Text".to_string(),
1818 found: pattern_typed.resolved_type.type_name().to_string(),
1819 line: pattern.span.start.line,
1820 column: pattern.span.start.column,
1821 });
1822 }
1823
1824 let escape_typed = if let Some(esc) = escape {
1825 let typed = self.infer_type(esc, table)?;
1826 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
1827 return Err(PlannerError::TypeMismatch {
1828 expected: "Text".to_string(),
1829 found: typed.resolved_type.type_name().to_string(),
1830 line: esc.span.start.line,
1831 column: esc.span.start.column,
1832 });
1833 }
1834 Some(Box::new(typed))
1835 } else {
1836 None
1837 };
1838
1839 Ok(TypedExpr {
1840 kind: TypedExprKind::Like {
1841 expr: Box::new(expr_typed),
1842 pattern: Box::new(pattern_typed),
1843 escape: escape_typed,
1844 negated,
1845 kind,
1846 },
1847 resolved_type: ResolvedType::Boolean,
1848 span,
1849 })
1850 }
1851
1852 #[allow(clippy::too_many_arguments)]
1853 #[allow(clippy::too_many_arguments)]
1854 fn infer_like_type_with_scope(
1855 &self,
1856 expr: &Expr,
1857 pattern: &Expr,
1858 escape: Option<&Expr>,
1859 negated: bool,
1860 kind: PatternMatchKind,
1861 scope: &[ScopedTable],
1862 plan_subquery: &SubqueryPlanner<'_>,
1863 span: Span,
1864 ) -> Result<TypedExpr, PlannerError> {
1865 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1866 let pattern_typed = self.infer_type_with_scope(pattern, scope, plan_subquery)?;
1867
1868 if !matches!(
1869 expr_typed.resolved_type,
1870 ResolvedType::Text | ResolvedType::Null
1871 ) {
1872 return Err(PlannerError::TypeMismatch {
1873 expected: "Text".to_string(),
1874 found: expr_typed.resolved_type.type_name().to_string(),
1875 line: expr.span.start.line,
1876 column: expr.span.start.column,
1877 });
1878 }
1879
1880 if !matches!(
1881 pattern_typed.resolved_type,
1882 ResolvedType::Text | ResolvedType::Null
1883 ) {
1884 return Err(PlannerError::TypeMismatch {
1885 expected: "Text".to_string(),
1886 found: pattern_typed.resolved_type.type_name().to_string(),
1887 line: pattern.span.start.line,
1888 column: pattern.span.start.column,
1889 });
1890 }
1891
1892 let escape_typed = if let Some(esc) = escape {
1893 let typed = self.infer_type_with_scope(esc, scope, plan_subquery)?;
1894 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
1895 return Err(PlannerError::TypeMismatch {
1896 expected: "Text".to_string(),
1897 found: typed.resolved_type.type_name().to_string(),
1898 line: esc.span.start.line,
1899 column: esc.span.start.column,
1900 });
1901 }
1902 Some(Box::new(typed))
1903 } else {
1904 None
1905 };
1906
1907 Ok(TypedExpr {
1908 kind: TypedExprKind::Like {
1909 expr: Box::new(expr_typed),
1910 pattern: Box::new(pattern_typed),
1911 escape: escape_typed,
1912 negated,
1913 kind,
1914 },
1915 resolved_type: ResolvedType::Boolean,
1916 span,
1917 })
1918 }
1919
1920 #[allow(dead_code)]
1922 fn infer_in_list_type(
1923 &self,
1924 expr: &Expr,
1925 list: &[Expr],
1926 negated: bool,
1927 table: &TableMetadata,
1928 span: Span,
1929 ) -> Result<TypedExpr, PlannerError> {
1930 let expr_typed = self.infer_type(expr, table)?;
1931
1932 let typed_list: Vec<TypedExpr> = list
1933 .iter()
1934 .map(|item| {
1935 let typed = self.infer_type(item, table)?;
1936 self.check_comparison_op(
1938 &expr_typed.resolved_type,
1939 &typed.resolved_type,
1940 item.span,
1941 )?;
1942 Ok(typed)
1943 })
1944 .collect::<Result<Vec<_>, PlannerError>>()?;
1945
1946 Ok(TypedExpr {
1947 kind: TypedExprKind::InList {
1948 expr: Box::new(expr_typed),
1949 list: typed_list,
1950 negated,
1951 },
1952 resolved_type: ResolvedType::Boolean,
1953 span,
1954 })
1955 }
1956
1957 fn infer_in_list_type_with_scope(
1958 &self,
1959 expr: &Expr,
1960 list: &[Expr],
1961 negated: bool,
1962 scope: &[ScopedTable],
1963 plan_subquery: &SubqueryPlanner<'_>,
1964 span: Span,
1965 ) -> Result<TypedExpr, PlannerError> {
1966 if row_items(expr).is_some() || list.iter().any(|item| row_items(item).is_some()) {
1967 let mut args = self.infer_row_operand_with_scope(expr, scope, plan_subquery)?;
1968 let width = args.len();
1969 for item in list {
1970 let typed = self.infer_row_operand_with_scope(item, scope, plan_subquery)?;
1971 self.check_row_arity(width, typed.len(), item.span)?;
1972 self.check_row_types(&args[..width], &typed, item.span)?;
1973 args.extend(typed);
1974 }
1975 return Ok(internal_predicate(
1976 format!("{INTERNAL_ROW_IN}:{width}:{}", u8::from(negated)),
1977 args,
1978 span,
1979 ));
1980 }
1981
1982 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1983
1984 let typed_list: Vec<TypedExpr> = list
1985 .iter()
1986 .map(|item| {
1987 let typed = self.infer_type_with_scope(item, scope, plan_subquery)?;
1988 self.check_comparison_op(
1989 &expr_typed.resolved_type,
1990 &typed.resolved_type,
1991 item.span,
1992 )?;
1993 Ok(typed)
1994 })
1995 .collect::<Result<Vec<_>, PlannerError>>()?;
1996
1997 Ok(TypedExpr {
1998 kind: TypedExprKind::InList {
1999 expr: Box::new(expr_typed),
2000 list: typed_list,
2001 negated,
2002 },
2003 resolved_type: ResolvedType::Boolean,
2004 span,
2005 })
2006 }
2007
2008 fn infer_truth_predicate_with_scope(
2009 &self,
2010 expr: &Expr,
2011 value: TruthValue,
2012 negated: bool,
2013 scope: &[ScopedTable],
2014 plan_subquery: &SubqueryPlanner<'_>,
2015 span: Span,
2016 ) -> Result<TypedExpr, PlannerError> {
2017 let typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
2018 if !matches!(
2019 typed.resolved_type,
2020 ResolvedType::Boolean | ResolvedType::Null
2021 ) {
2022 return Err(PlannerError::type_mismatch(
2023 "Boolean",
2024 typed.resolved_type.type_name(),
2025 expr.span,
2026 ));
2027 }
2028 let name = match value {
2029 TruthValue::True => INTERNAL_TRUTH_TRUE,
2030 TruthValue::False => INTERNAL_TRUTH_FALSE,
2031 TruthValue::Unknown => INTERNAL_TRUTH_UNKNOWN,
2032 };
2033 Ok(internal_predicate(
2034 format!("{name}:{}", u8::from(negated)),
2035 vec![typed],
2036 span,
2037 ))
2038 }
2039
2040 fn infer_distinct_predicate_with_scope(
2041 &self,
2042 left: &Expr,
2043 right: &Expr,
2044 negated: bool,
2045 scope: &[ScopedTable],
2046 plan_subquery: &SubqueryPlanner<'_>,
2047 span: Span,
2048 ) -> Result<TypedExpr, PlannerError> {
2049 let (mut left, right, width) =
2050 self.infer_row_pair_with_scope(left, right, scope, plan_subquery, span)?;
2051 left.extend(right);
2052 Ok(internal_predicate(
2053 format!("{INTERNAL_ROW_DISTINCT}:{width}:{}", u8::from(negated)),
2054 left,
2055 span,
2056 ))
2057 }
2058
2059 fn infer_row_pair_with_scope(
2060 &self,
2061 left: &Expr,
2062 right: &Expr,
2063 scope: &[ScopedTable],
2064 plan_subquery: &SubqueryPlanner<'_>,
2065 span: Span,
2066 ) -> Result<(Vec<TypedExpr>, Vec<TypedExpr>, usize), PlannerError> {
2067 let left = self.infer_row_operand_with_scope(left, scope, plan_subquery)?;
2068 let right = self.infer_row_operand_with_scope(right, scope, plan_subquery)?;
2069 let width = left.len();
2070 self.check_row_arity(width, right.len(), span)?;
2071 self.check_row_types(&left, &right, span)?;
2072 Ok((left, right, width))
2073 }
2074
2075 fn infer_row_operand_with_scope(
2076 &self,
2077 expr: &Expr,
2078 scope: &[ScopedTable],
2079 plan_subquery: &SubqueryPlanner<'_>,
2080 ) -> Result<Vec<TypedExpr>, PlannerError> {
2081 match row_items(expr) {
2082 Some(items) => items
2083 .iter()
2084 .map(|item| self.infer_type_with_scope(item, scope, plan_subquery))
2085 .collect(),
2086 None => Ok(vec![self.infer_type_with_scope(
2087 expr,
2088 scope,
2089 plan_subquery,
2090 )?]),
2091 }
2092 }
2093
2094 fn check_row_arity(
2095 &self,
2096 expected: usize,
2097 actual: usize,
2098 span: Span,
2099 ) -> Result<(), PlannerError> {
2100 if expected == actual {
2101 Ok(())
2102 } else {
2103 Err(PlannerError::RowArityMismatch {
2104 expected,
2105 actual,
2106 line: span.start.line,
2107 column: span.start.column,
2108 })
2109 }
2110 }
2111
2112 fn check_row_types(
2113 &self,
2114 left: &[TypedExpr],
2115 right: &[TypedExpr],
2116 span: Span,
2117 ) -> Result<(), PlannerError> {
2118 for (left, right) in left.iter().zip(right) {
2119 self.check_comparison_op(&left.resolved_type, &right.resolved_type, span)?;
2120 }
2121 Ok(())
2122 }
2123
2124 #[allow(dead_code)]
2126 fn infer_is_null_type(
2127 &self,
2128 expr: &Expr,
2129 negated: bool,
2130 table: &TableMetadata,
2131 span: Span,
2132 ) -> Result<TypedExpr, PlannerError> {
2133 let expr_typed = self.infer_type(expr, table)?;
2134
2135 Ok(TypedExpr {
2136 kind: TypedExprKind::IsNull {
2137 expr: Box::new(expr_typed),
2138 negated,
2139 },
2140 resolved_type: ResolvedType::Boolean,
2141 span,
2142 })
2143 }
2144
2145 fn infer_is_null_type_with_scope(
2146 &self,
2147 expr: &Expr,
2148 negated: bool,
2149 scope: &[ScopedTable],
2150 plan_subquery: &SubqueryPlanner<'_>,
2151 span: Span,
2152 ) -> Result<TypedExpr, PlannerError> {
2153 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
2154
2155 Ok(TypedExpr {
2156 kind: TypedExprKind::IsNull {
2157 expr: Box::new(expr_typed),
2158 negated,
2159 },
2160 resolved_type: ResolvedType::Boolean,
2161 span,
2162 })
2163 }
2164
2165 fn infer_vector_literal_type(
2167 &self,
2168 values: &[f64],
2169 span: Span,
2170 ) -> Result<TypedExpr, PlannerError> {
2171 Ok(TypedExpr {
2172 kind: TypedExprKind::VectorLiteral(values.to_vec()),
2173 resolved_type: ResolvedType::Vector {
2174 dimension: values.len() as u32,
2175 metric: VectorMetric::Cosine, },
2177 span,
2178 })
2179 }
2180
2181 pub fn normalize_metric(&self, metric: &str, span: Span) -> Result<VectorMetric, PlannerError> {
2193 match metric.to_lowercase().as_str() {
2194 "cosine" => Ok(VectorMetric::Cosine),
2195 "l2" => Ok(VectorMetric::L2),
2196 "inner" => Ok(VectorMetric::Inner),
2197 _ => Err(PlannerError::InvalidMetric {
2198 value: metric.to_string(),
2199 line: span.start.line,
2200 column: span.start.column,
2201 }),
2202 }
2203 }
2204
2205 pub fn check_function_call(
2210 &self,
2211 name: &str,
2212 args: &[TypedExpr],
2213 distinct: bool,
2214 star: bool,
2215 span: Span,
2216 ) -> Result<ResolvedType, PlannerError> {
2217 let lower_name = name.to_ascii_lowercase();
2218
2219 match lower_name.as_str() {
2220 "count" => self.check_count(args, distinct, star, span),
2221 "sum" => self.check_sum(args, distinct, star, span),
2222 "total" => self.check_total(args, distinct, star, span),
2223 "avg" => self.check_avg(args, distinct, star, span),
2224 "min" => self.check_min_max(args, distinct, star, span),
2225 "max" => self.check_min_max(args, distinct, star, span),
2226 "group_concat" => self.check_group_concat(args, distinct, star, span),
2227 "string_agg" => self.check_string_agg(args, distinct, star, span),
2228 "json_group_array" => self.check_json_group_array(args, star, span),
2229 "array_agg" => self
2230 .check_json_group_array(args, star, span)
2231 .map(|_| ResolvedType::Array(Box::new(args[0].resolved_type.clone()))),
2232 "json_group_object" => self.check_json_group_object(args, star, span),
2233 "jsonb_agg" => self
2234 .check_json_group_array(args, star, span)
2235 .map(|_| ResolvedType::Json),
2236 "jsonb_object_agg" => self
2237 .check_json_group_object(args, star, span)
2238 .map(|_| ResolvedType::Json),
2239 name if is_portable_aggregate_name(name) => {
2240 check_portable_aggregate(name, args, distinct, star, span)
2241 }
2242 "grouping" | "grouping_id" => {
2247 if distinct || star {
2248 return Err(PlannerError::invalid_expression(
2249 "GROUPING does not support DISTINCT or *".to_string(),
2250 ));
2251 }
2252 if args.is_empty() {
2253 return Err(PlannerError::invalid_expression(
2254 "GROUPING requires at least one argument".to_string(),
2255 ));
2256 }
2257 if args.len() > 63 {
2258 return Err(PlannerError::invalid_expression(
2259 "GROUPING accepts at most 63 arguments".to_string(),
2260 ));
2261 }
2262 Ok(ResolvedType::BigInt)
2263 }
2264 _ => {
2265 let Some(signature) = crate::scalar::signature(&lower_name) else {
2266 return Err(PlannerError::unsupported_feature(
2267 format!("function '{name}'"),
2268 "future",
2269 span,
2270 ));
2271 };
2272 if distinct || star {
2273 return Err(PlannerError::invalid_expression(format!(
2274 "scalar function '{name}' does not support DISTINCT or *"
2275 )));
2276 }
2277 signature.arity.validate(name, args.len(), span)?;
2278 (signature.check)(args)?;
2279 let types: Vec<_> = args.iter().map(|arg| arg.resolved_type.clone()).collect();
2280 match &signature.ret {
2281 crate::scalar::ReturnRule::Fixed(ty) => Ok(ty.clone()),
2282 crate::scalar::ReturnRule::FromArgs(rule) => rule(&types),
2283 crate::scalar::ReturnRule::FromTypedArgs(rule) => rule(args),
2284 }
2285 }
2286 }
2287 }
2288
2289 pub fn validate_having_expr(
2290 &self,
2291 expr: &TypedExpr,
2292 group_keys: &[TypedExpr],
2293 aggregates: &[AggregateExpr],
2294 ) -> Result<(), PlannerError> {
2295 use std::collections::HashSet;
2296
2297 let group_key_indices: HashSet<usize> = group_keys
2298 .iter()
2299 .filter_map(|expr| match &expr.kind {
2300 TypedExprKind::ColumnRef { column_index, .. } => Some(*column_index),
2301 _ => None,
2302 })
2303 .collect();
2304
2305 let aggregate_signatures: HashSet<AggregateSignature> = aggregates
2306 .iter()
2307 .map(aggregate_signature_from_expr)
2308 .collect();
2309
2310 fn walk(
2311 expr: &TypedExpr,
2312 group_key_indices: &HashSet<usize>,
2313 aggregate_signatures: &HashSet<AggregateSignature>,
2314 ) -> Result<(), PlannerError> {
2315 match &expr.kind {
2316 TypedExprKind::ColumnRef { column_index, .. } => {
2317 if group_key_indices.contains(column_index) {
2318 Ok(())
2319 } else {
2320 Err(PlannerError::invalid_expression(
2321 "column in HAVING must be in GROUP BY or be aggregated".to_string(),
2322 ))
2323 }
2324 }
2325 TypedExprKind::FunctionCall { name, args, .. }
2326 if name.eq_ignore_ascii_case("grouping")
2327 || name.eq_ignore_ascii_case("grouping_id") =>
2328 {
2329 for arg in args {
2333 match &arg.kind {
2334 TypedExprKind::ColumnRef { column_index, .. }
2335 if group_key_indices.contains(column_index) => {}
2336 _ => {
2337 return Err(PlannerError::invalid_expression(
2338 "arguments to GROUPING must be grouping expressions \
2339 of the query"
2340 .to_string(),
2341 ));
2342 }
2343 }
2344 }
2345 Ok(())
2346 }
2347 TypedExprKind::FunctionCall {
2348 name,
2349 args,
2350 distinct,
2351 star,
2352 filter,
2353 order_by,
2354 over: _,
2355 } if is_aggregate_name(name) => {
2356 let signature = aggregate_signature_from_call(
2357 name,
2358 args,
2359 *distinct,
2360 *star,
2361 filter.as_deref(),
2362 order_by,
2363 )?;
2364 if aggregate_signatures.contains(&signature) {
2365 Ok(())
2366 } else {
2367 Err(PlannerError::invalid_expression(
2368 "aggregate in HAVING must appear in plan".to_string(),
2369 ))
2370 }
2371 }
2372 TypedExprKind::BinaryOp { left, right, .. } => {
2373 walk(left, group_key_indices, aggregate_signatures)?;
2374 walk(right, group_key_indices, aggregate_signatures)
2375 }
2376 TypedExprKind::UnaryOp { operand, .. } => {
2377 walk(operand, group_key_indices, aggregate_signatures)
2378 }
2379 TypedExprKind::Case {
2380 operand,
2381 branches,
2382 else_expr,
2383 } => {
2384 if let Some(operand) = operand {
2385 walk(operand, group_key_indices, aggregate_signatures)?;
2386 }
2387 for branch in branches {
2388 walk(&branch.when, group_key_indices, aggregate_signatures)?;
2389 walk(&branch.then, group_key_indices, aggregate_signatures)?;
2390 }
2391 if let Some(else_expr) = else_expr {
2392 walk(else_expr, group_key_indices, aggregate_signatures)?;
2393 }
2394 Ok(())
2395 }
2396 TypedExprKind::FunctionCall { args, .. } => {
2397 for arg in args {
2398 walk(arg, group_key_indices, aggregate_signatures)?;
2399 }
2400 Ok(())
2401 }
2402 TypedExprKind::Between {
2403 expr, low, high, ..
2404 } => {
2405 walk(expr, group_key_indices, aggregate_signatures)?;
2406 walk(low, group_key_indices, aggregate_signatures)?;
2407 walk(high, group_key_indices, aggregate_signatures)
2408 }
2409 TypedExprKind::Like {
2410 expr,
2411 pattern,
2412 escape,
2413 ..
2414 } => {
2415 walk(expr, group_key_indices, aggregate_signatures)?;
2416 walk(pattern, group_key_indices, aggregate_signatures)?;
2417 if let Some(esc) = escape {
2418 walk(esc, group_key_indices, aggregate_signatures)?;
2419 }
2420 Ok(())
2421 }
2422 TypedExprKind::InList { expr, list, .. } => {
2423 walk(expr, group_key_indices, aggregate_signatures)?;
2424 for item in list {
2425 walk(item, group_key_indices, aggregate_signatures)?;
2426 }
2427 Ok(())
2428 }
2429 TypedExprKind::IsNull { expr, .. } => {
2430 walk(expr, group_key_indices, aggregate_signatures)
2431 }
2432 _ => Ok(()),
2433 }
2434 }
2435
2436 walk(expr, &group_key_indices, &aggregate_signatures)
2437 }
2438
2439 fn check_count(
2440 &self,
2441 args: &[TypedExpr],
2442 distinct: bool,
2443 star: bool,
2444 span: Span,
2445 ) -> Result<ResolvedType, PlannerError> {
2446 if star {
2447 if distinct {
2448 return Err(PlannerError::unsupported_feature(
2449 "COUNT(DISTINCT *)",
2450 "future",
2451 span,
2452 ));
2453 }
2454 if !args.is_empty() {
2455 return Err(PlannerError::type_mismatch(
2456 "no arguments with COUNT(*)",
2457 format!("{} arguments", args.len()),
2458 span,
2459 ));
2460 }
2461 return Ok(ResolvedType::BigInt);
2462 }
2463
2464 if args.len() != 1 {
2465 return Err(PlannerError::type_mismatch(
2466 "1 argument",
2467 format!("{} arguments", args.len()),
2468 span,
2469 ));
2470 }
2471
2472 if distinct {
2473 return Ok(ResolvedType::BigInt);
2474 }
2475
2476 Ok(ResolvedType::BigInt)
2477 }
2478
2479 fn check_sum(
2480 &self,
2481 args: &[TypedExpr],
2482 _distinct: bool,
2483 star: bool,
2484 span: Span,
2485 ) -> Result<ResolvedType, PlannerError> {
2486 if star {
2487 return Err(PlannerError::type_mismatch(
2488 "numeric argument",
2489 "COUNT(*) style",
2490 span,
2491 ));
2492 }
2493 let arg = self.require_single_arg(args, span)?;
2494 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2495 return Err(PlannerError::type_mismatch(
2496 "numeric",
2497 arg.resolved_type.type_name().to_string(),
2498 arg.span,
2499 ));
2500 }
2501 Ok(crate::planner::aggregate_expr::sum_result_type(
2502 &arg.resolved_type,
2503 ))
2504 }
2505
2506 fn check_total(
2507 &self,
2508 args: &[TypedExpr],
2509 distinct: bool,
2510 star: bool,
2511 span: Span,
2512 ) -> Result<ResolvedType, PlannerError> {
2513 if star {
2514 return Err(PlannerError::type_mismatch(
2515 "numeric argument",
2516 "COUNT(*) style",
2517 span,
2518 ));
2519 }
2520 if distinct {
2521 return Err(PlannerError::unsupported_feature(
2522 "TOTAL(DISTINCT ...)",
2523 "future",
2524 span,
2525 ));
2526 }
2527 let arg = self.require_single_arg(args, span)?;
2528 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2529 return Err(PlannerError::type_mismatch(
2530 "numeric",
2531 arg.resolved_type.type_name().to_string(),
2532 arg.span,
2533 ));
2534 }
2535 Ok(ResolvedType::Double)
2536 }
2537
2538 fn check_avg(
2539 &self,
2540 args: &[TypedExpr],
2541 _distinct: bool,
2542 star: bool,
2543 span: Span,
2544 ) -> Result<ResolvedType, PlannerError> {
2545 if star {
2546 return Err(PlannerError::type_mismatch(
2547 "numeric argument",
2548 "COUNT(*) style",
2549 span,
2550 ));
2551 }
2552 let arg = self.require_single_arg(args, span)?;
2553 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2554 return Err(PlannerError::type_mismatch(
2555 "numeric",
2556 arg.resolved_type.type_name().to_string(),
2557 arg.span,
2558 ));
2559 }
2560 Ok(ResolvedType::Double)
2561 }
2562
2563 fn check_min_max(
2564 &self,
2565 args: &[TypedExpr],
2566 _distinct: bool,
2567 star: bool,
2568 span: Span,
2569 ) -> Result<ResolvedType, PlannerError> {
2570 if star {
2571 return Err(PlannerError::type_mismatch(
2572 "argument",
2573 "COUNT(*) style",
2574 span,
2575 ));
2576 }
2577 let arg = self.require_single_arg(args, span)?;
2578 if matches!(arg.resolved_type, ResolvedType::Vector { .. }) {
2579 return Err(PlannerError::type_mismatch(
2580 "comparable",
2581 arg.resolved_type.type_name().to_string(),
2582 arg.span,
2583 ));
2584 }
2585 Ok(arg.resolved_type.clone())
2586 }
2587
2588 fn check_group_concat(
2589 &self,
2590 args: &[TypedExpr],
2591 _distinct: bool,
2592 star: bool,
2593 span: Span,
2594 ) -> Result<ResolvedType, PlannerError> {
2595 if star {
2596 return Err(PlannerError::type_mismatch(
2597 "text argument",
2598 "COUNT(*) style",
2599 span,
2600 ));
2601 }
2602 if args.is_empty() || args.len() > 2 {
2603 return Err(PlannerError::type_mismatch(
2604 "1 or 2 arguments",
2605 format!("{} arguments", args.len()),
2606 span,
2607 ));
2608 }
2609 if !matches!(
2610 args[0].resolved_type,
2611 ResolvedType::Text | ResolvedType::Null
2612 ) {
2613 return Err(PlannerError::type_mismatch(
2614 "Text",
2615 args[0].resolved_type.type_name().to_string(),
2616 args[0].span,
2617 ));
2618 }
2619 if args.len() == 2
2620 && !matches!(
2621 args[1].resolved_type,
2622 ResolvedType::Text | ResolvedType::Null
2623 )
2624 {
2625 return Err(PlannerError::type_mismatch(
2626 "Text",
2627 args[1].resolved_type.type_name().to_string(),
2628 args[1].span,
2629 ));
2630 }
2631 Ok(ResolvedType::Text)
2632 }
2633
2634 fn check_string_agg(
2635 &self,
2636 args: &[TypedExpr],
2637 _distinct: bool,
2638 star: bool,
2639 span: Span,
2640 ) -> Result<ResolvedType, PlannerError> {
2641 if star {
2642 return Err(PlannerError::type_mismatch(
2643 "text argument",
2644 "COUNT(*) style",
2645 span,
2646 ));
2647 }
2648 if args.len() != 2 {
2649 return Err(PlannerError::type_mismatch(
2650 "2 arguments",
2651 format!("{} arguments", args.len()),
2652 span,
2653 ));
2654 }
2655 if !matches!(
2656 args[0].resolved_type,
2657 ResolvedType::Text | ResolvedType::Null
2658 ) {
2659 return Err(PlannerError::type_mismatch(
2660 "Text",
2661 args[0].resolved_type.type_name().to_string(),
2662 args[0].span,
2663 ));
2664 }
2665 if !matches!(
2666 args[1].resolved_type,
2667 ResolvedType::Text | ResolvedType::Null
2668 ) {
2669 return Err(PlannerError::type_mismatch(
2670 "Text",
2671 args[1].resolved_type.type_name().to_string(),
2672 args[1].span,
2673 ));
2674 }
2675 Ok(ResolvedType::Text)
2676 }
2677
2678 fn check_json_group_array(
2679 &self,
2680 args: &[TypedExpr],
2681 star: bool,
2682 span: Span,
2683 ) -> Result<ResolvedType, PlannerError> {
2684 if star || args.len() != 1 {
2685 return Err(PlannerError::type_mismatch(
2686 "1 argument",
2687 format!("{} arguments", args.len()),
2688 span,
2689 ));
2690 }
2691 Ok(ResolvedType::Text)
2692 }
2693
2694 fn check_json_group_object(
2695 &self,
2696 args: &[TypedExpr],
2697 star: bool,
2698 span: Span,
2699 ) -> Result<ResolvedType, PlannerError> {
2700 if star || args.len() != 2 {
2701 return Err(PlannerError::type_mismatch(
2702 "2 arguments",
2703 format!("{} arguments", args.len()),
2704 span,
2705 ));
2706 }
2707 if !matches!(
2708 args[0].resolved_type,
2709 ResolvedType::Text | ResolvedType::Null
2710 ) {
2711 return Err(PlannerError::type_mismatch(
2712 "Text",
2713 args[0].resolved_type.type_name(),
2714 args[0].span,
2715 ));
2716 }
2717 Ok(ResolvedType::Text)
2718 }
2719
2720 fn require_single_arg<'b>(
2721 &self,
2722 args: &'b [TypedExpr],
2723 span: Span,
2724 ) -> Result<&'b TypedExpr, PlannerError> {
2725 if args.len() != 1 {
2726 return Err(PlannerError::type_mismatch(
2727 "1 argument",
2728 format!("{} arguments", args.len()),
2729 span,
2730 ));
2731 }
2732 Ok(&args[0])
2733 }
2734
2735 pub fn check_vector_distance(
2746 &self,
2747 args: &[TypedExpr],
2748 span: Span,
2749 ) -> Result<ResolvedType, PlannerError> {
2750 if args.len() != 3 {
2751 return Err(PlannerError::TypeMismatch {
2752 expected: "3 arguments".to_string(),
2753 found: format!("{} arguments", args.len()),
2754 line: span.start.line,
2755 column: span.start.column,
2756 });
2757 }
2758
2759 let col_dim = match &args[0].resolved_type {
2761 ResolvedType::Vector { dimension, .. } => *dimension,
2762 other => {
2763 return Err(PlannerError::TypeMismatch {
2764 expected: "Vector".to_string(),
2765 found: other.type_name().to_string(),
2766 line: args[0].span.start.line,
2767 column: args[0].span.start.column,
2768 });
2769 }
2770 };
2771
2772 let vec_dim = match &args[1].resolved_type {
2774 ResolvedType::Vector { dimension, .. } => *dimension,
2775 other => {
2776 return Err(PlannerError::TypeMismatch {
2777 expected: "Vector".to_string(),
2778 found: other.type_name().to_string(),
2779 line: args[1].span.start.line,
2780 column: args[1].span.start.column,
2781 });
2782 }
2783 };
2784
2785 self.check_vector_dimension(col_dim, vec_dim, args[1].span)?;
2787
2788 match &args[2].resolved_type {
2790 ResolvedType::Text => {
2791 if let TypedExprKind::Literal(Literal::String(s)) = &args[2].kind {
2793 self.normalize_metric(s, args[2].span)?;
2794 }
2795 }
2796 ResolvedType::Null => {
2797 return Err(PlannerError::TypeMismatch {
2799 expected: "Text (metric)".to_string(),
2800 found: "Null".to_string(),
2801 line: args[2].span.start.line,
2802 column: args[2].span.start.column,
2803 });
2804 }
2805 other => {
2806 return Err(PlannerError::TypeMismatch {
2807 expected: "Text (metric)".to_string(),
2808 found: other.type_name().to_string(),
2809 line: args[2].span.start.line,
2810 column: args[2].span.start.column,
2811 });
2812 }
2813 }
2814
2815 Ok(ResolvedType::Double)
2816 }
2817
2818 pub fn check_vector_similarity(
2824 &self,
2825 args: &[TypedExpr],
2826 span: Span,
2827 ) -> Result<ResolvedType, PlannerError> {
2828 self.check_vector_distance(args, span)
2830 }
2831
2832 pub fn check_vector_dimension(
2838 &self,
2839 expected: u32,
2840 found: u32,
2841 span: Span,
2842 ) -> Result<(), PlannerError> {
2843 if expected != found {
2844 Err(PlannerError::VectorDimensionMismatch {
2845 expected,
2846 found,
2847 line: span.start.line,
2848 column: span.start.column,
2849 })
2850 } else {
2851 Ok(())
2852 }
2853 }
2854
2855 pub fn check_insert_values(
2878 &self,
2879 table: &TableMetadata,
2880 columns: &[String],
2881 values: &[Vec<Expr>],
2882 span: Span,
2883 ) -> Result<Vec<Vec<TypedExpr>>, PlannerError> {
2884 let target_columns: Vec<&str> = if columns.is_empty() {
2886 table.column_names()
2887 } else {
2888 columns.iter().map(|s| s.as_str()).collect()
2889 };
2890
2891 let mut typed_rows = Vec::with_capacity(values.len());
2892
2893 for row in values {
2894 if row.len() != target_columns.len() {
2896 return Err(PlannerError::ColumnValueCountMismatch {
2897 columns: target_columns.len(),
2898 values: row.len(),
2899 line: span.start.line,
2900 column: span.start.column,
2901 });
2902 }
2903
2904 let mut typed_values = Vec::with_capacity(row.len());
2905
2906 for (value, col_name) in row.iter().zip(target_columns.iter()) {
2907 let col_meta =
2909 table
2910 .get_column(col_name)
2911 .ok_or_else(|| PlannerError::ColumnNotFound {
2912 column: col_name.to_string(),
2913 table: table.name.clone(),
2914 line: span.start.line,
2915 col: span.start.column,
2916 })?;
2917
2918 let typed_value = self.infer_type(value, table)?;
2920
2921 self.check_null_constraint(col_meta, &typed_value, value.span)?;
2923
2924 self.check_type_compatibility(
2926 &col_meta.data_type,
2927 &typed_value.resolved_type,
2928 value.span,
2929 )?;
2930
2931 let typed_value =
2932 self.coerce_column_value(&col_meta.data_type, typed_value, value.span);
2933
2934 if let (
2936 ResolvedType::Vector {
2937 dimension: expected_dim,
2938 ..
2939 },
2940 ResolvedType::Vector {
2941 dimension: actual_dim,
2942 ..
2943 },
2944 ) = (&col_meta.data_type, &typed_value.resolved_type)
2945 {
2946 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
2947 }
2948
2949 typed_values.push(typed_value);
2950 }
2951
2952 typed_rows.push(typed_values);
2953 }
2954
2955 Ok(typed_rows)
2956 }
2957
2958 pub fn check_assignment(
2969 &self,
2970 table: &TableMetadata,
2971 column: &str,
2972 value: &Expr,
2973 span: Span,
2974 ) -> Result<TypedExpr, PlannerError> {
2975 let col_meta = table
2977 .get_column(column)
2978 .ok_or_else(|| PlannerError::ColumnNotFound {
2979 column: column.to_string(),
2980 table: table.name.clone(),
2981 line: span.start.line,
2982 col: span.start.column,
2983 })?;
2984
2985 let typed_value = self.infer_type(value, table)?;
2987
2988 self.check_null_constraint(col_meta, &typed_value, value.span)?;
2990
2991 self.check_type_compatibility(&col_meta.data_type, &typed_value.resolved_type, value.span)?;
2993
2994 let typed_value = self.coerce_column_value(&col_meta.data_type, typed_value, value.span);
2995
2996 if let (
2998 ResolvedType::Vector {
2999 dimension: expected_dim,
3000 ..
3001 },
3002 ResolvedType::Vector {
3003 dimension: actual_dim,
3004 ..
3005 },
3006 ) = (&col_meta.data_type, &typed_value.resolved_type)
3007 {
3008 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
3009 }
3010
3011 Ok(typed_value)
3012 }
3013
3014 pub fn check_null_constraint(
3021 &self,
3022 column: &crate::catalog::ColumnMetadata,
3023 value: &TypedExpr,
3024 span: Span,
3025 ) -> Result<(), PlannerError> {
3026 if column.not_null && matches!(value.resolved_type, ResolvedType::Null) {
3027 Err(PlannerError::NullConstraintViolation {
3028 column: column.name.clone(),
3029 line: span.start.line,
3030 col: span.start.column,
3031 })
3032 } else {
3033 Ok(())
3034 }
3035 }
3036
3037 fn check_type_compatibility(
3045 &self,
3046 expected: &ResolvedType,
3047 actual: &ResolvedType,
3048 span: Span,
3049 ) -> Result<(), PlannerError> {
3050 if expected == actual {
3052 return Ok(());
3053 }
3054
3055 if actual.can_cast_to(expected) {
3057 return Ok(());
3058 }
3059
3060 if let (
3063 ResolvedType::Vector {
3064 dimension: d1,
3065 metric: _,
3066 },
3067 ResolvedType::Vector {
3068 dimension: d2,
3069 metric: _,
3070 },
3071 ) = (expected, actual)
3072 {
3073 if *d1 == *d2 {
3075 return Ok(());
3076 }
3077 }
3079
3080 Err(PlannerError::TypeMismatch {
3081 expected: expected.type_name().to_string(),
3082 found: actual.type_name().to_string(),
3083 line: span.start.line,
3084 column: span.start.column,
3085 })
3086 }
3087
3088 fn coerce_column_value(
3091 &self,
3092 expected: &ResolvedType,
3093 value: TypedExpr,
3094 span: Span,
3095 ) -> TypedExpr {
3096 if value.resolved_type != *expected
3097 && value.resolved_type != ResolvedType::Null
3098 && matches!(
3099 expected,
3100 ResolvedType::Integer
3101 | ResolvedType::BigInt
3102 | ResolvedType::Float
3103 | ResolvedType::Double
3104 | ResolvedType::Timestamp
3105 | ResolvedType::Date
3106 | ResolvedType::Time
3107 | ResolvedType::Interval
3108 | ResolvedType::Decimal { .. }
3109 )
3110 {
3111 TypedExpr::cast(value, expected.clone(), span)
3112 } else {
3113 value
3114 }
3115 }
3116}
3117
3118fn fold_integral_binary(
3119 left: &TypedExpr,
3120 op: BinaryOp,
3121 right: &TypedExpr,
3122 result_type: &ResolvedType,
3123 span: Span,
3124) -> Option<TypedExpr> {
3125 let value = |expr: &TypedExpr| match &expr.kind {
3126 TypedExprKind::Literal(Literal::Number(value)) => value.parse::<i64>().ok(),
3127 _ => None,
3128 };
3129 let left = value(left)?;
3130 let right = value(right)?;
3131 let folded = match op {
3132 BinaryOp::BitAnd => left & right,
3133 BinaryOp::BitOr => left | right,
3134 BinaryOp::BitXor => left ^ right,
3135 BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
3136 let width = if matches!(result_type, ResolvedType::BigInt) {
3137 64
3138 } else {
3139 32
3140 };
3141 if !(0..width).contains(&right) {
3142 return None;
3143 }
3144 if op == BinaryOp::ShiftRight {
3145 left >> right as u32
3146 } else {
3147 let shifted = i128::from(left) * (1_i128 << right as u32);
3148 if matches!(result_type, ResolvedType::BigInt) {
3149 i64::try_from(shifted).ok()?
3150 } else {
3151 i64::from(i32::try_from(shifted).ok()?)
3152 }
3153 }
3154 }
3155 _ => return None,
3156 };
3157 Some(TypedExpr::literal(
3158 Literal::Number(folded.to_string()),
3159 result_type.clone(),
3160 span,
3161 ))
3162}
3163
3164fn is_numeric_type(ty: &ResolvedType) -> bool {
3165 matches!(
3166 ty,
3167 ResolvedType::Integer
3168 | ResolvedType::BigInt
3169 | ResolvedType::Float
3170 | ResolvedType::Double
3171 | ResolvedType::Decimal { .. }
3172 )
3173}
3174
3175fn decimal_arithmetic_type(
3176 op: BinaryOp,
3177 left: &ResolvedType,
3178 right: &ResolvedType,
3179) -> Option<ResolvedType> {
3180 let parts = |ty: &ResolvedType| match ty {
3181 ResolvedType::Decimal { precision, scale } => Some((*precision, *scale)),
3182 ResolvedType::Integer => Some((10, 0)),
3183 ResolvedType::BigInt => Some((19, 0)),
3184 _ => None,
3185 };
3186 if !matches!(left, ResolvedType::Decimal { .. })
3187 && !matches!(right, ResolvedType::Decimal { .. })
3188 {
3189 return None;
3190 }
3191 let (lp, ls) = parts(left)?;
3192 let (rp, rs) = parts(right)?;
3193 let (raw_precision, raw_scale) = match op {
3194 BinaryOp::Add | BinaryOp::Sub => ((lp - ls).max(rp - rs) + ls.max(rs) + 1, ls.max(rs)),
3195 BinaryOp::Mul => (
3196 lp.saturating_add(rp).saturating_add(1),
3197 ls.saturating_add(rs),
3198 ),
3199 BinaryOp::Div => {
3200 let scale = 6_u8.max(ls.saturating_add(rp).saturating_add(1));
3201 ((lp - ls).saturating_add(rs).saturating_add(scale), scale)
3202 }
3203 _ => return None,
3204 };
3205 let reduction = raw_precision.saturating_sub(38);
3206 Some(ResolvedType::Decimal {
3207 precision: raw_precision.min(38),
3208 scale: raw_scale.saturating_sub(reduction),
3209 })
3210}
3211
3212fn validate_window_frame(
3213 function_name: &str,
3214 frame: &WindowFrame,
3215 order_by: &[SortExpr],
3216) -> Result<(), PlannerError> {
3217 if !is_aggregate_name(function_name)
3218 && !matches!(function_name, "first_value" | "last_value" | "nth_value")
3219 {
3220 return Err(PlannerError::invalid_expression(format!(
3221 "explicit window frames are only supported for aggregate functions and \
3222 FIRST_VALUE/LAST_VALUE/NTH_VALUE, not {}()",
3223 function_name.to_ascii_uppercase()
3224 )));
3225 }
3226 if order_by.is_empty() {
3227 return Err(PlannerError::invalid_expression(
3228 "explicit ROWS/RANGE window frames require ORDER BY for deterministic evaluation",
3229 ));
3230 }
3231 if matches!(frame.start_bound, WindowFrameBound::UnboundedFollowing) {
3232 return Err(PlannerError::invalid_expression(
3233 "window frame start cannot be UNBOUNDED FOLLOWING",
3234 ));
3235 }
3236 if matches!(frame.end_bound, WindowFrameBound::UnboundedPreceding) {
3237 return Err(PlannerError::invalid_expression(
3238 "window frame end cannot be UNBOUNDED PRECEDING",
3239 ));
3240 }
3241 if (matches!(frame.start_bound, WindowFrameBound::CurrentRow)
3242 && matches!(frame.end_bound, WindowFrameBound::Preceding(_)))
3243 || (matches!(frame.start_bound, WindowFrameBound::Following(_))
3244 && matches!(
3245 frame.end_bound,
3246 WindowFrameBound::Preceding(_) | WindowFrameBound::CurrentRow
3247 ))
3248 {
3249 return Err(PlannerError::invalid_expression(
3250 "window frame bounds are reversed",
3251 ));
3252 }
3253
3254 let has_offset = matches!(
3255 frame.start_bound,
3256 WindowFrameBound::Preceding(_) | WindowFrameBound::Following(_)
3257 ) || matches!(
3258 frame.end_bound,
3259 WindowFrameBound::Preceding(_) | WindowFrameBound::Following(_)
3260 );
3261 if frame.units == WindowFrameUnits::Range && has_offset {
3262 if order_by.len() != 1 {
3263 return Err(PlannerError::invalid_expression(
3264 "RANGE offset frames require exactly one ORDER BY expression",
3265 ));
3266 }
3267 if !is_numeric_type(&order_by[0].expr.resolved_type) {
3268 return Err(PlannerError::invalid_expression(format!(
3269 "RANGE offset ORDER BY expression must be numeric, found {:?}",
3270 order_by[0].expr.resolved_type
3271 )));
3272 }
3273 }
3274 Ok(())
3275}
3276
3277fn validate_offset_window_call(
3278 name: &str,
3279 arg_count: usize,
3280 distinct: bool,
3281 star: bool,
3282) -> Result<(), PlannerError> {
3283 let display_name = name.to_ascii_uppercase();
3284 if distinct {
3285 return Err(PlannerError::invalid_expression(format!(
3286 "{display_name}() window function does not accept DISTINCT"
3287 )));
3288 }
3289 if star {
3290 return Err(PlannerError::invalid_expression(format!(
3291 "{display_name}() window function does not accept a star argument"
3292 )));
3293 }
3294 if !(1..=3).contains(&arg_count) {
3295 return Err(PlannerError::invalid_expression(format!(
3296 "{display_name}() window function expects 1 to 3 arguments"
3297 )));
3298 }
3299 Ok(())
3300}
3301
3302fn validate_exact_window_call(
3303 name: &str,
3304 arg_count: usize,
3305 expected: usize,
3306 distinct: bool,
3307 star: bool,
3308) -> Result<(), PlannerError> {
3309 let display_name = name.to_ascii_uppercase();
3310 if distinct {
3311 return Err(PlannerError::invalid_expression(format!(
3312 "{display_name}() window function does not support DISTINCT"
3313 )));
3314 }
3315 if star {
3316 return Err(PlannerError::invalid_expression(format!(
3317 "{display_name}() window function does not support a star argument"
3318 )));
3319 }
3320 if arg_count != expected {
3321 let signature = match expected {
3322 0 => "no arguments",
3323 1 => "one argument",
3324 2 => "two arguments",
3325 _ => unreachable!("window signatures are bounded above"),
3326 };
3327 return Err(PlannerError::invalid_expression(format!(
3328 "{display_name}() window function takes {signature}"
3329 )));
3330 }
3331 Ok(())
3332}
3333
3334fn validate_positive_integer_argument(
3335 name: &str,
3336 argument: &TypedExpr,
3337) -> Result<(), PlannerError> {
3338 if matches!(
3339 argument.resolved_type,
3340 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
3341 ) {
3342 return Ok(());
3343 }
3344 Err(PlannerError::type_mismatch(
3345 format!("positive INTEGER {} argument", name.to_ascii_uppercase()),
3346 argument.resolved_type.type_name(),
3347 argument.span,
3348 ))
3349}
3350
3351fn coerce_compatible_result(expr: &mut TypedExpr, target: &ResolvedType) {
3352 if expr.resolved_type == *target || matches!(expr.resolved_type, ResolvedType::Null) {
3353 return;
3354 }
3355 let span = expr.span;
3356 *expr = TypedExpr::cast(expr.clone(), target.clone(), span);
3357}
3358
3359fn coerce_case_result(expr: &mut TypedExpr, target: &ResolvedType) {
3360 if expr.resolved_type == *target || matches!(expr.resolved_type, ResolvedType::Null) {
3361 return;
3362 }
3363 let span = expr.span;
3364 *expr = TypedExpr::cast(expr.clone(), target.clone(), span);
3365}
3366
3367#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3368struct AggregateSignature {
3369 name: String,
3370 distinct: bool,
3371 star: bool,
3372 arg_key: Option<String>,
3373 extra_arg_keys: Vec<String>,
3374 separator: Option<String>,
3375 filter_key: Option<String>,
3378 order_key: Option<String>,
3382}
3383
3384pub(crate) fn is_portable_aggregate_name(name: &str) -> bool {
3385 matches!(
3386 name,
3387 "variance"
3388 | "var_samp"
3389 | "var_pop"
3390 | "stddev"
3391 | "stddev_samp"
3392 | "stddev_pop"
3393 | "covar_samp"
3394 | "covar_pop"
3395 | "corr"
3396 | "median"
3397 | "mode"
3398 | "quantile_cont"
3399 | "regr_count"
3400 | "regr_avgx"
3401 | "regr_avgy"
3402 | "regr_sxx"
3403 | "regr_syy"
3404 | "regr_sxy"
3405 | "regr_slope"
3406 | "regr_intercept"
3407 | "regr_r2"
3408 | "any_value"
3409 | "first"
3410 | "last"
3411 | "arg_min"
3412 | "min_by"
3413 | "arg_max"
3414 | "max_by"
3415 | "bit_and"
3416 | "bit_or"
3417 | "bit_xor"
3418 | "bool_and"
3419 | "bool_or"
3420 )
3421}
3422
3423fn canonical_aggregate_name(name: &str) -> String {
3424 match name.to_ascii_lowercase().as_str() {
3425 "variance" | "var_samp" => "var_samp".into(),
3426 "stddev" | "stddev_samp" => "stddev_samp".into(),
3427 "min_by" => "arg_min".into(),
3428 "max_by" => "arg_max".into(),
3429 lower => lower.into(),
3430 }
3431}
3432
3433fn check_portable_aggregate(
3434 name: &str,
3435 args: &[TypedExpr],
3436 distinct: bool,
3437 star: bool,
3438 span: Span,
3439) -> Result<ResolvedType, PlannerError> {
3440 if distinct || star {
3441 return Err(PlannerError::invalid_expression(format!(
3442 "{} does not support DISTINCT or *",
3443 name.to_ascii_uppercase()
3444 )));
3445 }
3446 let expected = if matches!(
3447 name,
3448 "covar_samp"
3449 | "covar_pop"
3450 | "corr"
3451 | "quantile_cont"
3452 | "regr_count"
3453 | "regr_avgx"
3454 | "regr_avgy"
3455 | "regr_sxx"
3456 | "regr_syy"
3457 | "regr_sxy"
3458 | "regr_slope"
3459 | "regr_intercept"
3460 | "regr_r2"
3461 | "arg_min"
3462 | "min_by"
3463 | "arg_max"
3464 | "max_by"
3465 ) {
3466 2
3467 } else {
3468 1
3469 };
3470 if args.len() != expected {
3471 return Err(PlannerError::type_mismatch(
3472 format!("{expected} argument(s)"),
3473 format!("{} arguments", args.len()),
3474 span,
3475 ));
3476 }
3477
3478 let numeric = |arg: &TypedExpr| {
3479 matches!(
3480 arg.resolved_type,
3481 ResolvedType::Integer
3482 | ResolvedType::BigInt
3483 | ResolvedType::Float
3484 | ResolvedType::Double
3485 | ResolvedType::Null
3486 )
3487 };
3488 if matches!(
3489 name,
3490 "variance"
3491 | "var_samp"
3492 | "var_pop"
3493 | "stddev"
3494 | "stddev_samp"
3495 | "stddev_pop"
3496 | "median"
3497 | "quantile_cont"
3498 | "covar_samp"
3499 | "covar_pop"
3500 | "corr"
3501 | "regr_count"
3502 | "regr_avgx"
3503 | "regr_avgy"
3504 | "regr_sxx"
3505 | "regr_syy"
3506 | "regr_sxy"
3507 | "regr_slope"
3508 | "regr_intercept"
3509 | "regr_r2"
3510 ) && !args.iter().all(numeric)
3511 {
3512 return Err(PlannerError::type_mismatch(
3513 "numeric aggregate argument",
3514 args.iter()
3515 .find(|arg| !numeric(arg))
3516 .expect("non-numeric argument")
3517 .resolved_type
3518 .type_name(),
3519 span,
3520 ));
3521 }
3522 if name == "quantile_cont" {
3523 let _ = percentile_fraction_named(name, &args[1])?;
3524 }
3525 if matches!(name, "bit_and" | "bit_or" | "bit_xor")
3526 && !matches!(
3527 args[0].resolved_type,
3528 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
3529 )
3530 {
3531 return Err(PlannerError::type_mismatch(
3532 "INTEGER or BIGINT",
3533 args[0].resolved_type.type_name(),
3534 span,
3535 ));
3536 }
3537 if matches!(name, "bool_and" | "bool_or")
3538 && !matches!(
3539 args[0].resolved_type,
3540 ResolvedType::Boolean | ResolvedType::Null
3541 )
3542 {
3543 return Err(PlannerError::type_mismatch(
3544 "BOOLEAN",
3545 args[0].resolved_type.type_name(),
3546 span,
3547 ));
3548 }
3549
3550 Ok(match name {
3551 "regr_count" => ResolvedType::BigInt,
3552 "any_value" | "first" | "last" | "arg_min" | "min_by" | "arg_max" | "max_by" | "mode" => {
3553 args[0].resolved_type.clone()
3554 }
3555 "bit_and" | "bit_or" | "bit_xor" => args[0].resolved_type.clone(),
3556 "bool_and" | "bool_or" => ResolvedType::Boolean,
3557 _ => ResolvedType::Double,
3558 })
3559}
3560
3561fn is_aggregate_name(name: &str) -> bool {
3562 matches!(
3563 name.to_ascii_lowercase().as_str(),
3564 "count"
3565 | "sum"
3566 | "total"
3567 | "avg"
3568 | "min"
3569 | "max"
3570 | "group_concat"
3571 | "string_agg"
3572 | "json_group_array"
3573 | "array_agg"
3574 | "json_group_object"
3575 | "jsonb_agg"
3576 | "jsonb_object_agg"
3577 | "percentile_disc"
3578 | "percentile_cont"
3579 | "variance"
3580 | "var_samp"
3581 | "var_pop"
3582 | "stddev"
3583 | "stddev_samp"
3584 | "stddev_pop"
3585 | "covar_samp"
3586 | "covar_pop"
3587 | "corr"
3588 | "median"
3589 | "mode"
3590 | "quantile_cont"
3591 | "regr_count"
3592 | "regr_avgx"
3593 | "regr_avgy"
3594 | "regr_sxx"
3595 | "regr_syy"
3596 | "regr_sxy"
3597 | "regr_slope"
3598 | "regr_intercept"
3599 | "regr_r2"
3600 | "any_value"
3601 | "first"
3602 | "last"
3603 | "arg_min"
3604 | "min_by"
3605 | "arg_max"
3606 | "max_by"
3607 | "bit_and"
3608 | "bit_or"
3609 | "bit_xor"
3610 | "bool_and"
3611 | "bool_or"
3612 )
3613}
3614
3615fn is_ordered_set_aggregate_name(name: &str) -> bool {
3616 matches!(name, "percentile_disc" | "percentile_cont" | "mode")
3617}
3618
3619fn is_order_sensitive_aggregate_name(name: &str) -> bool {
3623 matches!(
3624 name.to_ascii_lowercase().as_str(),
3625 "group_concat"
3626 | "string_agg"
3627 | "percentile_disc"
3628 | "percentile_cont"
3629 | "mode"
3630 | "first"
3631 | "last"
3632 )
3633}
3634
3635pub(crate) fn percentile_fraction(arg: &TypedExpr) -> Result<f64, PlannerError> {
3638 percentile_fraction_named("percentile_disc", arg)
3639}
3640
3641pub(crate) fn percentile_fraction_named(name: &str, arg: &TypedExpr) -> Result<f64, PlannerError> {
3642 let literal = match &arg.kind {
3643 TypedExprKind::Literal(Literal::Number(text)) => text.parse::<f64>().ok(),
3644 TypedExprKind::UnaryOp {
3645 op: crate::ast::expr::UnaryOp::Minus,
3646 operand,
3647 } => match &operand.kind {
3648 TypedExprKind::Literal(Literal::Number(text)) => {
3649 text.parse::<f64>().ok().map(|value| -value)
3650 }
3651 _ => None,
3652 },
3653 _ => None,
3654 };
3655 let Some(value) = literal else {
3656 return Err(PlannerError::invalid_expression(format!(
3657 "{} fraction must be a numeric literal",
3658 name.to_ascii_uppercase()
3659 )));
3660 };
3661 if !(0.0..=1.0).contains(&value) {
3662 return Err(PlannerError::invalid_expression(format!(
3663 "{} fraction must be between 0 and 1",
3664 name.to_ascii_uppercase()
3665 )));
3666 }
3667 Ok(value)
3668}
3669
3670fn typed_sort_signature(order_by: &[SortExpr]) -> Option<String> {
3671 if order_by.is_empty() {
3672 return None;
3673 }
3674 Some(
3675 order_by
3676 .iter()
3677 .map(|sort| {
3678 format!(
3679 "{}|{}|{}",
3680 typed_expr_signature(&sort.expr),
3681 sort.asc,
3682 sort.nulls_first
3683 )
3684 })
3685 .collect::<Vec<_>>()
3686 .join(","),
3687 )
3688}
3689
3690fn aggregate_signature_from_expr(expr: &AggregateExpr) -> AggregateSignature {
3691 let (name, separator, star, arg) = match &expr.function {
3692 AggregateFunction::Count => (
3693 "count".to_string(),
3694 None,
3695 expr.arg.is_none(),
3696 expr.arg.as_ref(),
3697 ),
3698 AggregateFunction::Sum => ("sum".to_string(), None, false, expr.arg.as_ref()),
3699 AggregateFunction::Total => ("total".to_string(), None, false, expr.arg.as_ref()),
3700 AggregateFunction::Avg => ("avg".to_string(), None, false, expr.arg.as_ref()),
3701 AggregateFunction::Min => ("min".to_string(), None, false, expr.arg.as_ref()),
3702 AggregateFunction::Max => ("max".to_string(), None, false, expr.arg.as_ref()),
3703 AggregateFunction::GroupConcat { separator } => (
3704 "group_concat".to_string(),
3705 separator.clone(),
3706 false,
3707 expr.arg.as_ref(),
3708 ),
3709 AggregateFunction::StringAgg { separator } => (
3710 "string_agg".to_string(),
3711 separator.clone(),
3712 false,
3713 expr.arg.as_ref(),
3714 ),
3715 AggregateFunction::ArrayAgg => ("array_agg".to_string(), None, false, expr.arg.as_ref()),
3716 AggregateFunction::JsonGroupArray => (
3717 "json_group_array".to_string(),
3718 None,
3719 false,
3720 expr.arg.as_ref(),
3721 ),
3722 AggregateFunction::JsonGroupObject => (
3723 "json_group_object".to_string(),
3724 None,
3725 false,
3726 expr.arg.as_ref(),
3727 ),
3728 AggregateFunction::JsonbAgg => ("jsonb_agg".to_string(), None, false, expr.arg.as_ref()),
3729 AggregateFunction::JsonbObjectAgg => (
3730 "jsonb_object_agg".to_string(),
3731 None,
3732 false,
3733 expr.arg.as_ref(),
3734 ),
3735 AggregateFunction::PercentileDisc { fraction } => (
3738 "percentile_disc".to_string(),
3739 Some(format!("{fraction:?}")),
3740 false,
3741 None,
3742 ),
3743 AggregateFunction::PercentileCont { fraction } => (
3744 "percentile_cont".to_string(),
3745 Some(format!("{fraction:?}")),
3746 false,
3747 None,
3748 ),
3749 AggregateFunction::QuantileCont { fraction } => (
3750 "quantile_cont".to_string(),
3751 Some(format!("{fraction:?}")),
3752 false,
3753 expr.arg.as_ref(),
3754 ),
3755 AggregateFunction::Variance { sample } => (
3756 if *sample { "var_samp" } else { "var_pop" }.to_string(),
3757 None,
3758 false,
3759 expr.arg.as_ref(),
3760 ),
3761 AggregateFunction::Stddev { sample } => (
3762 if *sample { "stddev_samp" } else { "stddev_pop" }.to_string(),
3763 None,
3764 false,
3765 expr.arg.as_ref(),
3766 ),
3767 AggregateFunction::Covariance { sample } => (
3768 if *sample { "covar_samp" } else { "covar_pop" }.to_string(),
3769 None,
3770 false,
3771 expr.arg.as_ref(),
3772 ),
3773 AggregateFunction::Corr => ("corr".into(), None, false, expr.arg.as_ref()),
3774 AggregateFunction::Median => ("median".into(), None, false, expr.arg.as_ref()),
3775 AggregateFunction::Mode => (
3776 "mode".into(),
3777 None,
3778 false,
3779 expr.order_by
3780 .is_empty()
3781 .then_some(())
3782 .and(expr.arg.as_ref()),
3783 ),
3784 AggregateFunction::RegrCount => ("regr_count".into(), None, false, expr.arg.as_ref()),
3785 AggregateFunction::RegrAvgX => ("regr_avgx".into(), None, false, expr.arg.as_ref()),
3786 AggregateFunction::RegrAvgY => ("regr_avgy".into(), None, false, expr.arg.as_ref()),
3787 AggregateFunction::RegrSxx => ("regr_sxx".into(), None, false, expr.arg.as_ref()),
3788 AggregateFunction::RegrSyy => ("regr_syy".into(), None, false, expr.arg.as_ref()),
3789 AggregateFunction::RegrSxy => ("regr_sxy".into(), None, false, expr.arg.as_ref()),
3790 AggregateFunction::RegrSlope => ("regr_slope".into(), None, false, expr.arg.as_ref()),
3791 AggregateFunction::RegrIntercept => {
3792 ("regr_intercept".into(), None, false, expr.arg.as_ref())
3793 }
3794 AggregateFunction::RegrR2 => ("regr_r2".into(), None, false, expr.arg.as_ref()),
3795 AggregateFunction::AnyValue => ("any_value".into(), None, false, expr.arg.as_ref()),
3796 AggregateFunction::First => ("first".into(), None, false, expr.arg.as_ref()),
3797 AggregateFunction::Last => ("last".into(), None, false, expr.arg.as_ref()),
3798 AggregateFunction::ArgMin => ("arg_min".into(), None, false, expr.arg.as_ref()),
3799 AggregateFunction::ArgMax => ("arg_max".into(), None, false, expr.arg.as_ref()),
3800 AggregateFunction::BitAnd => ("bit_and".into(), None, false, expr.arg.as_ref()),
3801 AggregateFunction::BitOr => ("bit_or".into(), None, false, expr.arg.as_ref()),
3802 AggregateFunction::BitXor => ("bit_xor".into(), None, false, expr.arg.as_ref()),
3803 AggregateFunction::BoolAnd => ("bool_and".into(), None, false, expr.arg.as_ref()),
3804 AggregateFunction::BoolOr => ("bool_or".into(), None, false, expr.arg.as_ref()),
3805 };
3806 AggregateSignature {
3807 name,
3808 distinct: expr.distinct,
3809 star,
3810 arg_key: arg.map(typed_expr_signature),
3811 extra_arg_keys: expr.extra_args.iter().map(typed_expr_signature).collect(),
3812 separator,
3813 filter_key: expr.filter.as_ref().map(typed_expr_signature),
3814 order_key: typed_sort_signature(&expr.order_by),
3815 }
3816}
3817
3818fn aggregate_signature_from_call(
3819 name: &str,
3820 args: &[TypedExpr],
3821 distinct: bool,
3822 star: bool,
3823 filter: Option<&TypedExpr>,
3824 order_by: &[SortExpr],
3825) -> Result<AggregateSignature, PlannerError> {
3826 let lower = name.to_ascii_lowercase();
3827 let is_percentile = matches!(lower.as_str(), "percentile_disc" | "percentile_cont");
3828 let separator = if name.eq_ignore_ascii_case("group_concat") && args.len() == 2 {
3829 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3830 Some(value.clone())
3831 } else {
3832 return Err(PlannerError::invalid_expression(
3833 "GROUP_CONCAT separator must be a string literal".to_string(),
3834 ));
3835 }
3836 } else if name.eq_ignore_ascii_case("string_agg") && args.len() == 2 {
3837 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3838 Some(value.clone())
3839 } else {
3840 return Err(PlannerError::invalid_expression(
3841 "STRING_AGG separator must be a string literal".to_string(),
3842 ));
3843 }
3844 } else if is_percentile && args.len() == 1 {
3845 Some(format!(
3846 "{:?}",
3847 percentile_fraction_named(&lower, &args[0])?
3848 ))
3849 } else if lower == "quantile_cont" && args.len() == 2 {
3850 Some(format!(
3851 "{:?}",
3852 percentile_fraction_named(&lower, &args[1])?
3853 ))
3854 } else {
3855 None
3856 };
3857 Ok(AggregateSignature {
3858 name: canonical_aggregate_name(name),
3859 distinct,
3860 star,
3861 arg_key: if is_percentile {
3862 None
3863 } else {
3864 args.first().map(typed_expr_signature)
3865 },
3866 extra_arg_keys: if matches!(
3867 lower.as_str(),
3868 "group_concat" | "string_agg" | "percentile_disc" | "percentile_cont" | "quantile_cont"
3869 ) {
3870 Vec::new()
3871 } else {
3872 args.iter().skip(1).map(typed_expr_signature).collect()
3873 },
3874 separator,
3875 filter_key: filter.map(typed_expr_signature),
3876 order_key: if is_order_sensitive_aggregate_name(name) {
3877 typed_sort_signature(order_by)
3878 } else {
3879 None
3880 },
3881 })
3882}
3883
3884fn typed_expr_signature(expr: &TypedExpr) -> String {
3885 format!("{:?}", expr.kind)
3886}
3887
3888fn single_column_type(schema: &[ColumnMetadata], span: Span) -> Result<ResolvedType, PlannerError> {
3889 match schema {
3890 [column] => Ok(column.data_type.clone()),
3891 [] => Err(PlannerError::type_mismatch(
3892 "one-column subquery",
3893 "zero-column subquery",
3894 span,
3895 )),
3896 _ => Err(PlannerError::type_mismatch(
3897 "one-column subquery",
3898 format!("{} columns", schema.len()),
3899 span,
3900 )),
3901 }
3902}
3903
3904fn row_items(expr: &Expr) -> Option<&[Expr]> {
3905 match &expr.kind {
3906 ExprKind::Row { items } => Some(items),
3907 _ => None,
3908 }
3909}
3910
3911fn internal_predicate(name: String, args: Vec<TypedExpr>, span: Span) -> TypedExpr {
3912 TypedExpr::function_call(name, args, false, false, ResolvedType::Boolean, span)
3913}
3914
3915#[cfg(test)]
3917#[path = "type_checker/tests.rs"]
3918mod tests;