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