Skip to main content

polyglot_sql/optimizer/
annotate_types.rs

1//! Type Annotation for SQL Expressions
2//!
3//! This module provides type inference and annotation for SQL AST nodes.
4//! It walks the expression tree and assigns data types to expressions based on:
5//! - Literal values (strings, numbers, booleans)
6//! - Column references (from schema)
7//! - Function return types
8//! - Operator result types (with coercion rules)
9//!
10//! Based on SQLGlot's optimizer/annotate_types.py
11
12use std::collections::{HashMap, HashSet};
13
14use crate::dialects::DialectType;
15use crate::expressions::{
16    BinaryOp, DataType, DateTimeField, DotAccess, Expression, Function, IfFunc, ListAggOverflow,
17    Literal, Map, Nvl2Func, Struct, StructField, Subscript,
18};
19use crate::schema::{normalize_name, Schema, SchemaError, SchemaResult, TABLE_PARTS};
20
21/// Type coercion class for determining result types in binary operations.
22/// Higher-priority classes win during coercion.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub enum TypeCoercionClass {
25    /// Text types (CHAR, VARCHAR, TEXT)
26    Text = 0,
27    /// Numeric types (INT, FLOAT, DECIMAL, etc.)
28    Numeric = 1,
29    /// Time-like types (DATE, TIME, TIMESTAMP, INTERVAL)
30    Timelike = 2,
31}
32
33impl TypeCoercionClass {
34    /// Get the coercion class for a data type
35    pub fn from_data_type(dt: &DataType) -> Option<Self> {
36        match dt {
37            // Text types
38            DataType::Char { .. }
39            | DataType::VarChar { .. }
40            | DataType::Text
41            | DataType::Binary { .. }
42            | DataType::VarBinary { .. }
43            | DataType::Blob => Some(TypeCoercionClass::Text),
44
45            // Numeric types
46            DataType::Boolean
47            | DataType::TinyInt { .. }
48            | DataType::SmallInt { .. }
49            | DataType::Int { .. }
50            | DataType::BigInt { .. }
51            | DataType::Int128
52            | DataType::UInt8
53            | DataType::UInt16
54            | DataType::UInt32
55            | DataType::UInt64
56            | DataType::UInt128
57            | DataType::Float { .. }
58            | DataType::Double { .. }
59            | DataType::Decimal { .. } => Some(TypeCoercionClass::Numeric),
60
61            // Timelike types
62            DataType::Date
63            | DataType::Time { .. }
64            | DataType::Timestamp { .. }
65            | DataType::Interval { .. } => Some(TypeCoercionClass::Timelike),
66
67            // Other types don't have a coercion class
68            _ => None,
69        }
70    }
71}
72
73/// Type annotation configuration and state
74pub struct TypeAnnotator<'a> {
75    /// Schema for looking up column types
76    _schema: Option<&'a dyn Schema>,
77    /// Dialect for dialect-specific type rules
78    _dialect: Option<DialectType>,
79    /// Whether to annotate types for all expressions
80    annotate_aggregates: bool,
81    /// Function return type mappings
82    function_return_types: HashMap<String, DataType>,
83}
84
85impl<'a> TypeAnnotator<'a> {
86    /// Create a new type annotator
87    pub fn new(schema: Option<&'a dyn Schema>, dialect: Option<DialectType>) -> Self {
88        let mut annotator = Self {
89            _schema: schema,
90            _dialect: dialect,
91            annotate_aggregates: true,
92            function_return_types: HashMap::new(),
93        };
94        annotator.init_function_return_types();
95        annotator
96    }
97
98    /// Initialize function return type mappings
99    fn init_function_return_types(&mut self) {
100        // Aggregate functions
101        self.function_return_types
102            .insert("COUNT".to_string(), DataType::BigInt { length: None });
103        self.function_return_types.insert(
104            "SUM".to_string(),
105            DataType::Decimal {
106                precision: None,
107                scale: None,
108            },
109        );
110        self.function_return_types.insert(
111            "AVG".to_string(),
112            DataType::Double {
113                precision: None,
114                scale: None,
115            },
116        );
117
118        // String functions
119        if self._dialect == Some(DialectType::DuckDB) {
120            // DuckDB's list-to-string and date-name functions always return
121            // VARCHAR, even when the argument's type is unresolved. Keep these
122            // rules dialect-specific: BigQuery's ARRAY_TO_STRING also has a
123            // BYTES overload.
124            for name in [
125                "ARRAY_TO_STRING",
126                "ARRAY_TO_STRING_COMMA_DEFAULT",
127                "MONTHNAME",
128                "DAYNAME",
129            ] {
130                self.function_return_types.insert(
131                    name.to_string(),
132                    DataType::VarChar {
133                        length: None,
134                        parenthesized_length: false,
135                    },
136                );
137            }
138        }
139        self.function_return_types.insert(
140            "CONCAT".to_string(),
141            DataType::VarChar {
142                length: None,
143                parenthesized_length: false,
144            },
145        );
146        self.function_return_types.insert(
147            "UPPER".to_string(),
148            DataType::VarChar {
149                length: None,
150                parenthesized_length: false,
151            },
152        );
153        self.function_return_types.insert(
154            "LOWER".to_string(),
155            DataType::VarChar {
156                length: None,
157                parenthesized_length: false,
158            },
159        );
160        self.function_return_types.insert(
161            "TRIM".to_string(),
162            DataType::VarChar {
163                length: None,
164                parenthesized_length: false,
165            },
166        );
167        self.function_return_types.insert(
168            "LTRIM".to_string(),
169            DataType::VarChar {
170                length: None,
171                parenthesized_length: false,
172            },
173        );
174        self.function_return_types.insert(
175            "RTRIM".to_string(),
176            DataType::VarChar {
177                length: None,
178                parenthesized_length: false,
179            },
180        );
181        self.function_return_types.insert(
182            "SUBSTRING".to_string(),
183            DataType::VarChar {
184                length: None,
185                parenthesized_length: false,
186            },
187        );
188        self.function_return_types.insert(
189            "SUBSTR".to_string(),
190            DataType::VarChar {
191                length: None,
192                parenthesized_length: false,
193            },
194        );
195        self.function_return_types.insert(
196            "REPLACE".to_string(),
197            DataType::VarChar {
198                length: None,
199                parenthesized_length: false,
200            },
201        );
202        self.function_return_types.insert(
203            "LENGTH".to_string(),
204            DataType::Int {
205                length: None,
206                integer_spelling: false,
207            },
208        );
209        self.function_return_types.insert(
210            "CHAR_LENGTH".to_string(),
211            DataType::Int {
212                length: None,
213                integer_spelling: false,
214            },
215        );
216
217        // Date/Time functions
218        self.function_return_types.insert(
219            "NOW".to_string(),
220            DataType::Timestamp {
221                precision: None,
222                timezone: false,
223            },
224        );
225        self.function_return_types.insert(
226            "CURRENT_TIMESTAMP".to_string(),
227            DataType::Timestamp {
228                precision: None,
229                timezone: false,
230            },
231        );
232        self.function_return_types
233            .insert("CURRENT_DATE".to_string(), DataType::Date);
234        self.function_return_types.insert(
235            "CURRENT_TIME".to_string(),
236            DataType::Time {
237                precision: None,
238                timezone: false,
239            },
240        );
241        self.function_return_types
242            .insert("DATE".to_string(), DataType::Date);
243        self.function_return_types.insert(
244            "YEAR".to_string(),
245            DataType::Int {
246                length: None,
247                integer_spelling: false,
248            },
249        );
250        self.function_return_types.insert(
251            "MONTH".to_string(),
252            DataType::Int {
253                length: None,
254                integer_spelling: false,
255            },
256        );
257        self.function_return_types.insert(
258            "DAY".to_string(),
259            DataType::Int {
260                length: None,
261                integer_spelling: false,
262            },
263        );
264        self.function_return_types.insert(
265            "HOUR".to_string(),
266            DataType::Int {
267                length: None,
268                integer_spelling: false,
269            },
270        );
271        self.function_return_types.insert(
272            "MINUTE".to_string(),
273            DataType::Int {
274                length: None,
275                integer_spelling: false,
276            },
277        );
278        self.function_return_types.insert(
279            "SECOND".to_string(),
280            DataType::Int {
281                length: None,
282                integer_spelling: false,
283            },
284        );
285        self.function_return_types.insert(
286            "EXTRACT".to_string(),
287            DataType::Int {
288                length: None,
289                integer_spelling: false,
290            },
291        );
292        self.function_return_types.insert(
293            "DATE_DIFF".to_string(),
294            DataType::Int {
295                length: None,
296                integer_spelling: false,
297            },
298        );
299        self.function_return_types.insert(
300            "DATEDIFF".to_string(),
301            DataType::Int {
302                length: None,
303                integer_spelling: false,
304            },
305        );
306
307        // Math functions
308        self.function_return_types.insert(
309            "ABS".to_string(),
310            DataType::Double {
311                precision: None,
312                scale: None,
313            },
314        );
315        self.function_return_types.insert(
316            "ROUND".to_string(),
317            DataType::Double {
318                precision: None,
319                scale: None,
320            },
321        );
322        self.function_return_types.insert(
323            "DATE_FORMAT".to_string(),
324            DataType::VarChar {
325                length: None,
326                parenthesized_length: false,
327            },
328        );
329        self.function_return_types.insert(
330            "FORMAT_DATE".to_string(),
331            DataType::VarChar {
332                length: None,
333                parenthesized_length: false,
334            },
335        );
336        self.function_return_types.insert(
337            "TIME_TO_STR".to_string(),
338            DataType::VarChar {
339                length: None,
340                parenthesized_length: false,
341            },
342        );
343        self.function_return_types.insert(
344            "SQRT".to_string(),
345            DataType::Double {
346                precision: None,
347                scale: None,
348            },
349        );
350        self.function_return_types.insert(
351            "POWER".to_string(),
352            DataType::Double {
353                precision: None,
354                scale: None,
355            },
356        );
357        self.function_return_types.insert(
358            "MOD".to_string(),
359            DataType::Int {
360                length: None,
361                integer_spelling: false,
362            },
363        );
364        self.function_return_types.insert(
365            "LOG".to_string(),
366            DataType::Double {
367                precision: None,
368                scale: None,
369            },
370        );
371        self.function_return_types.insert(
372            "LN".to_string(),
373            DataType::Double {
374                precision: None,
375                scale: None,
376            },
377        );
378        self.function_return_types.insert(
379            "EXP".to_string(),
380            DataType::Double {
381                precision: None,
382                scale: None,
383            },
384        );
385
386        // Null-handling functions return Unknown (infer from args)
387        self.function_return_types
388            .insert("COALESCE".to_string(), DataType::Unknown);
389        self.function_return_types
390            .insert("NULLIF".to_string(), DataType::Unknown);
391        self.function_return_types
392            .insert("GREATEST".to_string(), DataType::Unknown);
393        self.function_return_types
394            .insert("LEAST".to_string(), DataType::Unknown);
395    }
396
397    /// Annotate types for an expression tree
398    pub fn annotate(&mut self, expr: &Expression) -> Option<DataType> {
399        match expr {
400            // Literals
401            Expression::Literal(lit) => self.annotate_literal(lit),
402            Expression::Boolean(_) => Some(DataType::Boolean),
403            Expression::Null(_) => None, // NULL has no type
404
405            // Arithmetic binary operations
406            Expression::Add(op)
407            | Expression::Sub(op)
408            | Expression::Mul(op)
409            | Expression::Mod(op) => self.annotate_arithmetic(op, false),
410            Expression::Div(op) => self.annotate_arithmetic(op, true),
411
412            // Comparison operations - always boolean
413            Expression::Eq(_)
414            | Expression::Neq(_)
415            | Expression::Lt(_)
416            | Expression::Lte(_)
417            | Expression::Gt(_)
418            | Expression::Gte(_)
419            | Expression::Like(_)
420            | Expression::ILike(_) => Some(DataType::Boolean),
421
422            // Logical operations - always boolean
423            Expression::And(_) | Expression::Or(_) | Expression::Not(_) => Some(DataType::Boolean),
424
425            // Predicates - always boolean
426            Expression::Between(_)
427            | Expression::In(_)
428            | Expression::IsNull(_)
429            | Expression::IsTrue(_)
430            | Expression::IsFalse(_)
431            | Expression::Is(_)
432            | Expression::Exists(_) => Some(DataType::Boolean),
433
434            // String concatenation
435            Expression::Concat(_) => Some(DataType::VarChar {
436                length: None,
437                parenthesized_length: false,
438            }),
439
440            // Bitwise operations - integer
441            Expression::BitwiseAnd(_)
442            | Expression::BitwiseOr(_)
443            | Expression::BitwiseXor(_)
444            | Expression::BitwiseNot(_) => Some(DataType::BigInt { length: None }),
445
446            // Negation preserves type
447            Expression::Neg(op) => self.annotate(&op.this),
448
449            // Transparent wrappers preserve the type of their inner expression.
450            Expression::Paren(paren) => self.annotate(&paren.this),
451            Expression::Annotated(annotated) => self.annotate(&annotated.this),
452
453            // Functions
454            Expression::Function(func) => self.annotate_function(func),
455            Expression::IfFunc(if_func) => self.annotate_if_func(if_func),
456            Expression::Nvl2(nvl2) => self.annotate_nvl2(nvl2),
457            Expression::Coalesce(coalesce) => self.coerce_arg_types(&coalesce.expressions),
458
459            // Typed aggregate functions
460            Expression::Count(_) => Some(DataType::BigInt { length: None }),
461            Expression::Sum(agg) => self.annotate_sum(&agg.this),
462            Expression::SumIf(f) => self.annotate_sum(&f.this),
463            Expression::Avg(_) => Some(DataType::Double {
464                precision: None,
465                scale: None,
466            }),
467            Expression::Min(agg) => self.annotate(&agg.this),
468            Expression::Max(agg) => self.annotate(&agg.this),
469            Expression::Median(agg) => {
470                if self._dialect == Some(DialectType::DuckDB) {
471                    self.annotate_duckdb_median(&agg.this)
472                } else {
473                    None
474                }
475            }
476            Expression::GroupConcat(_) | Expression::StringAgg(_) | Expression::ListAgg(_) => {
477                Some(DataType::VarChar {
478                    length: None,
479                    parenthesized_length: false,
480                })
481            }
482
483            // Generic aggregate function
484            Expression::AggregateFunction(agg) => {
485                if !self.annotate_aggregates {
486                    return None;
487                }
488                let func_name = agg.name.to_uppercase();
489                self.get_aggregate_return_type(&func_name, &agg.args)
490            }
491
492            // Column references - look up type from schema if available
493            Expression::Column(col) => {
494                if let Some(schema) = &self._schema {
495                    let table_name = col.table.as_ref().map(|t| t.name.as_str()).unwrap_or("");
496                    schema.get_column_type(table_name, &col.name.name).ok()
497                } else {
498                    None
499                }
500            }
501
502            // Cast expressions
503            Expression::Cast(cast) => Some(cast.to.clone()),
504            Expression::SafeCast(cast) => Some(cast.to.clone()),
505            Expression::TryCast(cast) => Some(cast.to.clone()),
506
507            // Subqueries - type is the type of the first SELECT expression
508            Expression::Subquery(subq) => {
509                if let Expression::Select(select) = &subq.this {
510                    if let Some(first) = select.expressions.first() {
511                        self.annotate(first)
512                    } else {
513                        None
514                    }
515                } else {
516                    None
517                }
518            }
519
520            // CASE expression - common type of all result branches
521            Expression::Case(case) => self.coerce_expression_types(
522                case.whens
523                    .iter()
524                    .map(|(_, result)| result)
525                    .chain(case.else_.iter()),
526            ),
527
528            // Array expressions. Bracket constructors such as ARRAY[...] and LIST[...]
529            // parse as ArrayFunc, while some normalized expressions use Array.
530            Expression::Array(arr) => self.annotate_array(&arr.expressions),
531            Expression::ArrayFunc(arr) => self.annotate_array(&arr.expressions),
532
533            // Interval expressions
534            Expression::Interval(_) => Some(DataType::Interval {
535                unit: None,
536                to: None,
537            }),
538
539            // Window functions inherit type from their function
540            Expression::WindowFunction(window) => self.annotate(&window.this),
541
542            // Date/time expressions
543            Expression::CurrentDate(_) => Some(DataType::Date),
544            Expression::CurrentTime(_) => Some(DataType::Time {
545                precision: None,
546                timezone: false,
547            }),
548            Expression::CurrentTimestamp(_) | Expression::CurrentTimestampLTZ(_) => {
549                Some(DataType::Timestamp {
550                    precision: None,
551                    timezone: false,
552                })
553            }
554
555            // Date functions
556            Expression::DateAdd(_)
557            | Expression::DateSub(_)
558            | Expression::ToDate(_)
559            | Expression::Date(_) => Some(DataType::Date),
560            Expression::DateDiff(_) => Some(DataType::Int {
561                length: None,
562                integer_spelling: false,
563            }),
564            Expression::Extract(extract) if matches!(self._dialect, Some(DialectType::DuckDB)) => {
565                // DuckDB returns fractional epoch seconds and Julian days as DOUBLE.
566                let fractional = match &extract.field {
567                    DateTimeField::Epoch => true,
568                    DateTimeField::Custom(name) => {
569                        name.eq_ignore_ascii_case("epoch") || name.eq_ignore_ascii_case("julian")
570                    }
571                    _ => false,
572                };
573                Some(if fractional {
574                    DataType::Double {
575                        precision: None,
576                        scale: None,
577                    }
578                } else {
579                    DataType::BigInt { length: None }
580                })
581            }
582            Expression::Extract(_) => Some(DataType::Int {
583                length: None,
584                integer_spelling: false,
585            }),
586            Expression::ToTimestamp(_) => Some(DataType::Timestamp {
587                precision: None,
588                timezone: false,
589            }),
590
591            // String functions
592            Expression::Upper(_)
593            | Expression::Lower(_)
594            | Expression::Trim(_)
595            | Expression::LTrim(_)
596            | Expression::RTrim(_)
597            | Expression::Replace(_)
598            | Expression::Substring(_)
599            | Expression::Reverse(_)
600            | Expression::Left(_)
601            | Expression::Right(_)
602            | Expression::Repeat(_)
603            | Expression::Lpad(_)
604            | Expression::Rpad(_)
605            | Expression::ConcatWs(_)
606            | Expression::Overlay(_) => Some(DataType::VarChar {
607                length: None,
608                parenthesized_length: false,
609            }),
610            Expression::Length(_) => Some(DataType::Int {
611                length: None,
612                integer_spelling: false,
613            }),
614
615            // Math functions
616            Expression::Abs(_)
617            | Expression::Sqrt(_)
618            | Expression::Cbrt(_)
619            | Expression::Ln(_)
620            | Expression::Exp(_)
621            | Expression::Power(_)
622            | Expression::Log(_) => Some(DataType::Double {
623                precision: None,
624                scale: None,
625            }),
626            Expression::Round(_) => Some(DataType::Double {
627                precision: None,
628                scale: None,
629            }),
630            Expression::Floor(f) => self.annotate_math_function(&f.this),
631            Expression::Ceil(f) => self.annotate_math_function(&f.this),
632            Expression::Sign(s) => self.annotate(&s.this),
633            Expression::DateFormat(_) | Expression::FormatDate(_) | Expression::TimeToStr(_) => {
634                Some(DataType::VarChar {
635                    length: None,
636                    parenthesized_length: false,
637                })
638            }
639
640            // Greatest/Least - coerce argument types
641            Expression::Greatest(v) | Expression::Least(v) => self.coerce_arg_types(&v.expressions),
642
643            // Alias - type of the inner expression
644            Expression::Alias(alias) => self.annotate(&alias.this),
645
646            // SELECT expressions - no scalar type
647            Expression::Select(_) => None,
648
649            // ============================================
650            // 3.1.8: Array/Map Indexing (Subscript/Bracket)
651            // ============================================
652            Expression::Subscript(sub) => self.annotate_subscript(sub),
653
654            // Dot access (struct.field) - resolve the field from the base STRUCT type
655            Expression::Dot(dot) => self.annotate_dot(dot),
656
657            // ============================================
658            // 3.1.9: STRUCT Construction
659            // ============================================
660            Expression::Struct(s) => self.annotate_struct(s),
661
662            // ============================================
663            // 3.1.10: MAP Construction
664            // ============================================
665            Expression::Map(map) => self.annotate_map(map),
666            Expression::MapFromEntries(mfe) => {
667                // MAP_FROM_ENTRIES(array_of_pairs) - infer from array element type
668                if let Some(DataType::Array { element_type, .. }) = self.annotate(&mfe.this) {
669                    if let DataType::Struct { fields, .. } = *element_type {
670                        if fields.len() >= 2 {
671                            return Some(DataType::Map {
672                                key_type: Box::new(fields[0].data_type.clone()),
673                                value_type: Box::new(fields[1].data_type.clone()),
674                            });
675                        }
676                    }
677                }
678                Some(DataType::Map {
679                    key_type: Box::new(DataType::Unknown),
680                    value_type: Box::new(DataType::Unknown),
681                })
682            }
683
684            // ============================================
685            // 3.1.11: SetOperation Type Coercion
686            // ============================================
687            Expression::Union(union) => self.annotate_set_operation(&union.left, &union.right),
688            Expression::Intersect(intersect) => {
689                self.annotate_set_operation(&intersect.left, &intersect.right)
690            }
691            Expression::Except(except) => self.annotate_set_operation(&except.left, &except.right),
692
693            // ============================================
694            // 3.1.12: UDTF Type Handling
695            // ============================================
696            Expression::Lateral(lateral) => {
697                // LATERAL subquery - type is the subquery's type
698                self.annotate(&lateral.this)
699            }
700            Expression::LateralView(lv) => {
701                // LATERAL VIEW - returns the exploded type
702                self.annotate_lateral_view(lv)
703            }
704            Expression::Unnest(unnest) => {
705                // UNNEST(array) - returns the element type of the array
706                if let Some(DataType::Array { element_type, .. }) = self.annotate(&unnest.this) {
707                    Some(*element_type)
708                } else {
709                    None
710                }
711            }
712            Expression::Explode(explode) => {
713                // EXPLODE(array) - returns the element type
714                if let Some(DataType::Array { element_type, .. }) = self.annotate(&explode.this) {
715                    Some(*element_type)
716                } else if let Some(DataType::Map {
717                    key_type,
718                    value_type,
719                }) = self.annotate(&explode.this)
720                {
721                    // EXPLODE(map) returns struct(key, value)
722                    Some(DataType::Struct {
723                        fields: vec![
724                            StructField::new("key".to_string(), *key_type),
725                            StructField::new("value".to_string(), *value_type),
726                        ],
727                        nested: false,
728                    })
729                } else {
730                    None
731                }
732            }
733            Expression::ExplodeOuter(explode) => {
734                // EXPLODE_OUTER - same as EXPLODE but preserves nulls
735                if let Some(DataType::Array { element_type, .. }) = self.annotate(&explode.this) {
736                    Some(*element_type)
737                } else {
738                    None
739                }
740            }
741            Expression::GenerateSeries(gs) => {
742                // GENERATE_SERIES returns the type of start/end
743                if let Some(ref start) = gs.start {
744                    self.annotate(start)
745                } else if let Some(ref end) = gs.end {
746                    self.annotate(end)
747                } else {
748                    Some(DataType::Int {
749                        length: None,
750                        integer_spelling: false,
751                    })
752                }
753            }
754
755            // Other expressions - unknown
756            _ => None,
757        }
758    }
759
760    /// Annotate types in-place on the expression tree (bottom-up).
761    ///
762    /// First recurses into children, then computes this node's type using the
763    /// read-only `annotate` method, and finally stores the result via
764    /// `set_inferred_type`.
765    pub fn annotate_in_place(&mut self, expr: &mut Expression) {
766        // 1. Recurse into children (bottom-up)
767        self.annotate_children_in_place(expr);
768
769        // 2. Compute this node's type using the read-only method
770        //    (children already have their types set, but `annotate` re-derives
771        //    from structure, which is fine since the structure hasn't changed)
772        let dt = self.annotate(expr);
773
774        // 3. Store on the node
775        if let Some(data_type) = dt {
776            expr.set_inferred_type(data_type);
777        }
778    }
779
780    /// Recursively annotate children of an expression in-place.
781    fn annotate_children_in_place(&mut self, expr: &mut Expression) {
782        match expr {
783            // Binary operations
784            Expression::And(op)
785            | Expression::Or(op)
786            | Expression::Add(op)
787            | Expression::Sub(op)
788            | Expression::Mul(op)
789            | Expression::Div(op)
790            | Expression::Mod(op)
791            | Expression::Eq(op)
792            | Expression::Neq(op)
793            | Expression::Lt(op)
794            | Expression::Lte(op)
795            | Expression::Gt(op)
796            | Expression::Gte(op)
797            | Expression::Concat(op)
798            | Expression::BitwiseAnd(op)
799            | Expression::BitwiseOr(op)
800            | Expression::BitwiseXor(op)
801            | Expression::Adjacent(op)
802            | Expression::TsMatch(op)
803            | Expression::PropertyEQ(op)
804            | Expression::ArrayContainsAll(op)
805            | Expression::ArrayContainedBy(op)
806            | Expression::ArrayOverlaps(op)
807            | Expression::JSONBContainsAllTopKeys(op)
808            | Expression::JSONBContainsAnyTopKeys(op)
809            | Expression::JSONBDeleteAtPath(op)
810            | Expression::ExtendsLeft(op)
811            | Expression::ExtendsRight(op)
812            | Expression::Is(op)
813            | Expression::MemberOf(op)
814            | Expression::Match(op)
815            | Expression::NullSafeEq(op)
816            | Expression::NullSafeNeq(op)
817            | Expression::Glob(op)
818            | Expression::BitwiseLeftShift(op)
819            | Expression::BitwiseRightShift(op) => {
820                self.annotate_in_place(&mut op.left);
821                self.annotate_in_place(&mut op.right);
822            }
823
824            // Like operations
825            Expression::Like(op) | Expression::ILike(op) => {
826                self.annotate_in_place(&mut op.left);
827                self.annotate_in_place(&mut op.right);
828            }
829
830            // Unary operations
831            Expression::Not(op) | Expression::Neg(op) | Expression::BitwiseNot(op) => {
832                self.annotate_in_place(&mut op.this);
833            }
834
835            // Cast
836            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
837                self.annotate_in_place(&mut c.this);
838            }
839
840            // Transparent wrappers
841            Expression::Paren(paren) => {
842                self.annotate_in_place(&mut paren.this);
843            }
844            Expression::Annotated(annotated) => {
845                self.annotate_in_place(&mut annotated.this);
846            }
847
848            // Case
849            Expression::Case(c) => {
850                if let Some(ref mut operand) = c.operand {
851                    self.annotate_in_place(operand);
852                }
853                for (cond, then_expr) in &mut c.whens {
854                    self.annotate_in_place(cond);
855                    self.annotate_in_place(then_expr);
856                }
857                if let Some(ref mut else_expr) = c.else_ {
858                    self.annotate_in_place(else_expr);
859                }
860            }
861
862            // Array constructors
863            Expression::Array(a) => {
864                for expression in &mut a.expressions {
865                    self.annotate_in_place(expression);
866                }
867            }
868            Expression::ArrayFunc(a) => {
869                for expression in &mut a.expressions {
870                    self.annotate_in_place(expression);
871                }
872            }
873
874            // Alias
875            Expression::Alias(a) => {
876                self.annotate_in_place(&mut a.this);
877            }
878
879            // Column - leaf node, no children to recurse
880            Expression::Column(_) => {}
881
882            // Dot access - the field is an identifier, so only the base is typed
883            Expression::Dot(dot) => {
884                self.annotate_in_place(&mut dot.this);
885            }
886
887            // Function
888            Expression::Function(f) => {
889                for arg in &mut f.args {
890                    self.annotate_in_place(arg);
891                }
892            }
893            Expression::Unnest(unnest) => {
894                self.annotate_in_place(&mut unnest.this);
895                for expression in &mut unnest.expressions {
896                    self.annotate_in_place(expression);
897                }
898            }
899
900            // Dedicated conditional functions
901            Expression::IfFunc(f) => {
902                self.annotate_in_place(&mut f.condition);
903                self.annotate_in_place(&mut f.true_value);
904                if let Some(false_value) = &mut f.false_value {
905                    self.annotate_in_place(false_value);
906                }
907            }
908            Expression::Nvl2(f) => {
909                self.annotate_in_place(&mut f.this);
910                self.annotate_in_place(&mut f.true_value);
911                self.annotate_in_place(&mut f.false_value);
912            }
913
914            // AggregateFunction
915            Expression::AggregateFunction(f) => {
916                for arg in &mut f.args {
917                    self.annotate_in_place(arg);
918                }
919            }
920
921            // Dedicated aggregate / string functions
922            Expression::Count(f) => {
923                if let Some(this) = &mut f.this {
924                    self.annotate_in_place(this);
925                }
926                if let Some(filter) = &mut f.filter {
927                    self.annotate_in_place(filter);
928                }
929            }
930            Expression::GroupConcat(f) => {
931                self.annotate_in_place(&mut f.this);
932                if let Some(separator) = &mut f.separator {
933                    self.annotate_in_place(separator);
934                }
935                if let Some(order_by) = &mut f.order_by {
936                    for ordered in order_by {
937                        self.annotate_in_place(&mut ordered.this);
938                    }
939                }
940                if let Some(filter) = &mut f.filter {
941                    self.annotate_in_place(filter);
942                }
943            }
944            Expression::StringAgg(f) => {
945                self.annotate_in_place(&mut f.this);
946                if let Some(separator) = &mut f.separator {
947                    self.annotate_in_place(separator);
948                }
949                if let Some(order_by) = &mut f.order_by {
950                    for ordered in order_by {
951                        self.annotate_in_place(&mut ordered.this);
952                    }
953                }
954                if let Some(filter) = &mut f.filter {
955                    self.annotate_in_place(filter);
956                }
957                if let Some(limit) = &mut f.limit {
958                    self.annotate_in_place(limit);
959                }
960            }
961            Expression::ListAgg(f) => {
962                self.annotate_in_place(&mut f.this);
963                if let Some(separator) = &mut f.separator {
964                    self.annotate_in_place(separator);
965                }
966                if let Some(order_by) = &mut f.order_by {
967                    for ordered in order_by {
968                        self.annotate_in_place(&mut ordered.this);
969                    }
970                }
971                if let Some(filter) = &mut f.filter {
972                    self.annotate_in_place(filter);
973                }
974                if let Some(ListAggOverflow::Truncate {
975                    filler: Some(filler),
976                    ..
977                }) = &mut f.on_overflow
978                {
979                    self.annotate_in_place(filler);
980                }
981            }
982            Expression::SumIf(f) => {
983                self.annotate_in_place(&mut f.this);
984                self.annotate_in_place(&mut f.condition);
985                if let Some(filter) = &mut f.filter {
986                    self.annotate_in_place(filter);
987                }
988            }
989
990            // WindowFunction
991            Expression::WindowFunction(w) => {
992                self.annotate_in_place(&mut w.this);
993            }
994
995            // Subquery
996            Expression::Subquery(s) => {
997                self.annotate_in_place(&mut s.this);
998            }
999
1000            Expression::Trim(f) => {
1001                self.annotate_in_place(&mut f.this);
1002                if let Some(characters) = &mut f.characters {
1003                    self.annotate_in_place(characters);
1004                }
1005            }
1006
1007            // UnaryFunc variants
1008            Expression::Upper(f)
1009            | Expression::Lower(f)
1010            | Expression::Length(f)
1011            | Expression::LTrim(f)
1012            | Expression::RTrim(f)
1013            | Expression::Reverse(f)
1014            | Expression::Abs(f)
1015            | Expression::Sqrt(f)
1016            | Expression::Cbrt(f)
1017            | Expression::Ln(f)
1018            | Expression::Exp(f)
1019            | Expression::Sign(f)
1020            | Expression::Date(f)
1021            | Expression::Time(f)
1022            | Expression::Explode(f)
1023            | Expression::ExplodeOuter(f)
1024            | Expression::MapFromEntries(f)
1025            | Expression::MapKeys(f)
1026            | Expression::MapValues(f)
1027            | Expression::ArrayLength(f)
1028            | Expression::ArraySize(f)
1029            | Expression::Cardinality(f)
1030            | Expression::ArrayReverse(f)
1031            | Expression::ArrayDistinct(f)
1032            | Expression::ArrayFlatten(f)
1033            | Expression::ArrayCompact(f)
1034            | Expression::ToArray(f)
1035            | Expression::JsonArrayLength(f)
1036            | Expression::JsonKeys(f)
1037            | Expression::JsonType(f)
1038            | Expression::ParseJson(f)
1039            | Expression::ToJson(f)
1040            | Expression::Year(f)
1041            | Expression::Month(f)
1042            | Expression::Day(f)
1043            | Expression::Hour(f)
1044            | Expression::Minute(f)
1045            | Expression::Second(f)
1046            | Expression::Initcap(f)
1047            | Expression::Ascii(f)
1048            | Expression::Chr(f)
1049            | Expression::Soundex(f)
1050            | Expression::ByteLength(f)
1051            | Expression::Hex(f)
1052            | Expression::LowerHex(f)
1053            | Expression::Unicode(f)
1054            | Expression::Typeof(f)
1055            | Expression::BitwiseCount(f)
1056            | Expression::Epoch(f)
1057            | Expression::EpochMs(f)
1058            | Expression::Radians(f)
1059            | Expression::Degrees(f)
1060            | Expression::Sin(f)
1061            | Expression::Cos(f)
1062            | Expression::Tan(f)
1063            | Expression::Asin(f)
1064            | Expression::Acos(f)
1065            | Expression::Atan(f)
1066            | Expression::IsNan(f)
1067            | Expression::IsInf(f) => {
1068                self.annotate_in_place(&mut f.this);
1069            }
1070
1071            // BinaryFunc variants
1072            Expression::Power(f)
1073            | Expression::NullIf(f)
1074            | Expression::IfNull(f)
1075            | Expression::Nvl(f)
1076            | Expression::Contains(f)
1077            | Expression::StartsWith(f)
1078            | Expression::EndsWith(f)
1079            | Expression::Levenshtein(f)
1080            | Expression::ModFunc(f)
1081            | Expression::IntDiv(f)
1082            | Expression::Atan2(f)
1083            | Expression::AddMonths(f)
1084            | Expression::MonthsBetween(f)
1085            | Expression::NextDay(f)
1086            | Expression::UnixToTimeStr(f)
1087            | Expression::ArrayContains(f)
1088            | Expression::ArrayPosition(f)
1089            | Expression::ArrayAppend(f)
1090            | Expression::ArrayPrepend(f)
1091            | Expression::ArrayUnion(f)
1092            | Expression::ArrayExcept(f)
1093            | Expression::ArrayRemove(f)
1094            | Expression::StarMap(f)
1095            | Expression::MapFromArrays(f)
1096            | Expression::MapContainsKey(f)
1097            | Expression::ElementAt(f)
1098            | Expression::JsonMergePatch(f) => {
1099                self.annotate_in_place(&mut f.this);
1100                self.annotate_in_place(&mut f.expression);
1101            }
1102
1103            // VarArgFunc variants
1104            Expression::Coalesce(f)
1105            | Expression::Greatest(f)
1106            | Expression::Least(f)
1107            | Expression::ArrayConcat(f)
1108            | Expression::ArrayIntersect(f)
1109            | Expression::ArrayZip(f)
1110            | Expression::MapConcat(f)
1111            | Expression::JsonArray(f) => {
1112                for e in &mut f.expressions {
1113                    self.annotate_in_place(e);
1114                }
1115            }
1116
1117            // AggFunc variants
1118            Expression::Sum(f)
1119            | Expression::Avg(f)
1120            | Expression::Min(f)
1121            | Expression::Max(f)
1122            | Expression::ArrayAgg(f)
1123            | Expression::CountIf(f)
1124            | Expression::Stddev(f)
1125            | Expression::StddevPop(f)
1126            | Expression::StddevSamp(f)
1127            | Expression::Variance(f)
1128            | Expression::VarPop(f)
1129            | Expression::VarSamp(f)
1130            | Expression::Median(f)
1131            | Expression::Mode(f)
1132            | Expression::First(f)
1133            | Expression::Last(f)
1134            | Expression::AnyValue(f)
1135            | Expression::ApproxDistinct(f)
1136            | Expression::ApproxCountDistinct(f)
1137            | Expression::LogicalAnd(f)
1138            | Expression::LogicalOr(f)
1139            | Expression::Skewness(f)
1140            | Expression::ArrayConcatAgg(f)
1141            | Expression::ArrayUniqueAgg(f)
1142            | Expression::BoolXorAgg(f)
1143            | Expression::BitwiseAndAgg(f)
1144            | Expression::BitwiseOrAgg(f)
1145            | Expression::BitwiseXorAgg(f) => {
1146                self.annotate_in_place(&mut f.this);
1147            }
1148
1149            // Select - recurse into expressions
1150            Expression::Select(s) => {
1151                for e in &mut s.expressions {
1152                    self.annotate_in_place(e);
1153                }
1154            }
1155
1156            // Everything else - no children to recurse or not value-producing
1157            _ => {}
1158        }
1159    }
1160
1161    /// Annotate math functions like FLOOR/CEIL that return Double for integer inputs
1162    /// and preserve the input type otherwise (matching sqlglot's _annotate_math_functions).
1163    fn annotate_math_function(&mut self, arg: &Expression) -> Option<DataType> {
1164        let input_type = self.annotate(arg)?;
1165        match input_type {
1166            DataType::TinyInt { .. }
1167            | DataType::SmallInt { .. }
1168            | DataType::Int { .. }
1169            | DataType::BigInt { .. } => Some(DataType::Double {
1170                precision: None,
1171                scale: None,
1172            }),
1173            other => Some(other),
1174        }
1175    }
1176
1177    /// Annotate a subscript/bracket expression (array[index] or map[key])
1178    fn annotate_subscript(&mut self, sub: &Subscript) -> Option<DataType> {
1179        let base_type = self.annotate(&sub.this)?;
1180
1181        match base_type {
1182            DataType::Array { element_type, .. } => Some(*element_type),
1183            DataType::Map { value_type, .. } => Some(*value_type),
1184            DataType::Json | DataType::JsonB => Some(DataType::Json), // JSON indexing returns JSON
1185            DataType::VarChar { .. } | DataType::Text => {
1186                // String indexing returns a character
1187                Some(DataType::VarChar {
1188                    length: Some(1),
1189                    parenthesized_length: false,
1190                })
1191            }
1192            _ => None,
1193        }
1194    }
1195
1196    /// Annotate a named field lookup from the type of its base expression.
1197    fn annotate_dot(&mut self, dot: &DotAccess) -> Option<DataType> {
1198        let base_type = self.annotate(&dot.this)?;
1199        let DataType::Struct { fields, .. } = base_type else {
1200            return None;
1201        };
1202        let field_name = normalize_name(&dot.field.name, self._dialect, false, true);
1203
1204        fields
1205            .into_iter()
1206            .find(|field| normalize_name(&field.name, self._dialect, false, true) == field_name)
1207            .map(|field| field.data_type)
1208    }
1209
1210    /// Annotate a STRUCT literal
1211    fn annotate_struct(&mut self, s: &Struct) -> Option<DataType> {
1212        let fields: Vec<StructField> = s
1213            .fields
1214            .iter()
1215            .map(|(name, expr)| {
1216                let field_type = self.annotate(expr).unwrap_or(DataType::Unknown);
1217                StructField::new(name.clone().unwrap_or_default(), field_type)
1218            })
1219            .collect();
1220        Some(DataType::Struct {
1221            fields,
1222            nested: false,
1223        })
1224    }
1225
1226    /// Annotate a MAP literal
1227    fn annotate_map(&mut self, map: &Map) -> Option<DataType> {
1228        let key_type = if let Some(first_key) = map.keys.first() {
1229            self.annotate(first_key).unwrap_or(DataType::Unknown)
1230        } else {
1231            DataType::Unknown
1232        };
1233
1234        let value_type = if let Some(first_value) = map.values.first() {
1235            self.annotate(first_value).unwrap_or(DataType::Unknown)
1236        } else {
1237            DataType::Unknown
1238        };
1239
1240        Some(DataType::Map {
1241            key_type: Box::new(key_type),
1242            value_type: Box::new(value_type),
1243        })
1244    }
1245
1246    /// Annotate a SetOperation (UNION/INTERSECT/EXCEPT)
1247    /// Returns None since set operations produce relation types, not scalar types
1248    fn annotate_set_operation(
1249        &mut self,
1250        _left: &Expression,
1251        _right: &Expression,
1252    ) -> Option<DataType> {
1253        // Set operations produce relations, not scalar types
1254        // The column types would be coerced between left and right
1255        // For now, return None as this is a relation-level type
1256        None
1257    }
1258
1259    /// Annotate a LATERAL VIEW expression
1260    fn annotate_lateral_view(&mut self, lv: &crate::expressions::LateralView) -> Option<DataType> {
1261        // The type depends on the table-generating function
1262        self.annotate(&lv.this)
1263    }
1264
1265    /// Annotate a literal value
1266    fn annotate_literal(&self, lit: &Literal) -> Option<DataType> {
1267        match lit {
1268            Literal::String(_)
1269            | Literal::NationalString(_)
1270            | Literal::TripleQuotedString(_, _)
1271            | Literal::EscapeString(_)
1272            | Literal::DollarString(_)
1273            | Literal::RawString(_) => Some(DataType::VarChar {
1274                length: None,
1275                parenthesized_length: false,
1276            }),
1277            Literal::Number(n) => {
1278                // Try to determine if it's an integer or float
1279                if n.contains('.') || n.contains('e') || n.contains('E') {
1280                    Some(DataType::Double {
1281                        precision: None,
1282                        scale: None,
1283                    })
1284                } else {
1285                    // Check if it fits in an Int or needs BigInt
1286                    if let Ok(_) = n.parse::<i32>() {
1287                        Some(DataType::Int {
1288                            length: None,
1289                            integer_spelling: false,
1290                        })
1291                    } else {
1292                        Some(DataType::BigInt { length: None })
1293                    }
1294                }
1295            }
1296            Literal::HexString(_) | Literal::BitString(_) | Literal::ByteString(_) => {
1297                Some(DataType::VarBinary { length: None })
1298            }
1299            Literal::HexNumber(_) => Some(DataType::BigInt { length: None }),
1300            Literal::Date(_) => Some(DataType::Date),
1301            Literal::Time(_) => Some(DataType::Time {
1302                precision: None,
1303                timezone: false,
1304            }),
1305            Literal::Timestamp(_) => Some(DataType::Timestamp {
1306                precision: None,
1307                timezone: false,
1308            }),
1309            Literal::Datetime(_) => Some(DataType::Custom {
1310                name: "DATETIME".to_string(),
1311            }),
1312        }
1313    }
1314
1315    /// Annotate an arithmetic binary operation
1316    fn annotate_arithmetic(&mut self, op: &BinaryOp, division: bool) -> Option<DataType> {
1317        let left_type = self.annotate(&op.left);
1318        let right_type = self.annotate(&op.right);
1319
1320        match (left_type, right_type) {
1321            (Some(l), Some(r)) => {
1322                if self._dialect == Some(DialectType::DuckDB) {
1323                    if division && (is_unsigned_integer(&l) || is_unsigned_integer(&r)) {
1324                        return Some(double_type());
1325                    }
1326                    if let Some(result) = duckdb_unsigned_integer_coercion(&l, &r, true) {
1327                        return Some(result);
1328                    }
1329                }
1330                self.coerce_types(&l, &r)
1331            }
1332            (Some(t), None) | (None, Some(t)) => Some(t),
1333            (None, None) => None,
1334        }
1335    }
1336
1337    /// Annotate a function call
1338    fn annotate_function(&mut self, func: &Function) -> Option<DataType> {
1339        let func_name = func.name.to_uppercase();
1340
1341        if self._dialect == Some(DialectType::DuckDB) && !func.quoted {
1342            match func_name.as_str() {
1343                // Builder-created Function nodes use the same inference as the
1344                // dedicated nodes produced when parsing these built-ins.
1345                "SUM" => return func.args.first().and_then(|arg| self.annotate_sum(arg)),
1346                "COALESCE" | "IFNULL" => return self.coerce_arg_types(&func.args),
1347                "DATE_TRUNC" => return self.annotate_duckdb_date_trunc(func),
1348                "REGEXP_EXTRACT_ALL" => return self.annotate_duckdb_regexp_extract_all(func),
1349                _ => {}
1350            }
1351        }
1352
1353        // Check known function return types
1354        if let Some(return_type) = self.function_return_types.get(&func_name) {
1355            if *return_type != DataType::Unknown {
1356                return Some(return_type.clone());
1357            }
1358        }
1359
1360        // For functions with Unknown return type, infer from arguments
1361        match func_name.as_str() {
1362            "UNNEST" => func.args.first().and_then(|arg| match self.annotate(arg) {
1363                Some(DataType::Array { element_type, .. }) => Some(*element_type),
1364                _ => None,
1365            }),
1366            "COALESCE" | "IFNULL" | "NVL" | "ISNULL" => {
1367                // Return type of first non-null argument
1368                for arg in &func.args {
1369                    if let Some(arg_type) = self.annotate(arg) {
1370                        return Some(arg_type);
1371                    }
1372                }
1373                None
1374            }
1375            "NULLIF" => {
1376                // Return type of first argument
1377                func.args.first().and_then(|arg| self.annotate(arg))
1378            }
1379            "GREATEST" | "LEAST" => {
1380                // Coerce all argument types
1381                self.coerce_arg_types(&func.args)
1382            }
1383            "IF" | "IIF" => {
1384                // Return type of THEN/ELSE branches
1385                if func.args.len() >= 2 {
1386                    self.annotate(&func.args[1])
1387                } else {
1388                    None
1389                }
1390            }
1391            _ => {
1392                // Unknown function - try to infer from first argument
1393                func.args.first().and_then(|arg| self.annotate(arg))
1394            }
1395        }
1396    }
1397
1398    /// Infer DuckDB's string-list or named-capture struct-list regex result.
1399    /// The result depends on the group argument, not the input string's type.
1400    fn annotate_duckdb_regexp_extract_all(&mut self, func: &Function) -> Option<DataType> {
1401        if !(2..=4).contains(&func.args.len()) {
1402            return None;
1403        }
1404
1405        fn unwrap(mut expr: &Expression) -> &Expression {
1406            loop {
1407                expr = match expr {
1408                    Expression::Paren(p) => &p.this,
1409                    Expression::Annotated(a) => &a.this,
1410                    _ => return expr,
1411                };
1412            }
1413        }
1414
1415        let varchar = DataType::VarChar {
1416            length: None,
1417            parenthesized_length: false,
1418        };
1419        let element_type = if let Some(group) = func.args.get(2).map(unwrap) {
1420            let names = match group {
1421                Expression::Array(a) => Some(&a.expressions),
1422                Expression::ArrayFunc(a) => Some(&a.expressions),
1423                _ => None,
1424            };
1425            if let Some(names) = names {
1426                if names.is_empty() {
1427                    return None;
1428                }
1429                let mut seen = HashSet::new();
1430                let mut fields = Vec::with_capacity(names.len());
1431                for name in names {
1432                    let Expression::Literal(literal) = unwrap(name) else {
1433                        return None;
1434                    };
1435                    if !literal.is_string() {
1436                        return None;
1437                    }
1438                    let name = literal.value_str();
1439                    // DuckDB rejects duplicate names using ASCII-insensitive
1440                    // comparison, but preserves the original spelling.
1441                    if !seen.insert(name.to_ascii_lowercase()) {
1442                        return None;
1443                    }
1444                    fields.push(StructField::new(name.to_string(), varchar.clone()));
1445                }
1446                DataType::Struct {
1447                    fields,
1448                    nested: false,
1449                }
1450            } else if matches!(group, Expression::Null(_))
1451                || matches!(
1452                    self.annotate(group),
1453                    Some(
1454                        DataType::TinyInt { .. } | DataType::SmallInt { .. } | DataType::Int { .. }
1455                    )
1456                )
1457            {
1458                varchar
1459            } else {
1460                // Unknown group expressions or nonliteral name lists cannot
1461                // determine the element type. Do not fall back to the input.
1462                return None;
1463            }
1464        } else {
1465            varchar
1466        };
1467        Some(DataType::Array {
1468            element_type: Box::new(element_type),
1469            dimension: None,
1470        })
1471    }
1472
1473    /// Infer DuckDB's overloaded DATE_TRUNC return type from its temporal value argument.
1474    fn annotate_duckdb_date_trunc(&mut self, func: &Function) -> Option<DataType> {
1475        if func.args.len() != 2 {
1476            return None;
1477        }
1478
1479        match self.annotate(&func.args[1])? {
1480            DataType::Date => Some(DataType::Timestamp {
1481                precision: None,
1482                timezone: false,
1483            }),
1484            DataType::Timestamp { timezone, .. } => Some(DataType::Timestamp {
1485                precision: None,
1486                timezone,
1487            }),
1488            DataType::Interval { .. } => Some(DataType::Interval {
1489                unit: None,
1490                to: None,
1491            }),
1492            _ => None,
1493        }
1494    }
1495
1496    /// Annotate IF/IIF/IFF conditional function
1497    fn annotate_if_func(&mut self, func: &IfFunc) -> Option<DataType> {
1498        let true_type = self.annotate(&func.true_value);
1499        let false_type = func
1500            .false_value
1501            .as_ref()
1502            .and_then(|expr| self.annotate(expr));
1503
1504        match (true_type, false_type) {
1505            (Some(left), Some(right)) => self.coerce_types(&left, &right),
1506            (Some(dt), None) | (None, Some(dt)) => Some(dt),
1507            (None, None) => None,
1508        }
1509    }
1510
1511    /// Annotate NVL2 conditional function from its true/false branches
1512    fn annotate_nvl2(&mut self, func: &Nvl2Func) -> Option<DataType> {
1513        let true_type = self.annotate(&func.true_value);
1514        let false_type = self.annotate(&func.false_value);
1515
1516        match (true_type, false_type) {
1517            (Some(left), Some(right)) => self.coerce_types(&left, &right),
1518            (Some(dt), None) | (None, Some(dt)) => Some(dt),
1519            (None, None) => None,
1520        }
1521    }
1522
1523    /// Get return type for aggregate functions
1524    fn get_aggregate_return_type(
1525        &mut self,
1526        func_name: &str,
1527        args: &[Expression],
1528    ) -> Option<DataType> {
1529        match func_name {
1530            "COUNT" | "COUNT_IF" => Some(DataType::BigInt { length: None }),
1531            "SUM_IF" => {
1532                if let Some(arg) = args.first() {
1533                    self.annotate_sum(arg)
1534                } else {
1535                    Some(DataType::Decimal {
1536                        precision: None,
1537                        scale: None,
1538                    })
1539                }
1540            }
1541            "SUM" => {
1542                if let Some(arg) = args.first() {
1543                    self.annotate_sum(arg)
1544                } else {
1545                    Some(DataType::Decimal {
1546                        precision: None,
1547                        scale: None,
1548                    })
1549                }
1550            }
1551            "AVG" => Some(DataType::Double {
1552                precision: None,
1553                scale: None,
1554            }),
1555            "MIN" | "MAX" => {
1556                // DuckDB's two-argument MIN/MAX aggregate overloads return the
1557                // bottom/top N values as a list. Other parsed aggregate forms
1558                // preserve the input type.
1559                let input_type = args.first().and_then(|arg| self.annotate(arg));
1560                if args.len() == 2 {
1561                    input_type.map(|element_type| DataType::Array {
1562                        element_type: Box::new(element_type),
1563                        dimension: None,
1564                    })
1565                } else {
1566                    input_type
1567                }
1568            }
1569            "STRING_AGG" | "GROUP_CONCAT" | "LISTAGG" | "ARRAY_AGG" => Some(DataType::VarChar {
1570                length: None,
1571                parenthesized_length: false,
1572            }),
1573            "BOOL_AND" | "BOOL_OR" | "EVERY" | "ANY" | "SOME" => Some(DataType::Boolean),
1574            "BIT_AND" | "BIT_OR" | "BIT_XOR" => Some(DataType::BigInt { length: None }),
1575            "STDDEV" | "STDDEV_POP" | "STDDEV_SAMP" | "VARIANCE" | "VAR_POP" | "VAR_SAMP" => {
1576                Some(DataType::Double {
1577                    precision: None,
1578                    scale: None,
1579                })
1580            }
1581            "PERCENTILE_CONT" | "PERCENTILE_DISC" => {
1582                args.first().and_then(|arg| self.annotate(arg))
1583            }
1584            "MEDIAN" => args.first().and_then(|arg| {
1585                if self._dialect == Some(DialectType::DuckDB) {
1586                    self.annotate_duckdb_median(arg)
1587                } else {
1588                    self.annotate(arg)
1589                }
1590            }),
1591            _ => None,
1592        }
1593    }
1594
1595    /// DuckDB defines MEDIAN as QUANTILE_CONT(value, 0.5). Integral inputs are
1596    /// promoted to DOUBLE so an interpolated midpoint can be represented, and
1597    /// DATE inputs become TIMESTAMP. Other supported quantitative and ordinal
1598    /// types preserve their input type.
1599    fn annotate_duckdb_median(&mut self, arg: &Expression) -> Option<DataType> {
1600        match self.annotate(arg)? {
1601            DataType::TinyInt { .. }
1602            | DataType::SmallInt { .. }
1603            | DataType::Int { .. }
1604            | DataType::BigInt { .. }
1605            | DataType::Int128 => Some(DataType::Double {
1606                precision: None,
1607                scale: None,
1608            }),
1609            DataType::UInt8
1610            | DataType::UInt16
1611            | DataType::UInt32
1612            | DataType::UInt64
1613            | DataType::UInt128 => Some(double_type()),
1614            DataType::Date => Some(DataType::Timestamp {
1615                precision: None,
1616                timezone: false,
1617            }),
1618            DataType::Unknown => None,
1619            other => Some(other),
1620        }
1621    }
1622
1623    /// Annotate SUM function - promotes to at least BigInt
1624    fn annotate_sum(&mut self, arg: &Expression) -> Option<DataType> {
1625        let arg_type = self.annotate(arg);
1626        if self._dialect == Some(DialectType::DuckDB) {
1627            // These are DuckDB's bound aggregate result types, not the generic
1628            // integer-to-BIGINT promotion. UHUGEINT binds to the DOUBLE overload.
1629            return match arg_type? {
1630                DataType::Boolean
1631                | DataType::TinyInt { .. }
1632                | DataType::SmallInt { .. }
1633                | DataType::Int { .. }
1634                | DataType::BigInt { .. }
1635                | DataType::Int128
1636                | DataType::UInt8
1637                | DataType::UInt16
1638                | DataType::UInt32
1639                | DataType::UInt64 => Some(DataType::Int128),
1640                DataType::UInt128 | DataType::Float { .. } | DataType::Double { .. } => {
1641                    Some(double_type())
1642                }
1643                DataType::Decimal { precision, scale } => Some(DataType::Decimal {
1644                    precision: Some(38),
1645                    // DuckDB DECIMAL defaults to (18,3); DECIMAL(p) defaults to (p,0).
1646                    scale: Some(scale.unwrap_or(if precision.is_some() { 0 } else { 3 })),
1647                }),
1648                // Do not invent a concrete return type for an unresolved input.
1649                _ => None,
1650            };
1651        }
1652        match arg_type {
1653            Some(DataType::TinyInt { .. })
1654            | Some(DataType::SmallInt { .. })
1655            | Some(DataType::Int { .. }) => Some(DataType::BigInt { length: None }),
1656            Some(DataType::BigInt { .. }) => Some(DataType::BigInt { length: None }),
1657            Some(DataType::Int128) => Some(DataType::Int128),
1658            Some(DataType::Float { .. }) | Some(DataType::Double { .. }) => {
1659                Some(DataType::Double {
1660                    precision: None,
1661                    scale: None,
1662                })
1663            }
1664            Some(DataType::Decimal { precision, scale }) => {
1665                Some(DataType::Decimal { precision, scale })
1666            }
1667            _ => Some(DataType::Decimal {
1668                precision: None,
1669                scale: None,
1670            }),
1671        }
1672    }
1673
1674    /// Infer the type of an array constructor from all of its elements.
1675    fn annotate_array(&mut self, expressions: &[Expression]) -> Option<DataType> {
1676        let element_type = self
1677            .coerce_expression_types(expressions.iter())
1678            .unwrap_or(DataType::Unknown);
1679        Some(DataType::Array {
1680            element_type: Box::new(element_type),
1681            dimension: None,
1682        })
1683    }
1684
1685    /// Coerce multiple argument types to a common type
1686    fn coerce_arg_types(&mut self, args: &[Expression]) -> Option<DataType> {
1687        self.coerce_expression_types(args.iter())
1688    }
1689
1690    /// Coerce expression types from an iterator to a common type.
1691    fn coerce_expression_types<'b>(
1692        &mut self,
1693        args: impl IntoIterator<Item = &'b Expression>,
1694    ) -> Option<DataType> {
1695        let mut result_type: Option<DataType> = None;
1696        for arg in args {
1697            if let Some(arg_type) = self.annotate(arg) {
1698                result_type = match result_type {
1699                    Some(t) => self.coerce_types(&t, &arg_type),
1700                    None => Some(arg_type),
1701                };
1702            }
1703        }
1704        result_type
1705    }
1706
1707    /// Coerce two types to a common type
1708    fn coerce_types(&self, left: &DataType, right: &DataType) -> Option<DataType> {
1709        // If types are the same, return that type
1710        if left == right {
1711            return Some(left.clone());
1712        }
1713
1714        // Arrays coerce recursively based on their element types.
1715        if let (
1716            DataType::Array {
1717                element_type: left_element,
1718                dimension: left_dimension,
1719            },
1720            DataType::Array {
1721                element_type: right_element,
1722                dimension: right_dimension,
1723            },
1724        ) = (left, right)
1725        {
1726            return self
1727                .coerce_types(left_element, right_element)
1728                .map(|element_type| DataType::Array {
1729                    element_type: Box::new(element_type),
1730                    dimension: if left_dimension == right_dimension {
1731                        *left_dimension
1732                    } else {
1733                        None
1734                    },
1735                });
1736        }
1737
1738        // Special case: Interval + Date/Timestamp
1739        match (left, right) {
1740            (DataType::Date, DataType::Interval { .. })
1741            | (DataType::Interval { .. }, DataType::Date) => return Some(DataType::Date),
1742            (
1743                DataType::Timestamp {
1744                    precision,
1745                    timezone,
1746                },
1747                DataType::Interval { .. },
1748            )
1749            | (
1750                DataType::Interval { .. },
1751                DataType::Timestamp {
1752                    precision,
1753                    timezone,
1754                },
1755            ) => {
1756                return Some(DataType::Timestamp {
1757                    precision: *precision,
1758                    timezone: *timezone,
1759                });
1760            }
1761            _ => {}
1762        }
1763
1764        // Coerce based on class
1765        let left_class = TypeCoercionClass::from_data_type(left);
1766        let right_class = TypeCoercionClass::from_data_type(right);
1767
1768        match (left_class, right_class) {
1769            // Same class: use higher-precision type within class
1770            (Some(lc), Some(rc)) if lc == rc => {
1771                // For numeric, choose wider type
1772                if lc == TypeCoercionClass::Numeric {
1773                    Some(self.wider_numeric_type(left, right))
1774                } else {
1775                    // For text and timelike, left wins by default
1776                    Some(left.clone())
1777                }
1778            }
1779            // Different classes: higher-priority class wins
1780            (Some(lc), Some(rc)) => {
1781                if lc > rc {
1782                    Some(left.clone())
1783                } else {
1784                    Some(right.clone())
1785                }
1786            }
1787            // One unknown: use the known type
1788            (Some(_), None) => Some(left.clone()),
1789            (None, Some(_)) => Some(right.clone()),
1790            // Both unknown: return unknown
1791            (None, None) => Some(DataType::Unknown),
1792        }
1793    }
1794
1795    /// Get the wider numeric type
1796    fn wider_numeric_type(&self, left: &DataType, right: &DataType) -> DataType {
1797        if self._dialect == Some(DialectType::DuckDB) {
1798            if let Some(result) = duckdb_unsigned_integer_coercion(left, right, false) {
1799                return result;
1800            }
1801        }
1802        let order = |dt: &DataType| -> u8 {
1803            match dt {
1804                DataType::Boolean => 0,
1805                DataType::TinyInt { .. } | DataType::UInt8 => 1,
1806                DataType::SmallInt { .. } | DataType::UInt16 => 2,
1807                DataType::Int { .. } | DataType::UInt32 => 3,
1808                DataType::BigInt { .. } | DataType::UInt64 => 4,
1809                DataType::Int128 | DataType::UInt128 => 5,
1810                DataType::Float { .. } => 6,
1811                DataType::Double { .. } => 7,
1812                DataType::Decimal { .. } => 8,
1813                _ => 0,
1814            }
1815        };
1816
1817        if order(left) >= order(right) {
1818            left.clone()
1819        } else {
1820            right.clone()
1821        }
1822    }
1823}
1824
1825fn double_type() -> DataType {
1826    DataType::Double {
1827        precision: None,
1828        scale: None,
1829    }
1830}
1831
1832fn is_unsigned_integer(data_type: &DataType) -> bool {
1833    matches!(
1834        data_type,
1835        DataType::UInt8
1836            | DataType::UInt16
1837            | DataType::UInt32
1838            | DataType::UInt64
1839            | DataType::UInt128
1840    )
1841}
1842
1843/// Integer width and signedness, used only for DuckDB's unsigned coercion rules.
1844fn integer_shape(data_type: &DataType) -> Option<(u16, bool)> {
1845    Some(match data_type {
1846        DataType::Boolean => (0, false),
1847        DataType::TinyInt { .. } => (8, false),
1848        DataType::SmallInt { .. } => (16, false),
1849        DataType::Int { .. } => (32, false),
1850        DataType::BigInt { .. } => (64, false),
1851        DataType::Int128 => (128, false),
1852        DataType::UInt8 => (8, true),
1853        DataType::UInt16 => (16, true),
1854        DataType::UInt32 => (32, true),
1855        DataType::UInt64 => (64, true),
1856        DataType::UInt128 => (128, true),
1857        _ => return None,
1858    })
1859}
1860
1861fn signed_integer_type(bits: u16) -> DataType {
1862    match bits {
1863        0..=8 => DataType::TinyInt { length: None },
1864        9..=16 => DataType::SmallInt { length: None },
1865        17..=32 => DataType::Int {
1866            length: None,
1867            integer_spelling: false,
1868        },
1869        33..=64 => DataType::BigInt { length: None },
1870        65..=128 => DataType::Int128,
1871        _ => double_type(),
1872    }
1873}
1874
1875/// DuckDB uses function-overload binding for arithmetic, but combination casting
1876/// for CASE/COALESCE/arrays. In particular, small mixed arithmetic binds BIGINT.
1877fn duckdb_unsigned_integer_coercion(
1878    left: &DataType,
1879    right: &DataType,
1880    arithmetic: bool,
1881) -> Option<DataType> {
1882    let (left_bits, left_unsigned) = integer_shape(left)?;
1883    let (right_bits, right_unsigned) = integer_shape(right)?;
1884    if !left_unsigned && !right_unsigned {
1885        return None;
1886    }
1887    if left_unsigned && right_unsigned {
1888        return Some(if left_bits >= right_bits { left } else { right }.clone());
1889    }
1890    let (signed, unsigned, signed_type, unsigned_type) = if left_unsigned {
1891        (right_bits, left_bits, right, left)
1892    } else {
1893        (left_bits, right_bits, left, right)
1894    };
1895    if signed == 0 && !arithmetic {
1896        return Some(unsigned_type.clone());
1897    }
1898    if signed > unsigned {
1899        return Some(signed_type.clone());
1900    }
1901    if unsigned == 128 {
1902        // DuckDB 1.5's combination caster advances the signed input one width;
1903        // arithmetic instead binds DOUBLE. Match the engine's reported type,
1904        // rather than claiming this is a lossless conversion for every value.
1905        return Some(if arithmetic {
1906            double_type()
1907        } else {
1908            signed_integer_type(signed + 1)
1909        });
1910    }
1911    let required_bits = unsigned + 1;
1912    Some(signed_integer_type(if arithmetic {
1913        required_bits.max(64)
1914    } else {
1915        required_bits
1916    }))
1917}
1918
1919/// A schema layer whose entries are visible only while annotating one query
1920/// scope. The parent contains physical tables and any correlated outer scope;
1921/// CTE, derived-table, table-alias, and table-valued-function outputs stay in
1922/// this layer and therefore cannot leak into sibling scopes.
1923struct ScopedSchema<'a> {
1924    parent: Option<&'a dyn Schema>,
1925    tables: HashMap<String, HashMap<String, DataType>>,
1926    dialect: Option<DialectType>,
1927}
1928
1929impl<'a> ScopedSchema<'a> {
1930    fn new(parent: Option<&'a dyn Schema>, dialect: Option<DialectType>) -> Self {
1931        Self {
1932            parent,
1933            tables: HashMap::new(),
1934            dialect,
1935        }
1936    }
1937
1938    fn normalized(&self, name: &str, is_table: bool) -> String {
1939        normalize_name(name, self.dialect, is_table, true)
1940    }
1941
1942    fn local_column_type(&self, table: &str, column: &str) -> Option<DataType> {
1943        let table = self.normalized(table, true);
1944        let column = self.normalized(column, false);
1945        self.tables
1946            .get(&table)
1947            .and_then(|columns| columns.get(&column))
1948            .cloned()
1949    }
1950
1951    fn local_tables_for_column(&self, column: &str) -> Vec<String> {
1952        let column = self.normalized(column, false);
1953        self.tables
1954            .iter()
1955            .filter_map(|(table, columns)| columns.contains_key(&column).then(|| table.clone()))
1956            .collect()
1957    }
1958}
1959
1960impl Schema for ScopedSchema<'_> {
1961    fn dialect(&self) -> Option<DialectType> {
1962        self.dialect
1963            .or_else(|| self.parent.and_then(Schema::dialect))
1964    }
1965
1966    fn add_table(
1967        &mut self,
1968        table: &str,
1969        columns: &[(String, DataType)],
1970        _dialect: Option<DialectType>,
1971    ) -> SchemaResult<()> {
1972        let table = self.normalized(table, true);
1973        let columns = columns
1974            .iter()
1975            .map(|(name, data_type)| (self.normalized(name, false), data_type.clone()))
1976            .collect();
1977        self.tables.insert(table, columns);
1978        Ok(())
1979    }
1980
1981    fn column_names(&self, table: &str) -> SchemaResult<Vec<String>> {
1982        let table = self.normalized(table, true);
1983        if let Some(columns) = self.tables.get(&table) {
1984            return Ok(columns.keys().cloned().collect());
1985        }
1986        self.parent
1987            .ok_or_else(|| SchemaError::TableNotFound(table.clone()))?
1988            .column_names(&table)
1989    }
1990
1991    fn get_column_type(&self, table: &str, column: &str) -> SchemaResult<DataType> {
1992        if table.is_empty() {
1993            let local_tables = self.local_tables_for_column(column);
1994            return match local_tables.as_slice() {
1995                [local_table] => self.get_column_type(local_table, column),
1996                [] => self
1997                    .parent
1998                    .ok_or_else(|| SchemaError::ColumnNotFound {
1999                        table: String::new(),
2000                        column: column.to_string(),
2001                    })?
2002                    .get_column_type(table, column),
2003                _ => Err(SchemaError::AmbiguousTable {
2004                    table: String::new(),
2005                    matches: local_tables.join(", "),
2006                }),
2007            };
2008        }
2009
2010        let normalized_table = self.normalized(table, true);
2011        if self.tables.contains_key(&normalized_table) {
2012            return self.local_column_type(table, column).ok_or_else(|| {
2013                SchemaError::ColumnNotFound {
2014                    table: table.to_string(),
2015                    column: column.to_string(),
2016                }
2017            });
2018        }
2019
2020        self.parent
2021            .ok_or_else(|| SchemaError::ColumnNotFound {
2022                table: table.to_string(),
2023                column: column.to_string(),
2024            })?
2025            .get_column_type(table, column)
2026    }
2027
2028    fn has_column(&self, table: &str, column: &str) -> bool {
2029        self.get_column_type(table, column).is_ok()
2030    }
2031
2032    fn supported_table_args(&self) -> &[&str] {
2033        TABLE_PARTS
2034    }
2035
2036    fn is_empty(&self) -> bool {
2037        self.tables.is_empty() && self.parent.is_none_or(Schema::is_empty)
2038    }
2039
2040    fn depth(&self) -> usize {
2041        self.parent.map_or(1, |schema| schema.depth().max(1))
2042    }
2043
2044    fn find_tables_for_column(&self, column: &str) -> Vec<String> {
2045        let mut tables = self.local_tables_for_column(column);
2046        if let Some(parent) = self.parent {
2047            tables.extend(parent.find_tables_for_column(column));
2048        }
2049        let mut seen = HashSet::new();
2050        tables.retain(|table| seen.insert(table.clone()));
2051        tables
2052    }
2053}
2054
2055type OutputColumns = Vec<(String, DataType)>;
2056
2057fn table_name(table: &crate::expressions::TableRef) -> String {
2058    let mut parts = Vec::new();
2059    if let Some(catalog) = &table.catalog {
2060        parts.push(catalog.name.as_str());
2061    }
2062    if let Some(schema) = &table.schema {
2063        parts.push(schema.name.as_str());
2064    }
2065    parts.push(table.name.name.as_str());
2066    parts.join(".")
2067}
2068
2069fn table_columns(schema: &dyn Schema, table: &str) -> OutputColumns {
2070    schema
2071        .column_names(table)
2072        .unwrap_or_default()
2073        .into_iter()
2074        .map(|column| {
2075            let data_type = schema
2076                .get_column_type(table, &column)
2077                .unwrap_or(DataType::Unknown);
2078            (column, data_type)
2079        })
2080        .collect()
2081}
2082
2083fn apply_column_aliases(
2084    mut columns: OutputColumns,
2085    aliases: &[crate::expressions::Identifier],
2086) -> OutputColumns {
2087    for ((name, _), alias) in columns.iter_mut().zip(aliases) {
2088        *name = alias.name.clone();
2089    }
2090    columns
2091}
2092
2093fn projection_name(expression: &Expression) -> Option<String> {
2094    match expression {
2095        Expression::Alias(alias) => Some(alias.alias.name.clone()),
2096        Expression::Column(column) => Some(column.name.name.clone()),
2097        Expression::Identifier(identifier) => Some(identifier.name.clone()),
2098        _ => None,
2099    }
2100}
2101
2102fn projection_type(expression: &Expression) -> DataType {
2103    expression
2104        .inferred_type()
2105        .or_else(|| match expression {
2106            Expression::Alias(alias) => alias.this.inferred_type(),
2107            _ => None,
2108        })
2109        .cloned()
2110        .unwrap_or(DataType::Unknown)
2111}
2112
2113fn query_outputs(expressions: &[Expression]) -> OutputColumns {
2114    expressions
2115        .iter()
2116        .filter_map(|expression| {
2117            projection_name(expression).map(|name| (name, projection_type(expression)))
2118        })
2119        .collect()
2120}
2121
2122fn array_element_type(data_type: Option<&DataType>) -> DataType {
2123    match data_type {
2124        Some(DataType::Array { element_type, .. }) => (**element_type).clone(),
2125        _ => DataType::Unknown,
2126    }
2127}
2128
2129fn unnest_output_types(unnest: &crate::expressions::UnnestFunc) -> Vec<DataType> {
2130    let mut types = vec![array_element_type(unnest.this.inferred_type())];
2131    types.extend(
2132        unnest
2133            .expressions
2134            .iter()
2135            .map(|expression| array_element_type(expression.inferred_type())),
2136    );
2137    if unnest.with_ordinality || unnest.offset_alias.is_some() {
2138        types.push(DataType::BigInt { length: None });
2139    }
2140    types
2141}
2142
2143fn virtual_output_columns(
2144    expression: &Expression,
2145    source_alias: &str,
2146    column_aliases: &[crate::expressions::Identifier],
2147) -> OutputColumns {
2148    let (types, offset_alias) = match expression {
2149        Expression::Unnest(unnest) => (unnest_output_types(unnest), unnest.offset_alias.as_ref()),
2150        Expression::Explode(explode) | Expression::ExplodeOuter(explode) => {
2151            (vec![array_element_type(explode.this.inferred_type())], None)
2152        }
2153        Expression::Function(function) if function.name.eq_ignore_ascii_case("UNNEST") => (
2154            function
2155                .args
2156                .iter()
2157                .map(|argument| array_element_type(argument.inferred_type()))
2158                .collect(),
2159            None,
2160        ),
2161        _ => return Vec::new(),
2162    };
2163
2164    if column_aliases.is_empty() {
2165        let mut columns = Vec::new();
2166        if let Some(data_type) = types.first() {
2167            columns.push((source_alias.to_string(), data_type.clone()));
2168        }
2169        if let Some(offset_alias) = offset_alias {
2170            columns.push((offset_alias.name.clone(), DataType::BigInt { length: None }));
2171        }
2172        columns
2173    } else {
2174        column_aliases
2175            .iter()
2176            .zip(types)
2177            .map(|(alias, data_type)| (alias.name.clone(), data_type))
2178            .collect()
2179    }
2180}
2181
2182fn annotate_relation_source(
2183    expression: &mut Expression,
2184    schema: &mut ScopedSchema<'_>,
2185    dialect: Option<DialectType>,
2186) {
2187    match expression {
2188        Expression::Table(table) => {
2189            let source_table = table_name(table);
2190            let mut columns = table_columns(schema, &source_table);
2191            columns = apply_column_aliases(columns, &table.column_aliases);
2192            let visible_name = table
2193                .alias
2194                .as_ref()
2195                .map(|alias| alias.name.as_str())
2196                .unwrap_or(table.name.name.as_str());
2197            let _ = schema.add_table(visible_name, &columns, dialect);
2198        }
2199        Expression::Subquery(subquery) => {
2200            let mut columns = annotate_scoped_expression(&mut subquery.this, Some(schema), dialect);
2201            columns = apply_column_aliases(columns, &subquery.column_aliases);
2202            if let Some((_, first_type)) = columns.first() {
2203                subquery.inferred_type = Some(first_type.clone());
2204            }
2205            if let Some(alias) = &subquery.alias {
2206                let _ = schema.add_table(&alias.name, &columns, dialect);
2207            }
2208        }
2209        Expression::Alias(alias) => {
2210            match &mut alias.this {
2211                Expression::Subquery(subquery) => {
2212                    let columns =
2213                        annotate_scoped_expression(&mut subquery.this, Some(schema), dialect);
2214                    let columns = apply_column_aliases(columns, &alias.column_aliases);
2215                    let _ = schema.add_table(&alias.alias.name, &columns, dialect);
2216                    return;
2217                }
2218                _ => {
2219                    let mut annotator = TypeAnnotator::new(Some(schema), dialect);
2220                    annotator.annotate_in_place(&mut alias.this);
2221                }
2222            }
2223            let columns =
2224                virtual_output_columns(&alias.this, &alias.alias.name, &alias.column_aliases);
2225            if !columns.is_empty() {
2226                let _ = schema.add_table(&alias.alias.name, &columns, dialect);
2227            }
2228        }
2229        Expression::Unnest(_) => {
2230            let mut annotator = TypeAnnotator::new(Some(schema), dialect);
2231            annotator.annotate_in_place(expression);
2232            if let Expression::Unnest(unnest) = expression {
2233                if let Some(alias) = &unnest.alias {
2234                    let alias_name = alias.name.clone();
2235                    let columns = unnest_output_types(unnest)
2236                        .into_iter()
2237                        .next()
2238                        .map(|data_type| vec![(alias_name.clone(), data_type)])
2239                        .unwrap_or_default();
2240                    let _ = schema.add_table(&alias_name, &columns, dialect);
2241                }
2242            }
2243        }
2244        Expression::Lateral(lateral) => {
2245            let mut annotator = TypeAnnotator::new(Some(schema), dialect);
2246            annotator.annotate_in_place(&mut lateral.this);
2247            if let Some(alias) = &lateral.alias {
2248                let aliases: Vec<_> = lateral
2249                    .column_aliases
2250                    .iter()
2251                    .map(crate::expressions::Identifier::new)
2252                    .collect();
2253                let columns = virtual_output_columns(&lateral.this, alias, &aliases);
2254                if !columns.is_empty() {
2255                    let _ = schema.add_table(alias, &columns, dialect);
2256                }
2257            }
2258        }
2259        Expression::Paren(paren) => annotate_relation_source(&mut paren.this, schema, dialect),
2260        _ => {
2261            let mut annotator = TypeAnnotator::new(Some(schema), dialect);
2262            annotator.annotate_in_place(expression);
2263        }
2264    }
2265}
2266
2267fn annotate_with(
2268    with: &mut Option<crate::expressions::With>,
2269    schema: &mut ScopedSchema<'_>,
2270    dialect: Option<DialectType>,
2271) {
2272    if let Some(with) = with {
2273        for cte in &mut with.ctes {
2274            let columns = annotate_scoped_expression(&mut cte.this, Some(schema), dialect);
2275            let columns = apply_column_aliases(columns, &cte.columns);
2276            let _ = schema.add_table(&cte.alias.name, &columns, dialect);
2277        }
2278    }
2279}
2280
2281fn annotate_select(
2282    select: &mut crate::expressions::Select,
2283    parent: Option<&dyn Schema>,
2284    dialect: Option<DialectType>,
2285) -> OutputColumns {
2286    let mut schema = ScopedSchema::new(parent, dialect);
2287    annotate_with(&mut select.with, &mut schema, dialect);
2288
2289    if let Some(from) = &mut select.from {
2290        for source in &mut from.expressions {
2291            annotate_relation_source(source, &mut schema, dialect);
2292        }
2293    }
2294    for join in &mut select.joins {
2295        annotate_relation_source(&mut join.this, &mut schema, dialect);
2296    }
2297
2298    let mut annotator = TypeAnnotator::new(Some(&schema), dialect);
2299    for expression in &mut select.expressions {
2300        annotator.annotate_in_place(expression);
2301    }
2302    query_outputs(&select.expressions)
2303}
2304
2305fn annotate_scoped_expression(
2306    expression: &mut Expression,
2307    parent: Option<&dyn Schema>,
2308    dialect: Option<DialectType>,
2309) -> OutputColumns {
2310    match expression {
2311        Expression::Select(select) => annotate_select(select, parent, dialect),
2312        Expression::Subquery(subquery) => {
2313            let columns = annotate_scoped_expression(&mut subquery.this, parent, dialect);
2314            if let Some((_, first_type)) = columns.first() {
2315                subquery.inferred_type = Some(first_type.clone());
2316            }
2317            columns
2318        }
2319        Expression::Cte(cte) => annotate_scoped_expression(&mut cte.this, parent, dialect),
2320        Expression::Paren(paren) => annotate_scoped_expression(&mut paren.this, parent, dialect),
2321        Expression::Union(union) => {
2322            let mut schema = ScopedSchema::new(parent, dialect);
2323            annotate_with(&mut union.with, &mut schema, dialect);
2324            let columns = annotate_scoped_expression(&mut union.left, Some(&schema), dialect);
2325            annotate_scoped_expression(&mut union.right, Some(&schema), dialect);
2326            columns
2327        }
2328        Expression::Intersect(intersect) => {
2329            let mut schema = ScopedSchema::new(parent, dialect);
2330            annotate_with(&mut intersect.with, &mut schema, dialect);
2331            let columns = annotate_scoped_expression(&mut intersect.left, Some(&schema), dialect);
2332            annotate_scoped_expression(&mut intersect.right, Some(&schema), dialect);
2333            columns
2334        }
2335        Expression::Except(except) => {
2336            let mut schema = ScopedSchema::new(parent, dialect);
2337            annotate_with(&mut except.with, &mut schema, dialect);
2338            let columns = annotate_scoped_expression(&mut except.left, Some(&schema), dialect);
2339            annotate_scoped_expression(&mut except.right, Some(&schema), dialect);
2340            columns
2341        }
2342        _ => {
2343            let mut annotator = TypeAnnotator::new(parent, dialect);
2344            annotator.annotate_in_place(expression);
2345            Vec::new()
2346        }
2347    }
2348}
2349
2350/// Annotate types in-place on the expression tree.
2351///
2352/// Walks the AST bottom-up and sets `inferred_type` on each value-producing
2353/// node. After this call, `expr.inferred_type()` (and the same on any child
2354/// node) returns the inferred type.
2355pub fn annotate_types(
2356    expr: &mut Expression,
2357    schema: Option<&dyn Schema>,
2358    dialect: Option<DialectType>,
2359) {
2360    annotate_scoped_expression(expr, schema, dialect);
2361}
2362
2363#[cfg(test)]
2364mod tests {
2365    use super::*;
2366    use crate::expressions::{BooleanLiteral, Cast, ExtractFunc, Null};
2367    use crate::{parse_one, DialectType, MappingSchema, Schema};
2368
2369    fn make_int_literal(val: i64) -> Expression {
2370        Expression::Literal(Box::new(Literal::Number(val.to_string())))
2371    }
2372
2373    fn make_float_literal(val: f64) -> Expression {
2374        Expression::Literal(Box::new(Literal::Number(val.to_string())))
2375    }
2376
2377    fn make_string_literal(val: &str) -> Expression {
2378        Expression::Literal(Box::new(Literal::String(val.to_string())))
2379    }
2380
2381    fn make_bool_literal(val: bool) -> Expression {
2382        Expression::Boolean(BooleanLiteral { value: val })
2383    }
2384
2385    #[test]
2386    fn test_literal_types() {
2387        let mut annotator = TypeAnnotator::new(None, None);
2388
2389        // Integer literal
2390        let int_expr = make_int_literal(42);
2391        assert_eq!(
2392            annotator.annotate(&int_expr),
2393            Some(DataType::Int {
2394                length: None,
2395                integer_spelling: false
2396            })
2397        );
2398
2399        // Float literal
2400        let float_expr = make_float_literal(3.14);
2401        assert_eq!(
2402            annotator.annotate(&float_expr),
2403            Some(DataType::Double {
2404                precision: None,
2405                scale: None
2406            })
2407        );
2408
2409        // String literal
2410        let string_expr = make_string_literal("hello");
2411        assert_eq!(
2412            annotator.annotate(&string_expr),
2413            Some(DataType::VarChar {
2414                length: None,
2415                parenthesized_length: false
2416            })
2417        );
2418
2419        // Boolean literal
2420        let bool_expr = make_bool_literal(true);
2421        assert_eq!(annotator.annotate(&bool_expr), Some(DataType::Boolean));
2422
2423        // Null literal
2424        let null_expr = Expression::Null(Null);
2425        assert_eq!(annotator.annotate(&null_expr), None);
2426    }
2427
2428    #[test]
2429    fn test_comparison_types() {
2430        let mut annotator = TypeAnnotator::new(None, None);
2431
2432        // Comparison returns boolean
2433        let cmp = Expression::Gt(Box::new(BinaryOp::new(
2434            make_int_literal(1),
2435            make_int_literal(2),
2436        )));
2437        assert_eq!(annotator.annotate(&cmp), Some(DataType::Boolean));
2438
2439        // Equality returns boolean
2440        let eq = Expression::Eq(Box::new(BinaryOp::new(
2441            make_string_literal("a"),
2442            make_string_literal("b"),
2443        )));
2444        assert_eq!(annotator.annotate(&eq), Some(DataType::Boolean));
2445    }
2446
2447    #[test]
2448    fn test_arithmetic_types() {
2449        let mut annotator = TypeAnnotator::new(None, None);
2450
2451        // Int + Int = Int
2452        let add_int = Expression::Add(Box::new(BinaryOp::new(
2453            make_int_literal(1),
2454            make_int_literal(2),
2455        )));
2456        assert_eq!(
2457            annotator.annotate(&add_int),
2458            Some(DataType::Int {
2459                length: None,
2460                integer_spelling: false
2461            })
2462        );
2463
2464        // Int + Float = Double (wider type)
2465        let add_mixed = Expression::Add(Box::new(BinaryOp::new(
2466            make_int_literal(1),
2467            make_float_literal(2.5), // Use 2.5 so the string has a decimal point
2468        )));
2469        assert_eq!(
2470            annotator.annotate(&add_mixed),
2471            Some(DataType::Double {
2472                precision: None,
2473                scale: None
2474            })
2475        );
2476    }
2477
2478    #[test]
2479    fn test_string_concat_type() {
2480        let mut annotator = TypeAnnotator::new(None, None);
2481
2482        // String || String = VarChar
2483        let concat = Expression::Concat(Box::new(BinaryOp::new(
2484            make_string_literal("hello"),
2485            make_string_literal(" world"),
2486        )));
2487        assert_eq!(
2488            annotator.annotate(&concat),
2489            Some(DataType::VarChar {
2490                length: None,
2491                parenthesized_length: false
2492            })
2493        );
2494    }
2495
2496    #[test]
2497    fn test_cast_type() {
2498        let mut annotator = TypeAnnotator::new(None, None);
2499
2500        // CAST(1 AS VARCHAR)
2501        let cast = Expression::Cast(Box::new(Cast {
2502            this: make_int_literal(1),
2503            to: DataType::VarChar {
2504                length: Some(10),
2505                parenthesized_length: false,
2506            },
2507            trailing_comments: vec![],
2508            double_colon_syntax: false,
2509            format: None,
2510            default: None,
2511            inferred_type: None,
2512        }));
2513        assert_eq!(
2514            annotator.annotate(&cast),
2515            Some(DataType::VarChar {
2516                length: Some(10),
2517                parenthesized_length: false
2518            })
2519        );
2520    }
2521
2522    #[test]
2523    fn test_function_types() {
2524        let mut annotator = TypeAnnotator::new(None, None);
2525
2526        // COUNT returns BigInt
2527        let count =
2528            Expression::Function(Box::new(Function::new("COUNT", vec![make_int_literal(1)])));
2529        assert_eq!(
2530            annotator.annotate(&count),
2531            Some(DataType::BigInt { length: None })
2532        );
2533
2534        // UPPER returns VarChar
2535        let upper = Expression::Function(Box::new(Function::new(
2536            "UPPER",
2537            vec![make_string_literal("hello")],
2538        )));
2539        assert_eq!(
2540            annotator.annotate(&upper),
2541            Some(DataType::VarChar {
2542                length: None,
2543                parenthesized_length: false
2544            })
2545        );
2546
2547        // NOW returns Timestamp
2548        let now = Expression::Function(Box::new(Function::new("NOW", vec![])));
2549        assert_eq!(
2550            annotator.annotate(&now),
2551            Some(DataType::Timestamp {
2552                precision: None,
2553                timezone: false
2554            })
2555        );
2556    }
2557
2558    #[test]
2559    fn test_duckdb_trim_stores_inferred_types() {
2560        let varchar = DataType::VarChar {
2561            length: None,
2562            parenthesized_length: false,
2563        };
2564        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2565        schema
2566            .add_table(
2567                "records",
2568                &[
2569                    ("name".to_string(), varchar.clone()),
2570                    ("chars".to_string(), varchar.clone()),
2571                    ("values_json".to_string(), DataType::Json),
2572                ],
2573                None,
2574            )
2575            .unwrap();
2576
2577        for function in [
2578            "TRIM(name)",
2579            "trim(records.name)",
2580            "TRIM(name, chars)",
2581            "TRIM(BOTH chars FROM name)",
2582            "TRIM(LEADING chars FROM name)",
2583            "TRIM(TRAILING chars FROM name)",
2584            "TRIM(FROM name)",
2585            "TRIM('  hello  ')",
2586            "TRIM(NULL)",
2587            r#"TRIM(BOTH '"' FROM values_json->>0)"#,
2588        ] {
2589            let sql = format!("SELECT {function} AS normalized FROM records");
2590            for schema in [None, Some(&schema as &dyn Schema)] {
2591                let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
2592                let original_sql = expression.sql_for(DialectType::DuckDB);
2593                annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
2594                let Expression::Select(select) = &expression else {
2595                    panic!("Expected SELECT for {sql}");
2596                };
2597                let Expression::Alias(alias) = &select.expressions[0] else {
2598                    panic!("Expected alias for {sql}");
2599                };
2600                assert!(matches!(alias.this, Expression::Trim(_)), "{sql}");
2601                assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
2602                assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
2603                assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
2604            }
2605        }
2606    }
2607
2608    #[test]
2609    fn test_int128_inferred_types_and_widening() {
2610        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2611        schema
2612            .add_table(
2613                "measurements",
2614                &[("value".to_string(), DataType::Int128)],
2615                None,
2616            )
2617            .unwrap();
2618        for (projection, expected) in [
2619            ("CAST(value AS HUGEINT)", DataType::Int128),
2620            ("value", DataType::Int128),
2621            ("value + CAST(1 AS BIGINT)", DataType::Int128),
2622            ("CAST(1 AS BIGINT) + value", DataType::Int128),
2623            ("COALESCE(value, CAST(1 AS BIGINT))", DataType::Int128),
2624            ("COALESCE(CAST(1 AS BIGINT), value)", DataType::Int128),
2625            (
2626                "CASE WHEN TRUE THEN value ELSE CAST(1 AS BIGINT) END",
2627                DataType::Int128,
2628            ),
2629            ("SUM(value)", DataType::Int128),
2630            ("SUM(CAST(value AS BIGINT))", DataType::Int128),
2631            (
2632                "MEDIAN(value)",
2633                DataType::Double {
2634                    precision: None,
2635                    scale: None,
2636                },
2637            ),
2638            (
2639                "CAST(value AS HUGEINT[])",
2640                DataType::Array {
2641                    element_type: Box::new(DataType::Int128),
2642                    dimension: None,
2643                },
2644            ),
2645        ] {
2646            let sql = format!("SELECT {projection} AS widened FROM measurements");
2647            let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
2648            annotate_types(&mut expression, Some(&schema), Some(DialectType::DuckDB));
2649            let Expression::Select(select) = &expression else {
2650                panic!("expected SELECT")
2651            };
2652            let Expression::Alias(alias) = &select.expressions[0] else {
2653                panic!("expected alias")
2654            };
2655            assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
2656            assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
2657        }
2658        assert_eq!(
2659            TypeCoercionClass::from_data_type(&DataType::Int128),
2660            Some(TypeCoercionClass::Numeric)
2661        );
2662    }
2663
2664    #[test]
2665    fn test_duckdb_unsigned_integer_coercion_matrix() {
2666        // Verified against DuckDB 1.5.5: arithmetic overload binding and
2667        // combination casting do not select the same small integer widths.
2668        let signed = ["TINYINT", "SMALLINT", "INT", "BIGINT", "HUGEINT"];
2669        let unsigned = ["UTINYINT", "USMALLINT", "UINTEGER", "UBIGINT", "UHUGEINT"];
2670        let arithmetic = [
2671            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
2672            ["SMALLINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
2673            ["INT", "INT", "BIGINT", "HUGEINT", "DOUBLE"],
2674            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
2675            ["HUGEINT", "HUGEINT", "HUGEINT", "HUGEINT", "DOUBLE"],
2676        ];
2677        let combination = [
2678            ["SMALLINT", "INT", "BIGINT", "HUGEINT", "SMALLINT"],
2679            ["SMALLINT", "INT", "BIGINT", "HUGEINT", "INT"],
2680            ["INT", "INT", "BIGINT", "HUGEINT", "BIGINT"],
2681            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "HUGEINT"],
2682            ["HUGEINT", "HUGEINT", "HUGEINT", "HUGEINT", "DOUBLE"],
2683        ];
2684        for (i, s) in signed.iter().enumerate() {
2685            for (j, u) in unsigned.iter().enumerate() {
2686                for (a, b) in [(s, u), (u, s)] {
2687                    let a = format!("CAST(1 AS {a})");
2688                    let b = format!("CAST(1 AS {b})");
2689                    for (sql, expected) in [
2690                        (format!("{a} + {b}"), arithmetic[i][j]),
2691                        (format!("{a} - {b}"), arithmetic[i][j]),
2692                        (format!("{a} * {b}"), arithmetic[i][j]),
2693                        (format!("{a} % {b}"), arithmetic[i][j]),
2694                        (format!("{a} / {b}"), "DOUBLE"),
2695                        (format!("COALESCE({a}, {b})"), combination[i][j]),
2696                        (
2697                            format!("CASE WHEN TRUE THEN {a} ELSE {b} END"),
2698                            combination[i][j],
2699                        ),
2700                    ] {
2701                        let expr = parse_one(&sql, DialectType::DuckDB).unwrap();
2702                        let expected =
2703                            crate::parse_data_type(expected, DialectType::DuckDB).unwrap();
2704                        let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
2705                        assert_eq!(annotator.annotate(&expr), Some(expected), "{sql}");
2706                    }
2707                }
2708            }
2709        }
2710        for (i, a) in unsigned.iter().enumerate() {
2711            for (j, b) in unsigned.iter().enumerate() {
2712                let sql = format!("CAST(1 AS {a}) + CAST(1 AS {b})");
2713                let expr = parse_one(&sql, DialectType::DuckDB).unwrap();
2714                let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
2715                assert_eq!(
2716                    annotator.annotate(&expr),
2717                    Some(crate::parse_data_type(unsigned[i.max(j)], DialectType::DuckDB).unwrap()),
2718                    "{sql}"
2719                );
2720            }
2721        }
2722    }
2723
2724    #[test]
2725    fn test_duckdb_sum_return_types() {
2726        for (input, output) in [
2727            ("BOOLEAN", "INT128"),
2728            ("TINYINT", "INT128"),
2729            ("SMALLINT", "INT128"),
2730            ("INT", "INT128"),
2731            ("BIGINT", "INT128"),
2732            ("HUGEINT", "INT128"),
2733            ("UTINYINT", "INT128"),
2734            ("USMALLINT", "INT128"),
2735            ("UINTEGER", "INT128"),
2736            ("UBIGINT", "INT128"),
2737            ("UHUGEINT", "DOUBLE"),
2738            ("FLOAT", "DOUBLE"),
2739            ("DOUBLE", "DOUBLE"),
2740            ("DECIMAL(10,2)", "DECIMAL(38,2)"),
2741            ("DECIMAL", "DECIMAL(38,3)"),
2742            ("DECIMAL(10)", "DECIMAL(38,0)"),
2743        ] {
2744            let input_type = crate::parse_data_type(input, DialectType::DuckDB).unwrap();
2745            let expected = crate::parse_data_type(output, DialectType::DuckDB).unwrap();
2746            let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2747            schema
2748                .add_table("t", &[("x".to_string(), input_type)], None)
2749                .unwrap();
2750            let cast = parse_one(&format!("CAST(1 AS {input})"), DialectType::DuckDB).unwrap();
2751            let function = Expression::Function(Box::new(Function::new("SUM", vec![cast])));
2752            assert_eq!(
2753                TypeAnnotator::new(None, Some(DialectType::DuckDB)).annotate(&function),
2754                Some(expected.clone()),
2755                "SUM({input}) builder"
2756            );
2757            for arg in ["x".to_string(), format!("CAST(1 AS {input})")] {
2758                for projection in [
2759                    format!("SUM({arg})"),
2760                    format!("SUM(DISTINCT {arg})"),
2761                    format!("SUM({arg}) OVER ()"),
2762                    format!("SUM({arg}) FILTER (WHERE TRUE)"),
2763                ] {
2764                    let sql = format!("SELECT {projection} AS total FROM t");
2765                    let mut expr = parse_one(&sql, DialectType::DuckDB).unwrap();
2766                    annotate_types(&mut expr, Some(&schema), Some(DialectType::DuckDB));
2767                    let Expression::Select(select) = expr else {
2768                        unreachable!()
2769                    };
2770                    let Expression::Alias(alias) = &select.expressions[0] else {
2771                        unreachable!()
2772                    };
2773                    assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
2774                }
2775            }
2776        }
2777        let expr = parse_one("SUM(x)", DialectType::DuckDB).unwrap();
2778        assert_eq!(
2779            TypeAnnotator::new(None, Some(DialectType::DuckDB)).annotate(&expr),
2780            None
2781        );
2782        // The dialect-specific override must not change the generic/default rule.
2783        for dialect in [
2784            DialectType::Generic,
2785            DialectType::PostgreSQL,
2786            DialectType::MySQL,
2787            DialectType::BigQuery,
2788        ] {
2789            let expr = parse_one("SUM(CAST(1 AS BIGINT))", dialect).unwrap();
2790            assert_eq!(
2791                TypeAnnotator::new(None, Some(dialect)).annotate(&expr),
2792                Some(DataType::BigInt { length: None })
2793            );
2794        }
2795    }
2796
2797    #[test]
2798    fn test_trim_annotates_both_arguments_and_nested_children() {
2799        let varchar = DataType::VarChar {
2800            length: None,
2801            parenthesized_length: false,
2802        };
2803        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2804        schema
2805            .add_table(
2806                "users",
2807                &[
2808                    ("name".to_string(), varchar.clone()),
2809                    ("chars".to_string(), varchar.clone()),
2810                ],
2811                None,
2812            )
2813            .unwrap();
2814
2815        fn check_children(expr: &Expression, varchar: &DataType, has_schema: bool) -> usize {
2816            let mut count = 0;
2817            if matches!(
2818                expr,
2819                Expression::Trim(_)
2820                    | Expression::Lower(_)
2821                    | Expression::Upper(_)
2822                    | Expression::Column(_)
2823            ) {
2824                let expected = if matches!(expr, Expression::Column(_)) && !has_schema {
2825                    None
2826                } else {
2827                    Some(varchar)
2828                };
2829                assert_eq!(expr.inferred_type(), expected, "{}", expr.sql());
2830                count += 1;
2831            }
2832            crate::ast_children::for_each_child(expr, |_, child| {
2833                count += check_children(child, varchar, has_schema);
2834            });
2835            count
2836        }
2837
2838        for schema in [None, Some(&schema as &dyn Schema)] {
2839            let mut expression = parse_one(
2840                "SELECT TRIM(LOWER(TRIM(name)), UPPER(chars)) AS normalized FROM users",
2841                DialectType::DuckDB,
2842            )
2843            .unwrap();
2844            annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
2845            assert_eq!(check_children(&expression, &varchar, schema.is_some()), 6);
2846        }
2847    }
2848
2849    #[test]
2850    fn test_trim_type_metadata_roundtrip_across_dialects() {
2851        let varchar = DataType::VarChar {
2852            length: None,
2853            parenthesized_length: false,
2854        };
2855        for dialect in [
2856            DialectType::Generic,
2857            DialectType::DuckDB,
2858            DialectType::PostgreSQL,
2859            DialectType::Snowflake,
2860            DialectType::BigQuery,
2861            DialectType::MySQL,
2862        ] {
2863            let expression = parse_one("TRIM('  hello  ')", dialect).unwrap();
2864            assert!(matches!(expression, Expression::Trim(_)));
2865            assert_eq!(expression.inferred_type(), None);
2866            let unannotated = serde_json::to_value(&expression).unwrap();
2867            assert!(unannotated["trim"].get("inferred_type").is_none());
2868
2869            // Old AST JSON without type metadata remains deserializable.
2870            let mut restored: Expression = serde_json::from_value(unannotated).unwrap();
2871            assert_eq!(restored, expression);
2872            annotate_types(&mut restored, None, Some(dialect));
2873            assert_eq!(restored.inferred_type(), Some(&varchar), "{dialect:?}");
2874            assert_eq!(restored.sql_for(dialect), expression.sql_for(dialect));
2875
2876            let annotated = serde_json::to_value(&restored).unwrap();
2877            assert_eq!(annotated["trim"]["inferred_type"]["data_type"], "var_char");
2878            let roundtripped: Expression = serde_json::from_value(annotated).unwrap();
2879            assert_eq!(roundtripped, restored);
2880            assert_eq!(roundtripped.inferred_type(), Some(&varchar));
2881        }
2882    }
2883
2884    #[test]
2885    fn test_duckdb_regexp_extract_all_overload_types() {
2886        let varchar = DataType::VarChar {
2887            length: None,
2888            parenthesized_length: false,
2889        };
2890        let captures = DataType::Struct {
2891            fields: vec![
2892                StructField::new("Letter".to_string(), varchar.clone()),
2893                StructField::new("number".to_string(), varchar.clone()),
2894            ],
2895            nested: false,
2896        };
2897        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2898        schema
2899            .add_table("documents", &[("body".to_string(), varchar.clone())], None)
2900            .unwrap();
2901
2902        for (arguments, element_type) in [
2903            ("body, '[0-9]+'", &varchar),
2904            ("documents.body, '[0-9]+'", &varchar),
2905            ("'a1b22', '[0-9]+'", &varchar),
2906            ("'abc', '[0-9]+'", &varchar),
2907            ("NULL, '[0-9]+'", &varchar),
2908            ("body, '([a-z])([0-9]+)', 0", &varchar),
2909            ("body, '([a-z])([0-9]+)', 2", &varchar),
2910            ("body, '([a-z])([0-9]+)', (1), 'i'", &varchar),
2911            ("body, '([a-z])([0-9]+)', CAST(1 AS INTEGER)", &varchar),
2912            ("body, '([a-z])([0-9]+)', NULL", &varchar),
2913            ("body, '([a-z])([0-9]+)', ['Letter', 'number']", &captures),
2914            (
2915                "body, '([a-z])([0-9]+)', (['Letter', 'number']), 'i'",
2916                &captures,
2917            ),
2918            (
2919                "body, '([a-z])([0-9]+)', ARRAY['Letter', 'number']",
2920                &captures,
2921            ),
2922            (
2923                "NULL, '([a-z])([0-9]+)', LIST['Letter', 'number']",
2924                &captures,
2925            ),
2926        ] {
2927            let expected = DataType::Array {
2928                element_type: Box::new(element_type.clone()),
2929                dimension: None,
2930            };
2931            for name in ["REGEXP_EXTRACT_ALL", "ReGeXp_ExTrAcT_AlL"] {
2932                for schema in [None, Some(&schema as &dyn Schema)] {
2933                    let sql = format!("SELECT {name}({arguments}) AS matches FROM documents");
2934                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
2935                    let original_sql = expression.sql_for(DialectType::DuckDB);
2936                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
2937                    let Expression::Select(select) = &expression else {
2938                        panic!("Expected SELECT for {sql}");
2939                    };
2940                    let Expression::Alias(alias) = &select.expressions[0] else {
2941                        panic!("Expected alias for {sql}");
2942                    };
2943                    assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
2944                    assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
2945                    assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
2946                    let json = serde_json::to_value(&expression).unwrap();
2947                    let restored: Expression = serde_json::from_value(json).unwrap();
2948                    assert_eq!(restored, expression);
2949
2950                    let sql = format!(
2951                        "SELECT UNNEST({name}({arguments})) AS single_match FROM documents"
2952                    );
2953                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
2954                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
2955                    let Expression::Select(select) = &expression else {
2956                        panic!("Expected SELECT for {sql}");
2957                    };
2958                    let Expression::Alias(alias) = &select.expressions[0] else {
2959                        panic!("Expected alias for {sql}");
2960                    };
2961                    assert_eq!(alias.this.inferred_type(), Some(element_type), "{sql}");
2962                    assert_eq!(alias.inferred_type.as_ref(), Some(element_type), "{sql}");
2963                }
2964            }
2965        }
2966    }
2967
2968    #[test]
2969    fn test_duckdb_regexp_extract_all_unresolved_overloads() {
2970        let varchar = DataType::VarChar {
2971            length: None,
2972            parenthesized_length: false,
2973        };
2974        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
2975        schema
2976            .add_table(
2977                "documents",
2978                &[(
2979                    "names".to_string(),
2980                    DataType::Array {
2981                        element_type: Box::new(varchar),
2982                        dimension: None,
2983                    },
2984                )],
2985                None,
2986            )
2987            .unwrap();
2988
2989        // Do not fabricate a scalar or a precise struct for unknown capture
2990        // names, ambiguous overloads, or invalid literal name lists/arity.
2991        for arguments in [
2992            "",
2993            "'a1'",
2994            "'a1', '(a)(1)', 0, 'i', 1",
2995            "'a1', '(a)(1)', missing_group",
2996            "'a1', '(a)(1)', documents.names",
2997            "'a1', '(a)(1)', ['letter', missing_name]",
2998            "'a1', '(a)(1)', CAST(['letter', 'number'] AS VARCHAR[])",
2999            "'a1', '(a)(1)', []",
3000            "'a1', '(a)(1)', [NULL]",
3001            "'a1', '(a)(1)', ['letter', 'LETTER']",
3002            "'a1', '(a)(1)', 1.0",
3003            "'a1', '(a)(1)', CAST(1 AS BIGINT)",
3004        ] {
3005            let sql = format!("REGEXP_EXTRACT_ALL({arguments})");
3006            let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3007            let mut annotator = TypeAnnotator::new(Some(&schema), Some(DialectType::DuckDB));
3008            assert_eq!(annotator.annotate(&expression), None, "{sql}");
3009            annotator.annotate_in_place(&mut expression);
3010            assert_eq!(expression.inferred_type(), None, "{sql}");
3011        }
3012    }
3013
3014    #[test]
3015    fn test_regexp_extract_all_rule_is_duckdb_specific() {
3016        // Other dialects have different overloads, notably BigQuery BYTES.
3017        // Preserve their existing behavior rather than installing a global rule.
3018        for dialect in [
3019            None,
3020            Some(DialectType::Generic),
3021            Some(DialectType::BigQuery),
3022            Some(DialectType::Snowflake),
3023            Some(DialectType::Databricks),
3024        ] {
3025            for input in [
3026                make_string_literal("a1"),
3027                Expression::Literal(Box::new(Literal::ByteString("a1".to_string()))),
3028            ] {
3029                let mut annotator = TypeAnnotator::new(None, dialect);
3030                let expected = annotator.annotate(&input);
3031                let function = Expression::Function(Box::new(Function::new(
3032                    "REGEXP_EXTRACT_ALL",
3033                    vec![input, make_string_literal("[0-9]+")],
3034                )));
3035                assert_eq!(annotator.annotate(&function), expected, "{dialect:?}");
3036            }
3037        }
3038    }
3039
3040    #[test]
3041    fn test_duckdb_date_name_types() {
3042        let varchar = DataType::VarChar {
3043            length: None,
3044            parenthesized_length: false,
3045        };
3046        for input_type in [
3047            DataType::Date,
3048            DataType::Timestamp {
3049                precision: None,
3050                timezone: false,
3051            },
3052            DataType::Timestamp {
3053                precision: None,
3054                timezone: true,
3055            },
3056        ] {
3057            let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3058            schema
3059                .add_table(
3060                    "events",
3061                    &[("created_at".to_string(), input_type.clone())],
3062                    None,
3063                )
3064                .unwrap();
3065            for function in ["MONTHNAME", "MoNtHnAmE", "DAYNAME", "DaYnAmE"] {
3066                for input in [
3067                    "created_at",
3068                    "events.created_at",
3069                    "DATE '2026-01-01'",
3070                    "TIMESTAMP '2026-01-01 12:00:00'",
3071                    "TIMESTAMPTZ '2026-01-01 12:00:00+00'",
3072                    "NULL",
3073                ] {
3074                    let sql = format!("SELECT {function}({input}) AS label FROM events");
3075                    for schema in [None, Some(&schema as &dyn Schema)] {
3076                        let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3077                        let original_sql = expression.sql_for(DialectType::DuckDB);
3078                        annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3079                        let Expression::Select(select) = &expression else {
3080                            panic!("Expected SELECT for {sql}");
3081                        };
3082                        let Expression::Alias(alias) = &select.expressions[0] else {
3083                            panic!("Expected alias for {sql}");
3084                        };
3085                        assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
3086                        assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
3087                        if let Expression::Function(function) = &alias.this {
3088                            if let Expression::Column(column) = &function.args[0] {
3089                                assert_eq!(
3090                                    column.inferred_type.as_ref(),
3091                                    schema.map(|_| &input_type),
3092                                    "Argument type must remain unchanged for {sql}"
3093                                );
3094                            }
3095                        }
3096                        assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
3097                        let json = serde_json::to_value(&expression).unwrap();
3098                        let restored: Expression = serde_json::from_value(json).unwrap();
3099                        assert_eq!(restored, expression);
3100                    }
3101                }
3102            }
3103        }
3104    }
3105
3106    #[test]
3107    fn test_date_name_fixed_return_types_are_duckdb_specific() {
3108        for dialect in [
3109            None,
3110            Some(DialectType::Generic),
3111            Some(DialectType::PostgreSQL),
3112            Some(DialectType::MySQL),
3113            Some(DialectType::Snowflake),
3114        ] {
3115            let annotator = TypeAnnotator::new(None, dialect);
3116            for function in ["MONTHNAME", "DAYNAME"] {
3117                assert!(
3118                    !annotator.function_return_types.contains_key(function),
3119                    "Unexpected fixed return type for {function} in {dialect:?}"
3120                );
3121            }
3122        }
3123    }
3124
3125    #[test]
3126    fn test_duckdb_array_to_string_types() {
3127        let varchar = DataType::VarChar {
3128            length: None,
3129            parenthesized_length: false,
3130        };
3131        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3132        schema
3133            .add_table(
3134                "events",
3135                &[
3136                    ("label".to_string(), varchar.clone()),
3137                    (
3138                        "labels".to_string(),
3139                        DataType::Array {
3140                            element_type: Box::new(varchar.clone()),
3141                            dimension: None,
3142                        },
3143                    ),
3144                ],
3145                None,
3146            )
3147            .unwrap();
3148
3149        for function in [
3150            "ARRAY_TO_STRING",
3151            "array_to_string",
3152            "ArRaY_To_StRiNg_CoMmA_DeFaUlT",
3153        ] {
3154            for input in [
3155                "labels",
3156                "events.labels",
3157                "ARRAY_AGG(label)",
3158                "ARRAY_AGG(events.label)",
3159                "['a', NULL, 'b']",
3160                "[1, 2]",
3161                "[]",
3162                "CAST(NULL AS VARCHAR[])",
3163                "NULL",
3164            ] {
3165                let arguments = if function.eq_ignore_ascii_case("ARRAY_TO_STRING") {
3166                    format!("{input}, ', '")
3167                } else {
3168                    input.to_string()
3169                };
3170                let sql = format!("SELECT {function}({arguments}) AS label_text FROM events");
3171                for schema in [None, Some(&schema as &dyn Schema)] {
3172                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3173                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3174                    let Expression::Select(select) = &expression else {
3175                        panic!("Expected SELECT for {sql}");
3176                    };
3177                    let Expression::Alias(alias) = &select.expressions[0] else {
3178                        panic!("Expected alias for {sql}");
3179                    };
3180                    assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
3181                    assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
3182                }
3183            }
3184        }
3185    }
3186
3187    #[test]
3188    fn test_array_to_string_fixed_return_types_are_duckdb_specific() {
3189        // In particular, BigQuery also has a BYTES-returning overload, so it
3190        // must not inherit DuckDB's unconditional VARCHAR return-type rules.
3191        for dialect in [
3192            None,
3193            Some(DialectType::Generic),
3194            Some(DialectType::BigQuery),
3195            Some(DialectType::PostgreSQL),
3196            Some(DialectType::Snowflake),
3197        ] {
3198            let annotator = TypeAnnotator::new(None, dialect);
3199            for function in ["ARRAY_TO_STRING", "ARRAY_TO_STRING_COMMA_DEFAULT"] {
3200                assert!(
3201                    !annotator.function_return_types.contains_key(function),
3202                    "Unexpected fixed return type for {function} in {dialect:?}"
3203                );
3204            }
3205        }
3206    }
3207
3208    #[test]
3209    fn test_extract_types_are_dialect_and_field_specific() {
3210        let double = DataType::Double {
3211            precision: None,
3212            scale: None,
3213        };
3214        let bigint = DataType::BigInt { length: None };
3215        let int = DataType::Int {
3216            length: None,
3217            integer_spelling: false,
3218        };
3219
3220        for (field, duckdb_type) in [
3221            (DateTimeField::Year, &bigint),
3222            (DateTimeField::Second, &bigint),
3223            (DateTimeField::Millisecond, &bigint),
3224            (DateTimeField::Microsecond, &bigint),
3225            (DateTimeField::Custom("yearweek".to_string()), &bigint),
3226            (DateTimeField::Epoch, &double),
3227            (DateTimeField::Custom("EpOcH".to_string()), &double),
3228            (DateTimeField::Custom("julian".to_string()), &double),
3229            (DateTimeField::Custom("JuLiAn".to_string()), &double),
3230        ] {
3231            let extract = Expression::Extract(Box::new(ExtractFunc {
3232                this: make_string_literal("2024-02-03 04:05:06.123456"),
3233                field,
3234            }));
3235            for dialect in [
3236                None,
3237                Some(DialectType::Generic),
3238                Some(DialectType::PostgreSQL),
3239                Some(DialectType::Snowflake),
3240                Some(DialectType::DuckDB),
3241            ] {
3242                let expected = if dialect == Some(DialectType::DuckDB) {
3243                    duckdb_type
3244                } else {
3245                    &int
3246                };
3247                assert_eq!(
3248                    TypeAnnotator::new(None, dialect)
3249                        .annotate(&extract)
3250                        .as_ref(),
3251                    Some(expected),
3252                    "unexpected type for {extract:?} in {dialect:?}"
3253                );
3254            }
3255        }
3256    }
3257
3258    #[test]
3259    fn test_duckdb_date_trunc_overload_types() {
3260        fn cast_to(data_type: DataType) -> Expression {
3261            Expression::Cast(Box::new(Cast {
3262                this: make_string_literal("value"),
3263                to: data_type,
3264                format: None,
3265                trailing_comments: Vec::new(),
3266                double_colon_syntax: false,
3267                default: None,
3268                inferred_type: None,
3269            }))
3270        }
3271
3272        fn date_trunc(value: Expression) -> Expression {
3273            Expression::Function(Box::new(Function::new(
3274                "DATE_TRUNC",
3275                vec![make_string_literal("month"), value],
3276            )))
3277        }
3278
3279        let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
3280
3281        for input_type in [
3282            DataType::Date,
3283            DataType::Timestamp {
3284                precision: Some(9),
3285                timezone: false,
3286            },
3287        ] {
3288            assert_eq!(
3289                annotator.annotate(&date_trunc(cast_to(input_type))),
3290                Some(DataType::Timestamp {
3291                    precision: None,
3292                    timezone: false,
3293                })
3294            );
3295        }
3296
3297        assert_eq!(
3298            annotator.annotate(&date_trunc(cast_to(DataType::Timestamp {
3299                precision: Some(6),
3300                timezone: true,
3301            }))),
3302            Some(DataType::Timestamp {
3303                precision: None,
3304                timezone: true,
3305            })
3306        );
3307        assert_eq!(
3308            annotator.annotate(&date_trunc(cast_to(DataType::Interval {
3309                unit: Some("DAY".to_string()),
3310                to: Some("SECOND".to_string()),
3311            }))),
3312            Some(DataType::Interval {
3313                unit: None,
3314                to: None,
3315            })
3316        );
3317        assert_eq!(
3318            annotator.annotate(&date_trunc(Expression::Null(Null))),
3319            None
3320        );
3321    }
3322
3323    #[test]
3324    fn test_coalesce_type_inference() {
3325        let mut annotator = TypeAnnotator::new(None, None);
3326
3327        // COALESCE(NULL, 1) returns Int (type of first non-null arg)
3328        let coalesce = Expression::Function(Box::new(Function::new(
3329            "COALESCE",
3330            vec![Expression::Null(Null), make_int_literal(1)],
3331        )));
3332        assert_eq!(
3333            annotator.annotate(&coalesce),
3334            Some(DataType::Int {
3335                length: None,
3336                integer_spelling: false
3337            })
3338        );
3339
3340        let specialized_coalesce = Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
3341            expressions: vec![Expression::Null(Null), make_int_literal(1)],
3342            original_name: None,
3343            inferred_type: None,
3344        }));
3345        assert_eq!(
3346            annotator.annotate(&specialized_coalesce),
3347            Some(DataType::Int {
3348                length: None,
3349                integer_spelling: false
3350            })
3351        );
3352    }
3353
3354    #[test]
3355    fn test_type_coercion_class() {
3356        // Text types
3357        assert_eq!(
3358            TypeCoercionClass::from_data_type(&DataType::VarChar {
3359                length: None,
3360                parenthesized_length: false
3361            }),
3362            Some(TypeCoercionClass::Text)
3363        );
3364        assert_eq!(
3365            TypeCoercionClass::from_data_type(&DataType::Text),
3366            Some(TypeCoercionClass::Text)
3367        );
3368
3369        // Numeric types
3370        assert_eq!(
3371            TypeCoercionClass::from_data_type(&DataType::Int {
3372                length: None,
3373                integer_spelling: false
3374            }),
3375            Some(TypeCoercionClass::Numeric)
3376        );
3377        assert_eq!(
3378            TypeCoercionClass::from_data_type(&DataType::Double {
3379                precision: None,
3380                scale: None
3381            }),
3382            Some(TypeCoercionClass::Numeric)
3383        );
3384
3385        // Timelike types
3386        assert_eq!(
3387            TypeCoercionClass::from_data_type(&DataType::Date),
3388            Some(TypeCoercionClass::Timelike)
3389        );
3390        assert_eq!(
3391            TypeCoercionClass::from_data_type(&DataType::Timestamp {
3392                precision: None,
3393                timezone: false
3394            }),
3395            Some(TypeCoercionClass::Timelike)
3396        );
3397
3398        // Unknown types
3399        assert_eq!(TypeCoercionClass::from_data_type(&DataType::Json), None);
3400    }
3401
3402    #[test]
3403    fn test_wider_numeric_type() {
3404        let annotator = TypeAnnotator::new(None, None);
3405
3406        // Int vs BigInt -> BigInt
3407        let result = annotator.wider_numeric_type(
3408            &DataType::Int {
3409                length: None,
3410                integer_spelling: false,
3411            },
3412            &DataType::BigInt { length: None },
3413        );
3414        assert_eq!(result, DataType::BigInt { length: None });
3415
3416        // Float vs Double -> Double
3417        let result = annotator.wider_numeric_type(
3418            &DataType::Float {
3419                precision: None,
3420                scale: None,
3421                real_spelling: false,
3422            },
3423            &DataType::Double {
3424                precision: None,
3425                scale: None,
3426            },
3427        );
3428        assert_eq!(
3429            result,
3430            DataType::Double {
3431                precision: None,
3432                scale: None
3433            }
3434        );
3435
3436        // Int vs Double -> Double
3437        let result = annotator.wider_numeric_type(
3438            &DataType::Int {
3439                length: None,
3440                integer_spelling: false,
3441            },
3442            &DataType::Double {
3443                precision: None,
3444                scale: None,
3445            },
3446        );
3447        assert_eq!(
3448            result,
3449            DataType::Double {
3450                precision: None,
3451                scale: None
3452            }
3453        );
3454    }
3455
3456    #[test]
3457    fn test_aggregate_return_types() {
3458        let mut annotator = TypeAnnotator::new(None, None);
3459
3460        // SUM(int) returns BigInt
3461        let sum_type = annotator.get_aggregate_return_type("SUM", &[make_int_literal(1)]);
3462        assert_eq!(sum_type, Some(DataType::BigInt { length: None }));
3463
3464        // AVG always returns Double
3465        let avg_type = annotator.get_aggregate_return_type("AVG", &[make_int_literal(1)]);
3466        assert_eq!(
3467            avg_type,
3468            Some(DataType::Double {
3469                precision: None,
3470                scale: None
3471            })
3472        );
3473
3474        // MIN/MAX preserve input type
3475        let min_type = annotator.get_aggregate_return_type("MIN", &[make_string_literal("a")]);
3476        assert_eq!(
3477            min_type,
3478            Some(DataType::VarChar {
3479                length: None,
3480                parenthesized_length: false
3481            })
3482        );
3483
3484        // DuckDB MIN/MAX(value, n) return a list of the input type.
3485        let top_n_type = annotator
3486            .get_aggregate_return_type("MAX", &[make_string_literal("a"), make_int_literal(2)]);
3487        assert_eq!(
3488            top_n_type,
3489            Some(DataType::Array {
3490                element_type: Box::new(DataType::VarChar {
3491                    length: None,
3492                    parenthesized_length: false,
3493                }),
3494                dimension: None,
3495            })
3496        );
3497    }
3498
3499    #[test]
3500    fn test_date_literal_types() {
3501        let mut annotator = TypeAnnotator::new(None, None);
3502
3503        // DATE literal
3504        let date_expr = Expression::Literal(Box::new(Literal::Date("2024-01-15".to_string())));
3505        assert_eq!(annotator.annotate(&date_expr), Some(DataType::Date));
3506
3507        // TIME literal
3508        let time_expr = Expression::Literal(Box::new(Literal::Time("10:30:00".to_string())));
3509        assert_eq!(
3510            annotator.annotate(&time_expr),
3511            Some(DataType::Time {
3512                precision: None,
3513                timezone: false
3514            })
3515        );
3516
3517        // TIMESTAMP literal
3518        let ts_expr = Expression::Literal(Box::new(Literal::Timestamp(
3519            "2024-01-15 10:30:00".to_string(),
3520        )));
3521        assert_eq!(
3522            annotator.annotate(&ts_expr),
3523            Some(DataType::Timestamp {
3524                precision: None,
3525                timezone: false
3526            })
3527        );
3528    }
3529
3530    #[test]
3531    fn test_logical_operations() {
3532        let mut annotator = TypeAnnotator::new(None, None);
3533
3534        // AND returns boolean
3535        let and_expr = Expression::And(Box::new(BinaryOp::new(
3536            make_bool_literal(true),
3537            make_bool_literal(false),
3538        )));
3539        assert_eq!(annotator.annotate(&and_expr), Some(DataType::Boolean));
3540
3541        // OR returns boolean
3542        let or_expr = Expression::Or(Box::new(BinaryOp::new(
3543            make_bool_literal(true),
3544            make_bool_literal(false),
3545        )));
3546        assert_eq!(annotator.annotate(&or_expr), Some(DataType::Boolean));
3547
3548        // NOT returns boolean
3549        let not_expr = Expression::Not(Box::new(crate::expressions::UnaryOp::new(
3550            make_bool_literal(true),
3551        )));
3552        assert_eq!(annotator.annotate(&not_expr), Some(DataType::Boolean));
3553    }
3554
3555    // ========================================
3556    // Tests for newly implemented features
3557    // ========================================
3558
3559    #[test]
3560    fn test_subscript_array_type() {
3561        let mut annotator = TypeAnnotator::new(None, None);
3562
3563        // Array[index] returns element type
3564        let arr = Expression::Array(Box::new(crate::expressions::Array {
3565            expressions: vec![make_int_literal(1), make_int_literal(2)],
3566            inferred_type: None,
3567        }));
3568        let subscript = Expression::Subscript(Box::new(crate::expressions::Subscript {
3569            this: arr,
3570            index: make_int_literal(0),
3571        }));
3572        assert_eq!(
3573            annotator.annotate(&subscript),
3574            Some(DataType::Int {
3575                length: None,
3576                integer_spelling: false
3577            })
3578        );
3579    }
3580
3581    #[test]
3582    fn test_subscript_map_type() {
3583        let mut annotator = TypeAnnotator::new(None, None);
3584
3585        // Map[key] returns value type
3586        let map = Expression::Map(Box::new(crate::expressions::Map {
3587            keys: vec![make_string_literal("a")],
3588            values: vec![make_int_literal(1)],
3589        }));
3590        let subscript = Expression::Subscript(Box::new(crate::expressions::Subscript {
3591            this: map,
3592            index: make_string_literal("a"),
3593        }));
3594        assert_eq!(
3595            annotator.annotate(&subscript),
3596            Some(DataType::Int {
3597                length: None,
3598                integer_spelling: false
3599            })
3600        );
3601    }
3602
3603    #[test]
3604    fn test_struct_type() {
3605        let mut annotator = TypeAnnotator::new(None, None);
3606
3607        // STRUCT literal
3608        let struct_expr = Expression::Struct(Box::new(crate::expressions::Struct {
3609            fields: vec![
3610                (Some("name".to_string()), make_string_literal("Alice")),
3611                (Some("age".to_string()), make_int_literal(30)),
3612            ],
3613        }));
3614        let result = annotator.annotate(&struct_expr);
3615        assert!(matches!(result, Some(DataType::Struct { fields, .. }) if fields.len() == 2));
3616    }
3617
3618    #[test]
3619    fn test_map_type() {
3620        let mut annotator = TypeAnnotator::new(None, None);
3621
3622        // MAP literal
3623        let map_expr = Expression::Map(Box::new(crate::expressions::Map {
3624            keys: vec![make_string_literal("a"), make_string_literal("b")],
3625            values: vec![make_int_literal(1), make_int_literal(2)],
3626        }));
3627        let result = annotator.annotate(&map_expr);
3628        assert!(matches!(
3629            result,
3630            Some(DataType::Map { key_type, value_type })
3631            if matches!(*key_type, DataType::VarChar { .. })
3632               && matches!(*value_type, DataType::Int { .. })
3633        ));
3634    }
3635
3636    #[test]
3637    fn test_explode_array_type() {
3638        let mut annotator = TypeAnnotator::new(None, None);
3639
3640        // EXPLODE(array) returns element type
3641        let arr = Expression::Array(Box::new(crate::expressions::Array {
3642            expressions: vec![make_int_literal(1), make_int_literal(2)],
3643            inferred_type: None,
3644        }));
3645        let explode = Expression::Explode(Box::new(crate::expressions::UnaryFunc {
3646            this: arr,
3647            original_name: None,
3648            inferred_type: None,
3649        }));
3650        assert_eq!(
3651            annotator.annotate(&explode),
3652            Some(DataType::Int {
3653                length: None,
3654                integer_spelling: false
3655            })
3656        );
3657    }
3658
3659    #[test]
3660    fn test_unnest_array_type() {
3661        let mut annotator = TypeAnnotator::new(None, None);
3662
3663        // UNNEST(array) returns element type
3664        let arr = Expression::Array(Box::new(crate::expressions::Array {
3665            expressions: vec![make_string_literal("a"), make_string_literal("b")],
3666            inferred_type: None,
3667        }));
3668        let unnest = Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
3669            this: arr,
3670            expressions: Vec::new(),
3671            with_ordinality: false,
3672            alias: None,
3673            offset_alias: None,
3674            inferred_type: None,
3675        }));
3676        assert_eq!(
3677            annotator.annotate(&unnest),
3678            Some(DataType::VarChar {
3679                length: None,
3680                parenthesized_length: false
3681            })
3682        );
3683    }
3684
3685    #[test]
3686    fn test_annotate_duckdb_array_case_unnest_types() {
3687        let timestamp = DataType::Timestamp {
3688            precision: None,
3689            timezone: false,
3690        };
3691        let array_timestamp = DataType::Array {
3692            element_type: Box::new(timestamp.clone()),
3693            dimension: None,
3694        };
3695        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3696        schema
3697            .add_table(
3698                "events",
3699                &[
3700                    ("created_at".to_string(), timestamp.clone()),
3701                    ("closed_at".to_string(), timestamp.clone()),
3702                ],
3703                None,
3704            )
3705            .unwrap();
3706
3707        let mut expr = parse_one(
3708            "SELECT UNNEST(CASE WHEN closed_at IS NULL THEN ARRAY[created_at] \
3709             ELSE ARRAY[created_at, closed_at] END) AS event_at FROM events",
3710            DialectType::DuckDB,
3711        )
3712        .unwrap();
3713        annotate_types(&mut expr, Some(&schema), Some(DialectType::DuckDB));
3714
3715        let Expression::Select(select) = &expr else {
3716            panic!("expected select");
3717        };
3718        let Expression::Alias(alias) = &select.expressions[0] else {
3719            panic!("expected alias");
3720        };
3721        let Expression::Function(unnest) = &alias.this else {
3722            panic!("expected UNNEST function");
3723        };
3724        assert_eq!(unnest.name, "UNNEST");
3725        let Expression::Case(case) = &unnest.args[0] else {
3726            panic!("expected case");
3727        };
3728        let Expression::ArrayFunc(then_array) = &case.whens[0].1 else {
3729            panic!("expected ARRAY constructor in THEN branch");
3730        };
3731        let Expression::ArrayFunc(else_array) = case.else_.as_ref().expect("expected ELSE") else {
3732            panic!("expected ARRAY constructor in ELSE branch");
3733        };
3734
3735        assert_eq!(then_array.inferred_type.as_ref(), Some(&array_timestamp));
3736        assert_eq!(else_array.inferred_type.as_ref(), Some(&array_timestamp));
3737        assert_eq!(case.inferred_type.as_ref(), Some(&array_timestamp));
3738        assert_eq!(unnest.inferred_type.as_ref(), Some(&timestamp));
3739        assert_eq!(alias.inferred_type.as_ref(), Some(&timestamp));
3740        assert!(then_array
3741            .expressions
3742            .iter()
3743            .all(|expression| { expression.inferred_type() == Some(&timestamp) }));
3744        assert!(else_array
3745            .expressions
3746            .iter()
3747            .all(|expression| { expression.inferred_type() == Some(&timestamp) }));
3748    }
3749
3750    #[test]
3751    fn test_array_and_case_coerce_all_result_types() {
3752        let mut expr = parse_one(
3753            "SELECT CASE WHEN TRUE THEN ARRAY[1] ELSE ARRAY[2.5] END AS values",
3754            DialectType::DuckDB,
3755        )
3756        .unwrap();
3757        annotate_types(&mut expr, None, Some(DialectType::DuckDB));
3758
3759        let Expression::Select(select) = &expr else {
3760            panic!("expected select");
3761        };
3762        let Expression::Alias(alias) = &select.expressions[0] else {
3763            panic!("expected alias");
3764        };
3765        assert_eq!(
3766            alias.this.inferred_type(),
3767            Some(&DataType::Array {
3768                element_type: Box::new(DataType::Double {
3769                    precision: None,
3770                    scale: None,
3771                }),
3772                dimension: None,
3773            })
3774        );
3775    }
3776
3777    #[test]
3778    fn test_transparent_wrappers_preserve_scalar_types() {
3779        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3780        schema
3781            .add_table("flags", &[("flag".to_string(), DataType::Boolean)], None)
3782            .unwrap();
3783        let varchar = DataType::VarChar {
3784            length: None,
3785            parenthesized_length: false,
3786        };
3787
3788        let mut parenthesized = parse_one(
3789            "SELECT (CASE WHEN flag THEN 'yes' ELSE 'no' END) AS label FROM flags",
3790            DialectType::DuckDB,
3791        )
3792        .unwrap();
3793        annotate_types(&mut parenthesized, Some(&schema), Some(DialectType::DuckDB));
3794
3795        let Expression::Select(select) = &parenthesized else {
3796            panic!("expected select");
3797        };
3798        let Expression::Alias(alias) = &select.expressions[0] else {
3799            panic!("expected alias");
3800        };
3801        let Expression::Paren(paren) = &alias.this else {
3802            panic!("expected parenthesized expression");
3803        };
3804        let Expression::Case(case) = &paren.this else {
3805            panic!("expected case");
3806        };
3807        let Expression::Column(column) = &case.whens[0].0 else {
3808            panic!("expected column condition");
3809        };
3810
3811        assert_eq!(alias.this.inferred_type(), Some(&varchar));
3812        assert_eq!(case.inferred_type.as_ref(), Some(&varchar));
3813        assert_eq!(column.inferred_type.as_ref(), Some(&DataType::Boolean));
3814        assert_eq!(alias.inferred_type.as_ref(), Some(&varchar));
3815
3816        let mut annotated = parse_one(
3817            "SELECT CASE WHEN flag THEN 'yes' ELSE 'no' END /*tail*/ FROM flags",
3818            DialectType::DuckDB,
3819        )
3820        .unwrap();
3821        annotate_types(&mut annotated, Some(&schema), Some(DialectType::DuckDB));
3822
3823        let Expression::Select(select) = &annotated else {
3824            panic!("expected select");
3825        };
3826        let Expression::Annotated(annotated) = &select.expressions[0] else {
3827            panic!("expected comment annotation wrapper");
3828        };
3829        let Expression::Case(case) = &annotated.this else {
3830            panic!("expected case");
3831        };
3832        let Expression::Column(column) = &case.whens[0].0 else {
3833            panic!("expected column condition");
3834        };
3835
3836        assert_eq!(select.expressions[0].inferred_type(), Some(&varchar));
3837        assert_eq!(case.inferred_type.as_ref(), Some(&varchar));
3838        assert_eq!(column.inferred_type.as_ref(), Some(&DataType::Boolean));
3839    }
3840
3841    #[test]
3842    fn test_set_operation_type() {
3843        let mut annotator = TypeAnnotator::new(None, None);
3844
3845        // UNION/INTERSECT/EXCEPT return None (they produce relations, not scalars)
3846        let select = Expression::Select(Box::new(crate::expressions::Select::default()));
3847        let union = Expression::Union(Box::new(crate::expressions::Union {
3848            left: select.clone(),
3849            right: select.clone(),
3850            all: false,
3851            distinct: false,
3852            with: None,
3853            order_by: None,
3854            limit: None,
3855            offset: None,
3856            by_name: false,
3857            side: None,
3858            kind: None,
3859            corresponding: false,
3860            strict: false,
3861            on_columns: Vec::new(),
3862            distribute_by: None,
3863            sort_by: None,
3864            cluster_by: None,
3865        }));
3866        assert_eq!(annotator.annotate(&union), None);
3867    }
3868
3869    #[test]
3870    fn test_floor_ceil_input_dependent_types() {
3871        use crate::expressions::{CeilFunc, FloorFunc};
3872
3873        let mut annotator = TypeAnnotator::new(None, None);
3874
3875        // FLOOR/CEIL with integer literal → Double (integers get promoted)
3876        let floor_int = Expression::Floor(Box::new(FloorFunc {
3877            this: make_int_literal(42),
3878            scale: None,
3879            to: None,
3880        }));
3881        assert_eq!(
3882            annotator.annotate(&floor_int),
3883            Some(DataType::Double {
3884                precision: None,
3885                scale: None,
3886            })
3887        );
3888
3889        let ceil_int = Expression::Ceil(Box::new(CeilFunc {
3890            this: make_int_literal(42),
3891            decimals: None,
3892            to: None,
3893        }));
3894        assert_eq!(
3895            annotator.annotate(&ceil_int),
3896            Some(DataType::Double {
3897                precision: None,
3898                scale: None,
3899            })
3900        );
3901
3902        // FLOOR with float literal → Double (literals are always Double)
3903        let floor_float = Expression::Floor(Box::new(FloorFunc {
3904            this: make_float_literal(3.14),
3905            scale: None,
3906            to: None,
3907        }));
3908        assert_eq!(
3909            annotator.annotate(&floor_float),
3910            Some(DataType::Double {
3911                precision: None,
3912                scale: None,
3913            })
3914        );
3915
3916        // FLOOR via Function("FLOOR") path → falls through to arg-based inference
3917        let floor_fn =
3918            Expression::Function(Box::new(Function::new("FLOOR", vec![make_int_literal(1)])));
3919        assert_eq!(
3920            annotator.annotate(&floor_fn),
3921            Some(DataType::Int {
3922                length: None,
3923                integer_spelling: false,
3924            })
3925        );
3926    }
3927
3928    #[test]
3929    fn test_sign_preserves_input_type() {
3930        use crate::expressions::UnaryFunc;
3931
3932        let mut annotator = TypeAnnotator::new(None, None);
3933
3934        // SIGN with integer literal → Int (preserves input type)
3935        let sign_int = Expression::Sign(Box::new(UnaryFunc {
3936            this: make_int_literal(42),
3937            original_name: None,
3938            inferred_type: None,
3939        }));
3940        assert_eq!(
3941            annotator.annotate(&sign_int),
3942            Some(DataType::Int {
3943                length: None,
3944                integer_spelling: false,
3945            })
3946        );
3947
3948        // SIGN with float literal → Double (preserves input type)
3949        let sign_float = Expression::Sign(Box::new(UnaryFunc {
3950            this: make_float_literal(3.14),
3951            original_name: None,
3952            inferred_type: None,
3953        }));
3954        assert_eq!(
3955            annotator.annotate(&sign_float),
3956            Some(DataType::Double {
3957                precision: None,
3958                scale: None,
3959            })
3960        );
3961
3962        // SIGN with a CAST to INT → Int (preserves input type)
3963        let sign_cast = Expression::Sign(Box::new(UnaryFunc {
3964            this: Expression::Cast(Box::new(Cast {
3965                this: make_int_literal(42),
3966                to: DataType::Int {
3967                    length: None,
3968                    integer_spelling: false,
3969                },
3970                format: None,
3971                trailing_comments: Vec::new(),
3972                double_colon_syntax: false,
3973                default: None,
3974                inferred_type: None,
3975            })),
3976            original_name: None,
3977            inferred_type: None,
3978        }));
3979        assert_eq!(
3980            annotator.annotate(&sign_cast),
3981            Some(DataType::Int {
3982                length: None,
3983                integer_spelling: false,
3984            })
3985        );
3986    }
3987
3988    #[test]
3989    fn test_date_format_types() {
3990        use crate::expressions::{DateFormatFunc, TimeToStr};
3991
3992        let mut annotator = TypeAnnotator::new(None, None);
3993
3994        // DateFormat → VarChar
3995        let date_fmt = Expression::DateFormat(Box::new(DateFormatFunc {
3996            this: make_string_literal("2024-01-01"),
3997            format: make_string_literal("%Y-%m-%d"),
3998        }));
3999        assert_eq!(
4000            annotator.annotate(&date_fmt),
4001            Some(DataType::VarChar {
4002                length: None,
4003                parenthesized_length: false,
4004            })
4005        );
4006
4007        // FormatDate → VarChar
4008        let format_date = Expression::FormatDate(Box::new(DateFormatFunc {
4009            this: make_string_literal("2024-01-01"),
4010            format: make_string_literal("%Y-%m-%d"),
4011        }));
4012        assert_eq!(
4013            annotator.annotate(&format_date),
4014            Some(DataType::VarChar {
4015                length: None,
4016                parenthesized_length: false,
4017            })
4018        );
4019
4020        // TimeToStr → VarChar
4021        let time_to_str = Expression::TimeToStr(Box::new(TimeToStr {
4022            this: Box::new(make_string_literal("2024-01-01")),
4023            format: "%Y-%m-%d".to_string(),
4024            culture: None,
4025            zone: None,
4026        }));
4027        assert_eq!(
4028            annotator.annotate(&time_to_str),
4029            Some(DataType::VarChar {
4030                length: None,
4031                parenthesized_length: false,
4032            })
4033        );
4034
4035        // DATE_FORMAT via Function path → VarChar (uses function_return_types)
4036        let date_fmt_fn = Expression::Function(Box::new(Function::new(
4037            "DATE_FORMAT",
4038            vec![
4039                make_string_literal("2024-01-01"),
4040                make_string_literal("%Y-%m-%d"),
4041            ],
4042        )));
4043        assert_eq!(
4044            annotator.annotate(&date_fmt_fn),
4045            Some(DataType::VarChar {
4046                length: None,
4047                parenthesized_length: false,
4048            })
4049        );
4050    }
4051
4052    // ===== In-place annotation tests (Step 9) =====
4053
4054    #[test]
4055    fn test_annotate_in_place_sets_type_on_root() {
4056        // Literals don't have inferred_type field, so test with a BinaryOp
4057        let mut expr = Expression::Add(Box::new(BinaryOp::new(
4058            make_int_literal(1),
4059            make_int_literal(2),
4060        )));
4061        annotate_types(&mut expr, None, None);
4062        assert_eq!(
4063            expr.inferred_type(),
4064            Some(&DataType::Int {
4065                length: None,
4066                integer_spelling: false,
4067            })
4068        );
4069    }
4070
4071    #[test]
4072    fn test_annotate_in_place_sets_types_on_children() {
4073        // (a + b) + (c - d) where all are ints
4074        // This tests that inner BinaryOp children also get annotated
4075        let inner_add = Expression::Add(Box::new(BinaryOp::new(
4076            make_int_literal(1),
4077            make_float_literal(2.5),
4078        )));
4079        let inner_sub = Expression::Sub(Box::new(BinaryOp::new(
4080            make_int_literal(3),
4081            make_int_literal(4),
4082        )));
4083        let mut expr = Expression::Add(Box::new(BinaryOp::new(inner_add, inner_sub)));
4084        annotate_types(&mut expr, None, None);
4085
4086        // Root (Add) should be Double (wider of Double and Int)
4087        assert_eq!(
4088            expr.inferred_type(),
4089            Some(&DataType::Double {
4090                precision: None,
4091                scale: None,
4092            })
4093        );
4094
4095        // Children should also have types
4096        if let Expression::Add(op) = &expr {
4097            // Left child (1 + 2.5) should be Double
4098            assert_eq!(
4099                op.left.inferred_type(),
4100                Some(&DataType::Double {
4101                    precision: None,
4102                    scale: None,
4103                })
4104            );
4105            // Right child (3 - 4) should be Int
4106            assert_eq!(
4107                op.right.inferred_type(),
4108                Some(&DataType::Int {
4109                    length: None,
4110                    integer_spelling: false,
4111                })
4112            );
4113        } else {
4114            panic!("Expected Add expression");
4115        }
4116    }
4117
4118    #[test]
4119    fn test_annotate_in_place_comparison() {
4120        let mut expr = Expression::Eq(Box::new(BinaryOp::new(
4121            make_int_literal(1),
4122            make_int_literal(2),
4123        )));
4124        annotate_types(&mut expr, None, None);
4125        assert_eq!(expr.inferred_type(), Some(&DataType::Boolean));
4126    }
4127
4128    #[test]
4129    fn test_annotate_in_place_cast() {
4130        let mut expr = Expression::Cast(Box::new(Cast {
4131            this: make_int_literal(42),
4132            to: DataType::VarChar {
4133                length: None,
4134                parenthesized_length: false,
4135            },
4136            trailing_comments: vec![],
4137            double_colon_syntax: false,
4138            format: None,
4139            default: None,
4140            inferred_type: None,
4141        }));
4142        annotate_types(&mut expr, None, None);
4143        assert_eq!(
4144            expr.inferred_type(),
4145            Some(&DataType::VarChar {
4146                length: None,
4147                parenthesized_length: false,
4148            })
4149        );
4150    }
4151
4152    #[test]
4153    fn test_annotate_in_place_nested_expression() {
4154        // (1 + 2) > 0  -> should be Boolean at root, Int for the Add
4155        let add = Expression::Add(Box::new(BinaryOp::new(
4156            make_int_literal(1),
4157            make_int_literal(2),
4158        )));
4159        let mut expr = Expression::Gt(Box::new(BinaryOp::new(add, make_int_literal(0))));
4160        annotate_types(&mut expr, None, None);
4161
4162        assert_eq!(expr.inferred_type(), Some(&DataType::Boolean));
4163
4164        // The left child (Add) should be Int
4165        if let Expression::Gt(op) = &expr {
4166            assert_eq!(
4167                op.left.inferred_type(),
4168                Some(&DataType::Int {
4169                    length: None,
4170                    integer_spelling: false,
4171                })
4172            );
4173        }
4174    }
4175
4176    #[test]
4177    fn test_annotate_in_place_parsed_sql() {
4178        use crate::parser::Parser;
4179        let mut expr =
4180            Parser::parse_sql("SELECT 1 + 2.0, 'hello', TRUE").expect("parse failed")[0].clone();
4181        annotate_types(&mut expr, None, None);
4182
4183        // The expression tree should have types annotated throughout
4184        // We can't easily inspect deep inside a parsed Select, but at minimum
4185        // the root Select itself won't have a type (it's not value-producing)
4186        assert!(expr.inferred_type().is_none());
4187    }
4188
4189    #[test]
4190    fn test_inferred_type_json_roundtrip() {
4191        let mut expr = Expression::Add(Box::new(BinaryOp::new(
4192            make_int_literal(1),
4193            make_int_literal(2),
4194        )));
4195        annotate_types(&mut expr, None, None);
4196
4197        // Serialize to JSON
4198        let json = serde_json::to_string(&expr).expect("serialize failed");
4199        // The JSON should contain the inferred_type
4200        assert!(json.contains("inferred_type"));
4201
4202        // Deserialize back
4203        let deserialized: Expression = serde_json::from_str(&json).expect("deserialize failed");
4204        assert_eq!(
4205            deserialized.inferred_type(),
4206            Some(&DataType::Int {
4207                length: None,
4208                integer_spelling: false,
4209            })
4210        );
4211    }
4212
4213    #[test]
4214    fn test_inferred_type_none_not_serialized() {
4215        // When inferred_type is None, it should not appear in JSON
4216        let expr = Expression::Add(Box::new(BinaryOp::new(
4217            make_int_literal(1),
4218            make_int_literal(2),
4219        )));
4220        let json = serde_json::to_string(&expr).expect("serialize failed");
4221        assert!(!json.contains("inferred_type"));
4222    }
4223
4224    #[test]
4225    fn test_annotate_if_func_bigquery_node_and_alias_type() {
4226        let mut schema = MappingSchema::with_dialect(DialectType::BigQuery);
4227        schema
4228            .add_table(
4229                "t",
4230                &[("col1".to_string(), DataType::String { length: None })],
4231                None,
4232            )
4233            .unwrap();
4234
4235        let mut expr = parse_one(
4236            "SELECT IF(col1 IS NOT NULL, 1, 0) AS x FROM t",
4237            DialectType::BigQuery,
4238        )
4239        .unwrap();
4240        annotate_types(&mut expr, Some(&schema), Some(DialectType::BigQuery));
4241
4242        let Expression::Select(select) = &expr else {
4243            panic!("expected select");
4244        };
4245        let Expression::Alias(alias) = &select.expressions[0] else {
4246            panic!("expected alias");
4247        };
4248
4249        assert_eq!(
4250            alias.this.inferred_type(),
4251            Some(&DataType::Int {
4252                length: None,
4253                integer_spelling: false,
4254            })
4255        );
4256        assert_eq!(
4257            select.expressions[0].inferred_type(),
4258            Some(&DataType::Int {
4259                length: None,
4260                integer_spelling: false,
4261            })
4262        );
4263    }
4264
4265    #[test]
4266    fn test_annotate_nvl2_node_type() {
4267        let mut expr = parse_one("SELECT NVL2(a, 1, 0) AS x", DialectType::Generic).unwrap();
4268        annotate_types(&mut expr, None, None);
4269
4270        let Expression::Select(select) = &expr else {
4271            panic!("expected select");
4272        };
4273        let Expression::Alias(alias) = &select.expressions[0] else {
4274            panic!("expected alias");
4275        };
4276
4277        assert_eq!(
4278            alias.this.inferred_type(),
4279            Some(&DataType::Int {
4280                length: None,
4281                integer_spelling: false,
4282            })
4283        );
4284    }
4285
4286    #[test]
4287    fn test_annotate_count_node_type() {
4288        let mut expr = parse_one("SELECT COUNT(1) AS x", DialectType::Generic).unwrap();
4289        annotate_types(&mut expr, None, None);
4290
4291        let Expression::Select(select) = &expr else {
4292            panic!("expected select");
4293        };
4294        let Expression::Alias(alias) = &select.expressions[0] else {
4295            panic!("expected alias");
4296        };
4297
4298        assert_eq!(
4299            alias.this.inferred_type(),
4300            Some(&DataType::BigInt { length: None })
4301        );
4302    }
4303
4304    #[test]
4305    fn test_annotate_group_concat_node_type() {
4306        let mut expr = parse_one("SELECT GROUP_CONCAT(a) AS x", DialectType::Generic).unwrap();
4307        annotate_types(&mut expr, None, None);
4308
4309        let Expression::Select(select) = &expr else {
4310            panic!("expected select");
4311        };
4312        let Expression::Alias(alias) = &select.expressions[0] else {
4313            panic!("expected alias");
4314        };
4315
4316        assert_eq!(
4317            alias.this.inferred_type(),
4318            Some(&DataType::VarChar {
4319                length: None,
4320                parenthesized_length: false,
4321            })
4322        );
4323    }
4324
4325    #[test]
4326    fn test_annotate_sum_if_generic_aggregate_type() {
4327        let mut expr =
4328            parse_one("SELECT SUM_IF(1, a > 0) AS x FROM t", DialectType::Generic).unwrap();
4329        annotate_types(&mut expr, None, None);
4330
4331        let Expression::Select(select) = &expr else {
4332            panic!("expected select");
4333        };
4334        let Expression::Alias(alias) = &select.expressions[0] else {
4335            panic!("expected alias");
4336        };
4337
4338        assert_eq!(
4339            select.expressions[0].inferred_type(),
4340            Some(&DataType::BigInt { length: None })
4341        );
4342        assert_eq!(
4343            alias.this.inferred_type(),
4344            Some(&DataType::BigInt { length: None })
4345        );
4346    }
4347
4348    #[test]
4349    fn test_annotate_duckdb_median_node_types() {
4350        let cases = [
4351            (
4352                "SELECT MEDIAN(CAST(1 AS INTEGER)) AS result",
4353                DataType::Double {
4354                    precision: None,
4355                    scale: None,
4356                },
4357            ),
4358            (
4359                "SELECT MEDIAN(CAST(1 AS FLOAT)) AS result",
4360                DataType::Float {
4361                    precision: None,
4362                    scale: None,
4363                    real_spelling: false,
4364                },
4365            ),
4366            (
4367                "SELECT MEDIAN(CAST(1 AS DECIMAL(10, 2))) AS result",
4368                DataType::Decimal {
4369                    precision: Some(10),
4370                    scale: Some(2),
4371                },
4372            ),
4373            (
4374                "SELECT MEDIAN(CAST('2024-01-01' AS DATE)) AS result",
4375                DataType::Timestamp {
4376                    precision: None,
4377                    timezone: false,
4378                },
4379            ),
4380        ];
4381
4382        for (sql, expected) in cases {
4383            let mut expr = parse_one(sql, DialectType::DuckDB).unwrap();
4384            annotate_types(&mut expr, None, Some(DialectType::DuckDB));
4385
4386            let Expression::Select(select) = &expr else {
4387                panic!("expected select for {sql}");
4388            };
4389            let Expression::Alias(alias) = &select.expressions[0] else {
4390                panic!("expected alias for {sql}");
4391            };
4392
4393            assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
4394            assert_eq!(
4395                select.expressions[0].inferred_type(),
4396                Some(&expected),
4397                "{sql}"
4398            );
4399        }
4400    }
4401}