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