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
1871    // Boolean
1872    Boolean,
1873
1874    // Date/Time
1875    Date,
1876    Time {
1877        precision: Option<u32>,
1878    },
1879    Timestamp {
1880        precision: Option<u32>,
1881        with_tz: bool,
1882    },
1883    Interval,
1884    DateTime,
1885
1886    // Binary
1887    Blob,
1888    Bytea,
1889    Bytes,
1890
1891    // JSON
1892    Json,
1893    Jsonb,
1894
1895    // UUID
1896    Uuid,
1897
1898    // Complex types
1899    Array(Option<Box<DataType>>),
1900    Map {
1901        key: Box<DataType>,
1902        value: Box<DataType>,
1903    },
1904    Struct(Vec<(String, DataType)>),
1905    Tuple(Vec<DataType>),
1906
1907    // Special
1908    Null,
1909    Unknown(String),
1910    Variant,
1911    Object,
1912    Xml,
1913    Inet,
1914    Cidr,
1915    Macaddr,
1916    Bit(Option<u32>),
1917    Money,
1918    Serial,
1919    BigSerial,
1920    SmallSerial,
1921    Regclass,
1922    Regtype,
1923    Hstore,
1924    Geography,
1925    Geometry,
1926    Super,
1927}
1928
1929// ═══════════════════════════════════════════════════════════════════════
1930// Expression tree traversal helpers
1931// ═══════════════════════════════════════════════════════════════════════
1932
1933impl Expr {
1934    /// Recursively walk this expression tree, calling `visitor` on each node.
1935    /// If `visitor` returns `false`, children of that node are not visited.
1936    pub fn walk<F>(&self, visitor: &mut F)
1937    where
1938        F: FnMut(&Expr) -> bool,
1939    {
1940        if !visitor(self) {
1941            return;
1942        }
1943        match self {
1944            Expr::BinaryOp { left, right, .. } => {
1945                left.walk(visitor);
1946                right.walk(visitor);
1947            }
1948            Expr::UnaryOp { expr, .. } => expr.walk(visitor),
1949            Expr::Function { args, filter, .. } => {
1950                for arg in args {
1951                    arg.walk(visitor);
1952                }
1953                if let Some(f) = filter {
1954                    f.walk(visitor);
1955                }
1956            }
1957            Expr::Between {
1958                expr, low, high, ..
1959            } => {
1960                expr.walk(visitor);
1961                low.walk(visitor);
1962                high.walk(visitor);
1963            }
1964            Expr::InList { expr, list, .. } => {
1965                expr.walk(visitor);
1966                for item in list {
1967                    item.walk(visitor);
1968                }
1969            }
1970            Expr::InSubquery { expr, .. } => {
1971                expr.walk(visitor);
1972            }
1973            Expr::IsNull { expr, .. } => expr.walk(visitor),
1974            Expr::IsBool { expr, .. } => expr.walk(visitor),
1975            Expr::AnyOp { expr, right, .. } | Expr::AllOp { expr, right, .. } => {
1976                expr.walk(visitor);
1977                right.walk(visitor);
1978            }
1979            Expr::Like { expr, pattern, .. }
1980            | Expr::ILike { expr, pattern, .. }
1981            | Expr::SimilarTo { expr, pattern, .. } => {
1982                expr.walk(visitor);
1983                pattern.walk(visitor);
1984            }
1985            Expr::Case {
1986                operand,
1987                when_clauses,
1988                else_clause,
1989            } => {
1990                if let Some(op) = operand {
1991                    op.walk(visitor);
1992                }
1993                for (cond, result) in when_clauses {
1994                    cond.walk(visitor);
1995                    result.walk(visitor);
1996                }
1997                if let Some(el) = else_clause {
1998                    el.walk(visitor);
1999                }
2000            }
2001            Expr::Nested(inner) => inner.walk(visitor),
2002            Expr::Cast { expr, .. } | Expr::TryCast { expr, .. } => expr.walk(visitor),
2003            Expr::Extract { expr, .. } => expr.walk(visitor),
2004            Expr::Interval { value, .. } => value.walk(visitor),
2005            Expr::ArrayLiteral(items) | Expr::Tuple(items) | Expr::Coalesce(items) => {
2006                for item in items {
2007                    item.walk(visitor);
2008                }
2009            }
2010            Expr::If {
2011                condition,
2012                true_val,
2013                false_val,
2014            } => {
2015                condition.walk(visitor);
2016                true_val.walk(visitor);
2017                if let Some(fv) = false_val {
2018                    fv.walk(visitor);
2019                }
2020            }
2021            Expr::NullIf { expr, r#else } => {
2022                expr.walk(visitor);
2023                r#else.walk(visitor);
2024            }
2025            Expr::Collate { expr, .. } => expr.walk(visitor),
2026            Expr::Alias { expr, .. } => expr.walk(visitor),
2027            Expr::ArrayIndex { expr, index } => {
2028                expr.walk(visitor);
2029                index.walk(visitor);
2030            }
2031            Expr::JsonAccess { expr, path, .. } => {
2032                expr.walk(visitor);
2033                path.walk(visitor);
2034            }
2035            Expr::Lambda { body, .. } => body.walk(visitor),
2036            Expr::TypedFunction { func, filter, .. } => {
2037                func.walk_children(visitor);
2038                if let Some(f) = filter {
2039                    f.walk(visitor);
2040                }
2041            }
2042            Expr::Cube { exprs } | Expr::Rollup { exprs } => {
2043                for item in exprs {
2044                    item.walk(visitor);
2045                }
2046            }
2047            Expr::GroupingSets { sets } => {
2048                for item in sets {
2049                    item.walk(visitor);
2050                }
2051            }
2052            Expr::Commented { expr, .. } => expr.walk(visitor),
2053            // Leaf nodes
2054            Expr::Column { .. }
2055            | Expr::Number(_)
2056            | Expr::StringLiteral(_)
2057            | Expr::NationalStringLiteral(_)
2058            | Expr::Boolean(_)
2059            | Expr::Null
2060            | Expr::Wildcard
2061            | Expr::Star
2062            | Expr::Parameter(_)
2063            | Expr::TypeExpr(_)
2064            | Expr::QualifiedWildcard { .. }
2065            | Expr::Default
2066            | Expr::Subquery(_)
2067            | Expr::Exists { .. } => {}
2068        }
2069    }
2070
2071    /// Find the first expression matching the predicate.
2072    #[must_use]
2073    pub fn find<F>(&self, predicate: &F) -> Option<&Expr>
2074    where
2075        F: Fn(&Expr) -> bool,
2076    {
2077        let mut result = None;
2078        self.walk(&mut |expr| {
2079            if result.is_some() {
2080                return false;
2081            }
2082            if predicate(expr) {
2083                result = Some(expr as *const Expr);
2084                false
2085            } else {
2086                true
2087            }
2088        });
2089        // SAFETY: the pointer is valid as long as self is alive
2090        result.map(|p| unsafe { &*p })
2091    }
2092
2093    /// Find all expressions matching the predicate.
2094    #[must_use]
2095    pub fn find_all<F>(&self, predicate: &F) -> Vec<&Expr>
2096    where
2097        F: Fn(&Expr) -> bool,
2098    {
2099        let mut results: Vec<*const Expr> = Vec::new();
2100        self.walk(&mut |expr| {
2101            if predicate(expr) {
2102                results.push(expr as *const Expr);
2103            }
2104            true
2105        });
2106        results.into_iter().map(|p| unsafe { &*p }).collect()
2107    }
2108
2109    /// Transform this expression tree by applying a function to each node.
2110    /// The function can return a new expression to replace the current one.
2111    #[must_use]
2112    pub fn transform<F>(self, func: &F) -> Expr
2113    where
2114        F: Fn(Expr) -> Expr,
2115    {
2116        let transformed = match self {
2117            Expr::BinaryOp { left, op, right } => Expr::BinaryOp {
2118                left: Box::new(left.transform(func)),
2119                op,
2120                right: Box::new(right.transform(func)),
2121            },
2122            Expr::UnaryOp { op, expr } => Expr::UnaryOp {
2123                op,
2124                expr: Box::new(expr.transform(func)),
2125            },
2126            Expr::Function {
2127                name,
2128                args,
2129                distinct,
2130                filter,
2131                over,
2132                order_by,
2133                within_group,
2134            } => Expr::Function {
2135                name,
2136                args: args.into_iter().map(|a| a.transform(func)).collect(),
2137                distinct,
2138                filter: filter.map(|f| Box::new(f.transform(func))),
2139                over,
2140                order_by,
2141                within_group,
2142            },
2143            Expr::Nested(inner) => Expr::Nested(Box::new(inner.transform(func))),
2144            Expr::Cast { expr, data_type } => Expr::Cast {
2145                expr: Box::new(expr.transform(func)),
2146                data_type,
2147            },
2148            Expr::Between {
2149                expr,
2150                low,
2151                high,
2152                negated,
2153            } => Expr::Between {
2154                expr: Box::new(expr.transform(func)),
2155                low: Box::new(low.transform(func)),
2156                high: Box::new(high.transform(func)),
2157                negated,
2158            },
2159            Expr::Case {
2160                operand,
2161                when_clauses,
2162                else_clause,
2163            } => Expr::Case {
2164                operand: operand.map(|o| Box::new(o.transform(func))),
2165                when_clauses: when_clauses
2166                    .into_iter()
2167                    .map(|(c, r)| (c.transform(func), r.transform(func)))
2168                    .collect(),
2169                else_clause: else_clause.map(|e| Box::new(e.transform(func))),
2170            },
2171            Expr::IsBool {
2172                expr,
2173                value,
2174                negated,
2175            } => Expr::IsBool {
2176                expr: Box::new(expr.transform(func)),
2177                value,
2178                negated,
2179            },
2180            Expr::AnyOp { expr, op, right } => Expr::AnyOp {
2181                expr: Box::new(expr.transform(func)),
2182                op,
2183                right: Box::new(right.transform(func)),
2184            },
2185            Expr::AllOp { expr, op, right } => Expr::AllOp {
2186                expr: Box::new(expr.transform(func)),
2187                op,
2188                right: Box::new(right.transform(func)),
2189            },
2190            Expr::TypedFunction {
2191                func: tf,
2192                filter,
2193                over,
2194            } => Expr::TypedFunction {
2195                func: tf.transform_children(func),
2196                filter: filter.map(|f| Box::new(f.transform(func))),
2197                over,
2198            },
2199            Expr::InList {
2200                expr,
2201                list,
2202                negated,
2203            } => Expr::InList {
2204                expr: Box::new(expr.transform(func)),
2205                list: list.into_iter().map(|e| e.transform(func)).collect(),
2206                negated,
2207            },
2208            Expr::InSubquery {
2209                expr,
2210                subquery,
2211                negated,
2212            } => Expr::InSubquery {
2213                expr: Box::new(expr.transform(func)),
2214                subquery, // Statement — not transformable via Expr func
2215                negated,
2216            },
2217            Expr::IsNull { expr, negated } => Expr::IsNull {
2218                expr: Box::new(expr.transform(func)),
2219                negated,
2220            },
2221            Expr::Like {
2222                expr,
2223                pattern,
2224                negated,
2225                escape,
2226            } => Expr::Like {
2227                expr: Box::new(expr.transform(func)),
2228                pattern: Box::new(pattern.transform(func)),
2229                negated,
2230                escape: escape.map(|e| Box::new(e.transform(func))),
2231            },
2232            Expr::ILike {
2233                expr,
2234                pattern,
2235                negated,
2236                escape,
2237            } => Expr::ILike {
2238                expr: Box::new(expr.transform(func)),
2239                pattern: Box::new(pattern.transform(func)),
2240                negated,
2241                escape: escape.map(|e| Box::new(e.transform(func))),
2242            },
2243            Expr::TryCast { expr, data_type } => Expr::TryCast {
2244                expr: Box::new(expr.transform(func)),
2245                data_type,
2246            },
2247            Expr::Extract { field, expr } => Expr::Extract {
2248                field,
2249                expr: Box::new(expr.transform(func)),
2250            },
2251            Expr::Interval { value, unit } => Expr::Interval {
2252                value: Box::new(value.transform(func)),
2253                unit,
2254            },
2255            Expr::ArrayLiteral(elems) => {
2256                Expr::ArrayLiteral(elems.into_iter().map(|e| e.transform(func)).collect())
2257            }
2258            Expr::Tuple(elems) => {
2259                Expr::Tuple(elems.into_iter().map(|e| e.transform(func)).collect())
2260            }
2261            Expr::Coalesce(elems) => {
2262                Expr::Coalesce(elems.into_iter().map(|e| e.transform(func)).collect())
2263            }
2264            Expr::If {
2265                condition,
2266                true_val,
2267                false_val,
2268            } => Expr::If {
2269                condition: Box::new(condition.transform(func)),
2270                true_val: Box::new(true_val.transform(func)),
2271                false_val: false_val.map(|f| Box::new(f.transform(func))),
2272            },
2273            Expr::NullIf { expr, r#else } => Expr::NullIf {
2274                expr: Box::new(expr.transform(func)),
2275                r#else: Box::new(r#else.transform(func)),
2276            },
2277            Expr::Collate { expr, collation } => Expr::Collate {
2278                expr: Box::new(expr.transform(func)),
2279                collation,
2280            },
2281            Expr::Alias { expr, name } => Expr::Alias {
2282                expr: Box::new(expr.transform(func)),
2283                name,
2284            },
2285            Expr::ArrayIndex { expr, index } => Expr::ArrayIndex {
2286                expr: Box::new(expr.transform(func)),
2287                index: Box::new(index.transform(func)),
2288            },
2289            Expr::JsonAccess {
2290                expr,
2291                path,
2292                as_text,
2293            } => Expr::JsonAccess {
2294                expr: Box::new(expr.transform(func)),
2295                path: Box::new(path.transform(func)),
2296                as_text,
2297            },
2298            Expr::Lambda { params, body } => Expr::Lambda {
2299                params,
2300                body: Box::new(body.transform(func)),
2301            },
2302            Expr::Cube { exprs } => Expr::Cube {
2303                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
2304            },
2305            Expr::Rollup { exprs } => Expr::Rollup {
2306                exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
2307            },
2308            Expr::GroupingSets { sets } => Expr::GroupingSets {
2309                sets: sets.into_iter().map(|e| e.transform(func)).collect(),
2310            },
2311            Expr::Commented { expr, comments } => Expr::Commented {
2312                expr: Box::new(expr.transform(func)),
2313                comments,
2314            },
2315            other => other,
2316        };
2317        func(transformed)
2318    }
2319
2320    /// Check whether this expression is a column reference.
2321    #[must_use]
2322    pub fn is_column(&self) -> bool {
2323        matches!(self, Expr::Column { .. })
2324    }
2325
2326    /// Check whether this expression is a literal value (number, string, bool, null).
2327    #[must_use]
2328    pub fn is_literal(&self) -> bool {
2329        matches!(
2330            self,
2331            Expr::Number(_)
2332                | Expr::StringLiteral(_)
2333                | Expr::NationalStringLiteral(_)
2334                | Expr::Boolean(_)
2335                | Expr::Null
2336        )
2337    }
2338
2339    /// Get the SQL representation of this expression for display purposes.
2340    /// For full generation, use the Generator.
2341    #[must_use]
2342    pub fn sql(&self) -> String {
2343        use crate::generator::Generator;
2344        Generator::expr_to_sql(self)
2345    }
2346}
2347
2348/// Helper: collect all column references from an expression.
2349#[must_use]
2350pub fn find_columns(expr: &Expr) -> Vec<&Expr> {
2351    expr.find_all(&|e| matches!(e, Expr::Column { .. }))
2352}
2353
2354/// Helper: collect all table references from a statement.
2355#[must_use]
2356pub fn find_tables(statement: &Statement) -> Vec<&TableRef> {
2357    match statement {
2358        Statement::Select(sel) => {
2359            let mut tables = Vec::new();
2360            if let Some(from) = &sel.from {
2361                collect_table_refs_from_source(&from.source, &mut tables);
2362            }
2363            for join in &sel.joins {
2364                collect_table_refs_from_source(&join.table, &mut tables);
2365            }
2366            tables
2367        }
2368        Statement::Insert(ins) => vec![&ins.table],
2369        Statement::Update(upd) => vec![&upd.table],
2370        Statement::Delete(del) => vec![&del.table],
2371        Statement::CreateTable(ct) => vec![&ct.table],
2372        Statement::DropTable(dt) => vec![&dt.table],
2373        _ => vec![],
2374    }
2375}
2376
2377fn collect_table_refs_from_source<'a>(source: &'a TableSource, tables: &mut Vec<&'a TableRef>) {
2378    match source {
2379        TableSource::Table(table_ref) => tables.push(table_ref),
2380        TableSource::Subquery { .. } => {}
2381        TableSource::TableFunction { .. } => {}
2382        TableSource::Lateral { source } => collect_table_refs_from_source(source, tables),
2383        TableSource::Pivot { source, .. } | TableSource::Unpivot { source, .. } => {
2384            collect_table_refs_from_source(source, tables);
2385        }
2386        TableSource::Unnest { .. } => {}
2387    }
2388}