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(_) => {
417 return Err(PlannerError::unsupported_feature(
418 "INTERVAL literals require a SQL-TS semantic layer",
419 "0.9.0",
420 span,
421 ));
422 }
423 Literal::Boolean(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Boolean),
424 Literal::Null => (TypedExprKind::Literal(lit.clone()), ResolvedType::Null),
425 };
426
427 Ok(TypedExpr {
428 kind,
429 resolved_type,
430 span,
431 })
432 }
433
434 #[allow(dead_code)]
436 fn infer_column_ref_type(
437 &self,
438 table: &TableMetadata,
439 column_name: &str,
440 span: Span,
441 ) -> Result<TypedExpr, PlannerError> {
442 let (column_index, column) = table
444 .columns
445 .iter()
446 .enumerate()
447 .find(|(_, c)| c.name == column_name)
448 .ok_or_else(|| PlannerError::ColumnNotFound {
449 column: column_name.to_string(),
450 table: table.name.clone(),
451 line: span.start.line,
452 col: span.start.column,
453 })?;
454
455 Ok(TypedExpr {
456 kind: TypedExprKind::ColumnRef {
457 table: table.name.clone(),
458 column: column_name.to_string(),
459 column_index,
460 },
461 resolved_type: column.data_type.clone(),
462 span,
463 })
464 }
465
466 fn infer_column_ref_type_with_scope(
467 &self,
468 scope: &[ScopedTable],
469 table_qualifier: Option<&str>,
470 column_name: &str,
471 span: Span,
472 ) -> Result<TypedExpr, PlannerError> {
473 let levels = scope
474 .iter()
475 .map(|table| table.scope_level)
476 .collect::<BTreeSet<_>>();
477 let mut qualifier_found = false;
478
479 for level in levels {
480 let candidates = scope
481 .iter()
482 .filter(|table| table.scope_level == level)
483 .filter(|table| {
484 table_qualifier.is_some()
485 || !table.hidden_unqualified_columns.contains(column_name)
486 })
487 .collect::<Vec<_>>();
488 if candidates.is_empty() {
489 continue;
490 }
491 if let Some(qualifier) = table_qualifier {
492 let qualified = candidates
493 .iter()
494 .filter(|table| table.table.name == qualifier)
495 .collect::<Vec<_>>();
496 match qualified.len() {
497 0 => continue,
498 1 => qualifier_found = true,
499 _ => {
500 return Err(PlannerError::ambiguous_column(
501 column_name,
502 qualified
503 .iter()
504 .map(|table| table.table.name.clone())
505 .collect(),
506 span,
507 ));
508 }
509 }
510 }
511
512 let mut matches = candidates.iter().filter(|table| {
516 table_qualifier.is_none_or(|qualifier| table.table.name == qualifier)
517 && table.column_position(column_name).is_some()
518 });
519 let found = matches.next();
520 let second = matches.next();
521
522 match (found, second) {
523 (Some(_), Some(_)) => {
524 return Err(PlannerError::ambiguous_column(
525 column_name,
526 candidates
527 .iter()
528 .filter(|table| table.column_position(column_name).is_some())
529 .map(|table| table.table.name.clone())
530 .collect(),
531 span,
532 ));
533 }
534 (None, _) => {
535 if table_qualifier.is_some() {
536 return Err(PlannerError::column_not_found(
539 column_name,
540 candidates
541 .first()
542 .map(|table| table.table.name.as_str())
543 .unwrap_or("unknown"),
544 span,
545 ));
546 }
547 continue;
550 }
551 (Some(scoped), None) => {
552 let column_index = scoped
553 .column_position(column_name)
554 .expect("filtered on the column being present");
555 let column = &scoped.table.columns[column_index];
556 let own_ref = TypedExpr {
557 kind: TypedExprKind::ColumnRef {
558 table: scoped.table.name.clone(),
559 column: column_name.to_string(),
560 column_index: scoped.start_index + column_index,
561 },
562 resolved_type: column.data_type.clone(),
563 span,
564 };
565
566 if table_qualifier.is_none()
571 && let Some(partner_indices) =
572 scoped.merged_column_partners.get(column_name)
573 {
574 let mut args = Vec::with_capacity(partner_indices.len() + 1);
575 args.push(own_ref);
576 args.extend(partner_indices.iter().map(|&partner_index| TypedExpr {
577 kind: TypedExprKind::ColumnRef {
578 table: scoped.table.name.clone(),
579 column: column_name.to_string(),
580 column_index: partner_index,
581 },
582 resolved_type: column.data_type.clone(),
583 span,
584 }));
585 return Ok(TypedExpr {
586 kind: TypedExprKind::FunctionCall {
587 name: "coalesce".to_string(),
588 args,
589 distinct: false,
590 star: false,
591 filter: None,
592 order_by: Vec::new(),
593 over: None,
594 },
595 resolved_type: column.data_type.clone(),
596 span,
597 });
598 }
599
600 return Ok(own_ref);
601 }
602 }
603 }
604
605 let table = scope
606 .iter()
607 .min_by_key(|table| table.scope_level)
608 .map(|table| table.table.name.clone())
609 .unwrap_or_else(|| "unknown".to_string());
610 if let Some(qualifier) = table_qualifier
611 && !qualifier_found
612 {
613 return Err(PlannerError::table_not_found(qualifier, span));
614 }
615 Err(PlannerError::column_not_found(column_name, table, span))
616 }
617
618 #[allow(dead_code)]
620 fn infer_binary_op_type(
621 &self,
622 left: &Expr,
623 op: BinaryOp,
624 right: &Expr,
625 table: &TableMetadata,
626 span: Span,
627 ) -> Result<TypedExpr, PlannerError> {
628 let left_typed = self.infer_type(left, table)?;
629 let right_typed = self.infer_type(right, table)?;
630
631 let result_type = self.check_binary_op(
632 op,
633 &left_typed.resolved_type,
634 &right_typed.resolved_type,
635 span,
636 )?;
637
638 if let Some(folded) =
639 fold_integral_binary(&left_typed, op, &right_typed, &result_type, span)
640 {
641 return Ok(folded);
642 }
643
644 Ok(TypedExpr {
645 kind: TypedExprKind::BinaryOp {
646 left: Box::new(left_typed),
647 op,
648 right: Box::new(right_typed),
649 },
650 resolved_type: result_type,
651 span,
652 })
653 }
654
655 fn infer_binary_op_type_with_scope(
656 &self,
657 left: &Expr,
658 op: BinaryOp,
659 right: &Expr,
660 scope: &[ScopedTable],
661 plan_subquery: &SubqueryPlanner<'_>,
662 span: Span,
663 ) -> Result<TypedExpr, PlannerError> {
664 if row_items(left).is_some() || row_items(right).is_some() {
665 let internal = match op {
666 BinaryOp::Eq => INTERNAL_ROW_EQ,
667 BinaryOp::Neq => INTERNAL_ROW_NEQ,
668 BinaryOp::Lt => INTERNAL_ROW_LT,
669 BinaryOp::LtEq => INTERNAL_ROW_LTEQ,
670 BinaryOp::Gt => INTERNAL_ROW_GT,
671 BinaryOp::GtEq => INTERNAL_ROW_GTEQ,
672 _ => {
673 return Err(PlannerError::invalid_operator(
674 format!("{op:?}"),
675 "Row",
676 span,
677 ));
678 }
679 };
680 let (mut left, right, width) =
681 self.infer_row_pair_with_scope(left, right, scope, plan_subquery, span)?;
682 left.extend(right);
683 return Ok(internal_predicate(
684 format!("{internal}:{width}"),
685 left,
686 span,
687 ));
688 }
689
690 let left_typed = self.infer_type_with_scope(left, scope, plan_subquery)?;
691 let right_typed = self.infer_type_with_scope(right, scope, plan_subquery)?;
692
693 let result_type = self.check_binary_op(
694 op,
695 &left_typed.resolved_type,
696 &right_typed.resolved_type,
697 span,
698 )?;
699
700 if let Some(folded) =
701 fold_integral_binary(&left_typed, op, &right_typed, &result_type, span)
702 {
703 return Ok(folded);
704 }
705
706 Ok(TypedExpr {
707 kind: TypedExprKind::BinaryOp {
708 left: Box::new(left_typed),
709 op,
710 right: Box::new(right_typed),
711 },
712 resolved_type: result_type,
713 span,
714 })
715 }
716
717 fn infer_case_type_with_scope(
718 &self,
719 operand: Option<&Expr>,
720 branches: &[crate::ast::expr::CaseWhen],
721 else_expr: Option<&Expr>,
722 scope: &[ScopedTable],
723 plan_subquery: &SubqueryPlanner<'_>,
724 span: Span,
725 ) -> Result<TypedExpr, PlannerError> {
726 if branches.is_empty() {
727 return Err(PlannerError::invalid_expression(
728 "CASE expression requires at least one WHEN branch",
729 ));
730 }
731 let typed_operand = operand
732 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
733 .transpose()?;
734 let mut typed_branches = Vec::with_capacity(branches.len());
735 let mut result_type = ResolvedType::Null;
736
737 for branch in branches {
738 let condition = self.infer_type_with_scope(&branch.when, scope, plan_subquery)?;
739 if let Some(operand) = &typed_operand {
740 self.check_comparison_op(
741 &operand.resolved_type,
742 &condition.resolved_type,
743 condition.span,
744 )?;
745 } else if !matches!(
746 condition.resolved_type,
747 ResolvedType::Boolean | ResolvedType::Null
748 ) {
749 return Err(PlannerError::type_mismatch(
750 "Boolean",
751 condition.resolved_type.type_name(),
752 condition.span,
753 ));
754 }
755
756 let result = self.infer_type_with_scope(&branch.then, scope, plan_subquery)?;
757 result_type =
758 self.common_case_result_type(&result_type, &result.resolved_type, result.span)?;
759 typed_branches.push(TypedCaseWhen {
760 when: condition,
761 then: result,
762 });
763 }
764
765 let mut typed_else = else_expr
766 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
767 .transpose()?;
768 if let Some(else_expr) = &typed_else {
769 result_type = self.common_case_result_type(
770 &result_type,
771 &else_expr.resolved_type,
772 else_expr.span,
773 )?;
774 }
775
776 for branch in &mut typed_branches {
777 coerce_case_result(&mut branch.then, &result_type);
778 }
779 if let Some(else_expr) = &mut typed_else {
780 coerce_case_result(else_expr, &result_type);
781 }
782
783 Ok(TypedExpr {
784 kind: TypedExprKind::Case {
785 operand: typed_operand.map(Box::new),
786 branches: typed_branches,
787 else_expr: typed_else.map(Box::new),
788 },
789 resolved_type: result_type,
790 span,
791 })
792 }
793
794 fn common_case_result_type(
795 &self,
796 current: &ResolvedType,
797 next: &ResolvedType,
798 span: Span,
799 ) -> Result<ResolvedType, PlannerError> {
800 if matches!(current, ResolvedType::Null) {
801 return Ok(next.clone());
802 }
803 if matches!(next, ResolvedType::Null) || current == next {
804 return Ok(current.clone());
805 }
806 if is_numeric_type(current) && is_numeric_type(next) {
807 return self.check_arithmetic_op(current, next, span);
808 }
809 Err(PlannerError::type_mismatch(
810 current.type_name(),
811 next.type_name(),
812 span,
813 ))
814 }
815
816 pub fn check_binary_op(
828 &self,
829 op: BinaryOp,
830 left: &ResolvedType,
831 right: &ResolvedType,
832 span: Span,
833 ) -> Result<ResolvedType, PlannerError> {
834 use BinaryOp::*;
835 use ResolvedType::*;
836
837 match op {
838 Add | Sub | Mul | Div => {
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 check_modulo_op(
916 &self,
917 left: &ResolvedType,
918 right: &ResolvedType,
919 span: Span,
920 ) -> Result<ResolvedType, PlannerError> {
921 use ResolvedType::*;
922
923 if matches!(left, Null) || matches!(right, Null) {
924 return Ok(Null);
925 }
926
927 match (left, right) {
928 (Integer, Integer) => Ok(Integer),
929 (Integer, BigInt) | (BigInt, Integer) | (BigInt, BigInt) => Ok(BigInt),
930 _ => Err(PlannerError::InvalidOperator {
931 op: "modulo".to_string(),
932 type_name: format!("{} and {}", left.type_name(), right.type_name()),
933 line: span.start.line,
934 column: span.start.column,
935 }),
936 }
937 }
938
939 fn check_integral_op(
940 &self,
941 left: &ResolvedType,
942 right: &ResolvedType,
943 span: Span,
944 ) -> Result<ResolvedType, PlannerError> {
945 use ResolvedType::*;
946 if matches!(left, Null) || matches!(right, Null) {
947 return Ok(Null);
948 }
949 match (left, right) {
950 (Integer, Integer) => Ok(Integer),
951 (Integer | BigInt, Integer | BigInt) => Ok(BigInt),
952 _ => Err(PlannerError::invalid_operator(
953 "bitwise",
954 format!("{} and {}", left.type_name(), right.type_name()),
955 span,
956 )),
957 }
958 }
959
960 pub(crate) fn check_comparison_op(
962 &self,
963 left: &ResolvedType,
964 right: &ResolvedType,
965 span: Span,
966 ) -> Result<(), PlannerError> {
967 use ResolvedType::*;
968
969 if matches!(left, Null) || matches!(right, Null) {
971 return Ok(());
972 }
973
974 let compatible = match (left, right) {
976 (a, b) if a == b => true,
978
979 (Integer | BigInt | Float | Double, Integer | BigInt | Float | Double) => true,
981
982 (Text, Text) => true,
984
985 (Boolean, Boolean) => true,
987
988 (Timestamp, Timestamp) => true,
990
991 (Vector { dimension: d1, .. }, Vector { dimension: d2, .. }) => d1 == d2,
993
994 _ => false,
995 };
996
997 if compatible {
998 Ok(())
999 } else {
1000 Err(PlannerError::TypeMismatch {
1001 expected: left.type_name().to_string(),
1002 found: right.type_name().to_string(),
1003 line: span.start.line,
1004 column: span.start.column,
1005 })
1006 }
1007 }
1008
1009 fn check_logical_op(
1011 &self,
1012 left: &ResolvedType,
1013 right: &ResolvedType,
1014 span: Span,
1015 ) -> Result<(), PlannerError> {
1016 use ResolvedType::*;
1017
1018 let left_ok = matches!(left, Boolean | Null);
1020 let right_ok = matches!(right, Boolean | Null);
1021
1022 if !left_ok {
1023 return Err(PlannerError::TypeMismatch {
1024 expected: "Boolean".to_string(),
1025 found: left.type_name().to_string(),
1026 line: span.start.line,
1027 column: span.start.column,
1028 });
1029 }
1030
1031 if !right_ok {
1032 return Err(PlannerError::TypeMismatch {
1033 expected: "Boolean".to_string(),
1034 found: right.type_name().to_string(),
1035 line: span.start.line,
1036 column: span.start.column,
1037 });
1038 }
1039
1040 Ok(())
1041 }
1042
1043 fn check_string_concat_op(
1045 &self,
1046 left: &ResolvedType,
1047 right: &ResolvedType,
1048 span: Span,
1049 ) -> Result<(), PlannerError> {
1050 use ResolvedType::*;
1051
1052 let left_ok = matches!(left, Text | Null);
1054 let right_ok = matches!(right, Text | Null);
1055
1056 if !left_ok {
1057 return Err(PlannerError::TypeMismatch {
1058 expected: "Text".to_string(),
1059 found: left.type_name().to_string(),
1060 line: span.start.line,
1061 column: span.start.column,
1062 });
1063 }
1064
1065 if !right_ok {
1066 return Err(PlannerError::TypeMismatch {
1067 expected: "Text".to_string(),
1068 found: right.type_name().to_string(),
1069 line: span.start.line,
1070 column: span.start.column,
1071 });
1072 }
1073
1074 Ok(())
1075 }
1076
1077 #[allow(dead_code)]
1079 fn infer_unary_op_type(
1080 &self,
1081 op: UnaryOp,
1082 operand: &Expr,
1083 table: &TableMetadata,
1084 span: Span,
1085 ) -> Result<TypedExpr, PlannerError> {
1086 let operand_typed = self.infer_type(operand, table)?;
1087
1088 let result_type = match op {
1089 UnaryOp::Not => {
1090 if !matches!(
1092 operand_typed.resolved_type,
1093 ResolvedType::Boolean | ResolvedType::Null
1094 ) {
1095 return Err(PlannerError::TypeMismatch {
1096 expected: "Boolean".to_string(),
1097 found: operand_typed.resolved_type.type_name().to_string(),
1098 line: span.start.line,
1099 column: span.start.column,
1100 });
1101 }
1102 ResolvedType::Boolean
1103 }
1104 UnaryOp::Minus => {
1105 match &operand_typed.resolved_type {
1107 ResolvedType::Integer => ResolvedType::Integer,
1108 ResolvedType::BigInt => ResolvedType::BigInt,
1109 ResolvedType::Float => ResolvedType::Float,
1110 ResolvedType::Double => ResolvedType::Double,
1111 ResolvedType::Null => ResolvedType::Null,
1112 other => {
1113 return Err(PlannerError::InvalidOperator {
1114 op: "unary minus".to_string(),
1115 type_name: other.type_name().to_string(),
1116 line: span.start.line,
1117 column: span.start.column,
1118 });
1119 }
1120 }
1121 }
1122 UnaryOp::BitNot => match &operand_typed.resolved_type {
1123 ResolvedType::Integer => ResolvedType::Integer,
1124 ResolvedType::BigInt => ResolvedType::BigInt,
1125 ResolvedType::Null => ResolvedType::Null,
1126 other => {
1127 return Err(PlannerError::InvalidOperator {
1128 op: "bitwise not".to_string(),
1129 type_name: other.type_name().to_string(),
1130 line: span.start.line,
1131 column: span.start.column,
1132 });
1133 }
1134 },
1135 };
1136
1137 Ok(TypedExpr {
1138 kind: TypedExprKind::UnaryOp {
1139 op,
1140 operand: Box::new(operand_typed),
1141 },
1142 resolved_type: result_type,
1143 span,
1144 })
1145 }
1146
1147 fn infer_unary_op_type_with_scope(
1148 &self,
1149 op: UnaryOp,
1150 operand: &Expr,
1151 scope: &[ScopedTable],
1152 plan_subquery: &SubqueryPlanner<'_>,
1153 span: Span,
1154 ) -> Result<TypedExpr, PlannerError> {
1155 let operand_typed = self.infer_type_with_scope(operand, scope, plan_subquery)?;
1156
1157 let result_type = match op {
1158 UnaryOp::Not => {
1159 if !matches!(
1160 operand_typed.resolved_type,
1161 ResolvedType::Boolean | ResolvedType::Null
1162 ) {
1163 return Err(PlannerError::TypeMismatch {
1164 expected: "Boolean".to_string(),
1165 found: operand_typed.resolved_type.type_name().to_string(),
1166 line: span.start.line,
1167 column: span.start.column,
1168 });
1169 }
1170 ResolvedType::Boolean
1171 }
1172 UnaryOp::Minus => match &operand_typed.resolved_type {
1173 ResolvedType::Integer => ResolvedType::Integer,
1174 ResolvedType::BigInt => ResolvedType::BigInt,
1175 ResolvedType::Float => ResolvedType::Float,
1176 ResolvedType::Double => ResolvedType::Double,
1177 ResolvedType::Null => ResolvedType::Null,
1178 other => {
1179 return Err(PlannerError::InvalidOperator {
1180 op: "unary minus".to_string(),
1181 type_name: other.type_name().to_string(),
1182 line: span.start.line,
1183 column: span.start.column,
1184 });
1185 }
1186 },
1187 UnaryOp::BitNot => match &operand_typed.resolved_type {
1188 ResolvedType::Integer => ResolvedType::Integer,
1189 ResolvedType::BigInt => ResolvedType::BigInt,
1190 ResolvedType::Null => ResolvedType::Null,
1191 other => {
1192 return Err(PlannerError::InvalidOperator {
1193 op: "bitwise not".to_string(),
1194 type_name: other.type_name().to_string(),
1195 line: span.start.line,
1196 column: span.start.column,
1197 });
1198 }
1199 },
1200 };
1201
1202 Ok(TypedExpr {
1203 kind: TypedExprKind::UnaryOp {
1204 op,
1205 operand: Box::new(operand_typed),
1206 },
1207 resolved_type: result_type,
1208 span,
1209 })
1210 }
1211
1212 #[allow(dead_code)]
1214 fn infer_function_call_type(
1215 &self,
1216 name: &str,
1217 args: &[Expr],
1218 distinct: bool,
1219 star: bool,
1220 table: &TableMetadata,
1221 span: Span,
1222 ) -> Result<TypedExpr, PlannerError> {
1223 let typed_args: Vec<TypedExpr> = args
1225 .iter()
1226 .map(|arg| self.infer_type(arg, table))
1227 .collect::<Result<Vec<_>, _>>()?;
1228
1229 let result_type = self.check_function_call(name, &typed_args, distinct, star, span)?;
1231
1232 Ok(TypedExpr {
1233 kind: TypedExprKind::FunctionCall {
1234 name: name.to_string(),
1235 args: typed_args,
1236 distinct,
1237 star,
1238 filter: None,
1239 order_by: Vec::new(),
1240 over: None,
1241 },
1242 resolved_type: result_type,
1243 span,
1244 })
1245 }
1246
1247 #[allow(clippy::too_many_arguments)]
1248 fn infer_function_call_type_with_scope(
1249 &self,
1250 name: &str,
1251 args: &[Expr],
1252 distinct: bool,
1253 star: bool,
1254 order_by: &[crate::ast::dml::OrderByExpr],
1255 within_group: &[crate::ast::dml::OrderByExpr],
1256 filter: Option<&Expr>,
1257 over: Option<&WindowSpec>,
1258 scope: &[ScopedTable],
1259 plan_subquery: &SubqueryPlanner<'_>,
1260 span: Span,
1261 ) -> Result<TypedExpr, PlannerError> {
1262 let lower_name = name.to_ascii_lowercase();
1263 self.validate_aggregate_clause_placement(
1264 &lower_name,
1265 distinct,
1266 order_by,
1267 within_group,
1268 filter,
1269 over.is_some(),
1270 span,
1271 )?;
1272 if over.is_some() {
1273 match lower_name.as_str() {
1274 "lag" | "lead" => {
1275 validate_offset_window_call(name, args.len(), distinct, star)?;
1276 }
1277 "first_value" | "last_value" | "ntile" => {
1278 validate_exact_window_call(name, args.len(), 1, distinct, star)?;
1279 }
1280 "nth_value" => {
1281 validate_exact_window_call(name, args.len(), 2, distinct, star)?;
1282 }
1283 "percent_rank" | "cume_dist" => {
1284 validate_exact_window_call(name, args.len(), 0, distinct, star)?;
1285 }
1286 _ => {}
1287 }
1288 }
1289
1290 let mut typed_args: Vec<TypedExpr> = args
1291 .iter()
1292 .map(|arg| self.infer_type_with_scope(arg, scope, plan_subquery))
1293 .collect::<Result<Vec<_>, _>>()?;
1294
1295 let order_by_source = if within_group.is_empty() {
1298 order_by
1299 } else {
1300 within_group
1301 };
1302 let typed_order_by = order_by_source
1303 .iter()
1304 .map(|order| {
1305 let expr = self.infer_type_with_scope(&order.expr, scope, plan_subquery)?;
1306 if super::typed_expr_contains_aggregate(&expr) {
1307 return Err(PlannerError::invalid_expression(
1308 "aggregate functions are not allowed in aggregate ORDER BY".to_string(),
1309 ));
1310 }
1311 if super::typed_expr_contains_window(&expr) {
1312 return Err(PlannerError::invalid_expression(
1313 "window functions are not allowed in aggregate ORDER BY".to_string(),
1314 ));
1315 }
1316 Ok(SortExpr::new(
1317 expr,
1318 order.asc.unwrap_or(true),
1319 order.nulls_first.unwrap_or(false),
1320 ))
1321 })
1322 .collect::<Result<Vec<_>, PlannerError>>()?;
1323
1324 let typed_filter = filter
1325 .map(|predicate| {
1326 let typed = self.infer_type_with_scope(predicate, scope, plan_subquery)?;
1327 if super::typed_expr_contains_aggregate(&typed) {
1328 return Err(PlannerError::invalid_expression(
1329 "aggregate functions are not allowed in FILTER".to_string(),
1330 ));
1331 }
1332 if super::typed_expr_contains_window(&typed) {
1333 return Err(PlannerError::invalid_expression(
1334 "window functions are not allowed in FILTER".to_string(),
1335 ));
1336 }
1337 if !matches!(
1338 typed.resolved_type,
1339 ResolvedType::Boolean | ResolvedType::Null
1340 ) {
1341 return Err(PlannerError::type_mismatch(
1342 "BOOLEAN FILTER predicate",
1343 typed.resolved_type.type_name(),
1344 typed.span,
1345 ));
1346 }
1347 Ok(Box::new(typed))
1348 })
1349 .transpose()?;
1350
1351 if distinct && !typed_order_by.is_empty() {
1355 for sort in &typed_order_by {
1356 let key = super::distinct_on_expr_signature(&sort.expr);
1357 let appears = typed_args
1358 .iter()
1359 .any(|arg| super::distinct_on_expr_signature(arg) == key);
1360 if !appears {
1361 return Err(PlannerError::invalid_expression(
1362 "in an aggregate with DISTINCT, ORDER BY expressions must appear in \
1363 the argument list"
1364 .to_string(),
1365 ));
1366 }
1367 }
1368 }
1369
1370 let result_type = if over.is_some() {
1371 match lower_name.as_str() {
1372 "lag" | "lead" => self.infer_offset_window_result_type(name, &mut typed_args)?,
1373 "first_value" | "last_value" => typed_args[0].resolved_type.clone(),
1374 "nth_value" => {
1375 validate_positive_integer_argument(name, &typed_args[1])?;
1376 typed_args[0].resolved_type.clone()
1377 }
1378 "ntile" => {
1379 validate_positive_integer_argument(name, &typed_args[0])?;
1380 ResolvedType::BigInt
1381 }
1382 "percent_rank" | "cume_dist" => ResolvedType::Double,
1383 "row_number" | "rank" | "dense_rank" => {
1384 if !typed_args.is_empty() || distinct || star {
1385 return Err(PlannerError::invalid_expression(format!(
1386 "{}() window function takes no arguments",
1387 name.to_ascii_uppercase()
1388 )));
1389 }
1390 ResolvedType::BigInt
1391 }
1392 name if is_aggregate_name(name) => {
1393 self.check_function_call(name, &typed_args, distinct, star, span)?
1394 }
1395 _ => {
1396 return Err(PlannerError::unsupported_feature(
1397 format!("function '{}' with OVER", name),
1398 "future",
1399 span,
1400 ));
1401 }
1402 }
1403 } else if matches!(lower_name.as_str(), "percentile_disc" | "percentile_cont") {
1404 self.check_percentile(&lower_name, &typed_args, &typed_order_by, span)?
1405 } else if lower_name == "mode" && !within_group.is_empty() {
1406 if !typed_args.is_empty() || typed_order_by.len() != 1 {
1407 return Err(PlannerError::invalid_expression(
1408 "MODE requires no arguments and exactly one WITHIN GROUP sort expression"
1409 .to_string(),
1410 ));
1411 }
1412 typed_order_by[0].expr.resolved_type.clone()
1413 } else {
1414 self.check_function_call(name, &typed_args, distinct, star, span)?
1415 };
1416
1417 let typed_over = over
1418 .map(|window| {
1419 if let Some(base) = &window.base {
1420 return Err(PlannerError::invalid_expression(format!(
1421 "named window '{base}' was not resolved in its query block"
1422 )));
1423 }
1424 let partition_by = window
1425 .partition_by
1426 .iter()
1427 .map(|expr| self.infer_type_with_scope(expr, scope, plan_subquery))
1428 .collect::<Result<Vec<_>, _>>()?;
1429 let order_by = window
1430 .order_by
1431 .iter()
1432 .map(|order| {
1433 let expr = self.infer_type_with_scope(&order.expr, scope, plan_subquery)?;
1434 Ok(SortExpr::new(
1435 expr,
1436 order.asc.unwrap_or(true),
1437 order.nulls_first.unwrap_or(false),
1438 ))
1439 })
1440 .collect::<Result<Vec<_>, PlannerError>>()?;
1441 if let Some(frame) = &window.frame {
1442 validate_window_frame(&lower_name, frame, &order_by)?;
1443 }
1444 Ok(TypedWindowSpec {
1445 partition_by,
1446 order_by,
1447 frame: window.frame.clone(),
1448 })
1449 })
1450 .transpose()?;
1451
1452 Ok(TypedExpr {
1453 kind: TypedExprKind::FunctionCall {
1454 name: name.to_string(),
1455 args: typed_args,
1456 distinct,
1457 star,
1458 filter: typed_filter,
1459 order_by: typed_order_by,
1460 over: typed_over,
1461 },
1462 resolved_type: result_type,
1463 span,
1464 })
1465 }
1466
1467 #[allow(clippy::too_many_arguments)]
1470 fn validate_aggregate_clause_placement(
1471 &self,
1472 lower_name: &str,
1473 distinct: bool,
1474 order_by: &[crate::ast::dml::OrderByExpr],
1475 within_group: &[crate::ast::dml::OrderByExpr],
1476 filter: Option<&Expr>,
1477 has_over: bool,
1478 span: Span,
1479 ) -> Result<(), PlannerError> {
1480 let is_ordered_set = is_ordered_set_aggregate_name(lower_name);
1481 let is_aggregate = is_aggregate_name(lower_name);
1482
1483 if let Some(filter) = filter {
1484 if has_over {
1485 return Err(PlannerError::unsupported_feature(
1489 "FILTER on a window function call",
1490 "future",
1491 span,
1492 ));
1493 }
1494 if !is_aggregate {
1495 return Err(PlannerError::invalid_expression(format!(
1496 "FILTER (WHERE ...) is only valid for aggregate functions, not '{lower_name}'"
1497 )));
1498 }
1499 if super::expr_contains_subquery(filter) {
1500 return Err(PlannerError::unsupported_feature(
1501 "subquery in aggregate FILTER",
1502 "future",
1503 filter.span,
1504 ));
1505 }
1506 }
1507
1508 if !within_group.is_empty() {
1509 if has_over {
1510 return Err(PlannerError::invalid_expression(
1512 "WITHIN GROUP cannot be combined with OVER".to_string(),
1513 ));
1514 }
1515 if !is_ordered_set {
1516 return Err(PlannerError::invalid_expression(format!(
1517 "WITHIN GROUP is only valid for ordered-set aggregate functions, \
1518 not '{lower_name}'"
1519 )));
1520 }
1521 if distinct {
1522 return Err(PlannerError::invalid_expression(
1523 "DISTINCT is not supported with WITHIN GROUP".to_string(),
1524 ));
1525 }
1526 if within_group
1527 .iter()
1528 .any(|order| super::expr_contains_subquery(&order.expr))
1529 {
1530 return Err(PlannerError::unsupported_feature(
1531 "subquery in aggregate ORDER BY",
1532 "future",
1533 span,
1534 ));
1535 }
1536 }
1537
1538 if !order_by.is_empty() {
1539 if has_over {
1540 return Err(PlannerError::invalid_expression(
1543 "aggregate ORDER BY cannot be combined with OVER".to_string(),
1544 ));
1545 }
1546 if !is_aggregate || is_ordered_set {
1547 return Err(PlannerError::invalid_expression(format!(
1548 "ORDER BY in the argument list is only valid for aggregate functions, \
1549 not '{lower_name}'"
1550 )));
1551 }
1552 if order_by
1553 .iter()
1554 .any(|order| super::expr_contains_subquery(&order.expr))
1555 {
1556 return Err(PlannerError::unsupported_feature(
1557 "subquery in aggregate ORDER BY",
1558 "future",
1559 span,
1560 ));
1561 }
1562 }
1563
1564 if matches!(lower_name, "percentile_disc" | "percentile_cont") && within_group.is_empty() {
1565 return Err(PlannerError::invalid_expression(format!(
1566 "WITHIN GROUP (ORDER BY ...) is required for {}",
1567 lower_name.to_ascii_uppercase()
1568 )));
1569 }
1570
1571 Ok(())
1572 }
1573
1574 fn check_percentile(
1578 &self,
1579 name: &str,
1580 args: &[TypedExpr],
1581 order_by: &[SortExpr],
1582 span: Span,
1583 ) -> Result<ResolvedType, PlannerError> {
1584 if args.len() != 1 {
1585 return Err(PlannerError::type_mismatch(
1586 "1 argument",
1587 format!("{} arguments", args.len()),
1588 span,
1589 ));
1590 }
1591 let _ = percentile_fraction_named(name, &args[0])?;
1592 if order_by.len() != 1 {
1593 return Err(PlannerError::invalid_expression(format!(
1594 "{} requires WITHIN GROUP (ORDER BY ...) with exactly one sort expression",
1595 name.to_ascii_uppercase()
1596 )));
1597 }
1598 Ok(order_by[0].expr.resolved_type.clone())
1599 }
1600
1601 fn infer_offset_window_result_type(
1602 &self,
1603 name: &str,
1604 args: &mut [TypedExpr],
1605 ) -> Result<ResolvedType, PlannerError> {
1606 if let Some(offset) = args.get(1)
1607 && !matches!(
1608 offset.resolved_type,
1609 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
1610 )
1611 {
1612 return Err(PlannerError::type_mismatch(
1613 "INTEGER offset",
1614 offset.resolved_type.type_name(),
1615 offset.span,
1616 ));
1617 }
1618
1619 let value_type = args
1620 .first()
1621 .map(|arg| arg.resolved_type.clone())
1622 .ok_or_else(|| {
1623 PlannerError::invalid_expression(format!(
1624 "{}() window function expects 1 to 3 arguments",
1625 name.to_ascii_uppercase()
1626 ))
1627 })?;
1628 let result_type = if let Some(default) = args.get(2) {
1629 self.common_compatible_result_type(&value_type, &default.resolved_type, default.span)?
1630 } else {
1631 value_type
1632 };
1633
1634 coerce_compatible_result(&mut args[0], &result_type);
1635 if let Some(default) = args.get_mut(2) {
1636 coerce_compatible_result(default, &result_type);
1637 }
1638
1639 Ok(result_type)
1640 }
1641
1642 fn common_compatible_result_type(
1643 &self,
1644 current: &ResolvedType,
1645 next: &ResolvedType,
1646 span: Span,
1647 ) -> Result<ResolvedType, PlannerError> {
1648 if matches!(current, ResolvedType::Null) {
1649 return Ok(next.clone());
1650 }
1651 if matches!(next, ResolvedType::Null) || current == next {
1652 return Ok(current.clone());
1653 }
1654 if is_numeric_type(current) && is_numeric_type(next) {
1655 return self.check_arithmetic_op(current, next, span);
1656 }
1657 if next.can_cast_to(current) {
1658 return Ok(current.clone());
1659 }
1660 if current.can_cast_to(next) {
1661 return Ok(next.clone());
1662 }
1663 Err(PlannerError::type_mismatch(
1664 current.type_name(),
1665 next.type_name(),
1666 span,
1667 ))
1668 }
1669
1670 #[allow(dead_code)]
1672 fn infer_between_type(
1673 &self,
1674 expr: &Expr,
1675 low: &Expr,
1676 high: &Expr,
1677 negated: bool,
1678 table: &TableMetadata,
1679 span: Span,
1680 ) -> Result<TypedExpr, PlannerError> {
1681 let expr_typed = self.infer_type(expr, table)?;
1682 let low_typed = self.infer_type(low, table)?;
1683 let high_typed = self.infer_type(high, table)?;
1684
1685 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
1687 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
1688
1689 Ok(TypedExpr {
1690 kind: TypedExprKind::Between {
1691 expr: Box::new(expr_typed),
1692 low: Box::new(low_typed),
1693 high: Box::new(high_typed),
1694 negated,
1695 },
1696 resolved_type: ResolvedType::Boolean,
1697 span,
1698 })
1699 }
1700
1701 #[allow(clippy::too_many_arguments)]
1702 fn infer_between_type_with_scope(
1703 &self,
1704 expr: &Expr,
1705 low: &Expr,
1706 high: &Expr,
1707 negated: bool,
1708 scope: &[ScopedTable],
1709 plan_subquery: &SubqueryPlanner<'_>,
1710 span: Span,
1711 ) -> Result<TypedExpr, PlannerError> {
1712 if row_items(expr).is_some() || row_items(low).is_some() || row_items(high).is_some() {
1713 let expr_typed = self.infer_row_operand_with_scope(expr, scope, plan_subquery)?;
1714 let low_typed = self.infer_row_operand_with_scope(low, scope, plan_subquery)?;
1715 let high_typed = self.infer_row_operand_with_scope(high, scope, plan_subquery)?;
1716 let width = expr_typed.len();
1717 self.check_row_arity(width, low_typed.len(), span)?;
1718 self.check_row_arity(width, high_typed.len(), span)?;
1719 self.check_row_types(&expr_typed, &low_typed, span)?;
1720 self.check_row_types(&expr_typed, &high_typed, span)?;
1721 let mut args = expr_typed;
1722 args.extend(low_typed);
1723 args.extend(high_typed);
1724 return Ok(internal_predicate(
1725 format!("{INTERNAL_ROW_BETWEEN}:{width}:{}", u8::from(negated)),
1726 args,
1727 span,
1728 ));
1729 }
1730
1731 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1732 let low_typed = self.infer_type_with_scope(low, scope, plan_subquery)?;
1733 let high_typed = self.infer_type_with_scope(high, scope, plan_subquery)?;
1734 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
1735 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
1736
1737 Ok(TypedExpr {
1738 kind: TypedExprKind::Between {
1739 expr: Box::new(expr_typed),
1740 low: Box::new(low_typed),
1741 high: Box::new(high_typed),
1742 negated,
1743 },
1744 resolved_type: ResolvedType::Boolean,
1745 span,
1746 })
1747 }
1748
1749 #[allow(dead_code)]
1751 #[allow(clippy::too_many_arguments)]
1752 fn infer_like_type(
1753 &self,
1754 expr: &Expr,
1755 pattern: &Expr,
1756 escape: Option<&Expr>,
1757 negated: bool,
1758 kind: PatternMatchKind,
1759 table: &TableMetadata,
1760 span: Span,
1761 ) -> Result<TypedExpr, PlannerError> {
1762 let expr_typed = self.infer_type(expr, table)?;
1763 let pattern_typed = self.infer_type(pattern, table)?;
1764
1765 if !matches!(
1767 expr_typed.resolved_type,
1768 ResolvedType::Text | ResolvedType::Null
1769 ) {
1770 return Err(PlannerError::TypeMismatch {
1771 expected: "Text".to_string(),
1772 found: expr_typed.resolved_type.type_name().to_string(),
1773 line: expr.span.start.line,
1774 column: expr.span.start.column,
1775 });
1776 }
1777
1778 if !matches!(
1780 pattern_typed.resolved_type,
1781 ResolvedType::Text | ResolvedType::Null
1782 ) {
1783 return Err(PlannerError::TypeMismatch {
1784 expected: "Text".to_string(),
1785 found: pattern_typed.resolved_type.type_name().to_string(),
1786 line: pattern.span.start.line,
1787 column: pattern.span.start.column,
1788 });
1789 }
1790
1791 let escape_typed = if let Some(esc) = escape {
1792 let typed = self.infer_type(esc, table)?;
1793 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
1794 return Err(PlannerError::TypeMismatch {
1795 expected: "Text".to_string(),
1796 found: typed.resolved_type.type_name().to_string(),
1797 line: esc.span.start.line,
1798 column: esc.span.start.column,
1799 });
1800 }
1801 Some(Box::new(typed))
1802 } else {
1803 None
1804 };
1805
1806 Ok(TypedExpr {
1807 kind: TypedExprKind::Like {
1808 expr: Box::new(expr_typed),
1809 pattern: Box::new(pattern_typed),
1810 escape: escape_typed,
1811 negated,
1812 kind,
1813 },
1814 resolved_type: ResolvedType::Boolean,
1815 span,
1816 })
1817 }
1818
1819 #[allow(clippy::too_many_arguments)]
1820 #[allow(clippy::too_many_arguments)]
1821 fn infer_like_type_with_scope(
1822 &self,
1823 expr: &Expr,
1824 pattern: &Expr,
1825 escape: Option<&Expr>,
1826 negated: bool,
1827 kind: PatternMatchKind,
1828 scope: &[ScopedTable],
1829 plan_subquery: &SubqueryPlanner<'_>,
1830 span: Span,
1831 ) -> Result<TypedExpr, PlannerError> {
1832 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1833 let pattern_typed = self.infer_type_with_scope(pattern, scope, plan_subquery)?;
1834
1835 if !matches!(
1836 expr_typed.resolved_type,
1837 ResolvedType::Text | ResolvedType::Null
1838 ) {
1839 return Err(PlannerError::TypeMismatch {
1840 expected: "Text".to_string(),
1841 found: expr_typed.resolved_type.type_name().to_string(),
1842 line: expr.span.start.line,
1843 column: expr.span.start.column,
1844 });
1845 }
1846
1847 if !matches!(
1848 pattern_typed.resolved_type,
1849 ResolvedType::Text | ResolvedType::Null
1850 ) {
1851 return Err(PlannerError::TypeMismatch {
1852 expected: "Text".to_string(),
1853 found: pattern_typed.resolved_type.type_name().to_string(),
1854 line: pattern.span.start.line,
1855 column: pattern.span.start.column,
1856 });
1857 }
1858
1859 let escape_typed = if let Some(esc) = escape {
1860 let typed = self.infer_type_with_scope(esc, scope, plan_subquery)?;
1861 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
1862 return Err(PlannerError::TypeMismatch {
1863 expected: "Text".to_string(),
1864 found: typed.resolved_type.type_name().to_string(),
1865 line: esc.span.start.line,
1866 column: esc.span.start.column,
1867 });
1868 }
1869 Some(Box::new(typed))
1870 } else {
1871 None
1872 };
1873
1874 Ok(TypedExpr {
1875 kind: TypedExprKind::Like {
1876 expr: Box::new(expr_typed),
1877 pattern: Box::new(pattern_typed),
1878 escape: escape_typed,
1879 negated,
1880 kind,
1881 },
1882 resolved_type: ResolvedType::Boolean,
1883 span,
1884 })
1885 }
1886
1887 #[allow(dead_code)]
1889 fn infer_in_list_type(
1890 &self,
1891 expr: &Expr,
1892 list: &[Expr],
1893 negated: bool,
1894 table: &TableMetadata,
1895 span: Span,
1896 ) -> Result<TypedExpr, PlannerError> {
1897 let expr_typed = self.infer_type(expr, table)?;
1898
1899 let typed_list: Vec<TypedExpr> = list
1900 .iter()
1901 .map(|item| {
1902 let typed = self.infer_type(item, table)?;
1903 self.check_comparison_op(
1905 &expr_typed.resolved_type,
1906 &typed.resolved_type,
1907 item.span,
1908 )?;
1909 Ok(typed)
1910 })
1911 .collect::<Result<Vec<_>, PlannerError>>()?;
1912
1913 Ok(TypedExpr {
1914 kind: TypedExprKind::InList {
1915 expr: Box::new(expr_typed),
1916 list: typed_list,
1917 negated,
1918 },
1919 resolved_type: ResolvedType::Boolean,
1920 span,
1921 })
1922 }
1923
1924 fn infer_in_list_type_with_scope(
1925 &self,
1926 expr: &Expr,
1927 list: &[Expr],
1928 negated: bool,
1929 scope: &[ScopedTable],
1930 plan_subquery: &SubqueryPlanner<'_>,
1931 span: Span,
1932 ) -> Result<TypedExpr, PlannerError> {
1933 if row_items(expr).is_some() || list.iter().any(|item| row_items(item).is_some()) {
1934 let mut args = self.infer_row_operand_with_scope(expr, scope, plan_subquery)?;
1935 let width = args.len();
1936 for item in list {
1937 let typed = self.infer_row_operand_with_scope(item, scope, plan_subquery)?;
1938 self.check_row_arity(width, typed.len(), item.span)?;
1939 self.check_row_types(&args[..width], &typed, item.span)?;
1940 args.extend(typed);
1941 }
1942 return Ok(internal_predicate(
1943 format!("{INTERNAL_ROW_IN}:{width}:{}", u8::from(negated)),
1944 args,
1945 span,
1946 ));
1947 }
1948
1949 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1950
1951 let typed_list: Vec<TypedExpr> = list
1952 .iter()
1953 .map(|item| {
1954 let typed = self.infer_type_with_scope(item, scope, plan_subquery)?;
1955 self.check_comparison_op(
1956 &expr_typed.resolved_type,
1957 &typed.resolved_type,
1958 item.span,
1959 )?;
1960 Ok(typed)
1961 })
1962 .collect::<Result<Vec<_>, PlannerError>>()?;
1963
1964 Ok(TypedExpr {
1965 kind: TypedExprKind::InList {
1966 expr: Box::new(expr_typed),
1967 list: typed_list,
1968 negated,
1969 },
1970 resolved_type: ResolvedType::Boolean,
1971 span,
1972 })
1973 }
1974
1975 fn infer_truth_predicate_with_scope(
1976 &self,
1977 expr: &Expr,
1978 value: TruthValue,
1979 negated: bool,
1980 scope: &[ScopedTable],
1981 plan_subquery: &SubqueryPlanner<'_>,
1982 span: Span,
1983 ) -> Result<TypedExpr, PlannerError> {
1984 let typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1985 if !matches!(
1986 typed.resolved_type,
1987 ResolvedType::Boolean | ResolvedType::Null
1988 ) {
1989 return Err(PlannerError::type_mismatch(
1990 "Boolean",
1991 typed.resolved_type.type_name(),
1992 expr.span,
1993 ));
1994 }
1995 let name = match value {
1996 TruthValue::True => INTERNAL_TRUTH_TRUE,
1997 TruthValue::False => INTERNAL_TRUTH_FALSE,
1998 TruthValue::Unknown => INTERNAL_TRUTH_UNKNOWN,
1999 };
2000 Ok(internal_predicate(
2001 format!("{name}:{}", u8::from(negated)),
2002 vec![typed],
2003 span,
2004 ))
2005 }
2006
2007 fn infer_distinct_predicate_with_scope(
2008 &self,
2009 left: &Expr,
2010 right: &Expr,
2011 negated: bool,
2012 scope: &[ScopedTable],
2013 plan_subquery: &SubqueryPlanner<'_>,
2014 span: Span,
2015 ) -> Result<TypedExpr, PlannerError> {
2016 let (mut left, right, width) =
2017 self.infer_row_pair_with_scope(left, right, scope, plan_subquery, span)?;
2018 left.extend(right);
2019 Ok(internal_predicate(
2020 format!("{INTERNAL_ROW_DISTINCT}:{width}:{}", u8::from(negated)),
2021 left,
2022 span,
2023 ))
2024 }
2025
2026 fn infer_row_pair_with_scope(
2027 &self,
2028 left: &Expr,
2029 right: &Expr,
2030 scope: &[ScopedTable],
2031 plan_subquery: &SubqueryPlanner<'_>,
2032 span: Span,
2033 ) -> Result<(Vec<TypedExpr>, Vec<TypedExpr>, usize), PlannerError> {
2034 let left = self.infer_row_operand_with_scope(left, scope, plan_subquery)?;
2035 let right = self.infer_row_operand_with_scope(right, scope, plan_subquery)?;
2036 let width = left.len();
2037 self.check_row_arity(width, right.len(), span)?;
2038 self.check_row_types(&left, &right, span)?;
2039 Ok((left, right, width))
2040 }
2041
2042 fn infer_row_operand_with_scope(
2043 &self,
2044 expr: &Expr,
2045 scope: &[ScopedTable],
2046 plan_subquery: &SubqueryPlanner<'_>,
2047 ) -> Result<Vec<TypedExpr>, PlannerError> {
2048 match row_items(expr) {
2049 Some(items) => items
2050 .iter()
2051 .map(|item| self.infer_type_with_scope(item, scope, plan_subquery))
2052 .collect(),
2053 None => Ok(vec![self.infer_type_with_scope(
2054 expr,
2055 scope,
2056 plan_subquery,
2057 )?]),
2058 }
2059 }
2060
2061 fn check_row_arity(
2062 &self,
2063 expected: usize,
2064 actual: usize,
2065 span: Span,
2066 ) -> Result<(), PlannerError> {
2067 if expected == actual {
2068 Ok(())
2069 } else {
2070 Err(PlannerError::RowArityMismatch {
2071 expected,
2072 actual,
2073 line: span.start.line,
2074 column: span.start.column,
2075 })
2076 }
2077 }
2078
2079 fn check_row_types(
2080 &self,
2081 left: &[TypedExpr],
2082 right: &[TypedExpr],
2083 span: Span,
2084 ) -> Result<(), PlannerError> {
2085 for (left, right) in left.iter().zip(right) {
2086 self.check_comparison_op(&left.resolved_type, &right.resolved_type, span)?;
2087 }
2088 Ok(())
2089 }
2090
2091 #[allow(dead_code)]
2093 fn infer_is_null_type(
2094 &self,
2095 expr: &Expr,
2096 negated: bool,
2097 table: &TableMetadata,
2098 span: Span,
2099 ) -> Result<TypedExpr, PlannerError> {
2100 let expr_typed = self.infer_type(expr, table)?;
2101
2102 Ok(TypedExpr {
2103 kind: TypedExprKind::IsNull {
2104 expr: Box::new(expr_typed),
2105 negated,
2106 },
2107 resolved_type: ResolvedType::Boolean,
2108 span,
2109 })
2110 }
2111
2112 fn infer_is_null_type_with_scope(
2113 &self,
2114 expr: &Expr,
2115 negated: bool,
2116 scope: &[ScopedTable],
2117 plan_subquery: &SubqueryPlanner<'_>,
2118 span: Span,
2119 ) -> Result<TypedExpr, PlannerError> {
2120 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
2121
2122 Ok(TypedExpr {
2123 kind: TypedExprKind::IsNull {
2124 expr: Box::new(expr_typed),
2125 negated,
2126 },
2127 resolved_type: ResolvedType::Boolean,
2128 span,
2129 })
2130 }
2131
2132 fn infer_vector_literal_type(
2134 &self,
2135 values: &[f64],
2136 span: Span,
2137 ) -> Result<TypedExpr, PlannerError> {
2138 Ok(TypedExpr {
2139 kind: TypedExprKind::VectorLiteral(values.to_vec()),
2140 resolved_type: ResolvedType::Vector {
2141 dimension: values.len() as u32,
2142 metric: VectorMetric::Cosine, },
2144 span,
2145 })
2146 }
2147
2148 pub fn normalize_metric(&self, metric: &str, span: Span) -> Result<VectorMetric, PlannerError> {
2160 match metric.to_lowercase().as_str() {
2161 "cosine" => Ok(VectorMetric::Cosine),
2162 "l2" => Ok(VectorMetric::L2),
2163 "inner" => Ok(VectorMetric::Inner),
2164 _ => Err(PlannerError::InvalidMetric {
2165 value: metric.to_string(),
2166 line: span.start.line,
2167 column: span.start.column,
2168 }),
2169 }
2170 }
2171
2172 pub fn check_function_call(
2177 &self,
2178 name: &str,
2179 args: &[TypedExpr],
2180 distinct: bool,
2181 star: bool,
2182 span: Span,
2183 ) -> Result<ResolvedType, PlannerError> {
2184 let lower_name = name.to_ascii_lowercase();
2185
2186 match lower_name.as_str() {
2187 "count" => self.check_count(args, distinct, star, span),
2188 "sum" => self.check_sum(args, distinct, star, span),
2189 "total" => self.check_total(args, distinct, star, span),
2190 "avg" => self.check_avg(args, distinct, star, span),
2191 "min" => self.check_min_max(args, distinct, star, span),
2192 "max" => self.check_min_max(args, distinct, star, span),
2193 "group_concat" => self.check_group_concat(args, distinct, star, span),
2194 "string_agg" => self.check_string_agg(args, distinct, star, span),
2195 name if is_portable_aggregate_name(name) => {
2196 check_portable_aggregate(name, args, distinct, star, span)
2197 }
2198 "grouping" | "grouping_id" => {
2203 if distinct || star {
2204 return Err(PlannerError::invalid_expression(
2205 "GROUPING does not support DISTINCT or *".to_string(),
2206 ));
2207 }
2208 if args.is_empty() {
2209 return Err(PlannerError::invalid_expression(
2210 "GROUPING requires at least one argument".to_string(),
2211 ));
2212 }
2213 if args.len() > 63 {
2214 return Err(PlannerError::invalid_expression(
2215 "GROUPING accepts at most 63 arguments".to_string(),
2216 ));
2217 }
2218 Ok(ResolvedType::BigInt)
2219 }
2220 _ => {
2221 let Some(signature) = crate::scalar::signature(&lower_name) else {
2222 return Err(PlannerError::unsupported_feature(
2223 format!("function '{name}'"),
2224 "future",
2225 span,
2226 ));
2227 };
2228 if distinct || star {
2229 return Err(PlannerError::invalid_expression(format!(
2230 "scalar function '{name}' does not support DISTINCT or *"
2231 )));
2232 }
2233 signature.arity.validate(name, args.len(), span)?;
2234 (signature.check)(args)?;
2235 let types: Vec<_> = args.iter().map(|arg| arg.resolved_type.clone()).collect();
2236 match &signature.ret {
2237 crate::scalar::ReturnRule::Fixed(ty) => Ok(ty.clone()),
2238 crate::scalar::ReturnRule::FromArgs(rule) => rule(&types),
2239 }
2240 }
2241 }
2242 }
2243
2244 pub fn validate_having_expr(
2245 &self,
2246 expr: &TypedExpr,
2247 group_keys: &[TypedExpr],
2248 aggregates: &[AggregateExpr],
2249 ) -> Result<(), PlannerError> {
2250 use std::collections::HashSet;
2251
2252 let group_key_indices: HashSet<usize> = group_keys
2253 .iter()
2254 .filter_map(|expr| match &expr.kind {
2255 TypedExprKind::ColumnRef { column_index, .. } => Some(*column_index),
2256 _ => None,
2257 })
2258 .collect();
2259
2260 let aggregate_signatures: HashSet<AggregateSignature> = aggregates
2261 .iter()
2262 .map(aggregate_signature_from_expr)
2263 .collect();
2264
2265 fn walk(
2266 expr: &TypedExpr,
2267 group_key_indices: &HashSet<usize>,
2268 aggregate_signatures: &HashSet<AggregateSignature>,
2269 ) -> Result<(), PlannerError> {
2270 match &expr.kind {
2271 TypedExprKind::ColumnRef { column_index, .. } => {
2272 if group_key_indices.contains(column_index) {
2273 Ok(())
2274 } else {
2275 Err(PlannerError::invalid_expression(
2276 "column in HAVING must be in GROUP BY or be aggregated".to_string(),
2277 ))
2278 }
2279 }
2280 TypedExprKind::FunctionCall { name, args, .. }
2281 if name.eq_ignore_ascii_case("grouping")
2282 || name.eq_ignore_ascii_case("grouping_id") =>
2283 {
2284 for arg in args {
2288 match &arg.kind {
2289 TypedExprKind::ColumnRef { column_index, .. }
2290 if group_key_indices.contains(column_index) => {}
2291 _ => {
2292 return Err(PlannerError::invalid_expression(
2293 "arguments to GROUPING must be grouping expressions \
2294 of the query"
2295 .to_string(),
2296 ));
2297 }
2298 }
2299 }
2300 Ok(())
2301 }
2302 TypedExprKind::FunctionCall {
2303 name,
2304 args,
2305 distinct,
2306 star,
2307 filter,
2308 order_by,
2309 over: _,
2310 } if is_aggregate_name(name) => {
2311 let signature = aggregate_signature_from_call(
2312 name,
2313 args,
2314 *distinct,
2315 *star,
2316 filter.as_deref(),
2317 order_by,
2318 )?;
2319 if aggregate_signatures.contains(&signature) {
2320 Ok(())
2321 } else {
2322 Err(PlannerError::invalid_expression(
2323 "aggregate in HAVING must appear in plan".to_string(),
2324 ))
2325 }
2326 }
2327 TypedExprKind::BinaryOp { left, right, .. } => {
2328 walk(left, group_key_indices, aggregate_signatures)?;
2329 walk(right, group_key_indices, aggregate_signatures)
2330 }
2331 TypedExprKind::UnaryOp { operand, .. } => {
2332 walk(operand, group_key_indices, aggregate_signatures)
2333 }
2334 TypedExprKind::Case {
2335 operand,
2336 branches,
2337 else_expr,
2338 } => {
2339 if let Some(operand) = operand {
2340 walk(operand, group_key_indices, aggregate_signatures)?;
2341 }
2342 for branch in branches {
2343 walk(&branch.when, group_key_indices, aggregate_signatures)?;
2344 walk(&branch.then, group_key_indices, aggregate_signatures)?;
2345 }
2346 if let Some(else_expr) = else_expr {
2347 walk(else_expr, group_key_indices, aggregate_signatures)?;
2348 }
2349 Ok(())
2350 }
2351 TypedExprKind::FunctionCall { args, .. } => {
2352 for arg in args {
2353 walk(arg, group_key_indices, aggregate_signatures)?;
2354 }
2355 Ok(())
2356 }
2357 TypedExprKind::Between {
2358 expr, low, high, ..
2359 } => {
2360 walk(expr, group_key_indices, aggregate_signatures)?;
2361 walk(low, group_key_indices, aggregate_signatures)?;
2362 walk(high, group_key_indices, aggregate_signatures)
2363 }
2364 TypedExprKind::Like {
2365 expr,
2366 pattern,
2367 escape,
2368 ..
2369 } => {
2370 walk(expr, group_key_indices, aggregate_signatures)?;
2371 walk(pattern, group_key_indices, aggregate_signatures)?;
2372 if let Some(esc) = escape {
2373 walk(esc, group_key_indices, aggregate_signatures)?;
2374 }
2375 Ok(())
2376 }
2377 TypedExprKind::InList { expr, list, .. } => {
2378 walk(expr, group_key_indices, aggregate_signatures)?;
2379 for item in list {
2380 walk(item, group_key_indices, aggregate_signatures)?;
2381 }
2382 Ok(())
2383 }
2384 TypedExprKind::IsNull { expr, .. } => {
2385 walk(expr, group_key_indices, aggregate_signatures)
2386 }
2387 _ => Ok(()),
2388 }
2389 }
2390
2391 walk(expr, &group_key_indices, &aggregate_signatures)
2392 }
2393
2394 fn check_count(
2395 &self,
2396 args: &[TypedExpr],
2397 distinct: bool,
2398 star: bool,
2399 span: Span,
2400 ) -> Result<ResolvedType, PlannerError> {
2401 if star {
2402 if distinct {
2403 return Err(PlannerError::unsupported_feature(
2404 "COUNT(DISTINCT *)",
2405 "future",
2406 span,
2407 ));
2408 }
2409 if !args.is_empty() {
2410 return Err(PlannerError::type_mismatch(
2411 "no arguments with COUNT(*)",
2412 format!("{} arguments", args.len()),
2413 span,
2414 ));
2415 }
2416 return Ok(ResolvedType::BigInt);
2417 }
2418
2419 if args.len() != 1 {
2420 return Err(PlannerError::type_mismatch(
2421 "1 argument",
2422 format!("{} arguments", args.len()),
2423 span,
2424 ));
2425 }
2426
2427 if distinct {
2428 return Ok(ResolvedType::BigInt);
2429 }
2430
2431 Ok(ResolvedType::BigInt)
2432 }
2433
2434 fn check_sum(
2435 &self,
2436 args: &[TypedExpr],
2437 _distinct: bool,
2438 star: bool,
2439 span: Span,
2440 ) -> Result<ResolvedType, PlannerError> {
2441 if star {
2442 return Err(PlannerError::type_mismatch(
2443 "numeric argument",
2444 "COUNT(*) style",
2445 span,
2446 ));
2447 }
2448 let arg = self.require_single_arg(args, span)?;
2449 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2450 return Err(PlannerError::type_mismatch(
2451 "numeric",
2452 arg.resolved_type.type_name().to_string(),
2453 arg.span,
2454 ));
2455 }
2456 Ok(crate::planner::aggregate_expr::sum_result_type(
2457 &arg.resolved_type,
2458 ))
2459 }
2460
2461 fn check_total(
2462 &self,
2463 args: &[TypedExpr],
2464 distinct: bool,
2465 star: bool,
2466 span: Span,
2467 ) -> Result<ResolvedType, PlannerError> {
2468 if star {
2469 return Err(PlannerError::type_mismatch(
2470 "numeric argument",
2471 "COUNT(*) style",
2472 span,
2473 ));
2474 }
2475 if distinct {
2476 return Err(PlannerError::unsupported_feature(
2477 "TOTAL(DISTINCT ...)",
2478 "future",
2479 span,
2480 ));
2481 }
2482 let arg = self.require_single_arg(args, span)?;
2483 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2484 return Err(PlannerError::type_mismatch(
2485 "numeric",
2486 arg.resolved_type.type_name().to_string(),
2487 arg.span,
2488 ));
2489 }
2490 Ok(ResolvedType::Double)
2491 }
2492
2493 fn check_avg(
2494 &self,
2495 args: &[TypedExpr],
2496 _distinct: bool,
2497 star: bool,
2498 span: Span,
2499 ) -> Result<ResolvedType, PlannerError> {
2500 if star {
2501 return Err(PlannerError::type_mismatch(
2502 "numeric argument",
2503 "COUNT(*) style",
2504 span,
2505 ));
2506 }
2507 let arg = self.require_single_arg(args, span)?;
2508 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
2509 return Err(PlannerError::type_mismatch(
2510 "numeric",
2511 arg.resolved_type.type_name().to_string(),
2512 arg.span,
2513 ));
2514 }
2515 Ok(ResolvedType::Double)
2516 }
2517
2518 fn check_min_max(
2519 &self,
2520 args: &[TypedExpr],
2521 _distinct: bool,
2522 star: bool,
2523 span: Span,
2524 ) -> Result<ResolvedType, PlannerError> {
2525 if star {
2526 return Err(PlannerError::type_mismatch(
2527 "argument",
2528 "COUNT(*) style",
2529 span,
2530 ));
2531 }
2532 let arg = self.require_single_arg(args, span)?;
2533 if matches!(arg.resolved_type, ResolvedType::Vector { .. }) {
2534 return Err(PlannerError::type_mismatch(
2535 "comparable",
2536 arg.resolved_type.type_name().to_string(),
2537 arg.span,
2538 ));
2539 }
2540 Ok(arg.resolved_type.clone())
2541 }
2542
2543 fn check_group_concat(
2544 &self,
2545 args: &[TypedExpr],
2546 _distinct: bool,
2547 star: bool,
2548 span: Span,
2549 ) -> Result<ResolvedType, PlannerError> {
2550 if star {
2551 return Err(PlannerError::type_mismatch(
2552 "text argument",
2553 "COUNT(*) style",
2554 span,
2555 ));
2556 }
2557 if args.is_empty() || args.len() > 2 {
2558 return Err(PlannerError::type_mismatch(
2559 "1 or 2 arguments",
2560 format!("{} arguments", args.len()),
2561 span,
2562 ));
2563 }
2564 if !matches!(
2565 args[0].resolved_type,
2566 ResolvedType::Text | ResolvedType::Null
2567 ) {
2568 return Err(PlannerError::type_mismatch(
2569 "Text",
2570 args[0].resolved_type.type_name().to_string(),
2571 args[0].span,
2572 ));
2573 }
2574 if args.len() == 2
2575 && !matches!(
2576 args[1].resolved_type,
2577 ResolvedType::Text | ResolvedType::Null
2578 )
2579 {
2580 return Err(PlannerError::type_mismatch(
2581 "Text",
2582 args[1].resolved_type.type_name().to_string(),
2583 args[1].span,
2584 ));
2585 }
2586 Ok(ResolvedType::Text)
2587 }
2588
2589 fn check_string_agg(
2590 &self,
2591 args: &[TypedExpr],
2592 _distinct: bool,
2593 star: bool,
2594 span: Span,
2595 ) -> Result<ResolvedType, PlannerError> {
2596 if star {
2597 return Err(PlannerError::type_mismatch(
2598 "text argument",
2599 "COUNT(*) style",
2600 span,
2601 ));
2602 }
2603 if args.len() != 2 {
2604 return Err(PlannerError::type_mismatch(
2605 "2 arguments",
2606 format!("{} arguments", args.len()),
2607 span,
2608 ));
2609 }
2610 if !matches!(
2611 args[0].resolved_type,
2612 ResolvedType::Text | ResolvedType::Null
2613 ) {
2614 return Err(PlannerError::type_mismatch(
2615 "Text",
2616 args[0].resolved_type.type_name().to_string(),
2617 args[0].span,
2618 ));
2619 }
2620 if !matches!(
2621 args[1].resolved_type,
2622 ResolvedType::Text | ResolvedType::Null
2623 ) {
2624 return Err(PlannerError::type_mismatch(
2625 "Text",
2626 args[1].resolved_type.type_name().to_string(),
2627 args[1].span,
2628 ));
2629 }
2630 Ok(ResolvedType::Text)
2631 }
2632
2633 fn require_single_arg<'b>(
2634 &self,
2635 args: &'b [TypedExpr],
2636 span: Span,
2637 ) -> Result<&'b TypedExpr, PlannerError> {
2638 if args.len() != 1 {
2639 return Err(PlannerError::type_mismatch(
2640 "1 argument",
2641 format!("{} arguments", args.len()),
2642 span,
2643 ));
2644 }
2645 Ok(&args[0])
2646 }
2647
2648 pub fn check_vector_distance(
2659 &self,
2660 args: &[TypedExpr],
2661 span: Span,
2662 ) -> Result<ResolvedType, PlannerError> {
2663 if args.len() != 3 {
2664 return Err(PlannerError::TypeMismatch {
2665 expected: "3 arguments".to_string(),
2666 found: format!("{} arguments", args.len()),
2667 line: span.start.line,
2668 column: span.start.column,
2669 });
2670 }
2671
2672 let col_dim = match &args[0].resolved_type {
2674 ResolvedType::Vector { dimension, .. } => *dimension,
2675 other => {
2676 return Err(PlannerError::TypeMismatch {
2677 expected: "Vector".to_string(),
2678 found: other.type_name().to_string(),
2679 line: args[0].span.start.line,
2680 column: args[0].span.start.column,
2681 });
2682 }
2683 };
2684
2685 let vec_dim = match &args[1].resolved_type {
2687 ResolvedType::Vector { dimension, .. } => *dimension,
2688 other => {
2689 return Err(PlannerError::TypeMismatch {
2690 expected: "Vector".to_string(),
2691 found: other.type_name().to_string(),
2692 line: args[1].span.start.line,
2693 column: args[1].span.start.column,
2694 });
2695 }
2696 };
2697
2698 self.check_vector_dimension(col_dim, vec_dim, args[1].span)?;
2700
2701 match &args[2].resolved_type {
2703 ResolvedType::Text => {
2704 if let TypedExprKind::Literal(Literal::String(s)) = &args[2].kind {
2706 self.normalize_metric(s, args[2].span)?;
2707 }
2708 }
2709 ResolvedType::Null => {
2710 return Err(PlannerError::TypeMismatch {
2712 expected: "Text (metric)".to_string(),
2713 found: "Null".to_string(),
2714 line: args[2].span.start.line,
2715 column: args[2].span.start.column,
2716 });
2717 }
2718 other => {
2719 return Err(PlannerError::TypeMismatch {
2720 expected: "Text (metric)".to_string(),
2721 found: other.type_name().to_string(),
2722 line: args[2].span.start.line,
2723 column: args[2].span.start.column,
2724 });
2725 }
2726 }
2727
2728 Ok(ResolvedType::Double)
2729 }
2730
2731 pub fn check_vector_similarity(
2737 &self,
2738 args: &[TypedExpr],
2739 span: Span,
2740 ) -> Result<ResolvedType, PlannerError> {
2741 self.check_vector_distance(args, span)
2743 }
2744
2745 pub fn check_vector_dimension(
2751 &self,
2752 expected: u32,
2753 found: u32,
2754 span: Span,
2755 ) -> Result<(), PlannerError> {
2756 if expected != found {
2757 Err(PlannerError::VectorDimensionMismatch {
2758 expected,
2759 found,
2760 line: span.start.line,
2761 column: span.start.column,
2762 })
2763 } else {
2764 Ok(())
2765 }
2766 }
2767
2768 pub fn check_insert_values(
2791 &self,
2792 table: &TableMetadata,
2793 columns: &[String],
2794 values: &[Vec<Expr>],
2795 span: Span,
2796 ) -> Result<Vec<Vec<TypedExpr>>, PlannerError> {
2797 let target_columns: Vec<&str> = if columns.is_empty() {
2799 table.column_names()
2800 } else {
2801 columns.iter().map(|s| s.as_str()).collect()
2802 };
2803
2804 let mut typed_rows = Vec::with_capacity(values.len());
2805
2806 for row in values {
2807 if row.len() != target_columns.len() {
2809 return Err(PlannerError::ColumnValueCountMismatch {
2810 columns: target_columns.len(),
2811 values: row.len(),
2812 line: span.start.line,
2813 column: span.start.column,
2814 });
2815 }
2816
2817 let mut typed_values = Vec::with_capacity(row.len());
2818
2819 for (value, col_name) in row.iter().zip(target_columns.iter()) {
2820 let col_meta =
2822 table
2823 .get_column(col_name)
2824 .ok_or_else(|| PlannerError::ColumnNotFound {
2825 column: col_name.to_string(),
2826 table: table.name.clone(),
2827 line: span.start.line,
2828 col: span.start.column,
2829 })?;
2830
2831 let typed_value = self.infer_type(value, table)?;
2833
2834 self.check_null_constraint(col_meta, &typed_value, value.span)?;
2836
2837 self.check_type_compatibility(
2839 &col_meta.data_type,
2840 &typed_value.resolved_type,
2841 value.span,
2842 )?;
2843
2844 let typed_value =
2845 self.coerce_column_value(&col_meta.data_type, typed_value, value.span);
2846
2847 if let (
2849 ResolvedType::Vector {
2850 dimension: expected_dim,
2851 ..
2852 },
2853 ResolvedType::Vector {
2854 dimension: actual_dim,
2855 ..
2856 },
2857 ) = (&col_meta.data_type, &typed_value.resolved_type)
2858 {
2859 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
2860 }
2861
2862 typed_values.push(typed_value);
2863 }
2864
2865 typed_rows.push(typed_values);
2866 }
2867
2868 Ok(typed_rows)
2869 }
2870
2871 pub fn check_assignment(
2882 &self,
2883 table: &TableMetadata,
2884 column: &str,
2885 value: &Expr,
2886 span: Span,
2887 ) -> Result<TypedExpr, PlannerError> {
2888 let col_meta = table
2890 .get_column(column)
2891 .ok_or_else(|| PlannerError::ColumnNotFound {
2892 column: column.to_string(),
2893 table: table.name.clone(),
2894 line: span.start.line,
2895 col: span.start.column,
2896 })?;
2897
2898 let typed_value = self.infer_type(value, table)?;
2900
2901 self.check_null_constraint(col_meta, &typed_value, value.span)?;
2903
2904 self.check_type_compatibility(&col_meta.data_type, &typed_value.resolved_type, value.span)?;
2906
2907 let typed_value = self.coerce_column_value(&col_meta.data_type, typed_value, value.span);
2908
2909 if let (
2911 ResolvedType::Vector {
2912 dimension: expected_dim,
2913 ..
2914 },
2915 ResolvedType::Vector {
2916 dimension: actual_dim,
2917 ..
2918 },
2919 ) = (&col_meta.data_type, &typed_value.resolved_type)
2920 {
2921 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
2922 }
2923
2924 Ok(typed_value)
2925 }
2926
2927 pub fn check_null_constraint(
2934 &self,
2935 column: &crate::catalog::ColumnMetadata,
2936 value: &TypedExpr,
2937 span: Span,
2938 ) -> Result<(), PlannerError> {
2939 if column.not_null && matches!(value.resolved_type, ResolvedType::Null) {
2940 Err(PlannerError::NullConstraintViolation {
2941 column: column.name.clone(),
2942 line: span.start.line,
2943 col: span.start.column,
2944 })
2945 } else {
2946 Ok(())
2947 }
2948 }
2949
2950 fn check_type_compatibility(
2958 &self,
2959 expected: &ResolvedType,
2960 actual: &ResolvedType,
2961 span: Span,
2962 ) -> Result<(), PlannerError> {
2963 if expected == actual {
2965 return Ok(());
2966 }
2967
2968 if actual.can_cast_to(expected) {
2970 return Ok(());
2971 }
2972
2973 if let (
2976 ResolvedType::Vector {
2977 dimension: d1,
2978 metric: _,
2979 },
2980 ResolvedType::Vector {
2981 dimension: d2,
2982 metric: _,
2983 },
2984 ) = (expected, actual)
2985 {
2986 if *d1 == *d2 {
2988 return Ok(());
2989 }
2990 }
2992
2993 Err(PlannerError::TypeMismatch {
2994 expected: expected.type_name().to_string(),
2995 found: actual.type_name().to_string(),
2996 line: span.start.line,
2997 column: span.start.column,
2998 })
2999 }
3000
3001 fn coerce_column_value(
3004 &self,
3005 expected: &ResolvedType,
3006 value: TypedExpr,
3007 span: Span,
3008 ) -> TypedExpr {
3009 if value.resolved_type != *expected
3010 && value.resolved_type != ResolvedType::Null
3011 && matches!(
3012 expected,
3013 ResolvedType::Integer
3014 | ResolvedType::BigInt
3015 | ResolvedType::Float
3016 | ResolvedType::Double
3017 | ResolvedType::Timestamp
3018 )
3019 {
3020 TypedExpr::cast(value, expected.clone(), span)
3021 } else {
3022 value
3023 }
3024 }
3025}
3026
3027fn fold_integral_binary(
3028 left: &TypedExpr,
3029 op: BinaryOp,
3030 right: &TypedExpr,
3031 result_type: &ResolvedType,
3032 span: Span,
3033) -> Option<TypedExpr> {
3034 let value = |expr: &TypedExpr| match &expr.kind {
3035 TypedExprKind::Literal(Literal::Number(value)) => value.parse::<i64>().ok(),
3036 _ => None,
3037 };
3038 let left = value(left)?;
3039 let right = value(right)?;
3040 let folded = match op {
3041 BinaryOp::BitAnd => left & right,
3042 BinaryOp::BitOr => left | right,
3043 BinaryOp::BitXor => left ^ right,
3044 BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
3045 let width = if matches!(result_type, ResolvedType::BigInt) {
3046 64
3047 } else {
3048 32
3049 };
3050 if !(0..width).contains(&right) {
3051 return None;
3052 }
3053 if op == BinaryOp::ShiftRight {
3054 left >> right as u32
3055 } else {
3056 let shifted = i128::from(left) * (1_i128 << right as u32);
3057 if matches!(result_type, ResolvedType::BigInt) {
3058 i64::try_from(shifted).ok()?
3059 } else {
3060 i64::from(i32::try_from(shifted).ok()?)
3061 }
3062 }
3063 }
3064 _ => return None,
3065 };
3066 Some(TypedExpr::literal(
3067 Literal::Number(folded.to_string()),
3068 result_type.clone(),
3069 span,
3070 ))
3071}
3072
3073fn is_numeric_type(ty: &ResolvedType) -> bool {
3074 matches!(
3075 ty,
3076 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Float | ResolvedType::Double
3077 )
3078}
3079
3080fn validate_window_frame(
3081 function_name: &str,
3082 frame: &WindowFrame,
3083 order_by: &[SortExpr],
3084) -> Result<(), PlannerError> {
3085 if !is_aggregate_name(function_name)
3086 && !matches!(function_name, "first_value" | "last_value" | "nth_value")
3087 {
3088 return Err(PlannerError::invalid_expression(format!(
3089 "explicit window frames are only supported for aggregate functions and \
3090 FIRST_VALUE/LAST_VALUE/NTH_VALUE, not {}()",
3091 function_name.to_ascii_uppercase()
3092 )));
3093 }
3094 if order_by.is_empty() {
3095 return Err(PlannerError::invalid_expression(
3096 "explicit ROWS/RANGE window frames require ORDER BY for deterministic evaluation",
3097 ));
3098 }
3099 if matches!(frame.start_bound, WindowFrameBound::UnboundedFollowing) {
3100 return Err(PlannerError::invalid_expression(
3101 "window frame start cannot be UNBOUNDED FOLLOWING",
3102 ));
3103 }
3104 if matches!(frame.end_bound, WindowFrameBound::UnboundedPreceding) {
3105 return Err(PlannerError::invalid_expression(
3106 "window frame end cannot be UNBOUNDED PRECEDING",
3107 ));
3108 }
3109 if (matches!(frame.start_bound, WindowFrameBound::CurrentRow)
3110 && matches!(frame.end_bound, WindowFrameBound::Preceding(_)))
3111 || (matches!(frame.start_bound, WindowFrameBound::Following(_))
3112 && matches!(
3113 frame.end_bound,
3114 WindowFrameBound::Preceding(_) | WindowFrameBound::CurrentRow
3115 ))
3116 {
3117 return Err(PlannerError::invalid_expression(
3118 "window frame bounds are reversed",
3119 ));
3120 }
3121
3122 let has_offset = matches!(
3123 frame.start_bound,
3124 WindowFrameBound::Preceding(_) | WindowFrameBound::Following(_)
3125 ) || matches!(
3126 frame.end_bound,
3127 WindowFrameBound::Preceding(_) | WindowFrameBound::Following(_)
3128 );
3129 if frame.units == WindowFrameUnits::Range && has_offset {
3130 if order_by.len() != 1 {
3131 return Err(PlannerError::invalid_expression(
3132 "RANGE offset frames require exactly one ORDER BY expression",
3133 ));
3134 }
3135 if !is_numeric_type(&order_by[0].expr.resolved_type) {
3136 return Err(PlannerError::invalid_expression(format!(
3137 "RANGE offset ORDER BY expression must be numeric, found {:?}",
3138 order_by[0].expr.resolved_type
3139 )));
3140 }
3141 }
3142 Ok(())
3143}
3144
3145fn validate_offset_window_call(
3146 name: &str,
3147 arg_count: usize,
3148 distinct: bool,
3149 star: bool,
3150) -> Result<(), PlannerError> {
3151 let display_name = name.to_ascii_uppercase();
3152 if distinct {
3153 return Err(PlannerError::invalid_expression(format!(
3154 "{display_name}() window function does not accept DISTINCT"
3155 )));
3156 }
3157 if star {
3158 return Err(PlannerError::invalid_expression(format!(
3159 "{display_name}() window function does not accept a star argument"
3160 )));
3161 }
3162 if !(1..=3).contains(&arg_count) {
3163 return Err(PlannerError::invalid_expression(format!(
3164 "{display_name}() window function expects 1 to 3 arguments"
3165 )));
3166 }
3167 Ok(())
3168}
3169
3170fn validate_exact_window_call(
3171 name: &str,
3172 arg_count: usize,
3173 expected: usize,
3174 distinct: bool,
3175 star: bool,
3176) -> Result<(), PlannerError> {
3177 let display_name = name.to_ascii_uppercase();
3178 if distinct {
3179 return Err(PlannerError::invalid_expression(format!(
3180 "{display_name}() window function does not support DISTINCT"
3181 )));
3182 }
3183 if star {
3184 return Err(PlannerError::invalid_expression(format!(
3185 "{display_name}() window function does not support a star argument"
3186 )));
3187 }
3188 if arg_count != expected {
3189 let signature = match expected {
3190 0 => "no arguments",
3191 1 => "one argument",
3192 2 => "two arguments",
3193 _ => unreachable!("window signatures are bounded above"),
3194 };
3195 return Err(PlannerError::invalid_expression(format!(
3196 "{display_name}() window function takes {signature}"
3197 )));
3198 }
3199 Ok(())
3200}
3201
3202fn validate_positive_integer_argument(
3203 name: &str,
3204 argument: &TypedExpr,
3205) -> Result<(), PlannerError> {
3206 if matches!(
3207 argument.resolved_type,
3208 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
3209 ) {
3210 return Ok(());
3211 }
3212 Err(PlannerError::type_mismatch(
3213 format!("positive INTEGER {} argument", name.to_ascii_uppercase()),
3214 argument.resolved_type.type_name(),
3215 argument.span,
3216 ))
3217}
3218
3219fn coerce_compatible_result(expr: &mut TypedExpr, target: &ResolvedType) {
3220 if expr.resolved_type == *target || matches!(expr.resolved_type, ResolvedType::Null) {
3221 return;
3222 }
3223 let span = expr.span;
3224 *expr = TypedExpr::cast(expr.clone(), target.clone(), span);
3225}
3226
3227fn coerce_case_result(expr: &mut TypedExpr, target: &ResolvedType) {
3228 if expr.resolved_type == *target || matches!(expr.resolved_type, ResolvedType::Null) {
3229 return;
3230 }
3231 let span = expr.span;
3232 *expr = TypedExpr::cast(expr.clone(), target.clone(), span);
3233}
3234
3235#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3236struct AggregateSignature {
3237 name: String,
3238 distinct: bool,
3239 star: bool,
3240 arg_key: Option<String>,
3241 extra_arg_keys: Vec<String>,
3242 separator: Option<String>,
3243 filter_key: Option<String>,
3246 order_key: Option<String>,
3250}
3251
3252pub(crate) fn is_portable_aggregate_name(name: &str) -> bool {
3253 matches!(
3254 name,
3255 "variance"
3256 | "var_samp"
3257 | "var_pop"
3258 | "stddev"
3259 | "stddev_samp"
3260 | "stddev_pop"
3261 | "covar_samp"
3262 | "covar_pop"
3263 | "corr"
3264 | "median"
3265 | "mode"
3266 | "quantile_cont"
3267 | "regr_count"
3268 | "regr_avgx"
3269 | "regr_avgy"
3270 | "regr_sxx"
3271 | "regr_syy"
3272 | "regr_sxy"
3273 | "regr_slope"
3274 | "regr_intercept"
3275 | "regr_r2"
3276 | "any_value"
3277 | "first"
3278 | "last"
3279 | "arg_min"
3280 | "min_by"
3281 | "arg_max"
3282 | "max_by"
3283 | "bit_and"
3284 | "bit_or"
3285 | "bit_xor"
3286 | "bool_and"
3287 | "bool_or"
3288 )
3289}
3290
3291fn canonical_aggregate_name(name: &str) -> String {
3292 match name.to_ascii_lowercase().as_str() {
3293 "variance" | "var_samp" => "var_samp".into(),
3294 "stddev" | "stddev_samp" => "stddev_samp".into(),
3295 "min_by" => "arg_min".into(),
3296 "max_by" => "arg_max".into(),
3297 lower => lower.into(),
3298 }
3299}
3300
3301fn check_portable_aggregate(
3302 name: &str,
3303 args: &[TypedExpr],
3304 distinct: bool,
3305 star: bool,
3306 span: Span,
3307) -> Result<ResolvedType, PlannerError> {
3308 if distinct || star {
3309 return Err(PlannerError::invalid_expression(format!(
3310 "{} does not support DISTINCT or *",
3311 name.to_ascii_uppercase()
3312 )));
3313 }
3314 let expected = if matches!(
3315 name,
3316 "covar_samp"
3317 | "covar_pop"
3318 | "corr"
3319 | "quantile_cont"
3320 | "regr_count"
3321 | "regr_avgx"
3322 | "regr_avgy"
3323 | "regr_sxx"
3324 | "regr_syy"
3325 | "regr_sxy"
3326 | "regr_slope"
3327 | "regr_intercept"
3328 | "regr_r2"
3329 | "arg_min"
3330 | "min_by"
3331 | "arg_max"
3332 | "max_by"
3333 ) {
3334 2
3335 } else {
3336 1
3337 };
3338 if args.len() != expected {
3339 return Err(PlannerError::type_mismatch(
3340 format!("{expected} argument(s)"),
3341 format!("{} arguments", args.len()),
3342 span,
3343 ));
3344 }
3345
3346 let numeric = |arg: &TypedExpr| {
3347 matches!(
3348 arg.resolved_type,
3349 ResolvedType::Integer
3350 | ResolvedType::BigInt
3351 | ResolvedType::Float
3352 | ResolvedType::Double
3353 | ResolvedType::Null
3354 )
3355 };
3356 if matches!(
3357 name,
3358 "variance"
3359 | "var_samp"
3360 | "var_pop"
3361 | "stddev"
3362 | "stddev_samp"
3363 | "stddev_pop"
3364 | "median"
3365 | "quantile_cont"
3366 | "covar_samp"
3367 | "covar_pop"
3368 | "corr"
3369 | "regr_count"
3370 | "regr_avgx"
3371 | "regr_avgy"
3372 | "regr_sxx"
3373 | "regr_syy"
3374 | "regr_sxy"
3375 | "regr_slope"
3376 | "regr_intercept"
3377 | "regr_r2"
3378 ) && !args.iter().all(numeric)
3379 {
3380 return Err(PlannerError::type_mismatch(
3381 "numeric aggregate argument",
3382 args.iter()
3383 .find(|arg| !numeric(arg))
3384 .expect("non-numeric argument")
3385 .resolved_type
3386 .type_name(),
3387 span,
3388 ));
3389 }
3390 if name == "quantile_cont" {
3391 let _ = percentile_fraction_named(name, &args[1])?;
3392 }
3393 if matches!(name, "bit_and" | "bit_or" | "bit_xor")
3394 && !matches!(
3395 args[0].resolved_type,
3396 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
3397 )
3398 {
3399 return Err(PlannerError::type_mismatch(
3400 "INTEGER or BIGINT",
3401 args[0].resolved_type.type_name(),
3402 span,
3403 ));
3404 }
3405 if matches!(name, "bool_and" | "bool_or")
3406 && !matches!(
3407 args[0].resolved_type,
3408 ResolvedType::Boolean | ResolvedType::Null
3409 )
3410 {
3411 return Err(PlannerError::type_mismatch(
3412 "BOOLEAN",
3413 args[0].resolved_type.type_name(),
3414 span,
3415 ));
3416 }
3417
3418 Ok(match name {
3419 "regr_count" => ResolvedType::BigInt,
3420 "any_value" | "first" | "last" | "arg_min" | "min_by" | "arg_max" | "max_by" | "mode" => {
3421 args[0].resolved_type.clone()
3422 }
3423 "bit_and" | "bit_or" | "bit_xor" => args[0].resolved_type.clone(),
3424 "bool_and" | "bool_or" => ResolvedType::Boolean,
3425 _ => ResolvedType::Double,
3426 })
3427}
3428
3429fn is_aggregate_name(name: &str) -> bool {
3430 matches!(
3431 name.to_ascii_lowercase().as_str(),
3432 "count"
3433 | "sum"
3434 | "total"
3435 | "avg"
3436 | "min"
3437 | "max"
3438 | "group_concat"
3439 | "string_agg"
3440 | "percentile_disc"
3441 | "percentile_cont"
3442 | "variance"
3443 | "var_samp"
3444 | "var_pop"
3445 | "stddev"
3446 | "stddev_samp"
3447 | "stddev_pop"
3448 | "covar_samp"
3449 | "covar_pop"
3450 | "corr"
3451 | "median"
3452 | "mode"
3453 | "quantile_cont"
3454 | "regr_count"
3455 | "regr_avgx"
3456 | "regr_avgy"
3457 | "regr_sxx"
3458 | "regr_syy"
3459 | "regr_sxy"
3460 | "regr_slope"
3461 | "regr_intercept"
3462 | "regr_r2"
3463 | "any_value"
3464 | "first"
3465 | "last"
3466 | "arg_min"
3467 | "min_by"
3468 | "arg_max"
3469 | "max_by"
3470 | "bit_and"
3471 | "bit_or"
3472 | "bit_xor"
3473 | "bool_and"
3474 | "bool_or"
3475 )
3476}
3477
3478fn is_ordered_set_aggregate_name(name: &str) -> bool {
3479 matches!(name, "percentile_disc" | "percentile_cont" | "mode")
3480}
3481
3482fn is_order_sensitive_aggregate_name(name: &str) -> bool {
3486 matches!(
3487 name.to_ascii_lowercase().as_str(),
3488 "group_concat"
3489 | "string_agg"
3490 | "percentile_disc"
3491 | "percentile_cont"
3492 | "mode"
3493 | "first"
3494 | "last"
3495 )
3496}
3497
3498pub(crate) fn percentile_fraction(arg: &TypedExpr) -> Result<f64, PlannerError> {
3501 percentile_fraction_named("percentile_disc", arg)
3502}
3503
3504pub(crate) fn percentile_fraction_named(name: &str, arg: &TypedExpr) -> Result<f64, PlannerError> {
3505 let literal = match &arg.kind {
3506 TypedExprKind::Literal(Literal::Number(text)) => text.parse::<f64>().ok(),
3507 TypedExprKind::UnaryOp {
3508 op: crate::ast::expr::UnaryOp::Minus,
3509 operand,
3510 } => match &operand.kind {
3511 TypedExprKind::Literal(Literal::Number(text)) => {
3512 text.parse::<f64>().ok().map(|value| -value)
3513 }
3514 _ => None,
3515 },
3516 _ => None,
3517 };
3518 let Some(value) = literal else {
3519 return Err(PlannerError::invalid_expression(format!(
3520 "{} fraction must be a numeric literal",
3521 name.to_ascii_uppercase()
3522 )));
3523 };
3524 if !(0.0..=1.0).contains(&value) {
3525 return Err(PlannerError::invalid_expression(format!(
3526 "{} fraction must be between 0 and 1",
3527 name.to_ascii_uppercase()
3528 )));
3529 }
3530 Ok(value)
3531}
3532
3533fn typed_sort_signature(order_by: &[SortExpr]) -> Option<String> {
3534 if order_by.is_empty() {
3535 return None;
3536 }
3537 Some(
3538 order_by
3539 .iter()
3540 .map(|sort| {
3541 format!(
3542 "{}|{}|{}",
3543 typed_expr_signature(&sort.expr),
3544 sort.asc,
3545 sort.nulls_first
3546 )
3547 })
3548 .collect::<Vec<_>>()
3549 .join(","),
3550 )
3551}
3552
3553fn aggregate_signature_from_expr(expr: &AggregateExpr) -> AggregateSignature {
3554 let (name, separator, star, arg) = match &expr.function {
3555 AggregateFunction::Count => (
3556 "count".to_string(),
3557 None,
3558 expr.arg.is_none(),
3559 expr.arg.as_ref(),
3560 ),
3561 AggregateFunction::Sum => ("sum".to_string(), None, false, expr.arg.as_ref()),
3562 AggregateFunction::Total => ("total".to_string(), None, false, expr.arg.as_ref()),
3563 AggregateFunction::Avg => ("avg".to_string(), None, false, expr.arg.as_ref()),
3564 AggregateFunction::Min => ("min".to_string(), None, false, expr.arg.as_ref()),
3565 AggregateFunction::Max => ("max".to_string(), None, false, expr.arg.as_ref()),
3566 AggregateFunction::GroupConcat { separator } => (
3567 "group_concat".to_string(),
3568 separator.clone(),
3569 false,
3570 expr.arg.as_ref(),
3571 ),
3572 AggregateFunction::StringAgg { separator } => (
3573 "string_agg".to_string(),
3574 separator.clone(),
3575 false,
3576 expr.arg.as_ref(),
3577 ),
3578 AggregateFunction::PercentileDisc { fraction } => (
3581 "percentile_disc".to_string(),
3582 Some(format!("{fraction:?}")),
3583 false,
3584 None,
3585 ),
3586 AggregateFunction::PercentileCont { fraction } => (
3587 "percentile_cont".to_string(),
3588 Some(format!("{fraction:?}")),
3589 false,
3590 None,
3591 ),
3592 AggregateFunction::QuantileCont { fraction } => (
3593 "quantile_cont".to_string(),
3594 Some(format!("{fraction:?}")),
3595 false,
3596 expr.arg.as_ref(),
3597 ),
3598 AggregateFunction::Variance { sample } => (
3599 if *sample { "var_samp" } else { "var_pop" }.to_string(),
3600 None,
3601 false,
3602 expr.arg.as_ref(),
3603 ),
3604 AggregateFunction::Stddev { sample } => (
3605 if *sample { "stddev_samp" } else { "stddev_pop" }.to_string(),
3606 None,
3607 false,
3608 expr.arg.as_ref(),
3609 ),
3610 AggregateFunction::Covariance { sample } => (
3611 if *sample { "covar_samp" } else { "covar_pop" }.to_string(),
3612 None,
3613 false,
3614 expr.arg.as_ref(),
3615 ),
3616 AggregateFunction::Corr => ("corr".into(), None, false, expr.arg.as_ref()),
3617 AggregateFunction::Median => ("median".into(), None, false, expr.arg.as_ref()),
3618 AggregateFunction::Mode => (
3619 "mode".into(),
3620 None,
3621 false,
3622 expr.order_by
3623 .is_empty()
3624 .then_some(())
3625 .and(expr.arg.as_ref()),
3626 ),
3627 AggregateFunction::RegrCount => ("regr_count".into(), None, false, expr.arg.as_ref()),
3628 AggregateFunction::RegrAvgX => ("regr_avgx".into(), None, false, expr.arg.as_ref()),
3629 AggregateFunction::RegrAvgY => ("regr_avgy".into(), None, false, expr.arg.as_ref()),
3630 AggregateFunction::RegrSxx => ("regr_sxx".into(), None, false, expr.arg.as_ref()),
3631 AggregateFunction::RegrSyy => ("regr_syy".into(), None, false, expr.arg.as_ref()),
3632 AggregateFunction::RegrSxy => ("regr_sxy".into(), None, false, expr.arg.as_ref()),
3633 AggregateFunction::RegrSlope => ("regr_slope".into(), None, false, expr.arg.as_ref()),
3634 AggregateFunction::RegrIntercept => {
3635 ("regr_intercept".into(), None, false, expr.arg.as_ref())
3636 }
3637 AggregateFunction::RegrR2 => ("regr_r2".into(), None, false, expr.arg.as_ref()),
3638 AggregateFunction::AnyValue => ("any_value".into(), None, false, expr.arg.as_ref()),
3639 AggregateFunction::First => ("first".into(), None, false, expr.arg.as_ref()),
3640 AggregateFunction::Last => ("last".into(), None, false, expr.arg.as_ref()),
3641 AggregateFunction::ArgMin => ("arg_min".into(), None, false, expr.arg.as_ref()),
3642 AggregateFunction::ArgMax => ("arg_max".into(), None, false, expr.arg.as_ref()),
3643 AggregateFunction::BitAnd => ("bit_and".into(), None, false, expr.arg.as_ref()),
3644 AggregateFunction::BitOr => ("bit_or".into(), None, false, expr.arg.as_ref()),
3645 AggregateFunction::BitXor => ("bit_xor".into(), None, false, expr.arg.as_ref()),
3646 AggregateFunction::BoolAnd => ("bool_and".into(), None, false, expr.arg.as_ref()),
3647 AggregateFunction::BoolOr => ("bool_or".into(), None, false, expr.arg.as_ref()),
3648 };
3649 AggregateSignature {
3650 name,
3651 distinct: expr.distinct,
3652 star,
3653 arg_key: arg.map(typed_expr_signature),
3654 extra_arg_keys: expr.extra_args.iter().map(typed_expr_signature).collect(),
3655 separator,
3656 filter_key: expr.filter.as_ref().map(typed_expr_signature),
3657 order_key: typed_sort_signature(&expr.order_by),
3658 }
3659}
3660
3661fn aggregate_signature_from_call(
3662 name: &str,
3663 args: &[TypedExpr],
3664 distinct: bool,
3665 star: bool,
3666 filter: Option<&TypedExpr>,
3667 order_by: &[SortExpr],
3668) -> Result<AggregateSignature, PlannerError> {
3669 let lower = name.to_ascii_lowercase();
3670 let is_percentile = matches!(lower.as_str(), "percentile_disc" | "percentile_cont");
3671 let separator = if name.eq_ignore_ascii_case("group_concat") && args.len() == 2 {
3672 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3673 Some(value.clone())
3674 } else {
3675 return Err(PlannerError::invalid_expression(
3676 "GROUP_CONCAT separator must be a string literal".to_string(),
3677 ));
3678 }
3679 } else if name.eq_ignore_ascii_case("string_agg") && args.len() == 2 {
3680 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
3681 Some(value.clone())
3682 } else {
3683 return Err(PlannerError::invalid_expression(
3684 "STRING_AGG separator must be a string literal".to_string(),
3685 ));
3686 }
3687 } else if is_percentile && args.len() == 1 {
3688 Some(format!(
3689 "{:?}",
3690 percentile_fraction_named(&lower, &args[0])?
3691 ))
3692 } else if lower == "quantile_cont" && args.len() == 2 {
3693 Some(format!(
3694 "{:?}",
3695 percentile_fraction_named(&lower, &args[1])?
3696 ))
3697 } else {
3698 None
3699 };
3700 Ok(AggregateSignature {
3701 name: canonical_aggregate_name(name),
3702 distinct,
3703 star,
3704 arg_key: if is_percentile {
3705 None
3706 } else {
3707 args.first().map(typed_expr_signature)
3708 },
3709 extra_arg_keys: if matches!(
3710 lower.as_str(),
3711 "group_concat" | "string_agg" | "percentile_disc" | "percentile_cont" | "quantile_cont"
3712 ) {
3713 Vec::new()
3714 } else {
3715 args.iter().skip(1).map(typed_expr_signature).collect()
3716 },
3717 separator,
3718 filter_key: filter.map(typed_expr_signature),
3719 order_key: if is_order_sensitive_aggregate_name(name) {
3720 typed_sort_signature(order_by)
3721 } else {
3722 None
3723 },
3724 })
3725}
3726
3727fn typed_expr_signature(expr: &TypedExpr) -> String {
3728 format!("{:?}", expr.kind)
3729}
3730
3731fn single_column_type(schema: &[ColumnMetadata], span: Span) -> Result<ResolvedType, PlannerError> {
3732 match schema {
3733 [column] => Ok(column.data_type.clone()),
3734 [] => Err(PlannerError::type_mismatch(
3735 "one-column subquery",
3736 "zero-column subquery",
3737 span,
3738 )),
3739 _ => Err(PlannerError::type_mismatch(
3740 "one-column subquery",
3741 format!("{} columns", schema.len()),
3742 span,
3743 )),
3744 }
3745}
3746
3747fn row_items(expr: &Expr) -> Option<&[Expr]> {
3748 match &expr.kind {
3749 ExprKind::Row { items } => Some(items),
3750 _ => None,
3751 }
3752}
3753
3754fn internal_predicate(name: String, args: Vec<TypedExpr>, span: Span) -> TypedExpr {
3755 TypedExpr::function_call(name, args, false, false, ResolvedType::Boolean, span)
3756}
3757
3758#[cfg(test)]
3760#[path = "type_checker/tests.rs"]
3761mod tests;