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        for cte in &mut with.ctes {
2484            let name = crate::binding::identifier_name(&cte.alias);
2485            // Bind the anchor before visiting a recursive arm. Only stable
2486            // output types are retained; widening through recursion is not a
2487            // regular UNION and needs engine-specific recursive-CTE validation.
2488            let recursive_anchor = if let Expression::Union(union) = &mut cte.this {
2489                let references_self = union.right.contains(|node| matches!(node,
2490                    Expression::Table(table) if table.schema.is_none() && table.catalog.is_none()
2491                        && normalize_name(&crate::binding::identifier_name(&table.name), dialect, false, true)
2492                            == normalize_name(&name, dialect, false, true)));
2493                if references_self {
2494                    let columns = annotate_scoped_expression_with_outer(
2495                        &mut union.left,
2496                        Some(schema),
2497                        dialect,
2498                        outer,
2499                    );
2500                    let columns = apply_column_aliases(columns, &cte.columns);
2501                    let _ = schema.add_table(&name, &columns, dialect);
2502                    Some(columns)
2503                } else {
2504                    None
2505                }
2506            } else {
2507                None
2508            };
2509            let mut columns =
2510                annotate_scoped_expression_with_outer(&mut cte.this, Some(schema), dialect, outer);
2511            if let Some(anchor) = recursive_anchor {
2512                for ((_, result), (_, initial)) in columns.iter_mut().zip(anchor) {
2513                    if *result != initial {
2514                        *result = DataType::Unknown;
2515                    }
2516                }
2517            }
2518            let columns = apply_column_aliases(columns, &cte.columns);
2519            let _ = schema.add_table(
2520                &crate::binding::identifier_name(&cte.alias),
2521                &columns,
2522                dialect,
2523            );
2524        }
2525    }
2526}
2527
2528fn annotate_select(
2529    select: &mut crate::expressions::Select,
2530    parent: Option<&dyn Schema>,
2531    dialect: Option<DialectType>,
2532    outer: Option<&dyn Schema>,
2533) -> OutputColumns {
2534    let mut ctes = ScopedSchema::new(parent, dialect);
2535    annotate_with(&mut select.with, &mut ctes, dialect, outer);
2536    let mut schema = ScopedSchema::new(Some(&ctes), dialect);
2537    schema.outer = outer;
2538
2539    if let Some(from) = &mut select.from {
2540        for source in &mut from.expressions {
2541            annotate_relation_source(source, &mut schema, dialect);
2542        }
2543    }
2544    for join in &mut select.joins {
2545        annotate_relation_source(&mut join.this, &mut schema, dialect);
2546    }
2547
2548    schema.lookup_parent_columns = false;
2549    let mut annotator = TypeAnnotator::new(Some(&schema), dialect);
2550    annotator.query_schema = Some(&ctes);
2551    for expression in &mut select.expressions {
2552        annotator.annotate_in_place(expression);
2553    }
2554    // Clause expressions use selected inputs, not the whole catalogue.
2555    if let Some(expression) = &mut select.prewhere {
2556        annotator.annotate_in_place(expression);
2557    }
2558    if let Some(clause) = &mut select.qualify {
2559        annotator.annotate_in_place(&mut clause.this);
2560    }
2561    if let Some(clause) = &mut select.where_clause {
2562        annotator.annotate_in_place(&mut clause.this);
2563    }
2564    if let Some(clause) = &mut select.having {
2565        annotator.annotate_in_place(&mut clause.this);
2566    }
2567    if let Some(group) = &mut select.group_by {
2568        for expression in &mut group.expressions {
2569            annotator.annotate_in_place(expression);
2570        }
2571    }
2572    if let Some(order) = &mut select.order_by {
2573        for expression in &mut order.expressions {
2574            annotator.annotate_in_place(&mut expression.this);
2575        }
2576    }
2577    for join in &mut select.joins {
2578        if let Some(on) = &mut join.on {
2579            annotator.annotate_in_place(on);
2580        }
2581        if let Some(condition) = &mut join.match_condition {
2582            annotator.annotate_in_place(condition);
2583        }
2584    }
2585    query_outputs(&select.expressions, dialect)
2586}
2587
2588fn annotate_scoped_expression(
2589    expression: &mut Expression,
2590    parent: Option<&dyn Schema>,
2591    dialect: Option<DialectType>,
2592) -> OutputColumns {
2593    annotate_scoped_expression_with_outer(expression, parent, dialect, None)
2594}
2595
2596fn annotate_scoped_expression_with_outer(
2597    expression: &mut Expression,
2598    parent: Option<&dyn Schema>,
2599    dialect: Option<DialectType>,
2600    outer: Option<&dyn Schema>,
2601) -> OutputColumns {
2602    match expression {
2603        Expression::Select(select) => annotate_select(select, parent, dialect, outer),
2604        Expression::Subquery(subquery) => {
2605            let columns =
2606                annotate_scoped_expression_with_outer(&mut subquery.this, parent, dialect, outer);
2607            if let Some((_, first_type)) = columns.first() {
2608                subquery.inferred_type = Some(first_type.clone());
2609            }
2610            columns
2611        }
2612        Expression::Cte(cte) => {
2613            annotate_scoped_expression_with_outer(&mut cte.this, parent, dialect, outer)
2614        }
2615        Expression::Paren(paren) => {
2616            annotate_scoped_expression_with_outer(&mut paren.this, parent, dialect, outer)
2617        }
2618        Expression::Union(union) => {
2619            let mut schema = ScopedSchema::new(parent, dialect);
2620            annotate_with(&mut union.with, &mut schema, dialect, outer);
2621            annotate_scoped_expression_with_outer(&mut union.left, Some(&schema), dialect, outer);
2622            annotate_scoped_expression_with_outer(&mut union.right, Some(&schema), dialect, outer);
2623            super::set_operation_types::query_columns(expression, dialect)
2624        }
2625        Expression::Intersect(intersect) => {
2626            let mut schema = ScopedSchema::new(parent, dialect);
2627            annotate_with(&mut intersect.with, &mut schema, dialect, outer);
2628            annotate_scoped_expression_with_outer(
2629                &mut intersect.left,
2630                Some(&schema),
2631                dialect,
2632                outer,
2633            );
2634            annotate_scoped_expression_with_outer(
2635                &mut intersect.right,
2636                Some(&schema),
2637                dialect,
2638                outer,
2639            );
2640            super::set_operation_types::query_columns(expression, dialect)
2641        }
2642        Expression::Except(except) => {
2643            let mut schema = ScopedSchema::new(parent, dialect);
2644            annotate_with(&mut except.with, &mut schema, dialect, outer);
2645            annotate_scoped_expression_with_outer(&mut except.left, Some(&schema), dialect, outer);
2646            annotate_scoped_expression_with_outer(&mut except.right, Some(&schema), dialect, outer);
2647            super::set_operation_types::query_columns(expression, dialect)
2648        }
2649        _ => {
2650            if let Some(mut selected) = crate::binding::dml_scope(expression) {
2651                let mut ctes = ScopedSchema::new(parent, dialect);
2652                annotate_with(&mut selected.with, &mut ctes, dialect, outer);
2653                let mut inputs = ScopedSchema::new(Some(&ctes), dialect);
2654                inputs.outer = outer;
2655                if let Some(from) = &mut selected.from {
2656                    for source in &mut from.expressions {
2657                        annotate_relation_source(source, &mut inputs, dialect);
2658                    }
2659                }
2660                for join in &mut selected.joins {
2661                    annotate_relation_source(&mut join.this, &mut inputs, dialect);
2662                }
2663                inputs.lookup_parent_columns = false;
2664                let mut annotator = TypeAnnotator::new(Some(&inputs), dialect);
2665                annotator.query_schema = Some(&ctes);
2666                crate::ast_children::for_each_child_mut(expression, |child| {
2667                    annotator.annotate_in_place(child)
2668                });
2669                return Vec::new();
2670            }
2671            let mut annotator = TypeAnnotator::new(parent, dialect);
2672            annotator.annotate_in_place(expression);
2673            Vec::new()
2674        }
2675    }
2676}
2677
2678/// Annotate types in-place on the expression tree.
2679///
2680/// Walks the AST bottom-up and sets `inferred_type` on each value-producing
2681/// node. After this call, `expr.inferred_type()` (and the same on any child
2682/// node) returns the inferred type.
2683pub fn annotate_types(
2684    expr: &mut Expression,
2685    schema: Option<&dyn Schema>,
2686    dialect: Option<DialectType>,
2687) {
2688    annotate_scoped_expression(expr, schema, dialect);
2689}
2690
2691#[cfg(test)]
2692mod tests {
2693    use super::*;
2694    use crate::expressions::{BooleanLiteral, Cast, ExtractFunc, Null};
2695    use crate::{parse_one, DialectType, MappingSchema, Schema};
2696
2697    #[test]
2698    fn set_operation_types_cover_all_dialects_454() {
2699        use DialectType::*;
2700        // Exercise real query annotation, including the external-source binding,
2701        // rather than just mirroring the policy dispatch.
2702        for dialect in [
2703            Generic,
2704            PostgreSQL,
2705            MySQL,
2706            BigQuery,
2707            Snowflake,
2708            DuckDB,
2709            SQLite,
2710            Hive,
2711            Spark,
2712            Trino,
2713            Presto,
2714            Redshift,
2715            TSQL,
2716            Oracle,
2717            ClickHouse,
2718            Databricks,
2719            Athena,
2720            Teradata,
2721            Doris,
2722            StarRocks,
2723            Materialize,
2724            RisingWave,
2725            SingleStore,
2726            CockroachDB,
2727            TiDB,
2728            Druid,
2729            Solr,
2730            Tableau,
2731            Dune,
2732            Fabric,
2733            Drill,
2734            Dremio,
2735            Exasol,
2736            DataFusion,
2737        ] {
2738            for (left, right) in [("INT", "BIGINT"), ("BIGINT", "INT")] {
2739                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");
2740                let mut expression = parse_one(&sql, dialect).unwrap();
2741                annotate_types(&mut expression, None, Some(dialect));
2742                let Expression::Select(select) = expression else {
2743                    panic!("select")
2744                };
2745                let actual = select.expressions[0].inferred_type();
2746                match dialect {
2747                    BigQuery => assert!(
2748                        matches!(actual, Some(DataType::Custom { name }) if name == "INT64"),
2749                        "{dialect:?}: {actual:?}"
2750                    ),
2751                    SQLite => assert!(
2752                        actual.is_none_or(|t| *t == DataType::Unknown),
2753                        "{dialect:?}: {actual:?}"
2754                    ),
2755                    Teradata if left == "INT" => assert!(
2756                        matches!(actual, Some(DataType::Int { .. })),
2757                        "{dialect:?}: {actual:?}"
2758                    ),
2759                    Oracle => assert!(
2760                        matches!(
2761                            actual,
2762                            Some(DataType::Oracle {
2763                                oracle_type: crate::expressions::OracleDataType::Number { .. }
2764                            })
2765                        ),
2766                        "{dialect:?}: {actual:?}"
2767                    ),
2768                    _ => assert!(
2769                        matches!(actual, Some(DataType::BigInt { .. })),
2770                        "{dialect:?}: {actual:?}"
2771                    ),
2772                }
2773            }
2774            let mut expression = parse_one(
2775                "SELECT t.a FROM (SELECT CAST(1 AS DECIMAL(10,2)) AS a UNION ALL SELECT missing_function() AS a) AS t", dialect
2776            ).unwrap();
2777            annotate_types(&mut expression, None, Some(dialect));
2778            let Expression::Select(select) = expression else {
2779                panic!("select")
2780            };
2781            assert!(
2782                select.expressions[0]
2783                    .inferred_type()
2784                    .is_none_or(|t| *t == DataType::Unknown),
2785                "{dialect:?}: {:?}",
2786                select.expressions[0].inferred_type()
2787            );
2788        }
2789    }
2790
2791    #[test]
2792    fn set_operation_coercion_parameters_and_isolation_454() {
2793        use super::super::set_operation_types::common_type;
2794        let mut literal_query = parse_one(
2795            "SELECT t.a FROM (SELECT 1.5 AS a UNION ALL SELECT 2.5 AS a) AS t",
2796            DialectType::PostgreSQL,
2797        )
2798        .unwrap();
2799        annotate_types(&mut literal_query, None, Some(DialectType::PostgreSQL));
2800        let Expression::Select(select) = literal_query else {
2801            panic!("select")
2802        };
2803        assert!(matches!(
2804            select.expressions[0].inferred_type(),
2805            Some(DataType::Decimal {
2806                precision: None,
2807                scale: None
2808            })
2809        ));
2810        let dec = |p, s| DataType::Decimal {
2811            precision: Some(p),
2812            scale: Some(s),
2813        };
2814        let varchar = |n| DataType::VarChar {
2815            length: Some(n),
2816            parenthesized_length: false,
2817        };
2818        let array = |t| DataType::Array {
2819            element_type: Box::new(t),
2820            dimension: None,
2821        };
2822        let int = DataType::Int {
2823            length: None,
2824            integer_spelling: false,
2825        };
2826        let double = double_type();
2827        for dialect in [
2828            DialectType::TSQL,
2829            DialectType::DuckDB,
2830            DialectType::DataFusion,
2831            DialectType::Trino,
2832        ] {
2833            assert_eq!(
2834                common_type(&dec(10, 2), &dec(8, 4), dialect, 0),
2835                Some(dec(12, 4))
2836            );
2837            assert_eq!(
2838                common_type(&dec(8, 4), &dec(10, 2), dialect, 0),
2839                Some(dec(12, 4))
2840            );
2841            assert_eq!(
2842                common_type(&dec(10, 2), &DataType::Unknown, dialect, 0),
2843                None
2844            );
2845            assert_eq!(
2846                common_type(&array(int.clone()), &array(DataType::Unknown), dialect, 0),
2847                None
2848            );
2849        }
2850        assert_eq!(
2851            common_type(&varchar(10), &varchar(30), DialectType::TSQL, 0),
2852            Some(varchar(30))
2853        );
2854        assert_eq!(
2855            common_type(
2856                &array(int.clone()),
2857                &array(double.clone()),
2858                DialectType::DuckDB,
2859                0
2860            ),
2861            Some(array(double))
2862        );
2863        assert_eq!(
2864            common_type(
2865                &DataType::BigInt { length: None },
2866                &DataType::UInt64,
2867                DialectType::DuckDB,
2868                0
2869            ),
2870            Some(DataType::Int128)
2871        );
2872        assert_eq!(
2873            common_type(&dec(38, 20), &dec(38, 0), DialectType::TSQL, 0),
2874            Some(dec(38, 0))
2875        );
2876        assert_eq!(
2877            common_type(&dec(38, 20), &dec(38, 0), DialectType::Trino, 0),
2878            None
2879        );
2880        assert_eq!(
2881            common_type(&dec(10, 2), &double_type(), DialectType::DataFusion, 0),
2882            Some(dec(30, 15))
2883        );
2884        assert_eq!(
2885            common_type(
2886                &DataType::UInt64,
2887                &DataType::BigInt { length: None },
2888                DialectType::ClickHouse,
2889                0
2890            ),
2891            None
2892        );
2893        assert_eq!(
2894            common_type(
2895                &DataType::UInt64,
2896                &DataType::BigInt { length: None },
2897                DialectType::DataFusion,
2898                0
2899            ),
2900            Some(dec(20, 0))
2901        );
2902        let structure = |fields: Vec<(&str, DataType)>| DataType::Struct {
2903            fields: fields
2904                .into_iter()
2905                .map(|(name, t)| StructField::new(name.to_owned(), t))
2906                .collect(),
2907            nested: false,
2908        };
2909        let left = structure(vec![("x", int.clone()), ("y", varchar(10))]);
2910        let right = structure(vec![("y", varchar(30)), ("x", double_type())]);
2911        let combined = structure(vec![("x", double_type()), ("y", varchar(30))]);
2912        assert_eq!(
2913            common_type(&left, &right, DialectType::DataFusion, 0),
2914            Some(combined)
2915        );
2916        let right = structure(vec![("z", DataType::Boolean), ("x", double_type())]);
2917        assert_eq!(
2918            common_type(
2919                &structure(vec![("x", int.clone())]),
2920                &right,
2921                DialectType::DuckDB,
2922                0
2923            ),
2924            Some(structure(vec![
2925                ("x", double_type()),
2926                ("z", DataType::Boolean)
2927            ]))
2928        );
2929        assert_eq!(
2930            common_type(
2931                &array(int.clone()),
2932                &array(double_type()),
2933                DialectType::BigQuery,
2934                0
2935            ),
2936            None
2937        );
2938        let map = |value_type| DataType::Map {
2939            key_type: Box::new(DataType::Text),
2940            value_type: Box::new(value_type),
2941        };
2942        assert_eq!(
2943            common_type(
2944                &map(int.clone()),
2945                &map(double_type()),
2946                DialectType::DuckDB,
2947                0
2948            ),
2949            Some(map(double_type()))
2950        );
2951        let timestamp = |p| DataType::Timestamp {
2952            precision: Some(p),
2953            timezone: false,
2954        };
2955        assert_eq!(
2956            common_type(&timestamp(3), &timestamp(6), DialectType::TSQL, 0),
2957            Some(timestamp(6))
2958        );
2959        assert_eq!(
2960            common_type(&DataType::Date, &timestamp(6), DialectType::PostgreSQL, 0),
2961            Some(timestamp(6))
2962        );
2963        for dialect in [
2964            DialectType::BigQuery,
2965            DialectType::Hive,
2966            DialectType::Databricks,
2967            DialectType::Generic,
2968            DialectType::BigQuery,
2969        ] {
2970            assert_eq!(common_type(&varchar(10), &DataType::Date, dialect, 0), None);
2971        }
2972    }
2973
2974    fn make_int_literal(val: i64) -> Expression {
2975        Expression::Literal(Box::new(Literal::Number(val.to_string())))
2976    }
2977
2978    fn make_float_literal(val: f64) -> Expression {
2979        Expression::Literal(Box::new(Literal::Number(val.to_string())))
2980    }
2981
2982    fn make_string_literal(val: &str) -> Expression {
2983        Expression::Literal(Box::new(Literal::String(val.to_string())))
2984    }
2985
2986    fn make_bool_literal(val: bool) -> Expression {
2987        Expression::Boolean(BooleanLiteral { value: val })
2988    }
2989
2990    #[test]
2991    fn test_annotate_cte_bindings_exclude_unselected_definitions() {
2992        for sql in [
2993            "WITH a AS (SELECT CAST('1' AS INT) AS n), b AS (SELECT n FROM a) SELECT n FROM b",
2994            "WITH unused AS (SELECT CAST('x' AS TEXT) AS n), a AS (SELECT CAST('1' AS INT) AS n) SELECT n FROM a",
2995            "WITH a(n) AS (SELECT CAST('1' AS INT)), b(m) AS (SELECT n FROM a) SELECT m FROM b",
2996        ] {
2997            let mut expression = parse_one(sql, DialectType::DuckDB).unwrap();
2998            annotate_types(&mut expression, None, Some(DialectType::DuckDB));
2999            let Expression::Select(select) = expression else { panic!("select") };
3000            assert!(matches!(select.expressions[0].inferred_type(), Some(DataType::Int { .. })), "{sql}");
3001        }
3002    }
3003
3004    #[test]
3005    fn test_annotate_does_not_resolve_unselected_or_ambiguous_columns() {
3006        let mut schema = MappingSchema::new();
3007        schema
3008            .add_table("unrelated", &[("n".into(), DataType::Text)], None)
3009            .unwrap();
3010        for sql in [
3011            "SELECT n FROM missing",
3012            "SELECT unrelated.n FROM missing",
3013            "WITH a AS (SELECT 1 AS n), b AS (SELECT 2 AS n) SELECT n FROM a CROSS JOIN b",
3014            "WITH unused AS (SELECT 1 AS n) SELECT n FROM missing",
3015        ] {
3016            let mut expression = parse_one(sql, DialectType::DuckDB).unwrap();
3017            annotate_types(&mut expression, Some(&schema), Some(DialectType::DuckDB));
3018            let Expression::Select(select) = expression else {
3019                panic!("select")
3020            };
3021            assert!(select.expressions[0].inferred_type().is_none(), "{sql}");
3022        }
3023    }
3024
3025    #[test]
3026    fn test_literal_types() {
3027        let mut annotator = TypeAnnotator::new(None, None);
3028
3029        // Integer literal
3030        let int_expr = make_int_literal(42);
3031        assert_eq!(
3032            annotator.annotate(&int_expr),
3033            Some(DataType::Int {
3034                length: None,
3035                integer_spelling: false
3036            })
3037        );
3038
3039        // Float literal
3040        let float_expr = make_float_literal(3.14);
3041        assert_eq!(
3042            annotator.annotate(&float_expr),
3043            Some(DataType::Double {
3044                precision: None,
3045                scale: None
3046            })
3047        );
3048
3049        // String literal
3050        let string_expr = make_string_literal("hello");
3051        assert_eq!(
3052            annotator.annotate(&string_expr),
3053            Some(DataType::VarChar {
3054                length: None,
3055                parenthesized_length: false
3056            })
3057        );
3058
3059        // Boolean literal
3060        let bool_expr = make_bool_literal(true);
3061        assert_eq!(annotator.annotate(&bool_expr), Some(DataType::Boolean));
3062
3063        // Null literal
3064        let null_expr = Expression::Null(Null);
3065        assert_eq!(annotator.annotate(&null_expr), None);
3066    }
3067
3068    #[test]
3069    fn test_comparison_types() {
3070        let mut annotator = TypeAnnotator::new(None, None);
3071
3072        // Comparison returns boolean
3073        let cmp = Expression::Gt(Box::new(BinaryOp::new(
3074            make_int_literal(1),
3075            make_int_literal(2),
3076        )));
3077        assert_eq!(annotator.annotate(&cmp), Some(DataType::Boolean));
3078
3079        // Equality returns boolean
3080        let eq = Expression::Eq(Box::new(BinaryOp::new(
3081            make_string_literal("a"),
3082            make_string_literal("b"),
3083        )));
3084        assert_eq!(annotator.annotate(&eq), Some(DataType::Boolean));
3085    }
3086
3087    #[test]
3088    fn test_arithmetic_types() {
3089        let mut annotator = TypeAnnotator::new(None, None);
3090
3091        // Int + Int = Int
3092        let add_int = Expression::Add(Box::new(BinaryOp::new(
3093            make_int_literal(1),
3094            make_int_literal(2),
3095        )));
3096        assert_eq!(
3097            annotator.annotate(&add_int),
3098            Some(DataType::Int {
3099                length: None,
3100                integer_spelling: false
3101            })
3102        );
3103
3104        // Int + Float = Double (wider type)
3105        let add_mixed = Expression::Add(Box::new(BinaryOp::new(
3106            make_int_literal(1),
3107            make_float_literal(2.5), // Use 2.5 so the string has a decimal point
3108        )));
3109        assert_eq!(
3110            annotator.annotate(&add_mixed),
3111            Some(DataType::Double {
3112                precision: None,
3113                scale: None
3114            })
3115        );
3116    }
3117
3118    #[test]
3119    fn test_string_concat_type() {
3120        let mut annotator = TypeAnnotator::new(None, None);
3121
3122        // String || String = VarChar
3123        let concat = Expression::Concat(Box::new(BinaryOp::new(
3124            make_string_literal("hello"),
3125            make_string_literal(" world"),
3126        )));
3127        assert_eq!(
3128            annotator.annotate(&concat),
3129            Some(DataType::VarChar {
3130                length: None,
3131                parenthesized_length: false
3132            })
3133        );
3134    }
3135
3136    #[test]
3137    fn test_cast_type() {
3138        let mut annotator = TypeAnnotator::new(None, None);
3139
3140        // CAST(1 AS VARCHAR)
3141        let cast = Expression::Cast(Box::new(Cast {
3142            this: make_int_literal(1),
3143            to: DataType::VarChar {
3144                length: Some(10),
3145                parenthesized_length: false,
3146            },
3147            trailing_comments: vec![],
3148            double_colon_syntax: false,
3149            format: None,
3150            default: None,
3151            inferred_type: None,
3152        }));
3153        assert_eq!(
3154            annotator.annotate(&cast),
3155            Some(DataType::VarChar {
3156                length: Some(10),
3157                parenthesized_length: false
3158            })
3159        );
3160    }
3161
3162    #[test]
3163    fn test_function_types() {
3164        let mut annotator = TypeAnnotator::new(None, None);
3165
3166        // COUNT returns BigInt
3167        let count =
3168            Expression::Function(Box::new(Function::new("COUNT", vec![make_int_literal(1)])));
3169        assert_eq!(
3170            annotator.annotate(&count),
3171            Some(DataType::BigInt { length: None })
3172        );
3173
3174        // UPPER returns VarChar
3175        let upper = Expression::Function(Box::new(Function::new(
3176            "UPPER",
3177            vec![make_string_literal("hello")],
3178        )));
3179        assert_eq!(
3180            annotator.annotate(&upper),
3181            Some(DataType::VarChar {
3182                length: None,
3183                parenthesized_length: false
3184            })
3185        );
3186
3187        // NOW returns Timestamp
3188        let now = Expression::Function(Box::new(Function::new("NOW", vec![])));
3189        assert_eq!(
3190            annotator.annotate(&now),
3191            Some(DataType::Timestamp {
3192                precision: None,
3193                timezone: false
3194            })
3195        );
3196    }
3197
3198    #[test]
3199    fn test_duckdb_trim_stores_inferred_types() {
3200        let varchar = DataType::VarChar {
3201            length: None,
3202            parenthesized_length: false,
3203        };
3204        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3205        schema
3206            .add_table(
3207                "records",
3208                &[
3209                    ("name".to_string(), varchar.clone()),
3210                    ("chars".to_string(), varchar.clone()),
3211                    ("values_json".to_string(), DataType::Json),
3212                ],
3213                None,
3214            )
3215            .unwrap();
3216
3217        for function in [
3218            "TRIM(name)",
3219            "trim(records.name)",
3220            "TRIM(name, chars)",
3221            "TRIM(BOTH chars FROM name)",
3222            "TRIM(LEADING chars FROM name)",
3223            "TRIM(TRAILING chars FROM name)",
3224            "TRIM(FROM name)",
3225            "TRIM('  hello  ')",
3226            "TRIM(NULL)",
3227            r#"TRIM(BOTH '"' FROM values_json->>0)"#,
3228        ] {
3229            let sql = format!("SELECT {function} AS normalized FROM records");
3230            for schema in [None, Some(&schema as &dyn Schema)] {
3231                let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3232                let original_sql = expression.sql_for(DialectType::DuckDB);
3233                annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3234                let Expression::Select(select) = &expression else {
3235                    panic!("Expected SELECT for {sql}");
3236                };
3237                let Expression::Alias(alias) = &select.expressions[0] else {
3238                    panic!("Expected alias for {sql}");
3239                };
3240                assert!(matches!(alias.this, Expression::Trim(_)), "{sql}");
3241                assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
3242                assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
3243                assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
3244            }
3245        }
3246    }
3247
3248    #[test]
3249    fn test_int128_inferred_types_and_widening() {
3250        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3251        schema
3252            .add_table(
3253                "measurements",
3254                &[("value".to_string(), DataType::Int128)],
3255                None,
3256            )
3257            .unwrap();
3258        for (projection, expected) in [
3259            ("CAST(value AS HUGEINT)", DataType::Int128),
3260            ("value", DataType::Int128),
3261            ("value + CAST(1 AS BIGINT)", DataType::Int128),
3262            ("CAST(1 AS BIGINT) + value", DataType::Int128),
3263            ("COALESCE(value, CAST(1 AS BIGINT))", DataType::Int128),
3264            ("COALESCE(CAST(1 AS BIGINT), value)", DataType::Int128),
3265            (
3266                "CASE WHEN TRUE THEN value ELSE CAST(1 AS BIGINT) END",
3267                DataType::Int128,
3268            ),
3269            ("SUM(value)", DataType::Int128),
3270            ("SUM(CAST(value AS BIGINT))", DataType::Int128),
3271            (
3272                "MEDIAN(value)",
3273                DataType::Double {
3274                    precision: None,
3275                    scale: None,
3276                },
3277            ),
3278            (
3279                "CAST(value AS HUGEINT[])",
3280                DataType::Array {
3281                    element_type: Box::new(DataType::Int128),
3282                    dimension: None,
3283                },
3284            ),
3285        ] {
3286            let sql = format!("SELECT {projection} AS widened FROM measurements");
3287            let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3288            annotate_types(&mut expression, Some(&schema), Some(DialectType::DuckDB));
3289            let Expression::Select(select) = &expression else {
3290                panic!("expected SELECT")
3291            };
3292            let Expression::Alias(alias) = &select.expressions[0] else {
3293                panic!("expected alias")
3294            };
3295            assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
3296            assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
3297        }
3298        assert_eq!(
3299            TypeCoercionClass::from_data_type(&DataType::Int128),
3300            Some(TypeCoercionClass::Numeric)
3301        );
3302    }
3303
3304    #[test]
3305    fn test_duckdb_unsigned_integer_coercion_matrix() {
3306        // Verified against DuckDB 1.5.5: arithmetic overload binding and
3307        // combination casting do not select the same small integer widths.
3308        let signed = ["TINYINT", "SMALLINT", "INT", "BIGINT", "HUGEINT"];
3309        let unsigned = ["UTINYINT", "USMALLINT", "UINTEGER", "UBIGINT", "UHUGEINT"];
3310        let arithmetic = [
3311            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
3312            ["SMALLINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
3313            ["INT", "INT", "BIGINT", "HUGEINT", "DOUBLE"],
3314            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "DOUBLE"],
3315            ["HUGEINT", "HUGEINT", "HUGEINT", "HUGEINT", "DOUBLE"],
3316        ];
3317        let combination = [
3318            ["SMALLINT", "INT", "BIGINT", "HUGEINT", "SMALLINT"],
3319            ["SMALLINT", "INT", "BIGINT", "HUGEINT", "INT"],
3320            ["INT", "INT", "BIGINT", "HUGEINT", "BIGINT"],
3321            ["BIGINT", "BIGINT", "BIGINT", "HUGEINT", "HUGEINT"],
3322            ["HUGEINT", "HUGEINT", "HUGEINT", "HUGEINT", "DOUBLE"],
3323        ];
3324        for (i, s) in signed.iter().enumerate() {
3325            for (j, u) in unsigned.iter().enumerate() {
3326                for (a, b) in [(s, u), (u, s)] {
3327                    let a = format!("CAST(1 AS {a})");
3328                    let b = format!("CAST(1 AS {b})");
3329                    for (sql, expected) in [
3330                        (format!("{a} + {b}"), arithmetic[i][j]),
3331                        (format!("{a} - {b}"), arithmetic[i][j]),
3332                        (format!("{a} * {b}"), arithmetic[i][j]),
3333                        (format!("{a} % {b}"), arithmetic[i][j]),
3334                        (format!("{a} / {b}"), "DOUBLE"),
3335                        (format!("COALESCE({a}, {b})"), combination[i][j]),
3336                        (
3337                            format!("CASE WHEN TRUE THEN {a} ELSE {b} END"),
3338                            combination[i][j],
3339                        ),
3340                    ] {
3341                        let expr = parse_one(&sql, DialectType::DuckDB).unwrap();
3342                        let expected =
3343                            crate::parse_data_type(expected, DialectType::DuckDB).unwrap();
3344                        let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
3345                        assert_eq!(annotator.annotate(&expr), Some(expected), "{sql}");
3346                    }
3347                }
3348            }
3349        }
3350        for (i, a) in unsigned.iter().enumerate() {
3351            for (j, b) in unsigned.iter().enumerate() {
3352                let sql = format!("CAST(1 AS {a}) + CAST(1 AS {b})");
3353                let expr = parse_one(&sql, DialectType::DuckDB).unwrap();
3354                let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
3355                assert_eq!(
3356                    annotator.annotate(&expr),
3357                    Some(crate::parse_data_type(unsigned[i.max(j)], DialectType::DuckDB).unwrap()),
3358                    "{sql}"
3359                );
3360            }
3361        }
3362    }
3363
3364    #[test]
3365    fn test_duckdb_sum_return_types() {
3366        for (input, output) in [
3367            ("BOOLEAN", "INT128"),
3368            ("TINYINT", "INT128"),
3369            ("SMALLINT", "INT128"),
3370            ("INT", "INT128"),
3371            ("BIGINT", "INT128"),
3372            ("HUGEINT", "INT128"),
3373            ("UTINYINT", "INT128"),
3374            ("USMALLINT", "INT128"),
3375            ("UINTEGER", "INT128"),
3376            ("UBIGINT", "INT128"),
3377            ("UHUGEINT", "DOUBLE"),
3378            ("FLOAT", "DOUBLE"),
3379            ("DOUBLE", "DOUBLE"),
3380            ("DECIMAL(10,2)", "DECIMAL(38,2)"),
3381            ("DECIMAL", "DECIMAL(38,3)"),
3382            ("DECIMAL(10)", "DECIMAL(38,0)"),
3383        ] {
3384            let input_type = crate::parse_data_type(input, DialectType::DuckDB).unwrap();
3385            let expected = crate::parse_data_type(output, DialectType::DuckDB).unwrap();
3386            let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3387            schema
3388                .add_table("t", &[("x".to_string(), input_type)], None)
3389                .unwrap();
3390            let cast = parse_one(&format!("CAST(1 AS {input})"), DialectType::DuckDB).unwrap();
3391            let function = Expression::Function(Box::new(Function::new("SUM", vec![cast])));
3392            assert_eq!(
3393                TypeAnnotator::new(None, Some(DialectType::DuckDB)).annotate(&function),
3394                Some(expected.clone()),
3395                "SUM({input}) builder"
3396            );
3397            for arg in ["x".to_string(), format!("CAST(1 AS {input})")] {
3398                for projection in [
3399                    format!("SUM({arg})"),
3400                    format!("SUM(DISTINCT {arg})"),
3401                    format!("SUM({arg}) OVER ()"),
3402                    format!("SUM({arg}) FILTER (WHERE TRUE)"),
3403                ] {
3404                    let sql = format!("SELECT {projection} AS total FROM t");
3405                    let mut expr = parse_one(&sql, DialectType::DuckDB).unwrap();
3406                    annotate_types(&mut expr, Some(&schema), Some(DialectType::DuckDB));
3407                    let Expression::Select(select) = expr else {
3408                        unreachable!()
3409                    };
3410                    let Expression::Alias(alias) = &select.expressions[0] else {
3411                        unreachable!()
3412                    };
3413                    assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
3414                }
3415            }
3416        }
3417        let expr = parse_one("SUM(x)", DialectType::DuckDB).unwrap();
3418        assert_eq!(
3419            TypeAnnotator::new(None, Some(DialectType::DuckDB)).annotate(&expr),
3420            None
3421        );
3422        // The dialect-specific override must not change the generic/default rule.
3423        for dialect in [
3424            DialectType::Generic,
3425            DialectType::PostgreSQL,
3426            DialectType::MySQL,
3427            DialectType::BigQuery,
3428        ] {
3429            let expr = parse_one("SUM(CAST(1 AS BIGINT))", dialect).unwrap();
3430            assert_eq!(
3431                TypeAnnotator::new(None, Some(dialect)).annotate(&expr),
3432                Some(DataType::BigInt { length: None })
3433            );
3434        }
3435    }
3436
3437    #[test]
3438    fn test_trim_annotates_both_arguments_and_nested_children() {
3439        let varchar = DataType::VarChar {
3440            length: None,
3441            parenthesized_length: false,
3442        };
3443        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3444        schema
3445            .add_table(
3446                "users",
3447                &[
3448                    ("name".to_string(), varchar.clone()),
3449                    ("chars".to_string(), varchar.clone()),
3450                ],
3451                None,
3452            )
3453            .unwrap();
3454
3455        fn check_children(expr: &Expression, varchar: &DataType, has_schema: bool) -> usize {
3456            let mut count = 0;
3457            if matches!(
3458                expr,
3459                Expression::Trim(_)
3460                    | Expression::Lower(_)
3461                    | Expression::Upper(_)
3462                    | Expression::Column(_)
3463            ) {
3464                let expected = if matches!(expr, Expression::Column(_)) && !has_schema {
3465                    None
3466                } else {
3467                    Some(varchar)
3468                };
3469                assert_eq!(expr.inferred_type(), expected, "{}", expr.sql());
3470                count += 1;
3471            }
3472            crate::ast_children::for_each_child(expr, |_, child| {
3473                count += check_children(child, varchar, has_schema);
3474            });
3475            count
3476        }
3477
3478        for schema in [None, Some(&schema as &dyn Schema)] {
3479            let mut expression = parse_one(
3480                "SELECT TRIM(LOWER(TRIM(name)), UPPER(chars)) AS normalized FROM users",
3481                DialectType::DuckDB,
3482            )
3483            .unwrap();
3484            annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3485            assert_eq!(check_children(&expression, &varchar, schema.is_some()), 6);
3486        }
3487    }
3488
3489    #[test]
3490    fn test_trim_type_metadata_roundtrip_across_dialects() {
3491        let varchar = DataType::VarChar {
3492            length: None,
3493            parenthesized_length: false,
3494        };
3495        for dialect in [
3496            DialectType::Generic,
3497            DialectType::DuckDB,
3498            DialectType::PostgreSQL,
3499            DialectType::Snowflake,
3500            DialectType::BigQuery,
3501            DialectType::MySQL,
3502        ] {
3503            let expression = parse_one("TRIM('  hello  ')", dialect).unwrap();
3504            assert!(matches!(expression, Expression::Trim(_)));
3505            assert_eq!(expression.inferred_type(), None);
3506            let unannotated = serde_json::to_value(&expression).unwrap();
3507            assert!(unannotated["trim"].get("inferred_type").is_none());
3508
3509            // Old AST JSON without type metadata remains deserializable.
3510            let mut restored: Expression = serde_json::from_value(unannotated).unwrap();
3511            assert_eq!(restored, expression);
3512            annotate_types(&mut restored, None, Some(dialect));
3513            assert_eq!(restored.inferred_type(), Some(&varchar), "{dialect:?}");
3514            assert_eq!(restored.sql_for(dialect), expression.sql_for(dialect));
3515
3516            let annotated = serde_json::to_value(&restored).unwrap();
3517            assert_eq!(annotated["trim"]["inferred_type"]["data_type"], "var_char");
3518            let roundtripped: Expression = serde_json::from_value(annotated).unwrap();
3519            assert_eq!(roundtripped, restored);
3520            assert_eq!(roundtripped.inferred_type(), Some(&varchar));
3521        }
3522    }
3523
3524    #[test]
3525    fn test_duckdb_regexp_extract_all_overload_types() {
3526        let varchar = DataType::VarChar {
3527            length: None,
3528            parenthesized_length: false,
3529        };
3530        let captures = DataType::Struct {
3531            fields: vec![
3532                StructField::new("Letter".to_string(), varchar.clone()),
3533                StructField::new("number".to_string(), varchar.clone()),
3534            ],
3535            nested: false,
3536        };
3537        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3538        schema
3539            .add_table("documents", &[("body".to_string(), varchar.clone())], None)
3540            .unwrap();
3541
3542        for (arguments, element_type) in [
3543            ("body, '[0-9]+'", &varchar),
3544            ("documents.body, '[0-9]+'", &varchar),
3545            ("'a1b22', '[0-9]+'", &varchar),
3546            ("'abc', '[0-9]+'", &varchar),
3547            ("NULL, '[0-9]+'", &varchar),
3548            ("body, '([a-z])([0-9]+)', 0", &varchar),
3549            ("body, '([a-z])([0-9]+)', 2", &varchar),
3550            ("body, '([a-z])([0-9]+)', (1), 'i'", &varchar),
3551            ("body, '([a-z])([0-9]+)', CAST(1 AS INTEGER)", &varchar),
3552            ("body, '([a-z])([0-9]+)', NULL", &varchar),
3553            ("body, '([a-z])([0-9]+)', ['Letter', 'number']", &captures),
3554            (
3555                "body, '([a-z])([0-9]+)', (['Letter', 'number']), 'i'",
3556                &captures,
3557            ),
3558            (
3559                "body, '([a-z])([0-9]+)', ARRAY['Letter', 'number']",
3560                &captures,
3561            ),
3562            (
3563                "NULL, '([a-z])([0-9]+)', LIST['Letter', 'number']",
3564                &captures,
3565            ),
3566        ] {
3567            let expected = DataType::Array {
3568                element_type: Box::new(element_type.clone()),
3569                dimension: None,
3570            };
3571            for name in ["REGEXP_EXTRACT_ALL", "ReGeXp_ExTrAcT_AlL"] {
3572                for schema in [None, Some(&schema as &dyn Schema)] {
3573                    let sql = format!("SELECT {name}({arguments}) AS matches FROM documents");
3574                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3575                    let original_sql = expression.sql_for(DialectType::DuckDB);
3576                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3577                    let Expression::Select(select) = &expression else {
3578                        panic!("Expected SELECT for {sql}");
3579                    };
3580                    let Expression::Alias(alias) = &select.expressions[0] else {
3581                        panic!("Expected alias for {sql}");
3582                    };
3583                    assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
3584                    assert_eq!(alias.inferred_type.as_ref(), Some(&expected), "{sql}");
3585                    assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
3586                    let json = serde_json::to_value(&expression).unwrap();
3587                    let restored: Expression = serde_json::from_value(json).unwrap();
3588                    assert_eq!(restored, expression);
3589
3590                    let sql = format!(
3591                        "SELECT UNNEST({name}({arguments})) AS single_match FROM documents"
3592                    );
3593                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3594                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3595                    let Expression::Select(select) = &expression else {
3596                        panic!("Expected SELECT for {sql}");
3597                    };
3598                    let Expression::Alias(alias) = &select.expressions[0] else {
3599                        panic!("Expected alias for {sql}");
3600                    };
3601                    assert_eq!(alias.this.inferred_type(), Some(element_type), "{sql}");
3602                    assert_eq!(alias.inferred_type.as_ref(), Some(element_type), "{sql}");
3603                }
3604            }
3605        }
3606    }
3607
3608    #[test]
3609    fn test_duckdb_regexp_extract_all_unresolved_overloads() {
3610        let varchar = DataType::VarChar {
3611            length: None,
3612            parenthesized_length: false,
3613        };
3614        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3615        schema
3616            .add_table(
3617                "documents",
3618                &[(
3619                    "names".to_string(),
3620                    DataType::Array {
3621                        element_type: Box::new(varchar),
3622                        dimension: None,
3623                    },
3624                )],
3625                None,
3626            )
3627            .unwrap();
3628
3629        // Do not fabricate a scalar or a precise struct for unknown capture
3630        // names, ambiguous overloads, or invalid literal name lists/arity.
3631        for arguments in [
3632            "",
3633            "'a1'",
3634            "'a1', '(a)(1)', 0, 'i', 1",
3635            "'a1', '(a)(1)', missing_group",
3636            "'a1', '(a)(1)', documents.names",
3637            "'a1', '(a)(1)', ['letter', missing_name]",
3638            "'a1', '(a)(1)', CAST(['letter', 'number'] AS VARCHAR[])",
3639            "'a1', '(a)(1)', []",
3640            "'a1', '(a)(1)', [NULL]",
3641            "'a1', '(a)(1)', ['letter', 'LETTER']",
3642            "'a1', '(a)(1)', 1.0",
3643            "'a1', '(a)(1)', CAST(1 AS BIGINT)",
3644        ] {
3645            let sql = format!("REGEXP_EXTRACT_ALL({arguments})");
3646            let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3647            let mut annotator = TypeAnnotator::new(Some(&schema), Some(DialectType::DuckDB));
3648            assert_eq!(annotator.annotate(&expression), None, "{sql}");
3649            annotator.annotate_in_place(&mut expression);
3650            assert_eq!(expression.inferred_type(), None, "{sql}");
3651        }
3652    }
3653
3654    #[test]
3655    fn test_regexp_extract_all_rule_is_duckdb_specific() {
3656        // Other dialects have different overloads, notably BigQuery BYTES.
3657        // Preserve their existing behavior rather than installing a global rule.
3658        for dialect in [
3659            None,
3660            Some(DialectType::Generic),
3661            Some(DialectType::BigQuery),
3662            Some(DialectType::Snowflake),
3663            Some(DialectType::Databricks),
3664        ] {
3665            for input in [
3666                make_string_literal("a1"),
3667                Expression::Literal(Box::new(Literal::ByteString("a1".to_string()))),
3668            ] {
3669                let mut annotator = TypeAnnotator::new(None, dialect);
3670                let expected = annotator.annotate(&input);
3671                let function = Expression::Function(Box::new(Function::new(
3672                    "REGEXP_EXTRACT_ALL",
3673                    vec![input, make_string_literal("[0-9]+")],
3674                )));
3675                assert_eq!(annotator.annotate(&function), expected, "{dialect:?}");
3676            }
3677        }
3678    }
3679
3680    #[test]
3681    fn test_duckdb_date_name_types() {
3682        let varchar = DataType::VarChar {
3683            length: None,
3684            parenthesized_length: false,
3685        };
3686        for input_type in [
3687            DataType::Date,
3688            DataType::Timestamp {
3689                precision: None,
3690                timezone: false,
3691            },
3692            DataType::Timestamp {
3693                precision: None,
3694                timezone: true,
3695            },
3696        ] {
3697            let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3698            schema
3699                .add_table(
3700                    "events",
3701                    &[("created_at".to_string(), input_type.clone())],
3702                    None,
3703                )
3704                .unwrap();
3705            for function in ["MONTHNAME", "MoNtHnAmE", "DAYNAME", "DaYnAmE"] {
3706                for input in [
3707                    "created_at",
3708                    "events.created_at",
3709                    "DATE '2026-01-01'",
3710                    "TIMESTAMP '2026-01-01 12:00:00'",
3711                    "TIMESTAMPTZ '2026-01-01 12:00:00+00'",
3712                    "NULL",
3713                ] {
3714                    let sql = format!("SELECT {function}({input}) AS label FROM events");
3715                    for schema in [None, Some(&schema as &dyn Schema)] {
3716                        let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3717                        let original_sql = expression.sql_for(DialectType::DuckDB);
3718                        annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3719                        let Expression::Select(select) = &expression else {
3720                            panic!("Expected SELECT for {sql}");
3721                        };
3722                        let Expression::Alias(alias) = &select.expressions[0] else {
3723                            panic!("Expected alias for {sql}");
3724                        };
3725                        assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
3726                        assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
3727                        if let Expression::Function(function) = &alias.this {
3728                            if let Expression::Column(column) = &function.args[0] {
3729                                assert_eq!(
3730                                    column.inferred_type.as_ref(),
3731                                    schema.map(|_| &input_type),
3732                                    "Argument type must remain unchanged for {sql}"
3733                                );
3734                            }
3735                        }
3736                        assert_eq!(expression.sql_for(DialectType::DuckDB), original_sql);
3737                        let json = serde_json::to_value(&expression).unwrap();
3738                        let restored: Expression = serde_json::from_value(json).unwrap();
3739                        assert_eq!(restored, expression);
3740                    }
3741                }
3742            }
3743        }
3744    }
3745
3746    #[test]
3747    fn test_date_name_fixed_return_types_are_duckdb_specific() {
3748        for dialect in [
3749            None,
3750            Some(DialectType::Generic),
3751            Some(DialectType::PostgreSQL),
3752            Some(DialectType::MySQL),
3753            Some(DialectType::Snowflake),
3754        ] {
3755            let annotator = TypeAnnotator::new(None, dialect);
3756            for function in ["MONTHNAME", "DAYNAME"] {
3757                assert!(
3758                    !annotator.function_return_types.contains_key(function),
3759                    "Unexpected fixed return type for {function} in {dialect:?}"
3760                );
3761            }
3762        }
3763    }
3764
3765    #[test]
3766    fn test_duckdb_array_to_string_types() {
3767        let varchar = DataType::VarChar {
3768            length: None,
3769            parenthesized_length: false,
3770        };
3771        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3772        schema
3773            .add_table(
3774                "events",
3775                &[
3776                    ("label".to_string(), varchar.clone()),
3777                    (
3778                        "labels".to_string(),
3779                        DataType::Array {
3780                            element_type: Box::new(varchar.clone()),
3781                            dimension: None,
3782                        },
3783                    ),
3784                ],
3785                None,
3786            )
3787            .unwrap();
3788
3789        for function in [
3790            "ARRAY_TO_STRING",
3791            "array_to_string",
3792            "ArRaY_To_StRiNg_CoMmA_DeFaUlT",
3793        ] {
3794            for input in [
3795                "labels",
3796                "events.labels",
3797                "ARRAY_AGG(label)",
3798                "ARRAY_AGG(events.label)",
3799                "['a', NULL, 'b']",
3800                "[1, 2]",
3801                "[]",
3802                "CAST(NULL AS VARCHAR[])",
3803                "NULL",
3804            ] {
3805                let arguments = if function.eq_ignore_ascii_case("ARRAY_TO_STRING") {
3806                    format!("{input}, ', '")
3807                } else {
3808                    input.to_string()
3809                };
3810                let sql = format!("SELECT {function}({arguments}) AS label_text FROM events");
3811                for schema in [None, Some(&schema as &dyn Schema)] {
3812                    let mut expression = parse_one(&sql, DialectType::DuckDB).unwrap();
3813                    annotate_types(&mut expression, schema, Some(DialectType::DuckDB));
3814                    let Expression::Select(select) = &expression else {
3815                        panic!("Expected SELECT for {sql}");
3816                    };
3817                    let Expression::Alias(alias) = &select.expressions[0] else {
3818                        panic!("Expected alias for {sql}");
3819                    };
3820                    assert_eq!(alias.this.inferred_type(), Some(&varchar), "{sql}");
3821                    assert_eq!(alias.inferred_type.as_ref(), Some(&varchar), "{sql}");
3822                }
3823            }
3824        }
3825    }
3826
3827    #[test]
3828    fn test_array_to_string_fixed_return_types_are_duckdb_specific() {
3829        // In particular, BigQuery also has a BYTES-returning overload, so it
3830        // must not inherit DuckDB's unconditional VARCHAR return-type rules.
3831        for dialect in [
3832            None,
3833            Some(DialectType::Generic),
3834            Some(DialectType::BigQuery),
3835            Some(DialectType::PostgreSQL),
3836            Some(DialectType::Snowflake),
3837        ] {
3838            let annotator = TypeAnnotator::new(None, dialect);
3839            for function in ["ARRAY_TO_STRING", "ARRAY_TO_STRING_COMMA_DEFAULT"] {
3840                assert!(
3841                    !annotator.function_return_types.contains_key(function),
3842                    "Unexpected fixed return type for {function} in {dialect:?}"
3843                );
3844            }
3845        }
3846    }
3847
3848    #[test]
3849    fn test_extract_types_are_dialect_and_field_specific() {
3850        let double = DataType::Double {
3851            precision: None,
3852            scale: None,
3853        };
3854        let bigint = DataType::BigInt { length: None };
3855        let int = DataType::Int {
3856            length: None,
3857            integer_spelling: false,
3858        };
3859
3860        for (field, duckdb_type) in [
3861            (DateTimeField::Year, &bigint),
3862            (DateTimeField::Second, &bigint),
3863            (DateTimeField::Millisecond, &bigint),
3864            (DateTimeField::Microsecond, &bigint),
3865            (DateTimeField::Custom("yearweek".to_string()), &bigint),
3866            (DateTimeField::Epoch, &double),
3867            (DateTimeField::Custom("EpOcH".to_string()), &double),
3868            (DateTimeField::Custom("julian".to_string()), &double),
3869            (DateTimeField::Custom("JuLiAn".to_string()), &double),
3870        ] {
3871            let extract = Expression::Extract(Box::new(ExtractFunc {
3872                this: make_string_literal("2024-02-03 04:05:06.123456"),
3873                field,
3874            }));
3875            for dialect in [
3876                None,
3877                Some(DialectType::Generic),
3878                Some(DialectType::PostgreSQL),
3879                Some(DialectType::Snowflake),
3880                Some(DialectType::DuckDB),
3881            ] {
3882                let expected = if dialect == Some(DialectType::DuckDB) {
3883                    duckdb_type
3884                } else {
3885                    &int
3886                };
3887                assert_eq!(
3888                    TypeAnnotator::new(None, dialect)
3889                        .annotate(&extract)
3890                        .as_ref(),
3891                    Some(expected),
3892                    "unexpected type for {extract:?} in {dialect:?}"
3893                );
3894            }
3895        }
3896    }
3897
3898    #[test]
3899    fn test_duckdb_date_trunc_overload_types() {
3900        fn cast_to(data_type: DataType) -> Expression {
3901            Expression::Cast(Box::new(Cast {
3902                this: make_string_literal("value"),
3903                to: data_type,
3904                format: None,
3905                trailing_comments: Vec::new(),
3906                double_colon_syntax: false,
3907                default: None,
3908                inferred_type: None,
3909            }))
3910        }
3911
3912        fn date_trunc(value: Expression) -> Expression {
3913            Expression::Function(Box::new(Function::new(
3914                "DATE_TRUNC",
3915                vec![make_string_literal("month"), value],
3916            )))
3917        }
3918
3919        let mut annotator = TypeAnnotator::new(None, Some(DialectType::DuckDB));
3920
3921        for input_type in [
3922            DataType::Date,
3923            DataType::Timestamp {
3924                precision: Some(9),
3925                timezone: false,
3926            },
3927        ] {
3928            assert_eq!(
3929                annotator.annotate(&date_trunc(cast_to(input_type))),
3930                Some(DataType::Timestamp {
3931                    precision: None,
3932                    timezone: false,
3933                })
3934            );
3935        }
3936
3937        assert_eq!(
3938            annotator.annotate(&date_trunc(cast_to(DataType::Timestamp {
3939                precision: Some(6),
3940                timezone: true,
3941            }))),
3942            Some(DataType::Timestamp {
3943                precision: None,
3944                timezone: true,
3945            })
3946        );
3947        assert_eq!(
3948            annotator.annotate(&date_trunc(cast_to(DataType::Interval {
3949                unit: Some("DAY".to_string()),
3950                to: Some("SECOND".to_string()),
3951            }))),
3952            Some(DataType::Interval {
3953                unit: None,
3954                to: None,
3955            })
3956        );
3957        assert_eq!(
3958            annotator.annotate(&date_trunc(Expression::Null(Null))),
3959            None
3960        );
3961    }
3962
3963    #[test]
3964    fn test_coalesce_type_inference() {
3965        let mut annotator = TypeAnnotator::new(None, None);
3966
3967        // COALESCE(NULL, 1) returns Int (type of first non-null arg)
3968        let coalesce = Expression::Function(Box::new(Function::new(
3969            "COALESCE",
3970            vec![Expression::Null(Null), make_int_literal(1)],
3971        )));
3972        assert_eq!(
3973            annotator.annotate(&coalesce),
3974            Some(DataType::Int {
3975                length: None,
3976                integer_spelling: false
3977            })
3978        );
3979
3980        let specialized_coalesce = Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
3981            expressions: vec![Expression::Null(Null), make_int_literal(1)],
3982            original_name: None,
3983            inferred_type: None,
3984        }));
3985        assert_eq!(
3986            annotator.annotate(&specialized_coalesce),
3987            Some(DataType::Int {
3988                length: None,
3989                integer_spelling: false
3990            })
3991        );
3992    }
3993
3994    #[test]
3995    fn test_type_coercion_class() {
3996        // Text types
3997        assert_eq!(
3998            TypeCoercionClass::from_data_type(&DataType::VarChar {
3999                length: None,
4000                parenthesized_length: false
4001            }),
4002            Some(TypeCoercionClass::Text)
4003        );
4004        assert_eq!(
4005            TypeCoercionClass::from_data_type(&DataType::Text),
4006            Some(TypeCoercionClass::Text)
4007        );
4008
4009        // Numeric types
4010        assert_eq!(
4011            TypeCoercionClass::from_data_type(&DataType::Int {
4012                length: None,
4013                integer_spelling: false
4014            }),
4015            Some(TypeCoercionClass::Numeric)
4016        );
4017        assert_eq!(
4018            TypeCoercionClass::from_data_type(&DataType::Double {
4019                precision: None,
4020                scale: None
4021            }),
4022            Some(TypeCoercionClass::Numeric)
4023        );
4024
4025        // Timelike types
4026        assert_eq!(
4027            TypeCoercionClass::from_data_type(&DataType::Date),
4028            Some(TypeCoercionClass::Timelike)
4029        );
4030        assert_eq!(
4031            TypeCoercionClass::from_data_type(&DataType::Timestamp {
4032                precision: None,
4033                timezone: false
4034            }),
4035            Some(TypeCoercionClass::Timelike)
4036        );
4037
4038        // Unknown types
4039        assert_eq!(TypeCoercionClass::from_data_type(&DataType::Json), None);
4040    }
4041
4042    #[test]
4043    fn test_wider_numeric_type() {
4044        let annotator = TypeAnnotator::new(None, None);
4045
4046        // Int vs BigInt -> BigInt
4047        let result = annotator.wider_numeric_type(
4048            &DataType::Int {
4049                length: None,
4050                integer_spelling: false,
4051            },
4052            &DataType::BigInt { length: None },
4053        );
4054        assert_eq!(result, DataType::BigInt { length: None });
4055
4056        // Float vs Double -> Double
4057        let result = annotator.wider_numeric_type(
4058            &DataType::Float {
4059                precision: None,
4060                scale: None,
4061                real_spelling: false,
4062            },
4063            &DataType::Double {
4064                precision: None,
4065                scale: None,
4066            },
4067        );
4068        assert_eq!(
4069            result,
4070            DataType::Double {
4071                precision: None,
4072                scale: None
4073            }
4074        );
4075
4076        // Int vs Double -> Double
4077        let result = annotator.wider_numeric_type(
4078            &DataType::Int {
4079                length: None,
4080                integer_spelling: false,
4081            },
4082            &DataType::Double {
4083                precision: None,
4084                scale: None,
4085            },
4086        );
4087        assert_eq!(
4088            result,
4089            DataType::Double {
4090                precision: None,
4091                scale: None
4092            }
4093        );
4094    }
4095
4096    #[test]
4097    fn test_aggregate_return_types() {
4098        let mut annotator = TypeAnnotator::new(None, None);
4099
4100        // SUM(int) returns BigInt
4101        let sum_type = annotator.get_aggregate_return_type("SUM", &[make_int_literal(1)]);
4102        assert_eq!(sum_type, Some(DataType::BigInt { length: None }));
4103
4104        // AVG always returns Double
4105        let avg_type = annotator.get_aggregate_return_type("AVG", &[make_int_literal(1)]);
4106        assert_eq!(
4107            avg_type,
4108            Some(DataType::Double {
4109                precision: None,
4110                scale: None
4111            })
4112        );
4113
4114        // MIN/MAX preserve input type
4115        let min_type = annotator.get_aggregate_return_type("MIN", &[make_string_literal("a")]);
4116        assert_eq!(
4117            min_type,
4118            Some(DataType::VarChar {
4119                length: None,
4120                parenthesized_length: false
4121            })
4122        );
4123
4124        // DuckDB MIN/MAX(value, n) return a list of the input type.
4125        let top_n_type = annotator
4126            .get_aggregate_return_type("MAX", &[make_string_literal("a"), make_int_literal(2)]);
4127        assert_eq!(
4128            top_n_type,
4129            Some(DataType::Array {
4130                element_type: Box::new(DataType::VarChar {
4131                    length: None,
4132                    parenthesized_length: false,
4133                }),
4134                dimension: None,
4135            })
4136        );
4137    }
4138
4139    #[test]
4140    fn test_date_literal_types() {
4141        let mut annotator = TypeAnnotator::new(None, None);
4142
4143        // DATE literal
4144        let date_expr = Expression::Literal(Box::new(Literal::Date("2024-01-15".to_string())));
4145        assert_eq!(annotator.annotate(&date_expr), Some(DataType::Date));
4146
4147        // TIME literal
4148        let time_expr = Expression::Literal(Box::new(Literal::Time("10:30:00".to_string())));
4149        assert_eq!(
4150            annotator.annotate(&time_expr),
4151            Some(DataType::Time {
4152                precision: None,
4153                timezone: false
4154            })
4155        );
4156
4157        // TIMESTAMP literal
4158        let ts_expr = Expression::Literal(Box::new(Literal::Timestamp(
4159            "2024-01-15 10:30:00".to_string(),
4160        )));
4161        assert_eq!(
4162            annotator.annotate(&ts_expr),
4163            Some(DataType::Timestamp {
4164                precision: None,
4165                timezone: false
4166            })
4167        );
4168    }
4169
4170    #[test]
4171    fn test_logical_operations() {
4172        let mut annotator = TypeAnnotator::new(None, None);
4173
4174        // AND returns boolean
4175        let and_expr = Expression::And(Box::new(BinaryOp::new(
4176            make_bool_literal(true),
4177            make_bool_literal(false),
4178        )));
4179        assert_eq!(annotator.annotate(&and_expr), Some(DataType::Boolean));
4180
4181        // OR returns boolean
4182        let or_expr = Expression::Or(Box::new(BinaryOp::new(
4183            make_bool_literal(true),
4184            make_bool_literal(false),
4185        )));
4186        assert_eq!(annotator.annotate(&or_expr), Some(DataType::Boolean));
4187
4188        // NOT returns boolean
4189        let not_expr = Expression::Not(Box::new(crate::expressions::UnaryOp::new(
4190            make_bool_literal(true),
4191        )));
4192        assert_eq!(annotator.annotate(&not_expr), Some(DataType::Boolean));
4193    }
4194
4195    // ========================================
4196    // Tests for newly implemented features
4197    // ========================================
4198
4199    #[test]
4200    fn test_subscript_array_type() {
4201        let mut annotator = TypeAnnotator::new(None, None);
4202
4203        // Array[index] returns element type
4204        let arr = Expression::Array(Box::new(crate::expressions::Array {
4205            expressions: vec![make_int_literal(1), make_int_literal(2)],
4206            inferred_type: None,
4207        }));
4208        let subscript = Expression::Subscript(Box::new(crate::expressions::Subscript {
4209            this: arr,
4210            index: make_int_literal(0),
4211        }));
4212        assert_eq!(
4213            annotator.annotate(&subscript),
4214            Some(DataType::Int {
4215                length: None,
4216                integer_spelling: false
4217            })
4218        );
4219    }
4220
4221    #[test]
4222    fn test_subscript_map_type() {
4223        let mut annotator = TypeAnnotator::new(None, None);
4224
4225        // Map[key] returns value type
4226        let map = Expression::Map(Box::new(crate::expressions::Map {
4227            keys: vec![make_string_literal("a")],
4228            values: vec![make_int_literal(1)],
4229        }));
4230        let subscript = Expression::Subscript(Box::new(crate::expressions::Subscript {
4231            this: map,
4232            index: make_string_literal("a"),
4233        }));
4234        assert_eq!(
4235            annotator.annotate(&subscript),
4236            Some(DataType::Int {
4237                length: None,
4238                integer_spelling: false
4239            })
4240        );
4241    }
4242
4243    #[test]
4244    fn test_struct_type() {
4245        let mut annotator = TypeAnnotator::new(None, None);
4246
4247        // STRUCT literal
4248        let struct_expr = Expression::Struct(Box::new(crate::expressions::Struct {
4249            fields: vec![
4250                (Some("name".to_string()), make_string_literal("Alice")),
4251                (Some("age".to_string()), make_int_literal(30)),
4252            ],
4253        }));
4254        let result = annotator.annotate(&struct_expr);
4255        assert!(matches!(result, Some(DataType::Struct { fields, .. }) if fields.len() == 2));
4256    }
4257
4258    #[test]
4259    fn test_map_type() {
4260        let mut annotator = TypeAnnotator::new(None, None);
4261
4262        // MAP literal
4263        let map_expr = Expression::Map(Box::new(crate::expressions::Map {
4264            keys: vec![make_string_literal("a"), make_string_literal("b")],
4265            values: vec![make_int_literal(1), make_int_literal(2)],
4266        }));
4267        let result = annotator.annotate(&map_expr);
4268        assert!(matches!(
4269            result,
4270            Some(DataType::Map { key_type, value_type })
4271            if matches!(*key_type, DataType::VarChar { .. })
4272               && matches!(*value_type, DataType::Int { .. })
4273        ));
4274    }
4275
4276    #[test]
4277    fn test_explode_array_type() {
4278        let mut annotator = TypeAnnotator::new(None, None);
4279
4280        // EXPLODE(array) returns element type
4281        let arr = Expression::Array(Box::new(crate::expressions::Array {
4282            expressions: vec![make_int_literal(1), make_int_literal(2)],
4283            inferred_type: None,
4284        }));
4285        let explode = Expression::Explode(Box::new(crate::expressions::UnaryFunc {
4286            this: arr,
4287            original_name: None,
4288            inferred_type: None,
4289        }));
4290        assert_eq!(
4291            annotator.annotate(&explode),
4292            Some(DataType::Int {
4293                length: None,
4294                integer_spelling: false
4295            })
4296        );
4297    }
4298
4299    #[test]
4300    fn test_unnest_array_type() {
4301        let mut annotator = TypeAnnotator::new(None, None);
4302
4303        // UNNEST(array) returns element type
4304        let arr = Expression::Array(Box::new(crate::expressions::Array {
4305            expressions: vec![make_string_literal("a"), make_string_literal("b")],
4306            inferred_type: None,
4307        }));
4308        let unnest = Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
4309            this: arr,
4310            expressions: Vec::new(),
4311            with_ordinality: false,
4312            alias: None,
4313            offset_alias: None,
4314            inferred_type: None,
4315        }));
4316        assert_eq!(
4317            annotator.annotate(&unnest),
4318            Some(DataType::VarChar {
4319                length: None,
4320                parenthesized_length: false
4321            })
4322        );
4323    }
4324
4325    #[test]
4326    fn test_annotate_duckdb_array_case_unnest_types() {
4327        let timestamp = DataType::Timestamp {
4328            precision: None,
4329            timezone: false,
4330        };
4331        let array_timestamp = DataType::Array {
4332            element_type: Box::new(timestamp.clone()),
4333            dimension: None,
4334        };
4335        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
4336        schema
4337            .add_table(
4338                "events",
4339                &[
4340                    ("created_at".to_string(), timestamp.clone()),
4341                    ("closed_at".to_string(), timestamp.clone()),
4342                ],
4343                None,
4344            )
4345            .unwrap();
4346
4347        let mut expr = parse_one(
4348            "SELECT UNNEST(CASE WHEN closed_at IS NULL THEN ARRAY[created_at] \
4349             ELSE ARRAY[created_at, closed_at] END) AS event_at FROM events",
4350            DialectType::DuckDB,
4351        )
4352        .unwrap();
4353        annotate_types(&mut expr, Some(&schema), Some(DialectType::DuckDB));
4354
4355        let Expression::Select(select) = &expr else {
4356            panic!("expected select");
4357        };
4358        let Expression::Alias(alias) = &select.expressions[0] else {
4359            panic!("expected alias");
4360        };
4361        let Expression::Function(unnest) = &alias.this else {
4362            panic!("expected UNNEST function");
4363        };
4364        assert_eq!(unnest.name, "UNNEST");
4365        let Expression::Case(case) = &unnest.args[0] else {
4366            panic!("expected case");
4367        };
4368        let Expression::ArrayFunc(then_array) = &case.whens[0].1 else {
4369            panic!("expected ARRAY constructor in THEN branch");
4370        };
4371        let Expression::ArrayFunc(else_array) = case.else_.as_ref().expect("expected ELSE") else {
4372            panic!("expected ARRAY constructor in ELSE branch");
4373        };
4374
4375        assert_eq!(then_array.inferred_type.as_ref(), Some(&array_timestamp));
4376        assert_eq!(else_array.inferred_type.as_ref(), Some(&array_timestamp));
4377        assert_eq!(case.inferred_type.as_ref(), Some(&array_timestamp));
4378        assert_eq!(unnest.inferred_type.as_ref(), Some(&timestamp));
4379        assert_eq!(alias.inferred_type.as_ref(), Some(&timestamp));
4380        assert!(then_array
4381            .expressions
4382            .iter()
4383            .all(|expression| { expression.inferred_type() == Some(&timestamp) }));
4384        assert!(else_array
4385            .expressions
4386            .iter()
4387            .all(|expression| { expression.inferred_type() == Some(&timestamp) }));
4388    }
4389
4390    #[test]
4391    fn test_array_and_case_coerce_all_result_types() {
4392        let mut expr = parse_one(
4393            "SELECT CASE WHEN TRUE THEN ARRAY[1] ELSE ARRAY[2.5] END AS values",
4394            DialectType::DuckDB,
4395        )
4396        .unwrap();
4397        annotate_types(&mut expr, None, Some(DialectType::DuckDB));
4398
4399        let Expression::Select(select) = &expr else {
4400            panic!("expected select");
4401        };
4402        let Expression::Alias(alias) = &select.expressions[0] else {
4403            panic!("expected alias");
4404        };
4405        assert_eq!(
4406            alias.this.inferred_type(),
4407            Some(&DataType::Array {
4408                element_type: Box::new(DataType::Double {
4409                    precision: None,
4410                    scale: None,
4411                }),
4412                dimension: None,
4413            })
4414        );
4415    }
4416
4417    #[test]
4418    fn test_transparent_wrappers_preserve_scalar_types() {
4419        let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
4420        schema
4421            .add_table("flags", &[("flag".to_string(), DataType::Boolean)], None)
4422            .unwrap();
4423        let varchar = DataType::VarChar {
4424            length: None,
4425            parenthesized_length: false,
4426        };
4427
4428        let mut parenthesized = parse_one(
4429            "SELECT (CASE WHEN flag THEN 'yes' ELSE 'no' END) AS label FROM flags",
4430            DialectType::DuckDB,
4431        )
4432        .unwrap();
4433        annotate_types(&mut parenthesized, Some(&schema), Some(DialectType::DuckDB));
4434
4435        let Expression::Select(select) = &parenthesized else {
4436            panic!("expected select");
4437        };
4438        let Expression::Alias(alias) = &select.expressions[0] else {
4439            panic!("expected alias");
4440        };
4441        let Expression::Paren(paren) = &alias.this else {
4442            panic!("expected parenthesized expression");
4443        };
4444        let Expression::Case(case) = &paren.this else {
4445            panic!("expected case");
4446        };
4447        let Expression::Column(column) = &case.whens[0].0 else {
4448            panic!("expected column condition");
4449        };
4450
4451        assert_eq!(alias.this.inferred_type(), Some(&varchar));
4452        assert_eq!(case.inferred_type.as_ref(), Some(&varchar));
4453        assert_eq!(column.inferred_type.as_ref(), Some(&DataType::Boolean));
4454        assert_eq!(alias.inferred_type.as_ref(), Some(&varchar));
4455
4456        let mut annotated = parse_one(
4457            "SELECT CASE WHEN flag THEN 'yes' ELSE 'no' END /*tail*/ FROM flags",
4458            DialectType::DuckDB,
4459        )
4460        .unwrap();
4461        annotate_types(&mut annotated, Some(&schema), Some(DialectType::DuckDB));
4462
4463        let Expression::Select(select) = &annotated else {
4464            panic!("expected select");
4465        };
4466        let Expression::Annotated(annotated) = &select.expressions[0] else {
4467            panic!("expected comment annotation wrapper");
4468        };
4469        let Expression::Case(case) = &annotated.this else {
4470            panic!("expected case");
4471        };
4472        let Expression::Column(column) = &case.whens[0].0 else {
4473            panic!("expected column condition");
4474        };
4475
4476        assert_eq!(select.expressions[0].inferred_type(), Some(&varchar));
4477        assert_eq!(case.inferred_type.as_ref(), Some(&varchar));
4478        assert_eq!(column.inferred_type.as_ref(), Some(&DataType::Boolean));
4479    }
4480
4481    #[test]
4482    fn test_set_operation_type() {
4483        let mut annotator = TypeAnnotator::new(None, None);
4484
4485        // UNION/INTERSECT/EXCEPT return None (they produce relations, not scalars)
4486        let select = Expression::Select(Box::new(crate::expressions::Select::default()));
4487        let union = Expression::Union(Box::new(crate::expressions::Union {
4488            left: select.clone(),
4489            right: select.clone(),
4490            all: false,
4491            distinct: false,
4492            with: None,
4493            order_by: None,
4494            limit: None,
4495            offset: None,
4496            by_name: false,
4497            side: None,
4498            kind: None,
4499            corresponding: false,
4500            strict: false,
4501            on_columns: Vec::new(),
4502            distribute_by: None,
4503            sort_by: None,
4504            cluster_by: None,
4505        }));
4506        assert_eq!(annotator.annotate(&union), None);
4507    }
4508
4509    #[test]
4510    fn test_floor_ceil_input_dependent_types() {
4511        use crate::expressions::{CeilFunc, FloorFunc};
4512
4513        let mut annotator = TypeAnnotator::new(None, None);
4514
4515        // FLOOR/CEIL with integer literal → Double (integers get promoted)
4516        let floor_int = Expression::Floor(Box::new(FloorFunc {
4517            this: make_int_literal(42),
4518            scale: None,
4519            to: None,
4520        }));
4521        assert_eq!(
4522            annotator.annotate(&floor_int),
4523            Some(DataType::Double {
4524                precision: None,
4525                scale: None,
4526            })
4527        );
4528
4529        let ceil_int = Expression::Ceil(Box::new(CeilFunc {
4530            this: make_int_literal(42),
4531            decimals: None,
4532            to: None,
4533        }));
4534        assert_eq!(
4535            annotator.annotate(&ceil_int),
4536            Some(DataType::Double {
4537                precision: None,
4538                scale: None,
4539            })
4540        );
4541
4542        // FLOOR with float literal → Double (literals are always Double)
4543        let floor_float = Expression::Floor(Box::new(FloorFunc {
4544            this: make_float_literal(3.14),
4545            scale: None,
4546            to: None,
4547        }));
4548        assert_eq!(
4549            annotator.annotate(&floor_float),
4550            Some(DataType::Double {
4551                precision: None,
4552                scale: None,
4553            })
4554        );
4555
4556        // FLOOR via Function("FLOOR") path → falls through to arg-based inference
4557        let floor_fn =
4558            Expression::Function(Box::new(Function::new("FLOOR", vec![make_int_literal(1)])));
4559        assert_eq!(
4560            annotator.annotate(&floor_fn),
4561            Some(DataType::Int {
4562                length: None,
4563                integer_spelling: false,
4564            })
4565        );
4566    }
4567
4568    #[test]
4569    fn test_sign_preserves_input_type() {
4570        use crate::expressions::UnaryFunc;
4571
4572        let mut annotator = TypeAnnotator::new(None, None);
4573
4574        // SIGN with integer literal → Int (preserves input type)
4575        let sign_int = Expression::Sign(Box::new(UnaryFunc {
4576            this: make_int_literal(42),
4577            original_name: None,
4578            inferred_type: None,
4579        }));
4580        assert_eq!(
4581            annotator.annotate(&sign_int),
4582            Some(DataType::Int {
4583                length: None,
4584                integer_spelling: false,
4585            })
4586        );
4587
4588        // SIGN with float literal → Double (preserves input type)
4589        let sign_float = Expression::Sign(Box::new(UnaryFunc {
4590            this: make_float_literal(3.14),
4591            original_name: None,
4592            inferred_type: None,
4593        }));
4594        assert_eq!(
4595            annotator.annotate(&sign_float),
4596            Some(DataType::Double {
4597                precision: None,
4598                scale: None,
4599            })
4600        );
4601
4602        // SIGN with a CAST to INT → Int (preserves input type)
4603        let sign_cast = Expression::Sign(Box::new(UnaryFunc {
4604            this: Expression::Cast(Box::new(Cast {
4605                this: make_int_literal(42),
4606                to: DataType::Int {
4607                    length: None,
4608                    integer_spelling: false,
4609                },
4610                format: None,
4611                trailing_comments: Vec::new(),
4612                double_colon_syntax: false,
4613                default: None,
4614                inferred_type: None,
4615            })),
4616            original_name: None,
4617            inferred_type: None,
4618        }));
4619        assert_eq!(
4620            annotator.annotate(&sign_cast),
4621            Some(DataType::Int {
4622                length: None,
4623                integer_spelling: false,
4624            })
4625        );
4626    }
4627
4628    #[test]
4629    fn test_date_format_types() {
4630        use crate::expressions::{DateFormatFunc, TimeToStr};
4631
4632        let mut annotator = TypeAnnotator::new(None, None);
4633
4634        // DateFormat → VarChar
4635        let date_fmt = Expression::DateFormat(Box::new(DateFormatFunc {
4636            this: make_string_literal("2024-01-01"),
4637            format: make_string_literal("%Y-%m-%d"),
4638        }));
4639        assert_eq!(
4640            annotator.annotate(&date_fmt),
4641            Some(DataType::VarChar {
4642                length: None,
4643                parenthesized_length: false,
4644            })
4645        );
4646
4647        // FormatDate → VarChar
4648        let format_date = Expression::FormatDate(Box::new(DateFormatFunc {
4649            this: make_string_literal("2024-01-01"),
4650            format: make_string_literal("%Y-%m-%d"),
4651        }));
4652        assert_eq!(
4653            annotator.annotate(&format_date),
4654            Some(DataType::VarChar {
4655                length: None,
4656                parenthesized_length: false,
4657            })
4658        );
4659
4660        // TimeToStr → VarChar
4661        let time_to_str = Expression::TimeToStr(Box::new(TimeToStr {
4662            this: Box::new(make_string_literal("2024-01-01")),
4663            format: "%Y-%m-%d".to_string(),
4664            culture: None,
4665            zone: None,
4666        }));
4667        assert_eq!(
4668            annotator.annotate(&time_to_str),
4669            Some(DataType::VarChar {
4670                length: None,
4671                parenthesized_length: false,
4672            })
4673        );
4674
4675        // DATE_FORMAT via Function path → VarChar (uses function_return_types)
4676        let date_fmt_fn = Expression::Function(Box::new(Function::new(
4677            "DATE_FORMAT",
4678            vec![
4679                make_string_literal("2024-01-01"),
4680                make_string_literal("%Y-%m-%d"),
4681            ],
4682        )));
4683        assert_eq!(
4684            annotator.annotate(&date_fmt_fn),
4685            Some(DataType::VarChar {
4686                length: None,
4687                parenthesized_length: false,
4688            })
4689        );
4690    }
4691
4692    // ===== In-place annotation tests (Step 9) =====
4693
4694    #[test]
4695    fn test_annotate_in_place_sets_type_on_root() {
4696        // Literals don't have inferred_type field, so test with a BinaryOp
4697        let mut expr = Expression::Add(Box::new(BinaryOp::new(
4698            make_int_literal(1),
4699            make_int_literal(2),
4700        )));
4701        annotate_types(&mut expr, None, None);
4702        assert_eq!(
4703            expr.inferred_type(),
4704            Some(&DataType::Int {
4705                length: None,
4706                integer_spelling: false,
4707            })
4708        );
4709    }
4710
4711    #[test]
4712    fn test_annotate_in_place_sets_types_on_children() {
4713        // (a + b) + (c - d) where all are ints
4714        // This tests that inner BinaryOp children also get annotated
4715        let inner_add = Expression::Add(Box::new(BinaryOp::new(
4716            make_int_literal(1),
4717            make_float_literal(2.5),
4718        )));
4719        let inner_sub = Expression::Sub(Box::new(BinaryOp::new(
4720            make_int_literal(3),
4721            make_int_literal(4),
4722        )));
4723        let mut expr = Expression::Add(Box::new(BinaryOp::new(inner_add, inner_sub)));
4724        annotate_types(&mut expr, None, None);
4725
4726        // Root (Add) should be Double (wider of Double and Int)
4727        assert_eq!(
4728            expr.inferred_type(),
4729            Some(&DataType::Double {
4730                precision: None,
4731                scale: None,
4732            })
4733        );
4734
4735        // Children should also have types
4736        if let Expression::Add(op) = &expr {
4737            // Left child (1 + 2.5) should be Double
4738            assert_eq!(
4739                op.left.inferred_type(),
4740                Some(&DataType::Double {
4741                    precision: None,
4742                    scale: None,
4743                })
4744            );
4745            // Right child (3 - 4) should be Int
4746            assert_eq!(
4747                op.right.inferred_type(),
4748                Some(&DataType::Int {
4749                    length: None,
4750                    integer_spelling: false,
4751                })
4752            );
4753        } else {
4754            panic!("Expected Add expression");
4755        }
4756    }
4757
4758    #[test]
4759    fn test_annotate_in_place_comparison() {
4760        let mut expr = Expression::Eq(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!(expr.inferred_type(), Some(&DataType::Boolean));
4766    }
4767
4768    #[test]
4769    fn test_annotate_in_place_cast() {
4770        let mut expr = Expression::Cast(Box::new(Cast {
4771            this: make_int_literal(42),
4772            to: DataType::VarChar {
4773                length: None,
4774                parenthesized_length: false,
4775            },
4776            trailing_comments: vec![],
4777            double_colon_syntax: false,
4778            format: None,
4779            default: None,
4780            inferred_type: None,
4781        }));
4782        annotate_types(&mut expr, None, None);
4783        assert_eq!(
4784            expr.inferred_type(),
4785            Some(&DataType::VarChar {
4786                length: None,
4787                parenthesized_length: false,
4788            })
4789        );
4790    }
4791
4792    #[test]
4793    fn test_annotate_in_place_nested_expression() {
4794        // (1 + 2) > 0  -> should be Boolean at root, Int for the Add
4795        let add = Expression::Add(Box::new(BinaryOp::new(
4796            make_int_literal(1),
4797            make_int_literal(2),
4798        )));
4799        let mut expr = Expression::Gt(Box::new(BinaryOp::new(add, make_int_literal(0))));
4800        annotate_types(&mut expr, None, None);
4801
4802        assert_eq!(expr.inferred_type(), Some(&DataType::Boolean));
4803
4804        // The left child (Add) should be Int
4805        if let Expression::Gt(op) = &expr {
4806            assert_eq!(
4807                op.left.inferred_type(),
4808                Some(&DataType::Int {
4809                    length: None,
4810                    integer_spelling: false,
4811                })
4812            );
4813        }
4814    }
4815
4816    #[test]
4817    fn test_annotate_in_place_parsed_sql() {
4818        use crate::parser::Parser;
4819        let mut expr =
4820            Parser::parse_sql("SELECT 1 + 2.0, 'hello', TRUE").expect("parse failed")[0].clone();
4821        annotate_types(&mut expr, None, None);
4822
4823        // The expression tree should have types annotated throughout
4824        // We can't easily inspect deep inside a parsed Select, but at minimum
4825        // the root Select itself won't have a type (it's not value-producing)
4826        assert!(expr.inferred_type().is_none());
4827    }
4828
4829    #[test]
4830    fn test_inferred_type_json_roundtrip() {
4831        let mut expr = Expression::Add(Box::new(BinaryOp::new(
4832            make_int_literal(1),
4833            make_int_literal(2),
4834        )));
4835        annotate_types(&mut expr, None, None);
4836
4837        // Serialize to JSON
4838        let json = serde_json::to_string(&expr).expect("serialize failed");
4839        // The JSON should contain the inferred_type
4840        assert!(json.contains("inferred_type"));
4841
4842        // Deserialize back
4843        let deserialized: Expression = serde_json::from_str(&json).expect("deserialize failed");
4844        assert_eq!(
4845            deserialized.inferred_type(),
4846            Some(&DataType::Int {
4847                length: None,
4848                integer_spelling: false,
4849            })
4850        );
4851    }
4852
4853    #[test]
4854    fn test_inferred_type_none_not_serialized() {
4855        // When inferred_type is None, it should not appear in JSON
4856        let expr = Expression::Add(Box::new(BinaryOp::new(
4857            make_int_literal(1),
4858            make_int_literal(2),
4859        )));
4860        let json = serde_json::to_string(&expr).expect("serialize failed");
4861        assert!(!json.contains("inferred_type"));
4862    }
4863
4864    #[test]
4865    fn test_annotate_if_func_bigquery_node_and_alias_type() {
4866        let mut schema = MappingSchema::with_dialect(DialectType::BigQuery);
4867        schema
4868            .add_table(
4869                "t",
4870                &[("col1".to_string(), DataType::String { length: None })],
4871                None,
4872            )
4873            .unwrap();
4874
4875        let mut expr = parse_one(
4876            "SELECT IF(col1 IS NOT NULL, 1, 0) AS x FROM t",
4877            DialectType::BigQuery,
4878        )
4879        .unwrap();
4880        annotate_types(&mut expr, Some(&schema), Some(DialectType::BigQuery));
4881
4882        let Expression::Select(select) = &expr else {
4883            panic!("expected select");
4884        };
4885        let Expression::Alias(alias) = &select.expressions[0] else {
4886            panic!("expected alias");
4887        };
4888
4889        assert_eq!(
4890            alias.this.inferred_type(),
4891            Some(&DataType::Int {
4892                length: None,
4893                integer_spelling: false,
4894            })
4895        );
4896        assert_eq!(
4897            select.expressions[0].inferred_type(),
4898            Some(&DataType::Int {
4899                length: None,
4900                integer_spelling: false,
4901            })
4902        );
4903    }
4904
4905    #[test]
4906    fn test_annotate_nvl2_node_type() {
4907        let mut expr = parse_one("SELECT NVL2(a, 1, 0) AS x", DialectType::Generic).unwrap();
4908        annotate_types(&mut expr, None, None);
4909
4910        let Expression::Select(select) = &expr else {
4911            panic!("expected select");
4912        };
4913        let Expression::Alias(alias) = &select.expressions[0] else {
4914            panic!("expected alias");
4915        };
4916
4917        assert_eq!(
4918            alias.this.inferred_type(),
4919            Some(&DataType::Int {
4920                length: None,
4921                integer_spelling: false,
4922            })
4923        );
4924    }
4925
4926    #[test]
4927    fn test_annotate_count_node_type() {
4928        let mut expr = parse_one("SELECT COUNT(1) AS x", DialectType::Generic).unwrap();
4929        annotate_types(&mut expr, None, None);
4930
4931        let Expression::Select(select) = &expr else {
4932            panic!("expected select");
4933        };
4934        let Expression::Alias(alias) = &select.expressions[0] else {
4935            panic!("expected alias");
4936        };
4937
4938        assert_eq!(
4939            alias.this.inferred_type(),
4940            Some(&DataType::BigInt { length: None })
4941        );
4942    }
4943
4944    #[test]
4945    fn test_annotate_group_concat_node_type() {
4946        let mut expr = parse_one("SELECT GROUP_CONCAT(a) AS x", DialectType::Generic).unwrap();
4947        annotate_types(&mut expr, None, None);
4948
4949        let Expression::Select(select) = &expr else {
4950            panic!("expected select");
4951        };
4952        let Expression::Alias(alias) = &select.expressions[0] else {
4953            panic!("expected alias");
4954        };
4955
4956        assert_eq!(
4957            alias.this.inferred_type(),
4958            Some(&DataType::VarChar {
4959                length: None,
4960                parenthesized_length: false,
4961            })
4962        );
4963    }
4964
4965    #[test]
4966    fn test_annotate_sum_if_generic_aggregate_type() {
4967        let mut expr =
4968            parse_one("SELECT SUM_IF(1, a > 0) AS x FROM t", DialectType::Generic).unwrap();
4969        annotate_types(&mut expr, None, None);
4970
4971        let Expression::Select(select) = &expr else {
4972            panic!("expected select");
4973        };
4974        let Expression::Alias(alias) = &select.expressions[0] else {
4975            panic!("expected alias");
4976        };
4977
4978        assert_eq!(
4979            select.expressions[0].inferred_type(),
4980            Some(&DataType::BigInt { length: None })
4981        );
4982        assert_eq!(
4983            alias.this.inferred_type(),
4984            Some(&DataType::BigInt { length: None })
4985        );
4986    }
4987
4988    #[test]
4989    fn test_annotate_duckdb_median_node_types() {
4990        let cases = [
4991            (
4992                "SELECT MEDIAN(CAST(1 AS INTEGER)) AS result",
4993                DataType::Double {
4994                    precision: None,
4995                    scale: None,
4996                },
4997            ),
4998            (
4999                "SELECT MEDIAN(CAST(1 AS FLOAT)) AS result",
5000                DataType::Float {
5001                    precision: None,
5002                    scale: None,
5003                    real_spelling: false,
5004                },
5005            ),
5006            (
5007                "SELECT MEDIAN(CAST(1 AS DECIMAL(10, 2))) AS result",
5008                DataType::Decimal {
5009                    precision: Some(10),
5010                    scale: Some(2),
5011                },
5012            ),
5013            (
5014                "SELECT MEDIAN(CAST('2024-01-01' AS DATE)) AS result",
5015                DataType::Timestamp {
5016                    precision: None,
5017                    timezone: false,
5018                },
5019            ),
5020        ];
5021
5022        for (sql, expected) in cases {
5023            let mut expr = parse_one(sql, DialectType::DuckDB).unwrap();
5024            annotate_types(&mut expr, None, Some(DialectType::DuckDB));
5025
5026            let Expression::Select(select) = &expr else {
5027                panic!("expected select for {sql}");
5028            };
5029            let Expression::Alias(alias) = &select.expressions[0] else {
5030                panic!("expected alias for {sql}");
5031            };
5032
5033            assert_eq!(alias.this.inferred_type(), Some(&expected), "{sql}");
5034            assert_eq!(
5035                select.expressions[0].inferred_type(),
5036                Some(&expected),
5037                "{sql}"
5038            );
5039        }
5040    }
5041}