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