1use crate::ast::Span;
8use crate::ast::Statement;
9use crate::ast::ddl::VectorMetric;
10use crate::ast::expr::{BinaryOp, Expr, ExprKind, Literal, Quantifier as AstQuantifier, UnaryOp};
11use crate::catalog::{Catalog, ColumnMetadata, TableMetadata};
12use crate::planner::aggregate_expr::{AggregateExpr, AggregateFunction};
13use crate::planner::error::PlannerError;
14use crate::planner::logical_plan::LogicalPlan;
15use crate::planner::name_resolver::NameResolver;
16use crate::planner::typed_expr::{Quantifier, TypedExpr, TypedExprKind};
17use crate::planner::types::ResolvedType;
18
19#[derive(Debug, Clone)]
21pub struct ScopedTable {
22 pub table: TableMetadata,
23 pub start_index: usize,
24}
25
26impl ScopedTable {
27 pub fn new(table: TableMetadata, start_index: usize) -> Self {
28 Self { table, start_index }
29 }
30}
31
32pub type SubqueryPlanner<'p> = dyn Fn(&Statement, &[ScopedTable]) -> Result<(LogicalPlan, Vec<ColumnMetadata>), PlannerError>
33 + 'p;
34
35pub struct TypeChecker<'a, C: Catalog + ?Sized> {
50 catalog: &'a C,
51}
52
53impl<'a, C: Catalog + ?Sized> TypeChecker<'a, C> {
54 pub fn new(catalog: &'a C) -> Self {
56 Self { catalog }
57 }
58
59 pub fn catalog(&self) -> &'a C {
61 self.catalog
62 }
63
64 pub fn infer_type(
76 &self,
77 expr: &Expr,
78 table: &TableMetadata,
79 ) -> Result<TypedExpr, PlannerError> {
80 let scope = [ScopedTable::new(table.clone(), 0)];
81 self.infer_type_with_scope(expr, &scope, &|stmt, _outer| {
82 let planner = crate::planner::Planner::new(self.catalog);
83 let plan = planner.plan(stmt)?;
84 Ok((plan, Vec::new()))
85 })
86 }
87
88 pub fn infer_type_with_scope(
89 &self,
90 expr: &Expr,
91 scope: &[ScopedTable],
92 plan_subquery: &SubqueryPlanner<'_>,
93 ) -> Result<TypedExpr, PlannerError> {
94 let span = expr.span;
95 match &expr.kind {
96 ExprKind::Literal { literal: lit } => self.infer_literal_type(lit, span),
97
98 ExprKind::ColumnRef {
99 table: table_qualifier,
100 column,
101 } => self.infer_column_ref_type_with_scope(
102 scope,
103 table_qualifier.as_deref(),
104 column,
105 span,
106 ),
107
108 ExprKind::BinaryOp { left, op, right } => {
109 self.infer_binary_op_type_with_scope(left, *op, right, scope, plan_subquery, span)
110 }
111
112 ExprKind::UnaryOp { op, operand } => {
113 self.infer_unary_op_type_with_scope(*op, operand, scope, plan_subquery, span)
114 }
115
116 ExprKind::FunctionCall {
117 name,
118 args,
119 distinct,
120 star,
121 } => self.infer_function_call_type_with_scope(
122 name,
123 args,
124 *distinct,
125 *star,
126 scope,
127 plan_subquery,
128 span,
129 ),
130
131 ExprKind::Between {
132 expr,
133 low,
134 high,
135 negated,
136 } => self.infer_between_type_with_scope(
137 expr,
138 low,
139 high,
140 *negated,
141 scope,
142 plan_subquery,
143 span,
144 ),
145
146 ExprKind::Like {
147 expr,
148 pattern,
149 escape,
150 negated,
151 } => self.infer_like_type_with_scope(
152 expr,
153 pattern,
154 escape.as_deref(),
155 *negated,
156 scope,
157 plan_subquery,
158 span,
159 ),
160
161 ExprKind::InList {
162 expr,
163 list,
164 negated,
165 } => {
166 self.infer_in_list_type_with_scope(expr, list, *negated, scope, plan_subquery, span)
167 }
168
169 ExprKind::IsNull { expr, negated } => {
170 self.infer_is_null_type_with_scope(expr, *negated, scope, plan_subquery, span)
171 }
172
173 ExprKind::VectorLiteral { values } => self.infer_vector_literal_type(values, span),
174
175 ExprKind::ScalarSubquery { subquery } => {
176 let (plan, schema) = plan_subquery(subquery, scope)?;
177 let value_type = single_column_type(&schema, span)?;
178 Ok(TypedExpr {
179 kind: TypedExprKind::ScalarSubquery(Box::new(plan)),
180 resolved_type: value_type,
181 span,
182 })
183 }
184 ExprKind::InSubquery {
185 expr,
186 subquery,
187 negated,
188 } => {
189 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
190 let (plan, schema) = plan_subquery(subquery, scope)?;
191 let value_type = single_column_type(&schema, span)?;
192 self.check_comparison_op(&expr_typed.resolved_type, &value_type, span)?;
193 Ok(TypedExpr {
194 kind: TypedExprKind::InSubquery {
195 expr: Box::new(expr_typed),
196 subquery: Box::new(plan),
197 negated: *negated,
198 },
199 resolved_type: ResolvedType::Boolean,
200 span,
201 })
202 }
203 ExprKind::Exists { subquery, negated } => {
204 let (plan, _schema) = plan_subquery(subquery, scope)?;
205 Ok(TypedExpr {
206 kind: TypedExprKind::Exists {
207 subquery: Box::new(plan),
208 negated: *negated,
209 },
210 resolved_type: ResolvedType::Boolean,
211 span,
212 })
213 }
214 ExprKind::Quantified {
215 expr,
216 op,
217 quantifier,
218 subquery,
219 } => {
220 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
221 let (plan, schema) = plan_subquery(subquery, scope)?;
222 let value_type = single_column_type(&schema, span)?;
223 self.check_binary_op(*op, &expr_typed.resolved_type, &value_type, span)?;
224 Ok(TypedExpr {
225 kind: TypedExprKind::Quantified {
226 expr: Box::new(expr_typed),
227 op: *op,
228 quantifier: match quantifier {
229 AstQuantifier::Any => Quantifier::Any,
230 AstQuantifier::All => Quantifier::All,
231 },
232 subquery: Box::new(plan),
233 },
234 resolved_type: ResolvedType::Boolean,
235 span,
236 })
237 }
238 }
239 }
240
241 fn infer_literal_type(&self, lit: &Literal, span: Span) -> Result<TypedExpr, PlannerError> {
243 let (kind, resolved_type) = match lit {
244 Literal::Number(s) => {
245 let resolved_type = if s.contains('.') || s.contains('e') || s.contains('E') {
247 ResolvedType::Double
248 } else {
249 if s.parse::<i32>().is_ok() {
251 ResolvedType::Integer
252 } else {
253 ResolvedType::BigInt
254 }
255 };
256 (TypedExprKind::Literal(lit.clone()), resolved_type)
257 }
258 Literal::String(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Text),
259 Literal::Boolean(_) => (TypedExprKind::Literal(lit.clone()), ResolvedType::Boolean),
260 Literal::Null => (TypedExprKind::Literal(lit.clone()), ResolvedType::Null),
261 };
262
263 Ok(TypedExpr {
264 kind,
265 resolved_type,
266 span,
267 })
268 }
269
270 #[allow(dead_code)]
272 fn infer_column_ref_type(
273 &self,
274 table: &TableMetadata,
275 column_name: &str,
276 span: Span,
277 ) -> Result<TypedExpr, PlannerError> {
278 let (column_index, column) = table
280 .columns
281 .iter()
282 .enumerate()
283 .find(|(_, c)| c.name == column_name)
284 .ok_or_else(|| PlannerError::ColumnNotFound {
285 column: column_name.to_string(),
286 table: table.name.clone(),
287 line: span.start.line,
288 col: span.start.column,
289 })?;
290
291 Ok(TypedExpr {
292 kind: TypedExprKind::ColumnRef {
293 table: table.name.clone(),
294 column: column_name.to_string(),
295 column_index,
296 },
297 resolved_type: column.data_type.clone(),
298 span,
299 })
300 }
301
302 fn infer_column_ref_type_with_scope(
303 &self,
304 scope: &[ScopedTable],
305 table_qualifier: Option<&str>,
306 column_name: &str,
307 span: Span,
308 ) -> Result<TypedExpr, PlannerError> {
309 let tables = scope.iter().map(|s| &s.table).collect::<Vec<_>>();
310 let resolver = NameResolver::new(self.catalog);
311 let resolved =
312 resolver.resolve_column_with_scope(&tables, table_qualifier, column_name, span)?;
313 let scoped = scope
314 .iter()
315 .find(|s| s.table.name == resolved.table_name)
316 .ok_or_else(|| PlannerError::table_not_found(&resolved.table_name, span))?;
317 Ok(TypedExpr {
318 kind: TypedExprKind::ColumnRef {
319 table: resolved.table_name,
320 column: resolved.column_name,
321 column_index: scoped.start_index + resolved.column_index,
322 },
323 resolved_type: resolved.resolved_type,
324 span,
325 })
326 }
327
328 #[allow(dead_code)]
330 fn infer_binary_op_type(
331 &self,
332 left: &Expr,
333 op: BinaryOp,
334 right: &Expr,
335 table: &TableMetadata,
336 span: Span,
337 ) -> Result<TypedExpr, PlannerError> {
338 let left_typed = self.infer_type(left, table)?;
339 let right_typed = self.infer_type(right, table)?;
340
341 let result_type = self.check_binary_op(
342 op,
343 &left_typed.resolved_type,
344 &right_typed.resolved_type,
345 span,
346 )?;
347
348 Ok(TypedExpr {
349 kind: TypedExprKind::BinaryOp {
350 left: Box::new(left_typed),
351 op,
352 right: Box::new(right_typed),
353 },
354 resolved_type: result_type,
355 span,
356 })
357 }
358
359 fn infer_binary_op_type_with_scope(
360 &self,
361 left: &Expr,
362 op: BinaryOp,
363 right: &Expr,
364 scope: &[ScopedTable],
365 plan_subquery: &SubqueryPlanner<'_>,
366 span: Span,
367 ) -> Result<TypedExpr, PlannerError> {
368 let left_typed = self.infer_type_with_scope(left, scope, plan_subquery)?;
369 let right_typed = self.infer_type_with_scope(right, scope, plan_subquery)?;
370
371 let result_type = self.check_binary_op(
372 op,
373 &left_typed.resolved_type,
374 &right_typed.resolved_type,
375 span,
376 )?;
377
378 Ok(TypedExpr {
379 kind: TypedExprKind::BinaryOp {
380 left: Box::new(left_typed),
381 op,
382 right: Box::new(right_typed),
383 },
384 resolved_type: result_type,
385 span,
386 })
387 }
388
389 pub fn check_binary_op(
401 &self,
402 op: BinaryOp,
403 left: &ResolvedType,
404 right: &ResolvedType,
405 span: Span,
406 ) -> Result<ResolvedType, PlannerError> {
407 use BinaryOp::*;
408 use ResolvedType::*;
409
410 match op {
411 Add | Sub | Mul | Div | Mod => {
413 let result = self.check_arithmetic_op(left, right, span)?;
414 Ok(result)
415 }
416
417 Eq | Neq | Lt | Gt | LtEq | GtEq => {
419 self.check_comparison_op(left, right, span)?;
420 Ok(Boolean)
421 }
422
423 And | Or => {
425 self.check_logical_op(left, right, span)?;
426 Ok(Boolean)
427 }
428
429 StringConcat => {
431 self.check_string_concat_op(left, right, span)?;
432 Ok(Text)
433 }
434 }
435 }
436
437 fn check_arithmetic_op(
439 &self,
440 left: &ResolvedType,
441 right: &ResolvedType,
442 span: Span,
443 ) -> Result<ResolvedType, PlannerError> {
444 use ResolvedType::*;
445
446 if matches!(left, Null) || matches!(right, Null) {
448 return Ok(Null);
449 }
450
451 match (left, right) {
453 (Integer, Integer) => Ok(Integer),
455 (Integer, BigInt) | (BigInt, Integer) | (BigInt, BigInt) => Ok(BigInt),
456 (Integer, Float) | (Float, Integer) | (Float, Float) => Ok(Float),
457 (Integer, Double)
458 | (Double, Integer)
459 | (BigInt, Float)
460 | (Float, BigInt)
461 | (BigInt, Double)
462 | (Double, BigInt)
463 | (Float, Double)
464 | (Double, Float)
465 | (Double, Double) => Ok(Double),
466
467 _ => Err(PlannerError::InvalidOperator {
468 op: "arithmetic".to_string(),
469 type_name: format!("{} and {}", left.type_name(), right.type_name()),
470 line: span.start.line,
471 column: span.start.column,
472 }),
473 }
474 }
475
476 pub(crate) fn check_comparison_op(
478 &self,
479 left: &ResolvedType,
480 right: &ResolvedType,
481 span: Span,
482 ) -> Result<(), PlannerError> {
483 use ResolvedType::*;
484
485 if matches!(left, Null) || matches!(right, Null) {
487 return Ok(());
488 }
489
490 let compatible = match (left, right) {
492 (a, b) if a == b => true,
494
495 (Integer | BigInt | Float | Double, Integer | BigInt | Float | Double) => true,
497
498 (Text, Text) => true,
500
501 (Boolean, Boolean) => true,
503
504 (Timestamp, Timestamp) => true,
506
507 (Vector { dimension: d1, .. }, Vector { dimension: d2, .. }) => d1 == d2,
509
510 _ => false,
511 };
512
513 if compatible {
514 Ok(())
515 } else {
516 Err(PlannerError::TypeMismatch {
517 expected: left.type_name().to_string(),
518 found: right.type_name().to_string(),
519 line: span.start.line,
520 column: span.start.column,
521 })
522 }
523 }
524
525 fn check_logical_op(
527 &self,
528 left: &ResolvedType,
529 right: &ResolvedType,
530 span: Span,
531 ) -> Result<(), PlannerError> {
532 use ResolvedType::*;
533
534 let left_ok = matches!(left, Boolean | Null);
536 let right_ok = matches!(right, Boolean | Null);
537
538 if !left_ok {
539 return Err(PlannerError::TypeMismatch {
540 expected: "Boolean".to_string(),
541 found: left.type_name().to_string(),
542 line: span.start.line,
543 column: span.start.column,
544 });
545 }
546
547 if !right_ok {
548 return Err(PlannerError::TypeMismatch {
549 expected: "Boolean".to_string(),
550 found: right.type_name().to_string(),
551 line: span.start.line,
552 column: span.start.column,
553 });
554 }
555
556 Ok(())
557 }
558
559 fn check_string_concat_op(
561 &self,
562 left: &ResolvedType,
563 right: &ResolvedType,
564 span: Span,
565 ) -> Result<(), PlannerError> {
566 use ResolvedType::*;
567
568 let left_ok = matches!(left, Text | Null);
570 let right_ok = matches!(right, Text | Null);
571
572 if !left_ok {
573 return Err(PlannerError::TypeMismatch {
574 expected: "Text".to_string(),
575 found: left.type_name().to_string(),
576 line: span.start.line,
577 column: span.start.column,
578 });
579 }
580
581 if !right_ok {
582 return Err(PlannerError::TypeMismatch {
583 expected: "Text".to_string(),
584 found: right.type_name().to_string(),
585 line: span.start.line,
586 column: span.start.column,
587 });
588 }
589
590 Ok(())
591 }
592
593 #[allow(dead_code)]
595 fn infer_unary_op_type(
596 &self,
597 op: UnaryOp,
598 operand: &Expr,
599 table: &TableMetadata,
600 span: Span,
601 ) -> Result<TypedExpr, PlannerError> {
602 let operand_typed = self.infer_type(operand, table)?;
603
604 let result_type = match op {
605 UnaryOp::Not => {
606 if !matches!(
608 operand_typed.resolved_type,
609 ResolvedType::Boolean | ResolvedType::Null
610 ) {
611 return Err(PlannerError::TypeMismatch {
612 expected: "Boolean".to_string(),
613 found: operand_typed.resolved_type.type_name().to_string(),
614 line: span.start.line,
615 column: span.start.column,
616 });
617 }
618 ResolvedType::Boolean
619 }
620 UnaryOp::Minus => {
621 match &operand_typed.resolved_type {
623 ResolvedType::Integer => ResolvedType::Integer,
624 ResolvedType::BigInt => ResolvedType::BigInt,
625 ResolvedType::Float => ResolvedType::Float,
626 ResolvedType::Double => ResolvedType::Double,
627 ResolvedType::Null => ResolvedType::Null,
628 other => {
629 return Err(PlannerError::InvalidOperator {
630 op: "unary minus".to_string(),
631 type_name: other.type_name().to_string(),
632 line: span.start.line,
633 column: span.start.column,
634 });
635 }
636 }
637 }
638 };
639
640 Ok(TypedExpr {
641 kind: TypedExprKind::UnaryOp {
642 op,
643 operand: Box::new(operand_typed),
644 },
645 resolved_type: result_type,
646 span,
647 })
648 }
649
650 fn infer_unary_op_type_with_scope(
651 &self,
652 op: UnaryOp,
653 operand: &Expr,
654 scope: &[ScopedTable],
655 plan_subquery: &SubqueryPlanner<'_>,
656 span: Span,
657 ) -> Result<TypedExpr, PlannerError> {
658 let operand_typed = self.infer_type_with_scope(operand, scope, plan_subquery)?;
659
660 let result_type = match op {
661 UnaryOp::Not => {
662 if !matches!(
663 operand_typed.resolved_type,
664 ResolvedType::Boolean | ResolvedType::Null
665 ) {
666 return Err(PlannerError::TypeMismatch {
667 expected: "Boolean".to_string(),
668 found: operand_typed.resolved_type.type_name().to_string(),
669 line: span.start.line,
670 column: span.start.column,
671 });
672 }
673 ResolvedType::Boolean
674 }
675 UnaryOp::Minus => match &operand_typed.resolved_type {
676 ResolvedType::Integer => ResolvedType::Integer,
677 ResolvedType::BigInt => ResolvedType::BigInt,
678 ResolvedType::Float => ResolvedType::Float,
679 ResolvedType::Double => ResolvedType::Double,
680 ResolvedType::Null => ResolvedType::Null,
681 other => {
682 return Err(PlannerError::InvalidOperator {
683 op: "unary minus".to_string(),
684 type_name: other.type_name().to_string(),
685 line: span.start.line,
686 column: span.start.column,
687 });
688 }
689 },
690 };
691
692 Ok(TypedExpr {
693 kind: TypedExprKind::UnaryOp {
694 op,
695 operand: Box::new(operand_typed),
696 },
697 resolved_type: result_type,
698 span,
699 })
700 }
701
702 #[allow(dead_code)]
704 fn infer_function_call_type(
705 &self,
706 name: &str,
707 args: &[Expr],
708 distinct: bool,
709 star: bool,
710 table: &TableMetadata,
711 span: Span,
712 ) -> Result<TypedExpr, PlannerError> {
713 let typed_args: Vec<TypedExpr> = args
715 .iter()
716 .map(|arg| self.infer_type(arg, table))
717 .collect::<Result<Vec<_>, _>>()?;
718
719 let result_type = self.check_function_call(name, &typed_args, distinct, star, span)?;
721
722 Ok(TypedExpr {
723 kind: TypedExprKind::FunctionCall {
724 name: name.to_string(),
725 args: typed_args,
726 distinct,
727 star,
728 },
729 resolved_type: result_type,
730 span,
731 })
732 }
733
734 #[allow(clippy::too_many_arguments)]
735 fn infer_function_call_type_with_scope(
736 &self,
737 name: &str,
738 args: &[Expr],
739 distinct: bool,
740 star: bool,
741 scope: &[ScopedTable],
742 plan_subquery: &SubqueryPlanner<'_>,
743 span: Span,
744 ) -> Result<TypedExpr, PlannerError> {
745 let typed_args: Vec<TypedExpr> = args
746 .iter()
747 .map(|arg| self.infer_type_with_scope(arg, scope, plan_subquery))
748 .collect::<Result<Vec<_>, _>>()?;
749 let result_type = self.check_function_call(name, &typed_args, distinct, star, span)?;
750
751 Ok(TypedExpr {
752 kind: TypedExprKind::FunctionCall {
753 name: name.to_string(),
754 args: typed_args,
755 distinct,
756 star,
757 },
758 resolved_type: result_type,
759 span,
760 })
761 }
762
763 #[allow(dead_code)]
765 fn infer_between_type(
766 &self,
767 expr: &Expr,
768 low: &Expr,
769 high: &Expr,
770 negated: bool,
771 table: &TableMetadata,
772 span: Span,
773 ) -> Result<TypedExpr, PlannerError> {
774 let expr_typed = self.infer_type(expr, table)?;
775 let low_typed = self.infer_type(low, table)?;
776 let high_typed = self.infer_type(high, table)?;
777
778 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
780 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
781
782 Ok(TypedExpr {
783 kind: TypedExprKind::Between {
784 expr: Box::new(expr_typed),
785 low: Box::new(low_typed),
786 high: Box::new(high_typed),
787 negated,
788 },
789 resolved_type: ResolvedType::Boolean,
790 span,
791 })
792 }
793
794 #[allow(clippy::too_many_arguments)]
795 fn infer_between_type_with_scope(
796 &self,
797 expr: &Expr,
798 low: &Expr,
799 high: &Expr,
800 negated: bool,
801 scope: &[ScopedTable],
802 plan_subquery: &SubqueryPlanner<'_>,
803 span: Span,
804 ) -> Result<TypedExpr, PlannerError> {
805 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
806 let low_typed = self.infer_type_with_scope(low, scope, plan_subquery)?;
807 let high_typed = self.infer_type_with_scope(high, scope, plan_subquery)?;
808 self.check_comparison_op(&expr_typed.resolved_type, &low_typed.resolved_type, span)?;
809 self.check_comparison_op(&expr_typed.resolved_type, &high_typed.resolved_type, span)?;
810
811 Ok(TypedExpr {
812 kind: TypedExprKind::Between {
813 expr: Box::new(expr_typed),
814 low: Box::new(low_typed),
815 high: Box::new(high_typed),
816 negated,
817 },
818 resolved_type: ResolvedType::Boolean,
819 span,
820 })
821 }
822
823 #[allow(dead_code)]
825 fn infer_like_type(
826 &self,
827 expr: &Expr,
828 pattern: &Expr,
829 escape: Option<&Expr>,
830 negated: bool,
831 table: &TableMetadata,
832 span: Span,
833 ) -> Result<TypedExpr, PlannerError> {
834 let expr_typed = self.infer_type(expr, table)?;
835 let pattern_typed = self.infer_type(pattern, table)?;
836
837 if !matches!(
839 expr_typed.resolved_type,
840 ResolvedType::Text | ResolvedType::Null
841 ) {
842 return Err(PlannerError::TypeMismatch {
843 expected: "Text".to_string(),
844 found: expr_typed.resolved_type.type_name().to_string(),
845 line: expr.span.start.line,
846 column: expr.span.start.column,
847 });
848 }
849
850 if !matches!(
852 pattern_typed.resolved_type,
853 ResolvedType::Text | ResolvedType::Null
854 ) {
855 return Err(PlannerError::TypeMismatch {
856 expected: "Text".to_string(),
857 found: pattern_typed.resolved_type.type_name().to_string(),
858 line: pattern.span.start.line,
859 column: pattern.span.start.column,
860 });
861 }
862
863 let escape_typed = if let Some(esc) = escape {
864 let typed = self.infer_type(esc, table)?;
865 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
866 return Err(PlannerError::TypeMismatch {
867 expected: "Text".to_string(),
868 found: typed.resolved_type.type_name().to_string(),
869 line: esc.span.start.line,
870 column: esc.span.start.column,
871 });
872 }
873 Some(Box::new(typed))
874 } else {
875 None
876 };
877
878 Ok(TypedExpr {
879 kind: TypedExprKind::Like {
880 expr: Box::new(expr_typed),
881 pattern: Box::new(pattern_typed),
882 escape: escape_typed,
883 negated,
884 },
885 resolved_type: ResolvedType::Boolean,
886 span,
887 })
888 }
889
890 #[allow(clippy::too_many_arguments)]
891 fn infer_like_type_with_scope(
892 &self,
893 expr: &Expr,
894 pattern: &Expr,
895 escape: Option<&Expr>,
896 negated: bool,
897 scope: &[ScopedTable],
898 plan_subquery: &SubqueryPlanner<'_>,
899 span: Span,
900 ) -> Result<TypedExpr, PlannerError> {
901 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
902 let pattern_typed = self.infer_type_with_scope(pattern, scope, plan_subquery)?;
903
904 if !matches!(
905 expr_typed.resolved_type,
906 ResolvedType::Text | ResolvedType::Null
907 ) {
908 return Err(PlannerError::TypeMismatch {
909 expected: "Text".to_string(),
910 found: expr_typed.resolved_type.type_name().to_string(),
911 line: expr.span.start.line,
912 column: expr.span.start.column,
913 });
914 }
915
916 if !matches!(
917 pattern_typed.resolved_type,
918 ResolvedType::Text | ResolvedType::Null
919 ) {
920 return Err(PlannerError::TypeMismatch {
921 expected: "Text".to_string(),
922 found: pattern_typed.resolved_type.type_name().to_string(),
923 line: pattern.span.start.line,
924 column: pattern.span.start.column,
925 });
926 }
927
928 let escape_typed = if let Some(esc) = escape {
929 let typed = self.infer_type_with_scope(esc, scope, plan_subquery)?;
930 if !matches!(typed.resolved_type, ResolvedType::Text | ResolvedType::Null) {
931 return Err(PlannerError::TypeMismatch {
932 expected: "Text".to_string(),
933 found: typed.resolved_type.type_name().to_string(),
934 line: esc.span.start.line,
935 column: esc.span.start.column,
936 });
937 }
938 Some(Box::new(typed))
939 } else {
940 None
941 };
942
943 Ok(TypedExpr {
944 kind: TypedExprKind::Like {
945 expr: Box::new(expr_typed),
946 pattern: Box::new(pattern_typed),
947 escape: escape_typed,
948 negated,
949 },
950 resolved_type: ResolvedType::Boolean,
951 span,
952 })
953 }
954
955 #[allow(dead_code)]
957 fn infer_in_list_type(
958 &self,
959 expr: &Expr,
960 list: &[Expr],
961 negated: bool,
962 table: &TableMetadata,
963 span: Span,
964 ) -> Result<TypedExpr, PlannerError> {
965 let expr_typed = self.infer_type(expr, table)?;
966
967 let typed_list: Vec<TypedExpr> = list
968 .iter()
969 .map(|item| {
970 let typed = self.infer_type(item, table)?;
971 self.check_comparison_op(
973 &expr_typed.resolved_type,
974 &typed.resolved_type,
975 item.span,
976 )?;
977 Ok(typed)
978 })
979 .collect::<Result<Vec<_>, PlannerError>>()?;
980
981 Ok(TypedExpr {
982 kind: TypedExprKind::InList {
983 expr: Box::new(expr_typed),
984 list: typed_list,
985 negated,
986 },
987 resolved_type: ResolvedType::Boolean,
988 span,
989 })
990 }
991
992 fn infer_in_list_type_with_scope(
993 &self,
994 expr: &Expr,
995 list: &[Expr],
996 negated: bool,
997 scope: &[ScopedTable],
998 plan_subquery: &SubqueryPlanner<'_>,
999 span: Span,
1000 ) -> Result<TypedExpr, PlannerError> {
1001 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1002
1003 let typed_list: Vec<TypedExpr> = list
1004 .iter()
1005 .map(|item| {
1006 let typed = self.infer_type_with_scope(item, scope, plan_subquery)?;
1007 self.check_comparison_op(
1008 &expr_typed.resolved_type,
1009 &typed.resolved_type,
1010 item.span,
1011 )?;
1012 Ok(typed)
1013 })
1014 .collect::<Result<Vec<_>, PlannerError>>()?;
1015
1016 Ok(TypedExpr {
1017 kind: TypedExprKind::InList {
1018 expr: Box::new(expr_typed),
1019 list: typed_list,
1020 negated,
1021 },
1022 resolved_type: ResolvedType::Boolean,
1023 span,
1024 })
1025 }
1026
1027 #[allow(dead_code)]
1029 fn infer_is_null_type(
1030 &self,
1031 expr: &Expr,
1032 negated: bool,
1033 table: &TableMetadata,
1034 span: Span,
1035 ) -> Result<TypedExpr, PlannerError> {
1036 let expr_typed = self.infer_type(expr, table)?;
1037
1038 Ok(TypedExpr {
1039 kind: TypedExprKind::IsNull {
1040 expr: Box::new(expr_typed),
1041 negated,
1042 },
1043 resolved_type: ResolvedType::Boolean,
1044 span,
1045 })
1046 }
1047
1048 fn infer_is_null_type_with_scope(
1049 &self,
1050 expr: &Expr,
1051 negated: bool,
1052 scope: &[ScopedTable],
1053 plan_subquery: &SubqueryPlanner<'_>,
1054 span: Span,
1055 ) -> Result<TypedExpr, PlannerError> {
1056 let expr_typed = self.infer_type_with_scope(expr, scope, plan_subquery)?;
1057
1058 Ok(TypedExpr {
1059 kind: TypedExprKind::IsNull {
1060 expr: Box::new(expr_typed),
1061 negated,
1062 },
1063 resolved_type: ResolvedType::Boolean,
1064 span,
1065 })
1066 }
1067
1068 fn infer_vector_literal_type(
1070 &self,
1071 values: &[f64],
1072 span: Span,
1073 ) -> Result<TypedExpr, PlannerError> {
1074 Ok(TypedExpr {
1075 kind: TypedExprKind::VectorLiteral(values.to_vec()),
1076 resolved_type: ResolvedType::Vector {
1077 dimension: values.len() as u32,
1078 metric: VectorMetric::Cosine, },
1080 span,
1081 })
1082 }
1083
1084 pub fn normalize_metric(&self, metric: &str, span: Span) -> Result<VectorMetric, PlannerError> {
1096 match metric.to_lowercase().as_str() {
1097 "cosine" => Ok(VectorMetric::Cosine),
1098 "l2" => Ok(VectorMetric::L2),
1099 "inner" => Ok(VectorMetric::Inner),
1100 _ => Err(PlannerError::InvalidMetric {
1101 value: metric.to_string(),
1102 line: span.start.line,
1103 column: span.start.column,
1104 }),
1105 }
1106 }
1107
1108 pub fn check_function_call(
1113 &self,
1114 name: &str,
1115 args: &[TypedExpr],
1116 distinct: bool,
1117 star: bool,
1118 span: Span,
1119 ) -> Result<ResolvedType, PlannerError> {
1120 let lower_name = name.to_lowercase();
1121
1122 match lower_name.as_str() {
1123 "count" => self.check_count(args, distinct, star, span),
1124 "sum" => self.check_sum(args, distinct, star, span),
1125 "total" => self.check_total(args, distinct, star, span),
1126 "avg" => self.check_avg(args, distinct, star, span),
1127 "min" => self.check_min_max(args, distinct, star, span),
1128 "max" => self.check_min_max(args, distinct, star, span),
1129 "group_concat" => self.check_group_concat(args, distinct, star, span),
1130 "string_agg" => self.check_string_agg(args, distinct, star, span),
1131 "vector_distance" => self.check_vector_distance(args, span),
1132 "vector_similarity" => self.check_vector_similarity(args, span),
1133 "vector_dims" => self.check_vector_dims(args, span),
1134 "vector_norm" => self.check_vector_norm(args, span),
1135 _ => {
1137 Err(PlannerError::UnsupportedFeature {
1139 feature: format!("function '{}'", name),
1140 version: "future".to_string(),
1141 line: span.start.line,
1142 column: span.start.column,
1143 })
1144 }
1145 }
1146 }
1147
1148 pub fn validate_having_expr(
1149 &self,
1150 expr: &TypedExpr,
1151 group_keys: &[TypedExpr],
1152 aggregates: &[AggregateExpr],
1153 ) -> Result<(), PlannerError> {
1154 use std::collections::HashSet;
1155
1156 let group_key_indices: HashSet<usize> = group_keys
1157 .iter()
1158 .filter_map(|expr| match &expr.kind {
1159 TypedExprKind::ColumnRef { column_index, .. } => Some(*column_index),
1160 _ => None,
1161 })
1162 .collect();
1163
1164 let aggregate_signatures: HashSet<AggregateSignature> = aggregates
1165 .iter()
1166 .map(aggregate_signature_from_expr)
1167 .collect();
1168
1169 fn walk(
1170 expr: &TypedExpr,
1171 group_key_indices: &HashSet<usize>,
1172 aggregate_signatures: &HashSet<AggregateSignature>,
1173 ) -> Result<(), PlannerError> {
1174 match &expr.kind {
1175 TypedExprKind::ColumnRef { column_index, .. } => {
1176 if group_key_indices.contains(column_index) {
1177 Ok(())
1178 } else {
1179 Err(PlannerError::invalid_expression(
1180 "column in HAVING must be in GROUP BY or be aggregated".to_string(),
1181 ))
1182 }
1183 }
1184 TypedExprKind::FunctionCall {
1185 name,
1186 args,
1187 distinct,
1188 star,
1189 } if is_aggregate_name(name) => {
1190 let signature = aggregate_signature_from_call(name, args, *distinct, *star)?;
1191 if aggregate_signatures.contains(&signature) {
1192 Ok(())
1193 } else {
1194 Err(PlannerError::invalid_expression(
1195 "aggregate in HAVING must appear in plan".to_string(),
1196 ))
1197 }
1198 }
1199 TypedExprKind::BinaryOp { left, right, .. } => {
1200 walk(left, group_key_indices, aggregate_signatures)?;
1201 walk(right, group_key_indices, aggregate_signatures)
1202 }
1203 TypedExprKind::UnaryOp { operand, .. } => {
1204 walk(operand, group_key_indices, aggregate_signatures)
1205 }
1206 TypedExprKind::FunctionCall { args, .. } => {
1207 for arg in args {
1208 walk(arg, group_key_indices, aggregate_signatures)?;
1209 }
1210 Ok(())
1211 }
1212 TypedExprKind::Between {
1213 expr, low, high, ..
1214 } => {
1215 walk(expr, group_key_indices, aggregate_signatures)?;
1216 walk(low, group_key_indices, aggregate_signatures)?;
1217 walk(high, group_key_indices, aggregate_signatures)
1218 }
1219 TypedExprKind::Like {
1220 expr,
1221 pattern,
1222 escape,
1223 ..
1224 } => {
1225 walk(expr, group_key_indices, aggregate_signatures)?;
1226 walk(pattern, group_key_indices, aggregate_signatures)?;
1227 if let Some(esc) = escape {
1228 walk(esc, group_key_indices, aggregate_signatures)?;
1229 }
1230 Ok(())
1231 }
1232 TypedExprKind::InList { expr, list, .. } => {
1233 walk(expr, group_key_indices, aggregate_signatures)?;
1234 for item in list {
1235 walk(item, group_key_indices, aggregate_signatures)?;
1236 }
1237 Ok(())
1238 }
1239 TypedExprKind::IsNull { expr, .. } => {
1240 walk(expr, group_key_indices, aggregate_signatures)
1241 }
1242 _ => Ok(()),
1243 }
1244 }
1245
1246 walk(expr, &group_key_indices, &aggregate_signatures)
1247 }
1248
1249 fn check_count(
1250 &self,
1251 args: &[TypedExpr],
1252 distinct: bool,
1253 star: bool,
1254 span: Span,
1255 ) -> Result<ResolvedType, PlannerError> {
1256 if star {
1257 if distinct {
1258 return Err(PlannerError::unsupported_feature(
1259 "COUNT(DISTINCT *)",
1260 "future",
1261 span,
1262 ));
1263 }
1264 if !args.is_empty() {
1265 return Err(PlannerError::type_mismatch(
1266 "no arguments with COUNT(*)",
1267 format!("{} arguments", args.len()),
1268 span,
1269 ));
1270 }
1271 return Ok(ResolvedType::BigInt);
1272 }
1273
1274 if args.len() != 1 {
1275 return Err(PlannerError::type_mismatch(
1276 "1 argument",
1277 format!("{} arguments", args.len()),
1278 span,
1279 ));
1280 }
1281
1282 if distinct {
1283 return Ok(ResolvedType::BigInt);
1284 }
1285
1286 Ok(ResolvedType::BigInt)
1287 }
1288
1289 fn check_sum(
1290 &self,
1291 args: &[TypedExpr],
1292 _distinct: bool,
1293 star: bool,
1294 span: Span,
1295 ) -> Result<ResolvedType, PlannerError> {
1296 if star {
1297 return Err(PlannerError::type_mismatch(
1298 "numeric argument",
1299 "COUNT(*) style",
1300 span,
1301 ));
1302 }
1303 let arg = self.require_single_arg(args, span)?;
1304 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
1305 return Err(PlannerError::type_mismatch(
1306 "numeric",
1307 arg.resolved_type.type_name().to_string(),
1308 arg.span,
1309 ));
1310 }
1311 Ok(ResolvedType::Double)
1312 }
1313
1314 fn check_total(
1315 &self,
1316 args: &[TypedExpr],
1317 distinct: bool,
1318 star: bool,
1319 span: Span,
1320 ) -> Result<ResolvedType, PlannerError> {
1321 if star {
1322 return Err(PlannerError::type_mismatch(
1323 "numeric argument",
1324 "COUNT(*) style",
1325 span,
1326 ));
1327 }
1328 if distinct {
1329 return Err(PlannerError::unsupported_feature(
1330 "TOTAL(DISTINCT ...)",
1331 "future",
1332 span,
1333 ));
1334 }
1335 let arg = self.require_single_arg(args, span)?;
1336 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
1337 return Err(PlannerError::type_mismatch(
1338 "numeric",
1339 arg.resolved_type.type_name().to_string(),
1340 arg.span,
1341 ));
1342 }
1343 Ok(ResolvedType::Double)
1344 }
1345
1346 fn check_avg(
1347 &self,
1348 args: &[TypedExpr],
1349 _distinct: bool,
1350 star: bool,
1351 span: Span,
1352 ) -> Result<ResolvedType, PlannerError> {
1353 if star {
1354 return Err(PlannerError::type_mismatch(
1355 "numeric argument",
1356 "COUNT(*) style",
1357 span,
1358 ));
1359 }
1360 let arg = self.require_single_arg(args, span)?;
1361 if !is_numeric_type(&arg.resolved_type) && arg.resolved_type != ResolvedType::Null {
1362 return Err(PlannerError::type_mismatch(
1363 "numeric",
1364 arg.resolved_type.type_name().to_string(),
1365 arg.span,
1366 ));
1367 }
1368 Ok(ResolvedType::Double)
1369 }
1370
1371 fn check_min_max(
1372 &self,
1373 args: &[TypedExpr],
1374 _distinct: bool,
1375 star: bool,
1376 span: Span,
1377 ) -> Result<ResolvedType, PlannerError> {
1378 if star {
1379 return Err(PlannerError::type_mismatch(
1380 "argument",
1381 "COUNT(*) style",
1382 span,
1383 ));
1384 }
1385 let arg = self.require_single_arg(args, span)?;
1386 if matches!(arg.resolved_type, ResolvedType::Vector { .. }) {
1387 return Err(PlannerError::type_mismatch(
1388 "comparable",
1389 arg.resolved_type.type_name().to_string(),
1390 arg.span,
1391 ));
1392 }
1393 Ok(arg.resolved_type.clone())
1394 }
1395
1396 fn check_group_concat(
1397 &self,
1398 args: &[TypedExpr],
1399 _distinct: bool,
1400 star: bool,
1401 span: Span,
1402 ) -> Result<ResolvedType, PlannerError> {
1403 if star {
1404 return Err(PlannerError::type_mismatch(
1405 "text argument",
1406 "COUNT(*) style",
1407 span,
1408 ));
1409 }
1410 if args.is_empty() || args.len() > 2 {
1411 return Err(PlannerError::type_mismatch(
1412 "1 or 2 arguments",
1413 format!("{} arguments", args.len()),
1414 span,
1415 ));
1416 }
1417 if !matches!(
1418 args[0].resolved_type,
1419 ResolvedType::Text | ResolvedType::Null
1420 ) {
1421 return Err(PlannerError::type_mismatch(
1422 "Text",
1423 args[0].resolved_type.type_name().to_string(),
1424 args[0].span,
1425 ));
1426 }
1427 if args.len() == 2
1428 && !matches!(
1429 args[1].resolved_type,
1430 ResolvedType::Text | ResolvedType::Null
1431 )
1432 {
1433 return Err(PlannerError::type_mismatch(
1434 "Text",
1435 args[1].resolved_type.type_name().to_string(),
1436 args[1].span,
1437 ));
1438 }
1439 Ok(ResolvedType::Text)
1440 }
1441
1442 fn check_string_agg(
1443 &self,
1444 args: &[TypedExpr],
1445 _distinct: bool,
1446 star: bool,
1447 span: Span,
1448 ) -> Result<ResolvedType, PlannerError> {
1449 if star {
1450 return Err(PlannerError::type_mismatch(
1451 "text argument",
1452 "COUNT(*) style",
1453 span,
1454 ));
1455 }
1456 if args.len() != 2 {
1457 return Err(PlannerError::type_mismatch(
1458 "2 arguments",
1459 format!("{} arguments", args.len()),
1460 span,
1461 ));
1462 }
1463 if !matches!(
1464 args[0].resolved_type,
1465 ResolvedType::Text | ResolvedType::Null
1466 ) {
1467 return Err(PlannerError::type_mismatch(
1468 "Text",
1469 args[0].resolved_type.type_name().to_string(),
1470 args[0].span,
1471 ));
1472 }
1473 if !matches!(
1474 args[1].resolved_type,
1475 ResolvedType::Text | ResolvedType::Null
1476 ) {
1477 return Err(PlannerError::type_mismatch(
1478 "Text",
1479 args[1].resolved_type.type_name().to_string(),
1480 args[1].span,
1481 ));
1482 }
1483 Ok(ResolvedType::Text)
1484 }
1485
1486 fn check_vector_dims(
1487 &self,
1488 args: &[TypedExpr],
1489 span: Span,
1490 ) -> Result<ResolvedType, PlannerError> {
1491 let arg = self.require_single_arg(args, span)?;
1492 if !matches!(
1493 arg.resolved_type,
1494 ResolvedType::Vector { .. } | ResolvedType::Null
1495 ) {
1496 return Err(PlannerError::type_mismatch(
1497 "Vector",
1498 arg.resolved_type.type_name().to_string(),
1499 arg.span,
1500 ));
1501 }
1502 Ok(ResolvedType::Integer)
1503 }
1504
1505 fn check_vector_norm(
1506 &self,
1507 args: &[TypedExpr],
1508 span: Span,
1509 ) -> Result<ResolvedType, PlannerError> {
1510 let arg = self.require_single_arg(args, span)?;
1511 if !matches!(
1512 arg.resolved_type,
1513 ResolvedType::Vector { .. } | ResolvedType::Null
1514 ) {
1515 return Err(PlannerError::type_mismatch(
1516 "Vector",
1517 arg.resolved_type.type_name().to_string(),
1518 arg.span,
1519 ));
1520 }
1521 Ok(ResolvedType::Double)
1522 }
1523
1524 fn require_single_arg<'b>(
1525 &self,
1526 args: &'b [TypedExpr],
1527 span: Span,
1528 ) -> Result<&'b TypedExpr, PlannerError> {
1529 if args.len() != 1 {
1530 return Err(PlannerError::type_mismatch(
1531 "1 argument",
1532 format!("{} arguments", args.len()),
1533 span,
1534 ));
1535 }
1536 Ok(&args[0])
1537 }
1538
1539 pub fn check_vector_distance(
1550 &self,
1551 args: &[TypedExpr],
1552 span: Span,
1553 ) -> Result<ResolvedType, PlannerError> {
1554 if args.len() != 3 {
1555 return Err(PlannerError::TypeMismatch {
1556 expected: "3 arguments".to_string(),
1557 found: format!("{} arguments", args.len()),
1558 line: span.start.line,
1559 column: span.start.column,
1560 });
1561 }
1562
1563 let col_dim = match &args[0].resolved_type {
1565 ResolvedType::Vector { dimension, .. } => *dimension,
1566 other => {
1567 return Err(PlannerError::TypeMismatch {
1568 expected: "Vector".to_string(),
1569 found: other.type_name().to_string(),
1570 line: args[0].span.start.line,
1571 column: args[0].span.start.column,
1572 });
1573 }
1574 };
1575
1576 let vec_dim = match &args[1].resolved_type {
1578 ResolvedType::Vector { dimension, .. } => *dimension,
1579 other => {
1580 return Err(PlannerError::TypeMismatch {
1581 expected: "Vector".to_string(),
1582 found: other.type_name().to_string(),
1583 line: args[1].span.start.line,
1584 column: args[1].span.start.column,
1585 });
1586 }
1587 };
1588
1589 self.check_vector_dimension(col_dim, vec_dim, args[1].span)?;
1591
1592 match &args[2].resolved_type {
1594 ResolvedType::Text => {
1595 if let TypedExprKind::Literal(Literal::String(s)) = &args[2].kind {
1597 self.normalize_metric(s, args[2].span)?;
1598 }
1599 }
1600 ResolvedType::Null => {
1601 return Err(PlannerError::TypeMismatch {
1603 expected: "Text (metric)".to_string(),
1604 found: "Null".to_string(),
1605 line: args[2].span.start.line,
1606 column: args[2].span.start.column,
1607 });
1608 }
1609 other => {
1610 return Err(PlannerError::TypeMismatch {
1611 expected: "Text (metric)".to_string(),
1612 found: other.type_name().to_string(),
1613 line: args[2].span.start.line,
1614 column: args[2].span.start.column,
1615 });
1616 }
1617 }
1618
1619 Ok(ResolvedType::Double)
1620 }
1621
1622 pub fn check_vector_similarity(
1628 &self,
1629 args: &[TypedExpr],
1630 span: Span,
1631 ) -> Result<ResolvedType, PlannerError> {
1632 self.check_vector_distance(args, span)
1634 }
1635
1636 pub fn check_vector_dimension(
1642 &self,
1643 expected: u32,
1644 found: u32,
1645 span: Span,
1646 ) -> Result<(), PlannerError> {
1647 if expected != found {
1648 Err(PlannerError::VectorDimensionMismatch {
1649 expected,
1650 found,
1651 line: span.start.line,
1652 column: span.start.column,
1653 })
1654 } else {
1655 Ok(())
1656 }
1657 }
1658
1659 pub fn check_insert_values(
1682 &self,
1683 table: &TableMetadata,
1684 columns: &[String],
1685 values: &[Vec<Expr>],
1686 span: Span,
1687 ) -> Result<Vec<Vec<TypedExpr>>, PlannerError> {
1688 let target_columns: Vec<&str> = if columns.is_empty() {
1690 table.column_names()
1691 } else {
1692 columns.iter().map(|s| s.as_str()).collect()
1693 };
1694
1695 let mut typed_rows = Vec::with_capacity(values.len());
1696
1697 for row in values {
1698 if row.len() != target_columns.len() {
1700 return Err(PlannerError::ColumnValueCountMismatch {
1701 columns: target_columns.len(),
1702 values: row.len(),
1703 line: span.start.line,
1704 column: span.start.column,
1705 });
1706 }
1707
1708 let mut typed_values = Vec::with_capacity(row.len());
1709
1710 for (value, col_name) in row.iter().zip(target_columns.iter()) {
1711 let col_meta =
1713 table
1714 .get_column(col_name)
1715 .ok_or_else(|| PlannerError::ColumnNotFound {
1716 column: col_name.to_string(),
1717 table: table.name.clone(),
1718 line: span.start.line,
1719 col: span.start.column,
1720 })?;
1721
1722 let typed_value = self.infer_type(value, table)?;
1724
1725 self.check_null_constraint(col_meta, &typed_value, value.span)?;
1727
1728 self.check_type_compatibility(
1730 &col_meta.data_type,
1731 &typed_value.resolved_type,
1732 value.span,
1733 )?;
1734
1735 if let (
1737 ResolvedType::Vector {
1738 dimension: expected_dim,
1739 ..
1740 },
1741 ResolvedType::Vector {
1742 dimension: actual_dim,
1743 ..
1744 },
1745 ) = (&col_meta.data_type, &typed_value.resolved_type)
1746 {
1747 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
1748 }
1749
1750 typed_values.push(typed_value);
1751 }
1752
1753 typed_rows.push(typed_values);
1754 }
1755
1756 Ok(typed_rows)
1757 }
1758
1759 pub fn check_assignment(
1770 &self,
1771 table: &TableMetadata,
1772 column: &str,
1773 value: &Expr,
1774 span: Span,
1775 ) -> Result<TypedExpr, PlannerError> {
1776 let col_meta = table
1778 .get_column(column)
1779 .ok_or_else(|| PlannerError::ColumnNotFound {
1780 column: column.to_string(),
1781 table: table.name.clone(),
1782 line: span.start.line,
1783 col: span.start.column,
1784 })?;
1785
1786 let typed_value = self.infer_type(value, table)?;
1788
1789 self.check_null_constraint(col_meta, &typed_value, value.span)?;
1791
1792 self.check_type_compatibility(&col_meta.data_type, &typed_value.resolved_type, value.span)?;
1794
1795 if let (
1797 ResolvedType::Vector {
1798 dimension: expected_dim,
1799 ..
1800 },
1801 ResolvedType::Vector {
1802 dimension: actual_dim,
1803 ..
1804 },
1805 ) = (&col_meta.data_type, &typed_value.resolved_type)
1806 {
1807 self.check_vector_dimension(*expected_dim, *actual_dim, value.span)?;
1808 }
1809
1810 Ok(typed_value)
1811 }
1812
1813 pub fn check_null_constraint(
1820 &self,
1821 column: &crate::catalog::ColumnMetadata,
1822 value: &TypedExpr,
1823 span: Span,
1824 ) -> Result<(), PlannerError> {
1825 if column.not_null && matches!(value.resolved_type, ResolvedType::Null) {
1826 Err(PlannerError::NullConstraintViolation {
1827 column: column.name.clone(),
1828 line: span.start.line,
1829 col: span.start.column,
1830 })
1831 } else {
1832 Ok(())
1833 }
1834 }
1835
1836 fn check_type_compatibility(
1844 &self,
1845 expected: &ResolvedType,
1846 actual: &ResolvedType,
1847 span: Span,
1848 ) -> Result<(), PlannerError> {
1849 if expected == actual {
1851 return Ok(());
1852 }
1853
1854 if actual.can_cast_to(expected) {
1856 return Ok(());
1857 }
1858
1859 if let (
1862 ResolvedType::Vector {
1863 dimension: d1,
1864 metric: _,
1865 },
1866 ResolvedType::Vector {
1867 dimension: d2,
1868 metric: _,
1869 },
1870 ) = (expected, actual)
1871 {
1872 if *d1 == *d2 {
1874 return Ok(());
1875 }
1876 }
1878
1879 Err(PlannerError::TypeMismatch {
1880 expected: expected.type_name().to_string(),
1881 found: actual.type_name().to_string(),
1882 line: span.start.line,
1883 column: span.start.column,
1884 })
1885 }
1886}
1887
1888fn is_numeric_type(ty: &ResolvedType) -> bool {
1889 matches!(
1890 ty,
1891 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Float | ResolvedType::Double
1892 )
1893}
1894
1895#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1896struct AggregateSignature {
1897 name: String,
1898 distinct: bool,
1899 star: bool,
1900 arg_key: Option<String>,
1901 separator: Option<String>,
1902}
1903
1904fn is_aggregate_name(name: &str) -> bool {
1905 matches!(
1906 name.to_ascii_lowercase().as_str(),
1907 "count" | "sum" | "total" | "avg" | "min" | "max" | "group_concat" | "string_agg"
1908 )
1909}
1910
1911fn aggregate_signature_from_expr(expr: &AggregateExpr) -> AggregateSignature {
1912 let (name, separator, star, arg) = match &expr.function {
1913 AggregateFunction::Count => (
1914 "count".to_string(),
1915 None,
1916 expr.arg.is_none(),
1917 expr.arg.as_ref(),
1918 ),
1919 AggregateFunction::Sum => ("sum".to_string(), None, false, expr.arg.as_ref()),
1920 AggregateFunction::Total => ("total".to_string(), None, false, expr.arg.as_ref()),
1921 AggregateFunction::Avg => ("avg".to_string(), None, false, expr.arg.as_ref()),
1922 AggregateFunction::Min => ("min".to_string(), None, false, expr.arg.as_ref()),
1923 AggregateFunction::Max => ("max".to_string(), None, false, expr.arg.as_ref()),
1924 AggregateFunction::GroupConcat { separator } => (
1925 "group_concat".to_string(),
1926 separator.clone(),
1927 false,
1928 expr.arg.as_ref(),
1929 ),
1930 AggregateFunction::StringAgg { separator } => (
1931 "string_agg".to_string(),
1932 separator.clone(),
1933 false,
1934 expr.arg.as_ref(),
1935 ),
1936 };
1937 AggregateSignature {
1938 name,
1939 distinct: expr.distinct,
1940 star,
1941 arg_key: arg.map(typed_expr_signature),
1942 separator,
1943 }
1944}
1945
1946fn aggregate_signature_from_call(
1947 name: &str,
1948 args: &[TypedExpr],
1949 distinct: bool,
1950 star: bool,
1951) -> Result<AggregateSignature, PlannerError> {
1952 let separator = if name.eq_ignore_ascii_case("group_concat") && args.len() == 2 {
1953 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
1954 Some(value.clone())
1955 } else {
1956 return Err(PlannerError::invalid_expression(
1957 "GROUP_CONCAT separator must be a string literal".to_string(),
1958 ));
1959 }
1960 } else if name.eq_ignore_ascii_case("string_agg") && args.len() == 2 {
1961 if let TypedExprKind::Literal(Literal::String(value)) = &args[1].kind {
1962 Some(value.clone())
1963 } else {
1964 return Err(PlannerError::invalid_expression(
1965 "STRING_AGG separator must be a string literal".to_string(),
1966 ));
1967 }
1968 } else {
1969 None
1970 };
1971 Ok(AggregateSignature {
1972 name: name.to_ascii_lowercase(),
1973 distinct,
1974 star,
1975 arg_key: args.first().map(typed_expr_signature),
1976 separator,
1977 })
1978}
1979
1980fn typed_expr_signature(expr: &TypedExpr) -> String {
1981 format!("{:?}", expr.kind)
1982}
1983
1984fn single_column_type(schema: &[ColumnMetadata], span: Span) -> Result<ResolvedType, PlannerError> {
1985 match schema {
1986 [column] => Ok(column.data_type.clone()),
1987 [] => Err(PlannerError::type_mismatch(
1988 "one-column subquery",
1989 "zero-column subquery",
1990 span,
1991 )),
1992 _ => Err(PlannerError::type_mismatch(
1993 "one-column subquery",
1994 format!("{} columns", schema.len()),
1995 span,
1996 )),
1997 }
1998}
1999
2000#[cfg(test)]
2002#[path = "type_checker/tests.rs"]
2003mod tests;