Skip to main content

sqlglot_rust/ast/
types.rs

1use serde::{Deserialize, Serialize};
2
3// ═══════════════════════════════════════════════════════════════════════
4// Comment types
5// ═══════════════════════════════════════════════════════════════════════
6
7/// The type of a SQL comment.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum CommentType {
10    /// Single-line comment starting with `--`
11    Line,
12    /// Block comment delimited by `/* ... */`
13    Block,
14    /// MySQL-style hash comment starting with `#`
15    Hash,
16}
17
18// ═══════════════════════════════════════════════════════════════════════
19// Identifier quoting style
20// ═══════════════════════════════════════════════════════════════════════
21
22/// How an identifier (column, table, alias) was quoted in the source SQL.
23///
24/// Used to preserve and transform quoting across dialects (e.g. backtick
25/// for MySQL/BigQuery → double-quote for PostgreSQL → bracket for T-SQL).
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
27pub enum QuoteStyle {
28    /// Bare / unquoted identifier
29    #[default]
30    None,
31    /// `"identifier"` — ANSI SQL, PostgreSQL, Oracle, Snowflake, etc.
32    DoubleQuote,
33    /// `` `identifier` `` — MySQL, BigQuery, Hive, Spark, etc.
34    Backtick,
35    /// `[identifier]` — T-SQL / SQL Server
36    Bracket,
37}
38
39impl QuoteStyle {
40    /// Returns the canonical quoting style for the given dialect.
41    #[must_use]
42    pub fn for_dialect(dialect: crate::dialects::Dialect) -> Self {
43        use crate::dialects::Dialect;
44        match dialect {
45            Dialect::Tsql | Dialect::Fabric => QuoteStyle::Bracket,
46            Dialect::Mysql
47            | Dialect::BigQuery
48            | Dialect::Hive
49            | Dialect::Spark
50            | Dialect::Databricks
51            | Dialect::Doris
52            | Dialect::SingleStore
53            | Dialect::StarRocks => QuoteStyle::Backtick,
54            // ANSI, Postgres, Oracle, Snowflake, Presto, Trino, etc.
55            _ => QuoteStyle::DoubleQuote,
56        }
57    }
58
59    /// Returns `true` when the identifier carries explicit quoting.
60    #[must_use]
61    pub fn is_quoted(self) -> bool {
62        !matches!(self, QuoteStyle::None)
63    }
64}
65
66// ═══════════════════════════════════════════════════════════════════════
67// Top-level statement types
68// ═══════════════════════════════════════════════════════════════════════
69
70/// A fully parsed SQL statement.
71///
72/// Corresponds to the top-level node in sqlglot's expression tree.
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub enum Statement {
75    Select(SelectStatement),
76    Insert(InsertStatement),
77    Update(UpdateStatement),
78    Delete(DeleteStatement),
79    CreateTable(CreateTableStatement),
80    DropTable(DropTableStatement),
81    /// UNION / INTERSECT / EXCEPT between queries
82    SetOperation(SetOperationStatement),
83    /// ALTER TABLE ...
84    AlterTable(AlterTableStatement),
85    /// CREATE VIEW ...
86    CreateView(CreateViewStatement),
87    /// DROP VIEW ...
88    DropView(DropViewStatement),
89    /// TRUNCATE TABLE ...
90    Truncate(TruncateStatement),
91    /// BEGIN / COMMIT / ROLLBACK
92    Transaction(TransactionStatement),
93    /// EXPLAIN <statement>
94    Explain(ExplainStatement),
95    /// USE database
96    Use(UseStatement),
97    /// MERGE INTO ... USING ... WHEN MATCHED / WHEN NOT MATCHED
98    Merge(MergeStatement),
99    /// Raw / passthrough expression (for expressions that don't fit a specific statement type)
100    Expression(Expr),
101    /// Catch-all for statements that the parser accepts for round-tripping
102    /// but does not model in detail (e.g. `SET extra_float_digits = 0`,
103    /// `SHOW VARIABLES LIKE 'innodb%'`, `GO`, `DECLARE @x INT = 1`,
104    /// `COMMENT ON COLUMN t.c IS 'x'`, `CREATE OPERATOR FAMILY …`,
105    /// vendor-specific `ALTER TABLE … COMPACT 'major'`).
106    ///
107    /// The verb (`kind`) preserves the original keyword(s) so the generator
108    /// can emit a literal round-trip; `body` is the raw rest of the
109    /// statement up to the terminating semicolon/EOF.
110    Command(CommandStatement),
111}
112
113// ═══════════════════════════════════════════════════════════════════════
114// SELECT
115// ═══════════════════════════════════════════════════════════════════════
116
117/// A SELECT statement, including CTEs.
118///
119/// Aligned with sqlglot's `Select` expression which wraps `With`, `From`,
120/// `Where`, `Group`, `Having`, `Order`, `Limit`, `Offset`, `Window`.
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub struct SelectStatement {
123    /// Comments attached to this statement.
124    #[serde(default, skip_serializing_if = "Vec::is_empty")]
125    pub comments: Vec<String>,
126    /// Common Table Expressions (WITH clause)
127    pub ctes: Vec<Cte>,
128    pub distinct: bool,
129    /// TOP N (TSQL-style)
130    pub top: Option<Box<Expr>>,
131    pub columns: Vec<SelectItem>,
132    pub from: Option<FromClause>,
133    pub joins: Vec<JoinClause>,
134    pub where_clause: Option<Expr>,
135    pub group_by: Vec<Expr>,
136    pub having: Option<Expr>,
137    pub order_by: Vec<OrderByItem>,
138    pub limit: Option<Expr>,
139    pub offset: Option<Expr>,
140    /// Oracle-style FETCH FIRST n ROWS ONLY
141    pub fetch_first: Option<Expr>,
142    /// QUALIFY clause (BigQuery, Snowflake)
143    pub qualify: Option<Expr>,
144    /// Named WINDOW definitions
145    pub window_definitions: Vec<WindowDefinition>,
146}
147
148/// A Common Table Expression: `name [(col1, col2)] AS [NOT] MATERIALIZED (query)`
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150pub struct Cte {
151    pub name: String,
152    #[serde(default)]
153    pub name_quote_style: QuoteStyle,
154    pub columns: Vec<String>,
155    pub query: Box<Statement>,
156    pub materialized: Option<bool>,
157    pub recursive: bool,
158}
159
160/// Named WINDOW definition: `window_name AS (window_spec)`
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub struct WindowDefinition {
163    pub name: String,
164    pub spec: WindowSpec,
165}
166
167// ═══════════════════════════════════════════════════════════════════════
168// Set operations (UNION, INTERSECT, EXCEPT)
169// ═══════════════════════════════════════════════════════════════════════
170
171/// UNION / INTERSECT / EXCEPT between two or more queries.
172#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub struct SetOperationStatement {
174    /// Comments attached to this statement.
175    #[serde(default, skip_serializing_if = "Vec::is_empty")]
176    pub comments: Vec<String>,
177    pub op: SetOperationType,
178    pub all: bool,
179    pub left: Box<Statement>,
180    pub right: Box<Statement>,
181    pub order_by: Vec<OrderByItem>,
182    pub limit: Option<Expr>,
183    pub offset: Option<Expr>,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
187pub enum SetOperationType {
188    Union,
189    Intersect,
190    Except,
191}
192
193// ═══════════════════════════════════════════════════════════════════════
194// SELECT items and FROM
195// ═══════════════════════════════════════════════════════════════════════
196
197/// An item in a SELECT list.
198#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
199pub enum SelectItem {
200    /// `*`
201    Wildcard,
202    /// `table.*`
203    QualifiedWildcard { table: String },
204    /// An expression with optional alias: `expr AS alias`
205    Expr {
206        expr: Expr,
207        alias: Option<String>,
208        #[serde(default)]
209        alias_quote_style: QuoteStyle,
210    },
211}
212
213/// A FROM clause, now supporting subqueries and multiple tables.
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub struct FromClause {
216    pub source: TableSource,
217}
218
219/// A table source can be a table reference, subquery, or table function.
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221pub enum TableSource {
222    Table(TableRef),
223    Subquery {
224        query: Box<Statement>,
225        alias: Option<String>,
226        #[serde(default)]
227        alias_quote_style: QuoteStyle,
228    },
229    TableFunction {
230        name: String,
231        args: Vec<Expr>,
232        alias: Option<String>,
233        #[serde(default)]
234        alias_quote_style: QuoteStyle,
235    },
236    /// LATERAL subquery or function
237    Lateral {
238        source: Box<TableSource>,
239    },
240    /// UNNEST(array_expr)
241    Unnest {
242        expr: Box<Expr>,
243        alias: Option<String>,
244        #[serde(default)]
245        alias_quote_style: QuoteStyle,
246        with_offset: bool,
247    },
248    /// PIVOT (aggregate FOR column IN (values))
249    Pivot {
250        source: Box<TableSource>,
251        aggregate: Box<Expr>,
252        for_column: String,
253        in_values: Vec<PivotValue>,
254        alias: Option<String>,
255        #[serde(default)]
256        alias_quote_style: QuoteStyle,
257    },
258    /// UNPIVOT (value_column FOR name_column IN (columns))
259    Unpivot {
260        source: Box<TableSource>,
261        value_column: String,
262        for_column: String,
263        in_columns: Vec<PivotValue>,
264        alias: Option<String>,
265        #[serde(default)]
266        alias_quote_style: QuoteStyle,
267    },
268}
269
270/// A value/column in a PIVOT IN list, optionally aliased.
271#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
272pub struct PivotValue {
273    pub value: Expr,
274    pub alias: Option<String>,
275    #[serde(default)]
276    pub alias_quote_style: QuoteStyle,
277}
278
279/// A reference to a table.
280#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
281pub struct TableRef {
282    pub catalog: Option<String>,
283    pub schema: Option<String>,
284    pub name: String,
285    pub alias: Option<String>,
286    /// How the table name was quoted in the source SQL.
287    #[serde(default)]
288    pub name_quote_style: QuoteStyle,
289    /// How the alias was quoted in the source SQL.
290    #[serde(default)]
291    pub alias_quote_style: QuoteStyle,
292}
293
294/// A JOIN clause.
295#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
296pub struct JoinClause {
297    pub join_type: JoinType,
298    pub table: TableSource,
299    pub on: Option<Expr>,
300    pub using: Vec<String>,
301}
302
303/// The type of JOIN.
304#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
305pub enum JoinType {
306    Inner,
307    Left,
308    Right,
309    Full,
310    Cross,
311    /// NATURAL JOIN
312    Natural,
313    /// LATERAL JOIN
314    Lateral,
315}
316
317/// An ORDER BY item.
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
319pub struct OrderByItem {
320    pub expr: Expr,
321    pub ascending: bool,
322    /// NULLS FIRST / NULLS LAST
323    pub nulls_first: Option<bool>,
324}
325
326// ═══════════════════════════════════════════════════════════════════════
327// Expressions (the core of the AST)
328// ═══════════════════════════════════════════════════════════════════════
329
330/// An expression in SQL.
331///
332/// This enum is aligned with sqlglot's Expression class hierarchy.
333/// Key additions over the basic implementation:
334/// - Subquery, Exists, Cast, Extract, Window functions
335/// - TypedString, Interval, Array/Struct constructors
336/// - Postgres-style casting (`::`)
337#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
338pub enum Expr {
339    /// A column reference, possibly qualified: `[catalog.][schema.]table.column`
340    Column {
341        table: Option<String>,
342        name: String,
343        /// How the column name was quoted in the source SQL.
344        #[serde(default)]
345        quote_style: QuoteStyle,
346        /// How the table qualifier was quoted, if present.
347        #[serde(default)]
348        table_quote_style: QuoteStyle,
349    },
350    /// A numeric literal.
351    Number(String),
352    /// A string literal.
353    StringLiteral(String),
354    /// A national character string literal: `N'...'`.
355    NationalStringLiteral(String),
356    /// A boolean literal.
357    Boolean(bool),
358    /// NULL literal.
359    Null,
360    /// A binary operation: `left op right`
361    BinaryOp {
362        left: Box<Expr>,
363        op: BinaryOperator,
364        right: Box<Expr>,
365    },
366    /// A unary operation: `op expr`
367    UnaryOp { op: UnaryOperator, expr: Box<Expr> },
368    /// A function call: `name(args...)` with optional DISTINCT, ORDER BY, etc.
369    Function {
370        name: String,
371        args: Vec<Expr>,
372        distinct: bool,
373        /// FILTER (WHERE expr) clause on aggregate
374        filter: Option<Box<Expr>>,
375        /// OVER window specification for window functions
376        over: Option<WindowSpec>,
377        /// Inline ORDER BY inside the argument list, or a trailing
378        /// WITHIN GROUP (ORDER BY …) clause. Both surface forms are
379        /// stored here; the generator picks the right emission based
380        /// on `within_group`.
381        #[serde(default, skip_serializing_if = "Vec::is_empty")]
382        order_by: Vec<OrderByItem>,
383        /// True iff the ORDER BY originally came from a trailing
384        /// `WITHIN GROUP (ORDER BY …)` clause rather than an inline
385        /// `ORDER BY` inside the argument list.
386        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
387        within_group: bool,
388    },
389    /// `expr BETWEEN low AND high`
390    Between {
391        expr: Box<Expr>,
392        low: Box<Expr>,
393        high: Box<Expr>,
394        negated: bool,
395    },
396    /// `expr IN (list...)` or `expr IN (subquery)`
397    InList {
398        expr: Box<Expr>,
399        list: Vec<Expr>,
400        negated: bool,
401    },
402    /// `expr IN (SELECT ...)`
403    InSubquery {
404        expr: Box<Expr>,
405        subquery: Box<Statement>,
406        negated: bool,
407    },
408    /// `expr op ANY(subexpr)` — PostgreSQL array/subquery comparison
409    AnyOp {
410        expr: Box<Expr>,
411        op: BinaryOperator,
412        right: Box<Expr>,
413    },
414    /// `expr op ALL(subexpr)` — PostgreSQL array/subquery comparison
415    AllOp {
416        expr: Box<Expr>,
417        op: BinaryOperator,
418        right: Box<Expr>,
419    },
420    /// `expr IS [NOT] NULL`
421    IsNull { expr: Box<Expr>, negated: bool },
422    /// `expr IS [NOT] TRUE` / `expr IS [NOT] FALSE`
423    IsBool {
424        expr: Box<Expr>,
425        value: bool,
426        negated: bool,
427    },
428    /// `expr [NOT] LIKE pattern [ESCAPE escape_char]`
429    Like {
430        expr: Box<Expr>,
431        pattern: Box<Expr>,
432        negated: bool,
433        escape: Option<Box<Expr>>,
434    },
435    /// `expr [NOT] ILIKE pattern [ESCAPE escape_char]` (case-insensitive LIKE)
436    ILike {
437        expr: Box<Expr>,
438        pattern: Box<Expr>,
439        negated: bool,
440        escape: Option<Box<Expr>>,
441    },
442    /// `expr [NOT] SIMILAR TO pattern [ESCAPE escape_char]`
443    SimilarTo {
444        expr: Box<Expr>,
445        pattern: Box<Expr>,
446        negated: bool,
447        escape: Option<Box<Expr>>,
448    },
449    /// `CASE [operand] WHEN ... THEN ... ELSE ... END`
450    Case {
451        operand: Option<Box<Expr>>,
452        when_clauses: Vec<(Expr, Expr)>,
453        else_clause: Option<Box<Expr>>,
454    },
455    /// A parenthesized sub-expression.
456    Nested(Box<Expr>),
457    /// A wildcard `*` used in contexts like `COUNT(*)`.
458    Wildcard,
459    /// A scalar subquery: `(SELECT ...)`
460    Subquery(Box<Statement>),
461    /// `EXISTS (SELECT ...)`
462    Exists {
463        subquery: Box<Statement>,
464        negated: bool,
465    },
466    /// `CAST(expr AS type)` or `expr::type` (PostgreSQL)
467    Cast {
468        expr: Box<Expr>,
469        data_type: DataType,
470    },
471    /// `TRY_CAST(expr AS type)`
472    TryCast {
473        expr: Box<Expr>,
474        data_type: DataType,
475    },
476    /// `EXTRACT(field FROM expr)`
477    Extract {
478        field: DateTimeField,
479        expr: Box<Expr>,
480    },
481    /// `INTERVAL 'value' unit`
482    Interval {
483        value: Box<Expr>,
484        unit: Option<DateTimeField>,
485    },
486    /// Array literal: `ARRAY[1, 2, 3]` or `[1, 2, 3]`
487    ArrayLiteral(Vec<Expr>),
488    /// Struct literal / row constructor: `(1, 'a', true)`
489    Tuple(Vec<Expr>),
490    /// `COALESCE(a, b, c)`
491    Coalesce(Vec<Expr>),
492    /// `IF(condition, true_val, false_val)` (MySQL, BigQuery)
493    If {
494        condition: Box<Expr>,
495        true_val: Box<Expr>,
496        false_val: Option<Box<Expr>>,
497    },
498    /// `NULLIF(a, b)`
499    NullIf { expr: Box<Expr>, r#else: Box<Expr> },
500    /// `expr COLLATE collation`
501    Collate { expr: Box<Expr>, collation: String },
502    /// Parameter / placeholder: `$1`, `?`, `:name`
503    Parameter(String),
504    /// A type expression used in DDL contexts or CAST
505    TypeExpr(DataType),
506    /// `table.*` in expression context
507    QualifiedWildcard { table: String },
508    /// Star expression `*`
509    Star,
510    /// Alias expression: `expr AS name`
511    Alias { expr: Box<Expr>, name: String },
512    /// Array access: `expr[index]`
513    ArrayIndex { expr: Box<Expr>, index: Box<Expr> },
514    /// JSON access: `expr->key` or `expr->>key`
515    JsonAccess {
516        expr: Box<Expr>,
517        path: Box<Expr>,
518        /// false = ->, true = ->>
519        as_text: bool,
520    },
521    /// Lambda expression: `x -> x + 1`
522    Lambda {
523        params: Vec<String>,
524        body: Box<Expr>,
525    },
526    /// `DEFAULT` keyword in INSERT/UPDATE contexts
527    Default,
528    /// `CUBE(a, b, ...)` in GROUP BY clause
529    Cube { exprs: Vec<Expr> },
530    /// `ROLLUP(a, b, ...)` in GROUP BY clause
531    Rollup { exprs: Vec<Expr> },
532    /// `GROUPING SETS ((a, b), (c), ...)` in GROUP BY clause
533    GroupingSets { sets: Vec<Expr> },
534    /// A typed function expression with semantic awareness.
535    /// Enables per-function, per-dialect code generation and transpilation.
536    TypedFunction {
537        func: TypedFunction,
538        /// FILTER (WHERE expr) clause on aggregate
539        filter: Option<Box<Expr>>,
540        /// OVER window specification for window functions
541        over: Option<WindowSpec>,
542    },
543    /// An expression with attached SQL comments.
544    /// Wraps an inner expression so that comments survive transformations.
545    Commented {
546        expr: Box<Expr>,
547        comments: Vec<String>,
548    },
549}
550
551// ═══════════════════════════════════════════════════════════════════════
552// Window specification
553// ═══════════════════════════════════════════════════════════════════════
554
555/// Window specification for window functions: OVER (PARTITION BY ... ORDER BY ... frame)
556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
557pub struct WindowSpec {
558    /// Reference to a named window
559    pub window_ref: Option<String>,
560    pub partition_by: Vec<Expr>,
561    pub order_by: Vec<OrderByItem>,
562    pub frame: Option<WindowFrame>,
563}
564
565#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
566pub struct WindowFrame {
567    pub kind: WindowFrameKind,
568    pub start: WindowFrameBound,
569    pub end: Option<WindowFrameBound>,
570}
571
572#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
573pub enum WindowFrameKind {
574    Rows,
575    Range,
576    Groups,
577}
578
579#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
580pub enum WindowFrameBound {
581    CurrentRow,
582    Preceding(Option<Box<Expr>>), // None = UNBOUNDED PRECEDING
583    Following(Option<Box<Expr>>), // None = UNBOUNDED FOLLOWING
584}
585
586// ═══════════════════════════════════════════════════════════════════════
587// Date/time fields (for EXTRACT, INTERVAL)
588// ═══════════════════════════════════════════════════════════════════════
589
590#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
591pub enum DateTimeField {
592    Year,
593    Quarter,
594    Month,
595    Week,
596    Day,
597    DayOfWeek,
598    DayOfYear,
599    Hour,
600    Minute,
601    Second,
602    Millisecond,
603    Microsecond,
604    Nanosecond,
605    Epoch,
606    Timezone,
607    TimezoneHour,
608    TimezoneMinute,
609}
610
611// ═══════════════════════════════════════════════════════════════════════
612// Trim type
613// ═══════════════════════════════════════════════════════════════════════
614
615/// The type of TRIM operation.
616#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
617pub enum TrimType {
618    Leading,
619    Trailing,
620    Both,
621}
622
623// ═══════════════════════════════════════════════════════════════════════
624// Typed function expressions
625// ═══════════════════════════════════════════════════════════════════════
626
627/// Typed function variants enabling per-function transpilation rules,
628/// function signature validation, and dialect-specific code generation.
629///
630/// Each variant carries semantically typed arguments rather than a generic
631/// `Vec<Expr>`, allowing the generator to emit dialect-specific SQL.
632#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
633pub enum TypedFunction {
634    // ── Date/Time ──────────────────────────────────────────────────────
635    /// `DATE_ADD(expr, interval)` — add an interval to a date/timestamp
636    DateAdd {
637        expr: Box<Expr>,
638        interval: Box<Expr>,
639        unit: Option<DateTimeField>,
640    },
641    /// `DATE_DIFF(start, end)` — difference between two dates
642    DateDiff {
643        start: Box<Expr>,
644        end: Box<Expr>,
645        unit: Option<DateTimeField>,
646    },
647    /// `DATE_TRUNC(unit, expr)` — truncate to the given precision
648    DateTrunc {
649        unit: DateTimeField,
650        expr: Box<Expr>,
651    },
652    /// `DATE_SUB(expr, interval)` — subtract an interval from a date
653    DateSub {
654        expr: Box<Expr>,
655        interval: Box<Expr>,
656        unit: Option<DateTimeField>,
657    },
658    /// `CURRENT_DATE`
659    CurrentDate,
660    /// `CURRENT_TIME` — returns the current time of day (ANSI / PostgreSQL).
661    CurrentTime,
662    /// `CURRENT_TIMESTAMP` / `NOW()` / `GETDATE()`
663    CurrentTimestamp,
664    /// `STR_TO_TIME(expr, format)` / `TO_TIMESTAMP` / `PARSE_DATETIME`
665    StrToTime { expr: Box<Expr>, format: Box<Expr> },
666    /// `TIME_TO_STR(expr, format)` / `DATE_FORMAT` / `FORMAT_DATETIME`
667    TimeToStr { expr: Box<Expr>, format: Box<Expr> },
668    /// `TS_OR_DS_TO_DATE(expr)` — convert timestamp or date-string to date
669    TsOrDsToDate { expr: Box<Expr> },
670    /// `YEAR(expr)` — extract year from a date/timestamp
671    Year { expr: Box<Expr> },
672    /// `MONTH(expr)` — extract month from a date/timestamp
673    Month { expr: Box<Expr> },
674    /// `DAY(expr)` — extract day from a date/timestamp
675    Day { expr: Box<Expr> },
676
677    // ── String ─────────────────────────────────────────────────────────
678    /// `TRIM([LEADING|TRAILING|BOTH] [chars FROM] expr)`
679    Trim {
680        expr: Box<Expr>,
681        trim_type: TrimType,
682        trim_chars: Option<Box<Expr>>,
683    },
684    /// `SUBSTRING(expr, start [, length])` / `SUBSTR`
685    Substring {
686        expr: Box<Expr>,
687        start: Box<Expr>,
688        length: Option<Box<Expr>>,
689    },
690    /// `UPPER(expr)` / `UCASE`
691    Upper { expr: Box<Expr> },
692    /// `LOWER(expr)` / `LCASE`
693    Lower { expr: Box<Expr> },
694    /// `REGEXP_LIKE(expr, pattern [, flags])` / `~` (Postgres)
695    RegexpLike {
696        expr: Box<Expr>,
697        pattern: Box<Expr>,
698        flags: Option<Box<Expr>>,
699    },
700    /// `REGEXP_EXTRACT(expr, pattern [, group_index])`
701    RegexpExtract {
702        expr: Box<Expr>,
703        pattern: Box<Expr>,
704        group_index: Option<Box<Expr>>,
705    },
706    /// `REGEXP_REPLACE(expr, pattern, replacement [, flags])`
707    RegexpReplace {
708        expr: Box<Expr>,
709        pattern: Box<Expr>,
710        replacement: Box<Expr>,
711        flags: Option<Box<Expr>>,
712    },
713    /// `CONCAT_WS(separator, expr, ...)`
714    ConcatWs {
715        separator: Box<Expr>,
716        exprs: Vec<Expr>,
717    },
718    /// `SPLIT(expr, delimiter)` / `STRING_SPLIT`
719    Split {
720        expr: Box<Expr>,
721        delimiter: Box<Expr>,
722    },
723    /// `INITCAP(expr)` — capitalize first letter of each word
724    Initcap { expr: Box<Expr> },
725    /// `LENGTH(expr)` / `LEN`
726    Length { expr: Box<Expr> },
727    /// `REPLACE(expr, from, to)`
728    Replace {
729        expr: Box<Expr>,
730        from: Box<Expr>,
731        to: Box<Expr>,
732    },
733    /// `REVERSE(expr)`
734    Reverse { expr: Box<Expr> },
735    /// `LEFT(expr, n)`
736    Left { expr: Box<Expr>, n: Box<Expr> },
737    /// `RIGHT(expr, n)`
738    Right { expr: Box<Expr>, n: Box<Expr> },
739    /// `LPAD(expr, length [, pad])`
740    Lpad {
741        expr: Box<Expr>,
742        length: Box<Expr>,
743        pad: Option<Box<Expr>>,
744    },
745    /// `RPAD(expr, length [, pad])`
746    Rpad {
747        expr: Box<Expr>,
748        length: Box<Expr>,
749        pad: Option<Box<Expr>>,
750    },
751
752    // ── Aggregate ──────────────────────────────────────────────────────
753    /// `COUNT(expr)` or `COUNT(DISTINCT expr)` or `COUNT(*)`
754    Count { expr: Box<Expr>, distinct: bool },
755    /// `SUM([DISTINCT] expr)`
756    Sum { expr: Box<Expr>, distinct: bool },
757    /// `AVG([DISTINCT] expr)`
758    Avg { expr: Box<Expr>, distinct: bool },
759    /// `MIN(expr)`
760    Min { expr: Box<Expr> },
761    /// `MAX(expr)`
762    Max { expr: Box<Expr> },
763    /// `ARRAY_AGG([DISTINCT] expr)` / `LIST` / `COLLECT_LIST`
764    ArrayAgg { expr: Box<Expr>, distinct: bool },
765    /// `APPROX_DISTINCT(expr)` / `APPROX_COUNT_DISTINCT`
766    ApproxDistinct { expr: Box<Expr> },
767    /// `VARIANCE(expr)` / `VAR_SAMP` (sample variance)
768    Variance { expr: Box<Expr> },
769    /// `VAR_POP(expr)` (population variance)
770    VariancePop { expr: Box<Expr> },
771    /// `STDDEV(expr)` / `STDDEV_SAMP` (sample standard deviation)
772    Stddev { expr: Box<Expr> },
773    /// `STDDEV_POP(expr)` (population standard deviation)
774    StddevPop { expr: Box<Expr> },
775    /// `GROUP_CONCAT([DISTINCT] expr [, ...] [ORDER BY ...] [SEPARATOR sep])` (MySQL)
776    /// / `STRING_AGG(expr, sep [ORDER BY ...])` (Postgres/BigQuery/TSQL)
777    /// / `LISTAGG(expr, sep [WITHIN GROUP (ORDER BY ...)])` (Oracle/Redshift/Snowflake)
778    /// / `GROUP_CONCAT(expr, sep)` (SQLite)
779    GroupConcat {
780        /// The expression(s) being concatenated. MySQL allows multiple positional
781        /// args which are implicitly concatenated; most other dialects accept one.
782        exprs: Vec<Expr>,
783        /// Optional separator. When absent, dialect default applies (`,` for MySQL).
784        separator: Option<Box<Expr>>,
785        /// Optional ORDER BY clause for the aggregate input.
786        order_by: Vec<OrderByItem>,
787        /// DISTINCT modifier on input rows.
788        distinct: bool,
789    },
790
791    // ── Array ──────────────────────────────────────────────────────────
792    /// `ARRAY_CONCAT(arr1, arr2)` / `ARRAY_CAT`
793    ArrayConcat { arrays: Vec<Expr> },
794    /// `ARRAY_CONTAINS(array, element)` / `ARRAY_POSITION`
795    ArrayContains {
796        array: Box<Expr>,
797        element: Box<Expr>,
798    },
799    /// `ARRAY_SIZE(expr)` / `ARRAY_LENGTH` / `CARDINALITY`
800    ArraySize { expr: Box<Expr> },
801    /// `EXPLODE(expr)` — Hive/Spark array expansion
802    Explode { expr: Box<Expr> },
803    /// `GENERATE_SERIES(start, stop [, step])`
804    GenerateSeries {
805        start: Box<Expr>,
806        stop: Box<Expr>,
807        step: Option<Box<Expr>>,
808    },
809    /// `FLATTEN(expr)` — flatten nested arrays
810    Flatten { expr: Box<Expr> },
811
812    // ── JSON ───────────────────────────────────────────────────────────
813    /// `JSON_EXTRACT(expr, path)` / `JSON_VALUE` / `->` (Postgres)
814    JSONExtract { expr: Box<Expr>, path: Box<Expr> },
815    /// `JSON_EXTRACT_SCALAR(expr, path)` / `->>`
816    JSONExtractScalar { expr: Box<Expr>, path: Box<Expr> },
817    /// `PARSE_JSON(expr)` / `JSON_PARSE`
818    ParseJSON { expr: Box<Expr> },
819    /// `JSON_FORMAT(expr)` / `TO_JSON`
820    JSONFormat { expr: Box<Expr> },
821
822    // ── Window ─────────────────────────────────────────────────────────
823    /// `ROW_NUMBER()`
824    RowNumber,
825    /// `RANK()`
826    Rank,
827    /// `DENSE_RANK()`
828    DenseRank,
829    /// `NTILE(n)`
830    NTile { n: Box<Expr> },
831    /// `LEAD(expr [, offset [, default]])`
832    Lead {
833        expr: Box<Expr>,
834        offset: Option<Box<Expr>>,
835        default: Option<Box<Expr>>,
836    },
837    /// `LAG(expr [, offset [, default]])`
838    Lag {
839        expr: Box<Expr>,
840        offset: Option<Box<Expr>>,
841        default: Option<Box<Expr>>,
842    },
843    /// `FIRST_VALUE(expr)`
844    FirstValue { expr: Box<Expr> },
845    /// `LAST_VALUE(expr)`
846    LastValue { expr: Box<Expr> },
847
848    // ── Math ───────────────────────────────────────────────────────────
849    /// `ABS(expr)`
850    Abs { expr: Box<Expr> },
851    /// `CEIL(expr)` / `CEILING`
852    Ceil { expr: Box<Expr> },
853    /// `FLOOR(expr)`
854    Floor { expr: Box<Expr> },
855    /// `ROUND(expr [, decimals])`
856    Round {
857        expr: Box<Expr>,
858        decimals: Option<Box<Expr>>,
859    },
860    /// `LOG(expr [, base])` — semantics vary by dialect
861    Log {
862        expr: Box<Expr>,
863        base: Option<Box<Expr>>,
864    },
865    /// `LN(expr)` — natural logarithm
866    Ln { expr: Box<Expr> },
867    /// `POW(base, exponent)` / `POWER`
868    Pow {
869        base: Box<Expr>,
870        exponent: Box<Expr>,
871    },
872    /// `SQRT(expr)`
873    Sqrt { expr: Box<Expr> },
874    /// `GREATEST(expr, ...)`
875    Greatest { exprs: Vec<Expr> },
876    /// `LEAST(expr, ...)`
877    Least { exprs: Vec<Expr> },
878    /// `MOD(a, b)` — modulo function
879    Mod { left: Box<Expr>, right: Box<Expr> },
880
881    // ── Conversion ─────────────────────────────────────────────────────
882    /// `HEX(expr)` / `TO_HEX`
883    Hex { expr: Box<Expr> },
884    /// `UNHEX(expr)` / `FROM_HEX`
885    Unhex { expr: Box<Expr> },
886    /// `MD5(expr)`
887    Md5 { expr: Box<Expr> },
888    /// `SHA(expr)` / `SHA1`
889    Sha { expr: Box<Expr> },
890    /// `SHA2(expr, bit_length)` — SHA-256/SHA-512
891    Sha2 {
892        expr: Box<Expr>,
893        bit_length: Box<Expr>,
894    },
895}
896
897impl TypedFunction {
898    /// Walk child expressions, calling `visitor` on each.
899    pub fn walk_children<F>(&self, visitor: &mut F)
900    where
901        F: FnMut(&Expr) -> bool,
902    {
903        match self {
904            // Date/Time
905            TypedFunction::DateAdd { expr, interval, .. }
906            | TypedFunction::DateSub { expr, interval, .. } => {
907                expr.walk(visitor);
908                interval.walk(visitor);
909            }
910            TypedFunction::DateDiff { start, end, .. } => {
911                start.walk(visitor);
912                end.walk(visitor);
913            }
914            TypedFunction::DateTrunc { expr, .. } => expr.walk(visitor),
915            TypedFunction::CurrentDate
916            | TypedFunction::CurrentTime
917            | TypedFunction::CurrentTimestamp => {}
918            TypedFunction::StrToTime { expr, format }
919            | TypedFunction::TimeToStr { expr, format } => {
920                expr.walk(visitor);
921                format.walk(visitor);
922            }
923            TypedFunction::TsOrDsToDate { expr }
924            | TypedFunction::Year { expr }
925            | TypedFunction::Month { expr }
926            | TypedFunction::Day { expr } => expr.walk(visitor),
927
928            // String
929            TypedFunction::Trim {
930                expr, trim_chars, ..
931            } => {
932                expr.walk(visitor);
933                if let Some(c) = trim_chars {
934                    c.walk(visitor);
935                }
936            }
937            TypedFunction::Substring {
938                expr,
939                start,
940                length,
941            } => {
942                expr.walk(visitor);
943                start.walk(visitor);
944                if let Some(l) = length {
945                    l.walk(visitor);
946                }
947            }
948            TypedFunction::Upper { expr }
949            | TypedFunction::Lower { expr }
950            | TypedFunction::Initcap { expr }
951            | TypedFunction::Length { expr }
952            | TypedFunction::Reverse { expr } => expr.walk(visitor),
953            TypedFunction::RegexpLike {
954                expr,
955                pattern,
956                flags,
957            } => {
958                expr.walk(visitor);
959                pattern.walk(visitor);
960                if let Some(f) = flags {
961                    f.walk(visitor);
962                }
963            }
964            TypedFunction::RegexpExtract {
965                expr,
966                pattern,
967                group_index,
968            } => {
969                expr.walk(visitor);
970                pattern.walk(visitor);
971                if let Some(g) = group_index {
972                    g.walk(visitor);
973                }
974            }
975            TypedFunction::RegexpReplace {
976                expr,
977                pattern,
978                replacement,
979                flags,
980            } => {
981                expr.walk(visitor);
982                pattern.walk(visitor);
983                replacement.walk(visitor);
984                if let Some(f) = flags {
985                    f.walk(visitor);
986                }
987            }
988            TypedFunction::ConcatWs { separator, exprs } => {
989                separator.walk(visitor);
990                for e in exprs {
991                    e.walk(visitor);
992                }
993            }
994            TypedFunction::Split { expr, delimiter } => {
995                expr.walk(visitor);
996                delimiter.walk(visitor);
997            }
998            TypedFunction::Replace { expr, from, to } => {
999                expr.walk(visitor);
1000                from.walk(visitor);
1001                to.walk(visitor);
1002            }
1003            TypedFunction::Left { expr, n } | TypedFunction::Right { expr, n } => {
1004                expr.walk(visitor);
1005                n.walk(visitor);
1006            }
1007            TypedFunction::Lpad { expr, length, pad }
1008            | TypedFunction::Rpad { expr, length, pad } => {
1009                expr.walk(visitor);
1010                length.walk(visitor);
1011                if let Some(p) = pad {
1012                    p.walk(visitor);
1013                }
1014            }
1015
1016            // Aggregate
1017            TypedFunction::Count { expr, .. }
1018            | TypedFunction::Sum { expr, .. }
1019            | TypedFunction::Avg { expr, .. }
1020            | TypedFunction::Min { expr }
1021            | TypedFunction::Max { expr }
1022            | TypedFunction::ArrayAgg { expr, .. }
1023            | TypedFunction::ApproxDistinct { expr }
1024            | TypedFunction::Variance { expr }
1025            | TypedFunction::VariancePop { expr }
1026            | TypedFunction::Stddev { expr }
1027            | TypedFunction::StddevPop { expr } => expr.walk(visitor),
1028            TypedFunction::GroupConcat {
1029                exprs,
1030                separator,
1031                order_by,
1032                ..
1033            } => {
1034                for e in exprs {
1035                    e.walk(visitor);
1036                }
1037                if let Some(s) = separator {
1038                    s.walk(visitor);
1039                }
1040                for o in order_by {
1041                    o.expr.walk(visitor);
1042                }
1043            }
1044
1045            // Array
1046            TypedFunction::ArrayConcat { arrays } => {
1047                for a in arrays {
1048                    a.walk(visitor);
1049                }
1050            }
1051            TypedFunction::ArrayContains { array, element } => {
1052                array.walk(visitor);
1053                element.walk(visitor);
1054            }
1055            TypedFunction::ArraySize { expr }
1056            | TypedFunction::Explode { expr }
1057            | TypedFunction::Flatten { expr } => expr.walk(visitor),
1058            TypedFunction::GenerateSeries { start, stop, step } => {
1059                start.walk(visitor);
1060                stop.walk(visitor);
1061                if let Some(s) = step {
1062                    s.walk(visitor);
1063                }
1064            }
1065
1066            // JSON
1067            TypedFunction::JSONExtract { expr, path }
1068            | TypedFunction::JSONExtractScalar { expr, path } => {
1069                expr.walk(visitor);
1070                path.walk(visitor);
1071            }
1072            TypedFunction::ParseJSON { expr } | TypedFunction::JSONFormat { expr } => {
1073                expr.walk(visitor)
1074            }
1075
1076            // Window
1077            TypedFunction::RowNumber | TypedFunction::Rank | TypedFunction::DenseRank => {}
1078            TypedFunction::NTile { n } => n.walk(visitor),
1079            TypedFunction::Lead {
1080                expr,
1081                offset,
1082                default,
1083            }
1084            | TypedFunction::Lag {
1085                expr,
1086                offset,
1087                default,
1088            } => {
1089                expr.walk(visitor);
1090                if let Some(o) = offset {
1091                    o.walk(visitor);
1092                }
1093                if let Some(d) = default {
1094                    d.walk(visitor);
1095                }
1096            }
1097            TypedFunction::FirstValue { expr } | TypedFunction::LastValue { expr } => {
1098                expr.walk(visitor)
1099            }
1100
1101            // Math
1102            TypedFunction::Abs { expr }
1103            | TypedFunction::Ceil { expr }
1104            | TypedFunction::Floor { expr }
1105            | TypedFunction::Ln { expr }
1106            | TypedFunction::Sqrt { expr } => expr.walk(visitor),
1107            TypedFunction::Round { expr, decimals } => {
1108                expr.walk(visitor);
1109                if let Some(d) = decimals {
1110                    d.walk(visitor);
1111                }
1112            }
1113            TypedFunction::Log { expr, base } => {
1114                expr.walk(visitor);
1115                if let Some(b) = base {
1116                    b.walk(visitor);
1117                }
1118            }
1119            TypedFunction::Pow { base, exponent } => {
1120                base.walk(visitor);
1121                exponent.walk(visitor);
1122            }
1123            TypedFunction::Greatest { exprs } | TypedFunction::Least { exprs } => {
1124                for e in exprs {
1125                    e.walk(visitor);
1126                }
1127            }
1128            TypedFunction::Mod { left, right } => {
1129                left.walk(visitor);
1130                right.walk(visitor);
1131            }
1132
1133            // Conversion
1134            TypedFunction::Hex { expr }
1135            | TypedFunction::Unhex { expr }
1136            | TypedFunction::Md5 { expr }
1137            | TypedFunction::Sha { expr } => expr.walk(visitor),
1138            TypedFunction::Sha2 { expr, bit_length } => {
1139                expr.walk(visitor);
1140                bit_length.walk(visitor);
1141            }
1142        }
1143    }
1144
1145    /// Transform child expressions, returning a new `TypedFunction`.
1146    #[must_use]
1147    pub fn transform_children<F>(self, func: &F) -> TypedFunction
1148    where
1149        F: Fn(Expr) -> Expr,
1150    {
1151        match self {
1152            // Date/Time
1153            TypedFunction::DateAdd {
1154                expr,
1155                interval,
1156                unit,
1157            } => TypedFunction::DateAdd {
1158                expr: Box::new(expr.transform(func)),
1159                interval: Box::new(interval.transform(func)),
1160                unit,
1161            },
1162            TypedFunction::DateDiff { start, end, unit } => TypedFunction::DateDiff {
1163                start: Box::new(start.transform(func)),
1164                end: Box::new(end.transform(func)),
1165                unit,
1166            },
1167            TypedFunction::DateTrunc { unit, expr } => TypedFunction::DateTrunc {
1168                unit,
1169                expr: Box::new(expr.transform(func)),
1170            },
1171            TypedFunction::DateSub {
1172                expr,
1173                interval,
1174                unit,
1175            } => TypedFunction::DateSub {
1176                expr: Box::new(expr.transform(func)),
1177                interval: Box::new(interval.transform(func)),
1178                unit,
1179            },
1180            TypedFunction::CurrentDate => TypedFunction::CurrentDate,
1181            TypedFunction::CurrentTime => TypedFunction::CurrentTime,
1182            TypedFunction::CurrentTimestamp => TypedFunction::CurrentTimestamp,
1183            TypedFunction::StrToTime { expr, format } => TypedFunction::StrToTime {
1184                expr: Box::new(expr.transform(func)),
1185                format: Box::new(format.transform(func)),
1186            },
1187            TypedFunction::TimeToStr { expr, format } => TypedFunction::TimeToStr {
1188                expr: Box::new(expr.transform(func)),
1189                format: Box::new(format.transform(func)),
1190            },
1191            TypedFunction::TsOrDsToDate { expr } => TypedFunction::TsOrDsToDate {
1192                expr: Box::new(expr.transform(func)),
1193            },
1194            TypedFunction::Year { expr } => TypedFunction::Year {
1195                expr: Box::new(expr.transform(func)),
1196            },
1197            TypedFunction::Month { expr } => TypedFunction::Month {
1198                expr: Box::new(expr.transform(func)),
1199            },
1200            TypedFunction::Day { expr } => TypedFunction::Day {
1201                expr: Box::new(expr.transform(func)),
1202            },
1203
1204            // String
1205            TypedFunction::Trim {
1206                expr,
1207                trim_type,
1208                trim_chars,
1209            } => TypedFunction::Trim {
1210                expr: Box::new(expr.transform(func)),
1211                trim_type,
1212                trim_chars: trim_chars.map(|c| Box::new(c.transform(func))),
1213            },
1214            TypedFunction::Substring {
1215                expr,
1216                start,
1217                length,
1218            } => TypedFunction::Substring {
1219                expr: Box::new(expr.transform(func)),
1220                start: Box::new(start.transform(func)),
1221                length: length.map(|l| Box::new(l.transform(func))),
1222            },
1223            TypedFunction::Upper { expr } => TypedFunction::Upper {
1224                expr: Box::new(expr.transform(func)),
1225            },
1226            TypedFunction::Lower { expr } => TypedFunction::Lower {
1227                expr: Box::new(expr.transform(func)),
1228            },
1229            TypedFunction::RegexpLike {
1230                expr,
1231                pattern,
1232                flags,
1233            } => TypedFunction::RegexpLike {
1234                expr: Box::new(expr.transform(func)),
1235                pattern: Box::new(pattern.transform(func)),
1236                flags: flags.map(|f| Box::new(f.transform(func))),
1237            },
1238            TypedFunction::RegexpExtract {
1239                expr,
1240                pattern,
1241                group_index,
1242            } => TypedFunction::RegexpExtract {
1243                expr: Box::new(expr.transform(func)),
1244                pattern: Box::new(pattern.transform(func)),
1245                group_index: group_index.map(|g| Box::new(g.transform(func))),
1246            },
1247            TypedFunction::RegexpReplace {
1248                expr,
1249                pattern,
1250                replacement,
1251                flags,
1252            } => TypedFunction::RegexpReplace {
1253                expr: Box::new(expr.transform(func)),
1254                pattern: Box::new(pattern.transform(func)),
1255                replacement: Box::new(replacement.transform(func)),
1256                flags: flags.map(|f| Box::new(f.transform(func))),
1257            },
1258            TypedFunction::ConcatWs { separator, exprs } => TypedFunction::ConcatWs {
1259                separator: Box::new(separator.transform(func)),
1260                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
1261            },
1262            TypedFunction::Split { expr, delimiter } => TypedFunction::Split {
1263                expr: Box::new(expr.transform(func)),
1264                delimiter: Box::new(delimiter.transform(func)),
1265            },
1266            TypedFunction::Initcap { expr } => TypedFunction::Initcap {
1267                expr: Box::new(expr.transform(func)),
1268            },
1269            TypedFunction::Length { expr } => TypedFunction::Length {
1270                expr: Box::new(expr.transform(func)),
1271            },
1272            TypedFunction::Replace { expr, from, to } => TypedFunction::Replace {
1273                expr: Box::new(expr.transform(func)),
1274                from: Box::new(from.transform(func)),
1275                to: Box::new(to.transform(func)),
1276            },
1277            TypedFunction::Reverse { expr } => TypedFunction::Reverse {
1278                expr: Box::new(expr.transform(func)),
1279            },
1280            TypedFunction::Left { expr, n } => TypedFunction::Left {
1281                expr: Box::new(expr.transform(func)),
1282                n: Box::new(n.transform(func)),
1283            },
1284            TypedFunction::Right { expr, n } => TypedFunction::Right {
1285                expr: Box::new(expr.transform(func)),
1286                n: Box::new(n.transform(func)),
1287            },
1288            TypedFunction::Lpad { expr, length, pad } => TypedFunction::Lpad {
1289                expr: Box::new(expr.transform(func)),
1290                length: Box::new(length.transform(func)),
1291                pad: pad.map(|p| Box::new(p.transform(func))),
1292            },
1293            TypedFunction::Rpad { expr, length, pad } => TypedFunction::Rpad {
1294                expr: Box::new(expr.transform(func)),
1295                length: Box::new(length.transform(func)),
1296                pad: pad.map(|p| Box::new(p.transform(func))),
1297            },
1298
1299            // Aggregate
1300            TypedFunction::Count { expr, distinct } => TypedFunction::Count {
1301                expr: Box::new(expr.transform(func)),
1302                distinct,
1303            },
1304            TypedFunction::Sum { expr, distinct } => TypedFunction::Sum {
1305                expr: Box::new(expr.transform(func)),
1306                distinct,
1307            },
1308            TypedFunction::Avg { expr, distinct } => TypedFunction::Avg {
1309                expr: Box::new(expr.transform(func)),
1310                distinct,
1311            },
1312            TypedFunction::Min { expr } => TypedFunction::Min {
1313                expr: Box::new(expr.transform(func)),
1314            },
1315            TypedFunction::Max { expr } => TypedFunction::Max {
1316                expr: Box::new(expr.transform(func)),
1317            },
1318            TypedFunction::ArrayAgg { expr, distinct } => TypedFunction::ArrayAgg {
1319                expr: Box::new(expr.transform(func)),
1320                distinct,
1321            },
1322            TypedFunction::ApproxDistinct { expr } => TypedFunction::ApproxDistinct {
1323                expr: Box::new(expr.transform(func)),
1324            },
1325            TypedFunction::Variance { expr } => TypedFunction::Variance {
1326                expr: Box::new(expr.transform(func)),
1327            },
1328            TypedFunction::VariancePop { expr } => TypedFunction::VariancePop {
1329                expr: Box::new(expr.transform(func)),
1330            },
1331            TypedFunction::Stddev { expr } => TypedFunction::Stddev {
1332                expr: Box::new(expr.transform(func)),
1333            },
1334            TypedFunction::StddevPop { expr } => TypedFunction::StddevPop {
1335                expr: Box::new(expr.transform(func)),
1336            },
1337            TypedFunction::GroupConcat {
1338                exprs,
1339                separator,
1340                order_by,
1341                distinct,
1342            } => TypedFunction::GroupConcat {
1343                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
1344                separator: separator.map(|s| Box::new(s.transform(func))),
1345                order_by: order_by
1346                    .into_iter()
1347                    .map(|o| OrderByItem {
1348                        expr: o.expr.transform(func),
1349                        ascending: o.ascending,
1350                        nulls_first: o.nulls_first,
1351                    })
1352                    .collect(),
1353                distinct,
1354            },
1355
1356            // Array
1357            TypedFunction::ArrayConcat { arrays } => TypedFunction::ArrayConcat {
1358                arrays: arrays.into_iter().map(|a| a.transform(func)).collect(),
1359            },
1360            TypedFunction::ArrayContains { array, element } => TypedFunction::ArrayContains {
1361                array: Box::new(array.transform(func)),
1362                element: Box::new(element.transform(func)),
1363            },
1364            TypedFunction::ArraySize { expr } => TypedFunction::ArraySize {
1365                expr: Box::new(expr.transform(func)),
1366            },
1367            TypedFunction::Explode { expr } => TypedFunction::Explode {
1368                expr: Box::new(expr.transform(func)),
1369            },
1370            TypedFunction::GenerateSeries { start, stop, step } => TypedFunction::GenerateSeries {
1371                start: Box::new(start.transform(func)),
1372                stop: Box::new(stop.transform(func)),
1373                step: step.map(|s| Box::new(s.transform(func))),
1374            },
1375            TypedFunction::Flatten { expr } => TypedFunction::Flatten {
1376                expr: Box::new(expr.transform(func)),
1377            },
1378
1379            // JSON
1380            TypedFunction::JSONExtract { expr, path } => TypedFunction::JSONExtract {
1381                expr: Box::new(expr.transform(func)),
1382                path: Box::new(path.transform(func)),
1383            },
1384            TypedFunction::JSONExtractScalar { expr, path } => TypedFunction::JSONExtractScalar {
1385                expr: Box::new(expr.transform(func)),
1386                path: Box::new(path.transform(func)),
1387            },
1388            TypedFunction::ParseJSON { expr } => TypedFunction::ParseJSON {
1389                expr: Box::new(expr.transform(func)),
1390            },
1391            TypedFunction::JSONFormat { expr } => TypedFunction::JSONFormat {
1392                expr: Box::new(expr.transform(func)),
1393            },
1394
1395            // Window
1396            TypedFunction::RowNumber => TypedFunction::RowNumber,
1397            TypedFunction::Rank => TypedFunction::Rank,
1398            TypedFunction::DenseRank => TypedFunction::DenseRank,
1399            TypedFunction::NTile { n } => TypedFunction::NTile {
1400                n: Box::new(n.transform(func)),
1401            },
1402            TypedFunction::Lead {
1403                expr,
1404                offset,
1405                default,
1406            } => TypedFunction::Lead {
1407                expr: Box::new(expr.transform(func)),
1408                offset: offset.map(|o| Box::new(o.transform(func))),
1409                default: default.map(|d| Box::new(d.transform(func))),
1410            },
1411            TypedFunction::Lag {
1412                expr,
1413                offset,
1414                default,
1415            } => TypedFunction::Lag {
1416                expr: Box::new(expr.transform(func)),
1417                offset: offset.map(|o| Box::new(o.transform(func))),
1418                default: default.map(|d| Box::new(d.transform(func))),
1419            },
1420            TypedFunction::FirstValue { expr } => TypedFunction::FirstValue {
1421                expr: Box::new(expr.transform(func)),
1422            },
1423            TypedFunction::LastValue { expr } => TypedFunction::LastValue {
1424                expr: Box::new(expr.transform(func)),
1425            },
1426
1427            // Math
1428            TypedFunction::Abs { expr } => TypedFunction::Abs {
1429                expr: Box::new(expr.transform(func)),
1430            },
1431            TypedFunction::Ceil { expr } => TypedFunction::Ceil {
1432                expr: Box::new(expr.transform(func)),
1433            },
1434            TypedFunction::Floor { expr } => TypedFunction::Floor {
1435                expr: Box::new(expr.transform(func)),
1436            },
1437            TypedFunction::Round { expr, decimals } => TypedFunction::Round {
1438                expr: Box::new(expr.transform(func)),
1439                decimals: decimals.map(|d| Box::new(d.transform(func))),
1440            },
1441            TypedFunction::Log { expr, base } => TypedFunction::Log {
1442                expr: Box::new(expr.transform(func)),
1443                base: base.map(|b| Box::new(b.transform(func))),
1444            },
1445            TypedFunction::Ln { expr } => TypedFunction::Ln {
1446                expr: Box::new(expr.transform(func)),
1447            },
1448            TypedFunction::Pow { base, exponent } => TypedFunction::Pow {
1449                base: Box::new(base.transform(func)),
1450                exponent: Box::new(exponent.transform(func)),
1451            },
1452            TypedFunction::Sqrt { expr } => TypedFunction::Sqrt {
1453                expr: Box::new(expr.transform(func)),
1454            },
1455            TypedFunction::Greatest { exprs } => TypedFunction::Greatest {
1456                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
1457            },
1458            TypedFunction::Least { exprs } => TypedFunction::Least {
1459                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
1460            },
1461            TypedFunction::Mod { left, right } => TypedFunction::Mod {
1462                left: Box::new(left.transform(func)),
1463                right: Box::new(right.transform(func)),
1464            },
1465
1466            // Conversion
1467            TypedFunction::Hex { expr } => TypedFunction::Hex {
1468                expr: Box::new(expr.transform(func)),
1469            },
1470            TypedFunction::Unhex { expr } => TypedFunction::Unhex {
1471                expr: Box::new(expr.transform(func)),
1472            },
1473            TypedFunction::Md5 { expr } => TypedFunction::Md5 {
1474                expr: Box::new(expr.transform(func)),
1475            },
1476            TypedFunction::Sha { expr } => TypedFunction::Sha {
1477                expr: Box::new(expr.transform(func)),
1478            },
1479            TypedFunction::Sha2 { expr, bit_length } => TypedFunction::Sha2 {
1480                expr: Box::new(expr.transform(func)),
1481                bit_length: Box::new(bit_length.transform(func)),
1482            },
1483        }
1484    }
1485}
1486
1487// ═══════════════════════════════════════════════════════════════════════
1488// Operators
1489// ═══════════════════════════════════════════════════════════════════════
1490
1491/// Binary operators.
1492#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1493pub enum BinaryOperator {
1494    Plus,
1495    Minus,
1496    Multiply,
1497    Divide,
1498    Modulo,
1499    Eq,
1500    Neq,
1501    Lt,
1502    Gt,
1503    LtEq,
1504    GtEq,
1505    And,
1506    Or,
1507    Xor,
1508    Concat,
1509    BitwiseAnd,
1510    BitwiseOr,
1511    BitwiseXor,
1512    ShiftLeft,
1513    ShiftRight,
1514    /// `->` JSON access operator
1515    Arrow,
1516    /// `->>` JSON text access
1517    DoubleArrow,
1518    /// `@>` PostgreSQL "contains" (arrays / jsonb / range)
1519    AtArrow,
1520    /// `<@` PostgreSQL "is contained by"
1521    ArrowAt,
1522}
1523
1524/// Unary operators.
1525#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1526pub enum UnaryOperator {
1527    Not,
1528    Minus,
1529    Plus,
1530    BitwiseNot,
1531}
1532
1533// ═══════════════════════════════════════════════════════════════════════
1534// DML statements
1535// ═══════════════════════════════════════════════════════════════════════
1536
1537/// An INSERT statement, now supporting INSERT ... SELECT and ON CONFLICT.
1538#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1539pub struct InsertStatement {
1540    /// Comments attached to this statement.
1541    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1542    pub comments: Vec<String>,
1543    pub table: TableRef,
1544    pub columns: Vec<String>,
1545    pub source: InsertSource,
1546    /// ON CONFLICT / ON DUPLICATE KEY
1547    pub on_conflict: Option<OnConflict>,
1548    /// RETURNING clause
1549    pub returning: Vec<SelectItem>,
1550}
1551
1552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1553pub enum InsertSource {
1554    Values(Vec<Vec<Expr>>),
1555    Query(Box<Statement>),
1556    Default,
1557}
1558
1559#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1560pub struct OnConflict {
1561    pub columns: Vec<String>,
1562    pub action: ConflictAction,
1563}
1564
1565#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1566pub enum ConflictAction {
1567    DoNothing,
1568    DoUpdate(Vec<(String, Expr)>),
1569}
1570
1571/// An UPDATE statement.
1572#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1573pub struct UpdateStatement {
1574    /// Comments attached to this statement.
1575    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1576    pub comments: Vec<String>,
1577    pub table: TableRef,
1578    pub assignments: Vec<(String, Expr)>,
1579    pub from: Option<FromClause>,
1580    pub where_clause: Option<Expr>,
1581    pub returning: Vec<SelectItem>,
1582}
1583
1584/// A DELETE statement.
1585#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1586pub struct DeleteStatement {
1587    /// Comments attached to this statement.
1588    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1589    pub comments: Vec<String>,
1590    pub table: TableRef,
1591    pub using: Option<FromClause>,
1592    pub where_clause: Option<Expr>,
1593    pub returning: Vec<SelectItem>,
1594}
1595
1596// ═══════════════════════════════════════════════════════════════════════
1597// MERGE statement
1598// ═══════════════════════════════════════════════════════════════════════
1599
1600/// A MERGE (UPSERT) statement.
1601///
1602/// MERGE INTO target USING source ON condition
1603///   WHEN MATCHED THEN UPDATE SET ...
1604///   WHEN NOT MATCHED THEN INSERT ...
1605#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1606pub struct MergeStatement {
1607    /// Comments attached to this statement.
1608    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1609    pub comments: Vec<String>,
1610    pub target: TableRef,
1611    pub source: TableSource,
1612    pub on: Expr,
1613    pub clauses: Vec<MergeClause>,
1614    /// OUTPUT clause (T-SQL extension)
1615    pub output: Vec<SelectItem>,
1616}
1617
1618/// A single WHEN MATCHED / WHEN NOT MATCHED clause in a MERGE statement.
1619#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1620pub struct MergeClause {
1621    pub kind: MergeClauseKind,
1622    /// Optional additional condition: WHEN MATCHED AND <condition>
1623    pub condition: Option<Expr>,
1624    pub action: MergeAction,
1625}
1626
1627/// The kind of WHEN clause in a MERGE statement.
1628#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1629pub enum MergeClauseKind {
1630    /// WHEN MATCHED
1631    Matched,
1632    /// WHEN NOT MATCHED (BY TARGET) — standard SQL and most dialects
1633    NotMatched,
1634    /// WHEN NOT MATCHED BY SOURCE — T-SQL extension
1635    NotMatchedBySource,
1636}
1637
1638/// The action to take in a MERGE WHEN clause.
1639#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1640pub enum MergeAction {
1641    /// UPDATE SET col = val, ...
1642    Update(Vec<(String, Expr)>),
1643    /// INSERT (columns) VALUES (values)
1644    Insert {
1645        columns: Vec<String>,
1646        values: Vec<Expr>,
1647    },
1648    /// INSERT ROW (BigQuery)
1649    InsertRow,
1650    /// DELETE
1651    Delete,
1652}
1653
1654// ═══════════════════════════════════════════════════════════════════════
1655// DDL statements
1656// ═══════════════════════════════════════════════════════════════════════
1657
1658/// A CREATE TABLE statement.
1659#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1660pub struct CreateTableStatement {
1661    /// Comments attached to this statement.
1662    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1663    pub comments: Vec<String>,
1664    pub if_not_exists: bool,
1665    pub temporary: bool,
1666    pub table: TableRef,
1667    pub columns: Vec<ColumnDef>,
1668    pub constraints: Vec<TableConstraint>,
1669    /// CREATE TABLE ... AS SELECT ...
1670    pub as_select: Option<Box<Statement>>,
1671}
1672
1673/// Table-level constraints.
1674#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1675pub enum TableConstraint {
1676    PrimaryKey {
1677        name: Option<String>,
1678        columns: Vec<String>,
1679    },
1680    Unique {
1681        name: Option<String>,
1682        columns: Vec<String>,
1683    },
1684    ForeignKey {
1685        name: Option<String>,
1686        columns: Vec<String>,
1687        ref_table: TableRef,
1688        ref_columns: Vec<String>,
1689        on_delete: Option<ReferentialAction>,
1690        on_update: Option<ReferentialAction>,
1691    },
1692    Check {
1693        name: Option<String>,
1694        expr: Expr,
1695    },
1696}
1697
1698#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1699pub enum ReferentialAction {
1700    Cascade,
1701    Restrict,
1702    NoAction,
1703    SetNull,
1704    SetDefault,
1705}
1706
1707/// A column definition in CREATE TABLE.
1708#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1709pub struct ColumnDef {
1710    pub name: String,
1711    pub data_type: DataType,
1712    pub nullable: Option<bool>,
1713    pub default: Option<Expr>,
1714    pub primary_key: bool,
1715    pub unique: bool,
1716    pub auto_increment: bool,
1717    pub collation: Option<String>,
1718    pub comment: Option<String>,
1719}
1720
1721/// ALTER TABLE statement.
1722#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1723pub struct AlterTableStatement {
1724    /// Comments attached to this statement.
1725    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1726    pub comments: Vec<String>,
1727    pub table: TableRef,
1728    pub actions: Vec<AlterTableAction>,
1729}
1730
1731#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1732pub enum AlterTableAction {
1733    AddColumn(ColumnDef),
1734    DropColumn { name: String, if_exists: bool },
1735    RenameColumn { old_name: String, new_name: String },
1736    AlterColumnType { name: String, data_type: DataType },
1737    AddConstraint(TableConstraint),
1738    DropConstraint { name: String },
1739    RenameTable { new_name: String },
1740}
1741
1742/// CREATE VIEW statement.
1743#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1744pub struct CreateViewStatement {
1745    /// Comments attached to this statement.
1746    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1747    pub comments: Vec<String>,
1748    pub name: TableRef,
1749    pub columns: Vec<String>,
1750    pub query: Box<Statement>,
1751    pub or_replace: bool,
1752    pub materialized: bool,
1753    pub if_not_exists: bool,
1754}
1755
1756/// DROP VIEW statement.
1757#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1758pub struct DropViewStatement {
1759    /// Comments attached to this statement.
1760    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1761    pub comments: Vec<String>,
1762    pub name: TableRef,
1763    pub if_exists: bool,
1764    pub materialized: bool,
1765}
1766
1767/// TRUNCATE TABLE statement.
1768#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1769pub struct TruncateStatement {
1770    /// Comments attached to this statement.
1771    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1772    pub comments: Vec<String>,
1773    pub table: TableRef,
1774}
1775
1776/// Transaction control statements.
1777#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1778pub enum TransactionStatement {
1779    Begin,
1780    Commit,
1781    Rollback,
1782    Savepoint(String),
1783    ReleaseSavepoint(String),
1784    RollbackTo(String),
1785}
1786
1787/// EXPLAIN statement.
1788#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1789pub struct ExplainStatement {
1790    /// Comments attached to this statement.
1791    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1792    pub comments: Vec<String>,
1793    pub analyze: bool,
1794    pub statement: Box<Statement>,
1795}
1796
1797/// USE database statement.
1798#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1799pub struct UseStatement {
1800    /// Comments attached to this statement.
1801    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1802    pub comments: Vec<String>,
1803    pub name: String,
1804}
1805
1806/// A raw-tail statement preserved verbatim for round-tripping.
1807///
1808/// Used for statements the parser accepts but does not model in detail —
1809/// `SET`, `SHOW`, `GO`, `DECLARE`, `COMMENT ON …`, `ANALYZE`, `DESCRIBE`,
1810/// `LOAD`, `EXPLAIN (FORMAT …)`, vendor-specific `ALTER TABLE` tails,
1811/// `CREATE OPERATOR/AGGREGATE/SEQUENCE/…`, etc. The verb sequence is
1812/// preserved in `kind` and the remainder of the statement (up to the next
1813/// `;` or EOF) is captured in `body` exactly as it appeared in the input.
1814#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1815pub struct CommandStatement {
1816    /// Comments attached to this statement.
1817    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1818    pub comments: Vec<String>,
1819    /// Leading verb(s), uppercased — e.g. "SET", "SHOW", "GO",
1820    /// "DECLARE", "COMMENT ON", "ANALYZE", "DESCRIBE", "LOAD",
1821    /// "CREATE OPERATOR", "ALTER TABLE", "REM".
1822    pub kind: String,
1823    /// Raw tail of the statement (everything after `kind`), reproduced
1824    /// verbatim so round-tripping preserves the original wording.
1825    pub body: String,
1826}
1827
1828/// A DROP TABLE statement.
1829#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1830pub struct DropTableStatement {
1831    /// Comments attached to this statement.
1832    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1833    pub comments: Vec<String>,
1834    pub if_exists: bool,
1835    pub table: TableRef,
1836    pub cascade: bool,
1837}
1838
1839// ═══════════════════════════════════════════════════════════════════════
1840// Data types
1841// ═══════════════════════════════════════════════════════════════════════
1842
1843/// SQL data types. Significantly expanded to match sqlglot's DataType.Type enum.
1844#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1845pub enum DataType {
1846    // Numeric
1847    TinyInt,
1848    SmallInt,
1849    Int,
1850    BigInt,
1851    Float,
1852    Double,
1853    Decimal {
1854        precision: Option<u32>,
1855        scale: Option<u32>,
1856    },
1857    Numeric {
1858        precision: Option<u32>,
1859        scale: Option<u32>,
1860    },
1861    Real,
1862
1863    // String
1864    Varchar(Option<u32>),
1865    Char(Option<u32>),
1866    Text,
1867    String,
1868    Binary(Option<u32>),
1869    Varbinary(Option<u32>),
1870    /// National fixed-length character type: `NCHAR(n)`. T-SQL `NCHAR` has no
1871    /// `MAX` form, so a plain length is exact.
1872    NChar(Option<u32>),
1873    /// National variable-length character type: `NVARCHAR(n)`.
1874    NVarchar(Option<u32>),
1875    /// `VARCHAR(MAX)` — a large-object string, distinct from bare `VARCHAR`
1876    /// (`Varchar(None)`) which carries no explicit length.
1877    VarcharMax,
1878    /// `NVARCHAR(MAX)` — a national large-object string.
1879    NvarcharMax,
1880
1881    // Boolean
1882    Boolean,
1883
1884    // Date/Time
1885    Date,
1886    Time {
1887        precision: Option<u32>,
1888    },
1889    Timestamp {
1890        precision: Option<u32>,
1891        with_tz: bool,
1892    },
1893    Interval,
1894    DateTime,
1895
1896    // Binary
1897    Blob,
1898    Bytea,
1899    Bytes,
1900
1901    // JSON
1902    Json,
1903    Jsonb,
1904
1905    // UUID
1906    Uuid,
1907
1908    // Complex types
1909    Array(Option<Box<DataType>>),
1910    Map {
1911        key: Box<DataType>,
1912        value: Box<DataType>,
1913    },
1914    Struct(Vec<(String, DataType)>),
1915    Tuple(Vec<DataType>),
1916
1917    // Special
1918    Null,
1919    Unknown(String),
1920    Variant,
1921    Object,
1922    Xml,
1923    Inet,
1924    Cidr,
1925    Macaddr,
1926    Bit(Option<u32>),
1927    Money,
1928    Serial,
1929    BigSerial,
1930    SmallSerial,
1931    Regclass,
1932    Regtype,
1933    Hstore,
1934    Geography,
1935    Geometry,
1936    Super,
1937}
1938
1939// ═══════════════════════════════════════════════════════════════════════
1940// Expression tree traversal helpers
1941// ═══════════════════════════════════════════════════════════════════════
1942
1943impl Expr {
1944    /// Recursively walk this expression tree, calling `visitor` on each node.
1945    /// If `visitor` returns `false`, children of that node are not visited.
1946    pub fn walk<F>(&self, visitor: &mut F)
1947    where
1948        F: FnMut(&Expr) -> bool,
1949    {
1950        if !visitor(self) {
1951            return;
1952        }
1953        match self {
1954            Expr::BinaryOp { left, right, .. } => {
1955                left.walk(visitor);
1956                right.walk(visitor);
1957            }
1958            Expr::UnaryOp { expr, .. } => expr.walk(visitor),
1959            Expr::Function { args, filter, .. } => {
1960                for arg in args {
1961                    arg.walk(visitor);
1962                }
1963                if let Some(f) = filter {
1964                    f.walk(visitor);
1965                }
1966            }
1967            Expr::Between {
1968                expr, low, high, ..
1969            } => {
1970                expr.walk(visitor);
1971                low.walk(visitor);
1972                high.walk(visitor);
1973            }
1974            Expr::InList { expr, list, .. } => {
1975                expr.walk(visitor);
1976                for item in list {
1977                    item.walk(visitor);
1978                }
1979            }
1980            Expr::InSubquery { expr, .. } => {
1981                expr.walk(visitor);
1982            }
1983            Expr::IsNull { expr, .. } => expr.walk(visitor),
1984            Expr::IsBool { expr, .. } => expr.walk(visitor),
1985            Expr::AnyOp { expr, right, .. } | Expr::AllOp { expr, right, .. } => {
1986                expr.walk(visitor);
1987                right.walk(visitor);
1988            }
1989            Expr::Like { expr, pattern, .. }
1990            | Expr::ILike { expr, pattern, .. }
1991            | Expr::SimilarTo { expr, pattern, .. } => {
1992                expr.walk(visitor);
1993                pattern.walk(visitor);
1994            }
1995            Expr::Case {
1996                operand,
1997                when_clauses,
1998                else_clause,
1999            } => {
2000                if let Some(op) = operand {
2001                    op.walk(visitor);
2002                }
2003                for (cond, result) in when_clauses {
2004                    cond.walk(visitor);
2005                    result.walk(visitor);
2006                }
2007                if let Some(el) = else_clause {
2008                    el.walk(visitor);
2009                }
2010            }
2011            Expr::Nested(inner) => inner.walk(visitor),
2012            Expr::Cast { expr, .. } | Expr::TryCast { expr, .. } => expr.walk(visitor),
2013            Expr::Extract { expr, .. } => expr.walk(visitor),
2014            Expr::Interval { value, .. } => value.walk(visitor),
2015            Expr::ArrayLiteral(items) | Expr::Tuple(items) | Expr::Coalesce(items) => {
2016                for item in items {
2017                    item.walk(visitor);
2018                }
2019            }
2020            Expr::If {
2021                condition,
2022                true_val,
2023                false_val,
2024            } => {
2025                condition.walk(visitor);
2026                true_val.walk(visitor);
2027                if let Some(fv) = false_val {
2028                    fv.walk(visitor);
2029                }
2030            }
2031            Expr::NullIf { expr, r#else } => {
2032                expr.walk(visitor);
2033                r#else.walk(visitor);
2034            }
2035            Expr::Collate { expr, .. } => expr.walk(visitor),
2036            Expr::Alias { expr, .. } => expr.walk(visitor),
2037            Expr::ArrayIndex { expr, index } => {
2038                expr.walk(visitor);
2039                index.walk(visitor);
2040            }
2041            Expr::JsonAccess { expr, path, .. } => {
2042                expr.walk(visitor);
2043                path.walk(visitor);
2044            }
2045            Expr::Lambda { body, .. } => body.walk(visitor),
2046            Expr::TypedFunction { func, filter, .. } => {
2047                func.walk_children(visitor);
2048                if let Some(f) = filter {
2049                    f.walk(visitor);
2050                }
2051            }
2052            Expr::Cube { exprs } | Expr::Rollup { exprs } => {
2053                for item in exprs {
2054                    item.walk(visitor);
2055                }
2056            }
2057            Expr::GroupingSets { sets } => {
2058                for item in sets {
2059                    item.walk(visitor);
2060                }
2061            }
2062            Expr::Commented { expr, .. } => expr.walk(visitor),
2063            // Leaf nodes
2064            Expr::Column { .. }
2065            | Expr::Number(_)
2066            | Expr::StringLiteral(_)
2067            | Expr::NationalStringLiteral(_)
2068            | Expr::Boolean(_)
2069            | Expr::Null
2070            | Expr::Wildcard
2071            | Expr::Star
2072            | Expr::Parameter(_)
2073            | Expr::TypeExpr(_)
2074            | Expr::QualifiedWildcard { .. }
2075            | Expr::Default
2076            | Expr::Subquery(_)
2077            | Expr::Exists { .. } => {}
2078        }
2079    }
2080
2081    /// Find the first expression matching the predicate.
2082    #[must_use]
2083    pub fn find<F>(&self, predicate: &F) -> Option<&Expr>
2084    where
2085        F: Fn(&Expr) -> bool,
2086    {
2087        let mut result = None;
2088        self.walk(&mut |expr| {
2089            if result.is_some() {
2090                return false;
2091            }
2092            if predicate(expr) {
2093                result = Some(expr as *const Expr);
2094                false
2095            } else {
2096                true
2097            }
2098        });
2099        // SAFETY: the pointer is valid as long as self is alive
2100        result.map(|p| unsafe { &*p })
2101    }
2102
2103    /// Find all expressions matching the predicate.
2104    #[must_use]
2105    pub fn find_all<F>(&self, predicate: &F) -> Vec<&Expr>
2106    where
2107        F: Fn(&Expr) -> bool,
2108    {
2109        let mut results: Vec<*const Expr> = Vec::new();
2110        self.walk(&mut |expr| {
2111            if predicate(expr) {
2112                results.push(expr as *const Expr);
2113            }
2114            true
2115        });
2116        results.into_iter().map(|p| unsafe { &*p }).collect()
2117    }
2118
2119    /// Transform this expression tree by applying a function to each node.
2120    /// The function can return a new expression to replace the current one.
2121    #[must_use]
2122    pub fn transform<F>(self, func: &F) -> Expr
2123    where
2124        F: Fn(Expr) -> Expr,
2125    {
2126        let transformed = match self {
2127            Expr::BinaryOp { left, op, right } => Expr::BinaryOp {
2128                left: Box::new(left.transform(func)),
2129                op,
2130                right: Box::new(right.transform(func)),
2131            },
2132            Expr::UnaryOp { op, expr } => Expr::UnaryOp {
2133                op,
2134                expr: Box::new(expr.transform(func)),
2135            },
2136            Expr::Function {
2137                name,
2138                args,
2139                distinct,
2140                filter,
2141                over,
2142                order_by,
2143                within_group,
2144            } => Expr::Function {
2145                name,
2146                args: args.into_iter().map(|a| a.transform(func)).collect(),
2147                distinct,
2148                filter: filter.map(|f| Box::new(f.transform(func))),
2149                over,
2150                order_by,
2151                within_group,
2152            },
2153            Expr::Nested(inner) => Expr::Nested(Box::new(inner.transform(func))),
2154            Expr::Cast { expr, data_type } => Expr::Cast {
2155                expr: Box::new(expr.transform(func)),
2156                data_type,
2157            },
2158            Expr::Between {
2159                expr,
2160                low,
2161                high,
2162                negated,
2163            } => Expr::Between {
2164                expr: Box::new(expr.transform(func)),
2165                low: Box::new(low.transform(func)),
2166                high: Box::new(high.transform(func)),
2167                negated,
2168            },
2169            Expr::Case {
2170                operand,
2171                when_clauses,
2172                else_clause,
2173            } => Expr::Case {
2174                operand: operand.map(|o| Box::new(o.transform(func))),
2175                when_clauses: when_clauses
2176                    .into_iter()
2177                    .map(|(c, r)| (c.transform(func), r.transform(func)))
2178                    .collect(),
2179                else_clause: else_clause.map(|e| Box::new(e.transform(func))),
2180            },
2181            Expr::IsBool {
2182                expr,
2183                value,
2184                negated,
2185            } => Expr::IsBool {
2186                expr: Box::new(expr.transform(func)),
2187                value,
2188                negated,
2189            },
2190            Expr::AnyOp { expr, op, right } => Expr::AnyOp {
2191                expr: Box::new(expr.transform(func)),
2192                op,
2193                right: Box::new(right.transform(func)),
2194            },
2195            Expr::AllOp { expr, op, right } => Expr::AllOp {
2196                expr: Box::new(expr.transform(func)),
2197                op,
2198                right: Box::new(right.transform(func)),
2199            },
2200            Expr::TypedFunction {
2201                func: tf,
2202                filter,
2203                over,
2204            } => Expr::TypedFunction {
2205                func: tf.transform_children(func),
2206                filter: filter.map(|f| Box::new(f.transform(func))),
2207                over,
2208            },
2209            Expr::InList {
2210                expr,
2211                list,
2212                negated,
2213            } => Expr::InList {
2214                expr: Box::new(expr.transform(func)),
2215                list: list.into_iter().map(|e| e.transform(func)).collect(),
2216                negated,
2217            },
2218            Expr::InSubquery {
2219                expr,
2220                subquery,
2221                negated,
2222            } => Expr::InSubquery {
2223                expr: Box::new(expr.transform(func)),
2224                subquery, // Statement — not transformable via Expr func
2225                negated,
2226            },
2227            Expr::IsNull { expr, negated } => Expr::IsNull {
2228                expr: Box::new(expr.transform(func)),
2229                negated,
2230            },
2231            Expr::Like {
2232                expr,
2233                pattern,
2234                negated,
2235                escape,
2236            } => Expr::Like {
2237                expr: Box::new(expr.transform(func)),
2238                pattern: Box::new(pattern.transform(func)),
2239                negated,
2240                escape: escape.map(|e| Box::new(e.transform(func))),
2241            },
2242            Expr::ILike {
2243                expr,
2244                pattern,
2245                negated,
2246                escape,
2247            } => Expr::ILike {
2248                expr: Box::new(expr.transform(func)),
2249                pattern: Box::new(pattern.transform(func)),
2250                negated,
2251                escape: escape.map(|e| Box::new(e.transform(func))),
2252            },
2253            Expr::TryCast { expr, data_type } => Expr::TryCast {
2254                expr: Box::new(expr.transform(func)),
2255                data_type,
2256            },
2257            Expr::Extract { field, expr } => Expr::Extract {
2258                field,
2259                expr: Box::new(expr.transform(func)),
2260            },
2261            Expr::Interval { value, unit } => Expr::Interval {
2262                value: Box::new(value.transform(func)),
2263                unit,
2264            },
2265            Expr::ArrayLiteral(elems) => {
2266                Expr::ArrayLiteral(elems.into_iter().map(|e| e.transform(func)).collect())
2267            }
2268            Expr::Tuple(elems) => {
2269                Expr::Tuple(elems.into_iter().map(|e| e.transform(func)).collect())
2270            }
2271            Expr::Coalesce(elems) => {
2272                Expr::Coalesce(elems.into_iter().map(|e| e.transform(func)).collect())
2273            }
2274            Expr::If {
2275                condition,
2276                true_val,
2277                false_val,
2278            } => Expr::If {
2279                condition: Box::new(condition.transform(func)),
2280                true_val: Box::new(true_val.transform(func)),
2281                false_val: false_val.map(|f| Box::new(f.transform(func))),
2282            },
2283            Expr::NullIf { expr, r#else } => Expr::NullIf {
2284                expr: Box::new(expr.transform(func)),
2285                r#else: Box::new(r#else.transform(func)),
2286            },
2287            Expr::Collate { expr, collation } => Expr::Collate {
2288                expr: Box::new(expr.transform(func)),
2289                collation,
2290            },
2291            Expr::Alias { expr, name } => Expr::Alias {
2292                expr: Box::new(expr.transform(func)),
2293                name,
2294            },
2295            Expr::ArrayIndex { expr, index } => Expr::ArrayIndex {
2296                expr: Box::new(expr.transform(func)),
2297                index: Box::new(index.transform(func)),
2298            },
2299            Expr::JsonAccess {
2300                expr,
2301                path,
2302                as_text,
2303            } => Expr::JsonAccess {
2304                expr: Box::new(expr.transform(func)),
2305                path: Box::new(path.transform(func)),
2306                as_text,
2307            },
2308            Expr::Lambda { params, body } => Expr::Lambda {
2309                params,
2310                body: Box::new(body.transform(func)),
2311            },
2312            Expr::Cube { exprs } => Expr::Cube {
2313                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
2314            },
2315            Expr::Rollup { exprs } => Expr::Rollup {
2316                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
2317            },
2318            Expr::GroupingSets { sets } => Expr::GroupingSets {
2319                sets: sets.into_iter().map(|e| e.transform(func)).collect(),
2320            },
2321            Expr::Commented { expr, comments } => Expr::Commented {
2322                expr: Box::new(expr.transform(func)),
2323                comments,
2324            },
2325            other => other,
2326        };
2327        func(transformed)
2328    }
2329
2330    /// Check whether this expression is a column reference.
2331    #[must_use]
2332    pub fn is_column(&self) -> bool {
2333        matches!(self, Expr::Column { .. })
2334    }
2335
2336    /// Check whether this expression is a literal value (number, string, bool, null).
2337    #[must_use]
2338    pub fn is_literal(&self) -> bool {
2339        matches!(
2340            self,
2341            Expr::Number(_)
2342                | Expr::StringLiteral(_)
2343                | Expr::NationalStringLiteral(_)
2344                | Expr::Boolean(_)
2345                | Expr::Null
2346        )
2347    }
2348
2349    /// Get the SQL representation of this expression for display purposes.
2350    /// For full generation, use the Generator.
2351    #[must_use]
2352    pub fn sql(&self) -> String {
2353        use crate::generator::Generator;
2354        Generator::expr_to_sql(self)
2355    }
2356}
2357
2358/// Helper: collect all column references from an expression.
2359#[must_use]
2360pub fn find_columns(expr: &Expr) -> Vec<&Expr> {
2361    expr.find_all(&|e| matches!(e, Expr::Column { .. }))
2362}
2363
2364/// Helper: collect all table references from a statement.
2365#[must_use]
2366pub fn find_tables(statement: &Statement) -> Vec<&TableRef> {
2367    match statement {
2368        Statement::Select(sel) => {
2369            let mut tables = Vec::new();
2370            if let Some(from) = &sel.from {
2371                collect_table_refs_from_source(&from.source, &mut tables);
2372            }
2373            for join in &sel.joins {
2374                collect_table_refs_from_source(&join.table, &mut tables);
2375            }
2376            tables
2377        }
2378        Statement::Insert(ins) => vec![&ins.table],
2379        Statement::Update(upd) => vec![&upd.table],
2380        Statement::Delete(del) => vec![&del.table],
2381        Statement::CreateTable(ct) => vec![&ct.table],
2382        Statement::DropTable(dt) => vec![&dt.table],
2383        _ => vec![],
2384    }
2385}
2386
2387fn collect_table_refs_from_source<'a>(source: &'a TableSource, tables: &mut Vec<&'a TableRef>) {
2388    match source {
2389        TableSource::Table(table_ref) => tables.push(table_ref),
2390        TableSource::Subquery { .. } => {}
2391        TableSource::TableFunction { .. } => {}
2392        TableSource::Lateral { source } => collect_table_refs_from_source(source, tables),
2393        TableSource::Pivot { source, .. } | TableSource::Unpivot { source, .. } => {
2394            collect_table_refs_from_source(source, tables);
2395        }
2396        TableSource::Unnest { .. } => {}
2397    }
2398}