Skip to main content

polyglot_sql/
transforms.rs

1//! SQL AST Transforms
2//!
3//! This module provides functions to transform SQL ASTs for dialect compatibility.
4//! These transforms are used during transpilation to convert dialect-specific features
5//! to forms that are supported by the target dialect.
6//!
7//! Based on the Python implementation in `sqlglot/transforms.py`.
8
9use crate::dialects::transform_recursive;
10use crate::dialects::{Dialect, DialectType};
11use crate::error::Result;
12use crate::expressions::{
13    Alias, BinaryOp, BooleanLiteral, Cast, DataType, Exists, Expression, From, Function,
14    Identifier, Join, JoinKind, Lateral, LateralView, Literal, NamedArgSeparator, NamedArgument,
15    Over, Select, StructField, Subquery, UnaryFunc, UnnestFunc, Where, With,
16};
17use std::cell::RefCell;
18
19/// Apply a chain of transforms to an expression
20///
21/// # Arguments
22/// * `expr` - The expression to transform
23/// * `transforms` - A list of transform functions to apply in order
24///
25/// # Returns
26/// The transformed expression
27pub fn preprocess<F>(expr: Expression, transforms: &[F]) -> Result<Expression>
28where
29    F: Fn(Expression) -> Result<Expression>,
30{
31    let mut result = expr;
32    for transform in transforms {
33        result = transform(result)?;
34    }
35    Ok(result)
36}
37
38/// Convert UNNEST to EXPLODE (for Spark/Hive compatibility)
39///
40/// UNNEST is standard SQL but Spark uses EXPLODE instead.
41pub fn unnest_to_explode(expr: Expression) -> Result<Expression> {
42    match expr {
43        Expression::Unnest(unnest) => {
44            Ok(Expression::Explode(Box::new(UnaryFunc::new(unnest.this))))
45        }
46        _ => Ok(expr),
47    }
48}
49
50/// Convert CROSS JOIN UNNEST to LATERAL VIEW EXPLODE/INLINE for Spark/Hive/Databricks.
51///
52/// This is a SELECT-level structural transformation that:
53/// 1. Converts UNNEST in FROM clause to INLINE/EXPLODE
54/// 2. Converts CROSS JOIN (LATERAL) UNNEST to LATERAL VIEW entries
55/// 3. For single-arg UNNEST: uses EXPLODE
56/// 4. For multi-arg UNNEST: uses INLINE(ARRAYS_ZIP(...))
57///
58/// Based on Python sqlglot's `unnest_to_explode` transform in transforms.py (lines 290-391).
59pub fn unnest_to_explode_select(expr: Expression) -> Result<Expression> {
60    transform_recursive(expr, &unnest_to_explode_select_inner)
61}
62
63/// Helper to determine the UDTF function for an UNNEST expression.
64/// Single-arg UNNEST → EXPLODE, multi-arg → INLINE
65fn make_udtf_expr(unnest: &UnnestFunc) -> Expression {
66    let has_multi_expr = !unnest.expressions.is_empty();
67    if has_multi_expr {
68        // Multi-arg: INLINE(ARRAYS_ZIP(arg1, arg2, ...))
69        let mut all_args = vec![unnest.this.clone()];
70        all_args.extend(unnest.expressions.iter().cloned());
71        let arrays_zip =
72            Expression::Function(Box::new(Function::new("ARRAYS_ZIP".to_string(), all_args)));
73        Expression::Function(Box::new(Function::new(
74            "INLINE".to_string(),
75            vec![arrays_zip],
76        )))
77    } else {
78        // Single-arg: EXPLODE(arg)
79        Expression::Explode(Box::new(UnaryFunc::new(unnest.this.clone())))
80    }
81}
82
83fn unnest_to_explode_select_inner(expr: Expression) -> Result<Expression> {
84    let Expression::Select(mut select) = expr else {
85        return Ok(expr);
86    };
87
88    // Process FROM clause: UNNEST items need conversion
89    if let Some(ref mut from) = select.from {
90        if from.expressions.len() >= 1 {
91            let mut new_from_exprs = Vec::new();
92            let mut new_lateral_views = Vec::new();
93            let first_is_unnest = is_unnest_expr(&from.expressions[0]);
94
95            for (idx, from_item) in from.expressions.drain(..).enumerate() {
96                if idx == 0 && first_is_unnest {
97                    // UNNEST is the first (and possibly only) item in FROM
98                    // Replace it with INLINE/EXPLODE, keeping alias
99                    let replaced = replace_from_unnest(from_item);
100                    new_from_exprs.push(replaced);
101                } else if idx > 0 && is_unnest_expr(&from_item) {
102                    // Additional UNNEST items in FROM (comma-joined) → LATERAL VIEW
103                    let (alias_name, column_aliases, unnest_func) = extract_unnest_info(from_item);
104                    let udtf = make_udtf_expr(&unnest_func);
105                    new_lateral_views.push(LateralView {
106                        this: udtf,
107                        table_alias: alias_name,
108                        column_aliases,
109                        outer: false,
110                    });
111                } else {
112                    new_from_exprs.push(from_item);
113                }
114            }
115
116            from.expressions = new_from_exprs;
117            // Append lateral views for comma-joined UNNESTs
118            select.lateral_views.extend(new_lateral_views);
119        }
120    }
121
122    // Process joins: CROSS JOIN (LATERAL) UNNEST → LATERAL VIEW
123    let mut remaining_joins = Vec::new();
124    for join in select.joins.drain(..) {
125        if matches!(join.kind, JoinKind::Cross | JoinKind::Inner) {
126            let (is_unnest, is_lateral) = check_join_unnest(&join.this);
127            if is_unnest {
128                // Extract UNNEST info from join, handling Lateral wrapper
129                let (lateral_alias, lateral_col_aliases, join_expr) = if is_lateral {
130                    if let Expression::Lateral(lat) = join.this {
131                        // Extract alias from Lateral struct
132                        let alias = lat.alias.map(|s| Identifier::new(&s));
133                        let col_aliases: Vec<Identifier> = lat
134                            .column_aliases
135                            .iter()
136                            .map(|s| Identifier::new(s))
137                            .collect();
138                        (alias, col_aliases, *lat.this)
139                    } else {
140                        (None, Vec::new(), join.this)
141                    }
142                } else {
143                    (None, Vec::new(), join.this)
144                };
145
146                let (alias_name, column_aliases, unnest_func) = extract_unnest_info(join_expr);
147
148                // Prefer Lateral's alias over UNNEST's alias
149                let final_alias = lateral_alias.or(alias_name);
150                let final_col_aliases = if !lateral_col_aliases.is_empty() {
151                    lateral_col_aliases
152                } else {
153                    column_aliases
154                };
155
156                // Use "unnest" as default alias if none provided (for single-arg case)
157                let table_alias = final_alias.or_else(|| Some(Identifier::new("unnest")));
158                let col_aliases = if final_col_aliases.is_empty() {
159                    vec![Identifier::new("unnest")]
160                } else {
161                    final_col_aliases
162                };
163
164                let udtf = make_udtf_expr(&unnest_func);
165                select.lateral_views.push(LateralView {
166                    this: udtf,
167                    table_alias,
168                    column_aliases: col_aliases,
169                    outer: false,
170                });
171            } else {
172                remaining_joins.push(join);
173            }
174        } else {
175            remaining_joins.push(join);
176        }
177    }
178    select.joins = remaining_joins;
179
180    Ok(Expression::Select(select))
181}
182
183/// Check if an expression is or wraps an UNNEST
184fn is_unnest_expr(expr: &Expression) -> bool {
185    match expr {
186        Expression::Unnest(_) => true,
187        Expression::Alias(a) => matches!(a.this, Expression::Unnest(_)),
188        _ => false,
189    }
190}
191
192/// Check if a join's expression is an UNNEST (possibly wrapped in Lateral)
193fn check_join_unnest(expr: &Expression) -> (bool, bool) {
194    match expr {
195        Expression::Unnest(_) => (true, false),
196        Expression::Alias(a) => {
197            if matches!(a.this, Expression::Unnest(_)) {
198                (true, false)
199            } else {
200                (false, false)
201            }
202        }
203        Expression::Lateral(lat) => match &*lat.this {
204            Expression::Unnest(_) => (true, true),
205            Expression::Alias(a) => {
206                if matches!(a.this, Expression::Unnest(_)) {
207                    (true, true)
208                } else {
209                    (false, true)
210                }
211            }
212            _ => (false, true),
213        },
214        _ => (false, false),
215    }
216}
217
218/// Replace an UNNEST in FROM with INLINE/EXPLODE, preserving alias structure
219fn replace_from_unnest(from_item: Expression) -> Expression {
220    match from_item {
221        Expression::Alias(mut a) => {
222            if let Expression::Unnest(unnest) = a.this {
223                a.this = make_udtf_expr(&unnest);
224            }
225            Expression::Alias(a)
226        }
227        Expression::Unnest(unnest) => make_udtf_expr(&unnest),
228        other => other,
229    }
230}
231
232/// Extract alias info and UnnestFunc from an expression (possibly wrapped in Alias)
233fn extract_unnest_info(expr: Expression) -> (Option<Identifier>, Vec<Identifier>, UnnestFunc) {
234    match expr {
235        Expression::Alias(a) => {
236            if let Expression::Unnest(unnest) = a.this {
237                (Some(a.alias), a.column_aliases, *unnest)
238            } else {
239                // Should not happen if we already checked is_unnest_expr
240                (
241                    Some(a.alias),
242                    a.column_aliases,
243                    UnnestFunc {
244                        this: a.this,
245                        expressions: Vec::new(),
246                        with_ordinality: false,
247                        alias: None,
248                        offset_alias: None,
249                    },
250                )
251            }
252        }
253        Expression::Unnest(unnest) => {
254            let alias = unnest.alias.clone();
255            (alias, Vec::new(), *unnest)
256        }
257        _ => (
258            None,
259            Vec::new(),
260            UnnestFunc {
261                this: expr,
262                expressions: Vec::new(),
263                with_ordinality: false,
264                alias: None,
265                offset_alias: None,
266            },
267        ),
268    }
269}
270
271/// Convert EXPLODE to UNNEST (for standard SQL compatibility)
272pub fn explode_to_unnest(expr: Expression) -> Result<Expression> {
273    match expr {
274        Expression::Explode(explode) => Ok(Expression::Unnest(Box::new(UnnestFunc {
275            this: explode.this,
276            expressions: Vec::new(),
277            with_ordinality: false,
278            alias: None,
279            offset_alias: None,
280        }))),
281        _ => Ok(expr),
282    }
283}
284
285/// Replace boolean literals for dialects that don't support them
286///
287/// Converts TRUE/FALSE to 1/0 for dialects like older MySQL versions
288pub fn replace_bool_with_int(expr: Expression) -> Result<Expression> {
289    match expr {
290        Expression::Boolean(b) => {
291            let value = if b.value { "1" } else { "0" };
292            Ok(Expression::Literal(Box::new(Literal::Number(
293                value.to_string(),
294            ))))
295        }
296        _ => Ok(expr),
297    }
298}
299
300/// Replace integer literals for dialects that prefer boolean
301///
302/// Converts 1/0 to TRUE/FALSE
303pub fn replace_int_with_bool(expr: Expression) -> Result<Expression> {
304    match expr {
305        Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(n) if n == "1" || n == "0") =>
306        {
307            let Literal::Number(n) = lit.as_ref() else {
308                unreachable!()
309            };
310            Ok(Expression::Boolean(BooleanLiteral { value: n == "1" }))
311        }
312        _ => Ok(expr),
313    }
314}
315
316/// Remove precision from parameterized types
317///
318/// Some dialects don't support precision parameters on certain types.
319/// This transform removes them, e.g., VARCHAR(255) → VARCHAR, DECIMAL(10,2) → DECIMAL
320pub fn remove_precision_parameterized_types(expr: Expression) -> Result<Expression> {
321    Ok(strip_type_params_recursive(expr))
322}
323
324/// Recursively strip type parameters from DataType values in an expression
325fn strip_type_params_recursive(expr: Expression) -> Expression {
326    match expr {
327        // Handle Cast expressions - strip precision from target type
328        Expression::Cast(mut cast) => {
329            cast.to = strip_data_type_params(cast.to);
330            // Also recursively process the expression being cast
331            cast.this = strip_type_params_recursive(cast.this);
332            Expression::Cast(cast)
333        }
334        // Handle TryCast expressions (uses same Cast struct)
335        Expression::TryCast(mut try_cast) => {
336            try_cast.to = strip_data_type_params(try_cast.to);
337            try_cast.this = strip_type_params_recursive(try_cast.this);
338            Expression::TryCast(try_cast)
339        }
340        // Handle SafeCast expressions (uses same Cast struct)
341        Expression::SafeCast(mut safe_cast) => {
342            safe_cast.to = strip_data_type_params(safe_cast.to);
343            safe_cast.this = strip_type_params_recursive(safe_cast.this);
344            Expression::SafeCast(safe_cast)
345        }
346        // For now, pass through other expressions
347        // A full implementation would recursively visit all nodes
348        _ => expr,
349    }
350}
351
352/// Strip precision/scale/length parameters from a DataType
353fn strip_data_type_params(dt: DataType) -> DataType {
354    match dt {
355        // Numeric types with precision/scale
356        DataType::Decimal { .. } => DataType::Decimal {
357            precision: None,
358            scale: None,
359        },
360        DataType::TinyInt { .. } => DataType::TinyInt { length: None },
361        DataType::SmallInt { .. } => DataType::SmallInt { length: None },
362        DataType::Int { .. } => DataType::Int {
363            length: None,
364            integer_spelling: false,
365        },
366        DataType::BigInt { .. } => DataType::BigInt { length: None },
367
368        // String types with length
369        DataType::Char { .. } => DataType::Char { length: None },
370        DataType::VarChar { .. } => DataType::VarChar {
371            length: None,
372            parenthesized_length: false,
373        },
374
375        // Binary types with length
376        DataType::Binary { .. } => DataType::Binary { length: None },
377        DataType::VarBinary { .. } => DataType::VarBinary { length: None },
378
379        // Bit types with length
380        DataType::Bit { .. } => DataType::Bit { length: None },
381        DataType::VarBit { .. } => DataType::VarBit { length: None },
382
383        // Time types with precision
384        DataType::Time { .. } => DataType::Time {
385            precision: None,
386            timezone: false,
387        },
388        DataType::Timestamp { timezone, .. } => DataType::Timestamp {
389            precision: None,
390            timezone,
391        },
392
393        // Array - recursively strip element type
394        DataType::Array {
395            element_type,
396            dimension,
397        } => DataType::Array {
398            element_type: Box::new(strip_data_type_params(*element_type)),
399            dimension,
400        },
401
402        // Map - recursively strip key and value types
403        DataType::Map {
404            key_type,
405            value_type,
406        } => DataType::Map {
407            key_type: Box::new(strip_data_type_params(*key_type)),
408            value_type: Box::new(strip_data_type_params(*value_type)),
409        },
410
411        // Struct - recursively strip field types
412        DataType::Struct { fields, nested } => DataType::Struct {
413            fields: fields
414                .into_iter()
415                .map(|f| {
416                    StructField::with_options(
417                        f.name,
418                        strip_data_type_params(f.data_type),
419                        f.options,
420                    )
421                })
422                .collect(),
423            nested,
424        },
425
426        // Vector - strip dimension
427        DataType::Vector { element_type, .. } => DataType::Vector {
428            element_type: element_type.map(|et| Box::new(strip_data_type_params(*et))),
429            dimension: None,
430        },
431
432        // Object - recursively strip field types
433        DataType::Object { fields, modifier } => DataType::Object {
434            fields: fields
435                .into_iter()
436                .map(|(name, ty, not_null)| (name, strip_data_type_params(ty), not_null))
437                .collect(),
438            modifier,
439        },
440
441        // Other types pass through unchanged
442        other => other,
443    }
444}
445
446/// Eliminate QUALIFY clause by converting to a subquery with WHERE filter
447///
448/// QUALIFY is supported by Snowflake, BigQuery, and DuckDB but not by most other dialects.
449///
450/// Converts:
451/// ```sql
452/// SELECT * FROM t QUALIFY ROW_NUMBER() OVER (...) = 1
453/// ```
454/// To:
455/// ```sql
456/// SELECT * FROM (SELECT *, ROW_NUMBER() OVER (...) AS _w FROM t) _t WHERE _w = 1
457/// ```
458///
459/// Reference: `transforms.py:194-255`
460pub fn eliminate_qualify(expr: Expression) -> Result<Expression> {
461    match expr {
462        Expression::Select(mut select) => {
463            if let Some(qualify) = select.qualify.take() {
464                // Python sqlglot approach:
465                // 1. Extract the window function from the qualify condition
466                // 2. Add it as _w alias to the inner select
467                // 3. Replace the window function reference with _w in the outer WHERE
468                // 4. Keep original select expressions in the outer query
469
470                let qualify_filter = qualify.this;
471                let window_alias_name = "_w".to_string();
472                let window_alias_ident = Identifier::new(window_alias_name.clone());
473
474                // Try to extract window function from comparison
475                // Pattern: WINDOW_FUNC = value -> inner adds WINDOW_FUNC AS _w, outer WHERE _w = value
476                let (window_expr, outer_where) =
477                    extract_window_from_condition(qualify_filter.clone(), &window_alias_ident);
478
479                if let Some(win_expr) = window_expr {
480                    // Add window function as _w alias to inner select
481                    let window_alias_expr =
482                        Expression::Alias(Box::new(crate::expressions::Alias {
483                            this: win_expr,
484                            alias: window_alias_ident.clone(),
485                            column_aliases: vec![],
486                            alias_explicit_as: false,
487                            alias_keyword: None,
488                            pre_alias_comments: vec![],
489                            trailing_comments: vec![],
490                            inferred_type: None,
491                        }));
492
493                    // For the outer SELECT, replace aliased expressions with just the alias reference
494                    // e.g., `1 AS other_id` in inner -> `other_id` in outer
495                    // Non-aliased expressions (columns, identifiers) stay as-is
496                    let outer_exprs: Vec<Expression> = select
497                        .expressions
498                        .iter()
499                        .map(|expr| {
500                            if let Expression::Alias(a) = expr {
501                                // Replace with just the alias identifier as a column reference
502                                Expression::Column(Box::new(crate::expressions::Column {
503                                    name: a.alias.clone(),
504                                    table: None,
505                                    join_mark: false,
506                                    trailing_comments: vec![],
507                                    span: None,
508                                    inferred_type: None,
509                                }))
510                            } else {
511                                expr.clone()
512                            }
513                        })
514                        .collect();
515                    select.expressions.push(window_alias_expr);
516
517                    // Create the inner subquery
518                    let inner_select = Expression::Select(select);
519                    let subquery = Subquery {
520                        this: inner_select,
521                        alias: Some(Identifier::new("_t".to_string())),
522                        column_aliases: vec![],
523                        alias_explicit_as: false,
524                        alias_keyword: None,
525                        order_by: None,
526                        limit: None,
527                        offset: None,
528                        distribute_by: None,
529                        sort_by: None,
530                        cluster_by: None,
531                        lateral: false,
532                        modifiers_inside: false,
533                        trailing_comments: vec![],
534                        inferred_type: None,
535                    };
536
537                    // Create the outer SELECT with alias-resolved expressions and WHERE _w <op> value
538                    let outer_select = Select {
539                        expressions: outer_exprs,
540                        from: Some(From {
541                            expressions: vec![Expression::Subquery(Box::new(subquery))],
542                        }),
543                        where_clause: Some(Where { this: outer_where }),
544                        ..Select::new()
545                    };
546
547                    return Ok(Expression::Select(Box::new(outer_select)));
548                } else {
549                    // Fallback: if we can't extract a window function, use old approach
550                    let qualify_alias = Expression::Alias(Box::new(crate::expressions::Alias {
551                        this: qualify_filter.clone(),
552                        alias: window_alias_ident.clone(),
553                        column_aliases: vec![],
554                        alias_explicit_as: false,
555                        alias_keyword: None,
556                        pre_alias_comments: vec![],
557                        trailing_comments: vec![],
558                        inferred_type: None,
559                    }));
560
561                    let original_exprs = select.expressions.clone();
562                    select.expressions.push(qualify_alias);
563
564                    let inner_select = Expression::Select(select);
565                    let subquery = Subquery {
566                        this: inner_select,
567                        alias: Some(Identifier::new("_t".to_string())),
568                        column_aliases: vec![],
569                        alias_explicit_as: false,
570                        alias_keyword: None,
571                        order_by: None,
572                        limit: None,
573                        offset: None,
574                        distribute_by: None,
575                        sort_by: None,
576                        cluster_by: None,
577                        lateral: false,
578                        modifiers_inside: false,
579                        trailing_comments: vec![],
580                        inferred_type: None,
581                    };
582
583                    let outer_select = Select {
584                        expressions: original_exprs,
585                        from: Some(From {
586                            expressions: vec![Expression::Subquery(Box::new(subquery))],
587                        }),
588                        where_clause: Some(Where {
589                            this: Expression::Column(Box::new(crate::expressions::Column {
590                                name: window_alias_ident,
591                                table: None,
592                                join_mark: false,
593                                trailing_comments: vec![],
594                                span: None,
595                                inferred_type: None,
596                            })),
597                        }),
598                        ..Select::new()
599                    };
600
601                    return Ok(Expression::Select(Box::new(outer_select)));
602                }
603            }
604            Ok(Expression::Select(select))
605        }
606        other => Ok(other),
607    }
608}
609
610/// Extract a window function from a qualify condition.
611/// Returns (window_expression, rewritten_condition) if found.
612/// The rewritten condition replaces the window function with a column reference to the alias.
613fn extract_window_from_condition(
614    condition: Expression,
615    alias: &Identifier,
616) -> (Option<Expression>, Expression) {
617    let alias_col = Expression::Column(Box::new(crate::expressions::Column {
618        name: alias.clone(),
619        table: None,
620        join_mark: false,
621        trailing_comments: vec![],
622        span: None,
623        inferred_type: None,
624    }));
625
626    // Check if condition is a simple comparison with a window function on one side
627    match condition {
628        // WINDOW_FUNC = value
629        Expression::Eq(ref op) => {
630            if is_window_expr(&op.left) {
631                (
632                    Some(op.left.clone()),
633                    Expression::Eq(Box::new(BinaryOp {
634                        left: alias_col,
635                        right: op.right.clone(),
636                        ..(**op).clone()
637                    })),
638                )
639            } else if is_window_expr(&op.right) {
640                (
641                    Some(op.right.clone()),
642                    Expression::Eq(Box::new(BinaryOp {
643                        left: op.left.clone(),
644                        right: alias_col,
645                        ..(**op).clone()
646                    })),
647                )
648            } else {
649                (None, condition)
650            }
651        }
652        Expression::Neq(ref op) => {
653            if is_window_expr(&op.left) {
654                (
655                    Some(op.left.clone()),
656                    Expression::Neq(Box::new(BinaryOp {
657                        left: alias_col,
658                        right: op.right.clone(),
659                        ..(**op).clone()
660                    })),
661                )
662            } else if is_window_expr(&op.right) {
663                (
664                    Some(op.right.clone()),
665                    Expression::Neq(Box::new(BinaryOp {
666                        left: op.left.clone(),
667                        right: alias_col,
668                        ..(**op).clone()
669                    })),
670                )
671            } else {
672                (None, condition)
673            }
674        }
675        Expression::Lt(ref op) => {
676            if is_window_expr(&op.left) {
677                (
678                    Some(op.left.clone()),
679                    Expression::Lt(Box::new(BinaryOp {
680                        left: alias_col,
681                        right: op.right.clone(),
682                        ..(**op).clone()
683                    })),
684                )
685            } else if is_window_expr(&op.right) {
686                (
687                    Some(op.right.clone()),
688                    Expression::Lt(Box::new(BinaryOp {
689                        left: op.left.clone(),
690                        right: alias_col,
691                        ..(**op).clone()
692                    })),
693                )
694            } else {
695                (None, condition)
696            }
697        }
698        Expression::Lte(ref op) => {
699            if is_window_expr(&op.left) {
700                (
701                    Some(op.left.clone()),
702                    Expression::Lte(Box::new(BinaryOp {
703                        left: alias_col,
704                        right: op.right.clone(),
705                        ..(**op).clone()
706                    })),
707                )
708            } else if is_window_expr(&op.right) {
709                (
710                    Some(op.right.clone()),
711                    Expression::Lte(Box::new(BinaryOp {
712                        left: op.left.clone(),
713                        right: alias_col,
714                        ..(**op).clone()
715                    })),
716                )
717            } else {
718                (None, condition)
719            }
720        }
721        Expression::Gt(ref op) => {
722            if is_window_expr(&op.left) {
723                (
724                    Some(op.left.clone()),
725                    Expression::Gt(Box::new(BinaryOp {
726                        left: alias_col,
727                        right: op.right.clone(),
728                        ..(**op).clone()
729                    })),
730                )
731            } else if is_window_expr(&op.right) {
732                (
733                    Some(op.right.clone()),
734                    Expression::Gt(Box::new(BinaryOp {
735                        left: op.left.clone(),
736                        right: alias_col,
737                        ..(**op).clone()
738                    })),
739                )
740            } else {
741                (None, condition)
742            }
743        }
744        Expression::Gte(ref op) => {
745            if is_window_expr(&op.left) {
746                (
747                    Some(op.left.clone()),
748                    Expression::Gte(Box::new(BinaryOp {
749                        left: alias_col,
750                        right: op.right.clone(),
751                        ..(**op).clone()
752                    })),
753                )
754            } else if is_window_expr(&op.right) {
755                (
756                    Some(op.right.clone()),
757                    Expression::Gte(Box::new(BinaryOp {
758                        left: op.left.clone(),
759                        right: alias_col,
760                        ..(**op).clone()
761                    })),
762                )
763            } else {
764                (None, condition)
765            }
766        }
767        // If the condition is just a window function (bare QUALIFY expression)
768        _ if is_window_expr(&condition) => (Some(condition), alias_col),
769        // Can't extract window function
770        _ => (None, condition),
771    }
772}
773
774/// Check if an expression is a window function
775fn is_window_expr(expr: &Expression) -> bool {
776    matches!(expr, Expression::Window(_) | Expression::WindowFunction(_))
777}
778
779/// Eliminate DISTINCT ON clause by converting to a subquery with ROW_NUMBER
780///
781/// DISTINCT ON is PostgreSQL-specific. For dialects that don't support it,
782/// this converts it to a subquery with a ROW_NUMBER() window function.
783///
784/// Converts:
785/// ```sql
786/// SELECT DISTINCT ON (a) a, b FROM t ORDER BY a, b
787/// ```
788/// To:
789/// ```sql
790/// SELECT a, b FROM (
791///     SELECT a, b, ROW_NUMBER() OVER (PARTITION BY a ORDER BY a, b) AS _row_number
792///     FROM t
793/// ) _t WHERE _row_number = 1
794/// ```
795///
796/// Reference: `transforms.py:138-191`
797pub fn eliminate_distinct_on(expr: Expression) -> Result<Expression> {
798    eliminate_distinct_on_for_dialect(expr, None, None)
799}
800
801/// Eliminate DISTINCT ON with dialect-specific NULL ordering behavior.
802///
803/// For dialects where NULLs don't sort first by default in DESC ordering,
804/// we need to add explicit NULL ordering to preserve DISTINCT ON semantics.
805pub fn eliminate_distinct_on_for_dialect(
806    expr: Expression,
807    target: Option<DialectType>,
808    source: Option<DialectType>,
809) -> Result<Expression> {
810    use crate::expressions::Case;
811
812    // PostgreSQL and DuckDB support DISTINCT ON natively - skip elimination
813    if matches!(
814        target,
815        Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB)
816    ) {
817        return Ok(expr);
818    }
819
820    // Determine NULL ordering mode based on target dialect
821    // Oracle/Redshift/Snowflake: NULLS FIRST is default for DESC -> no change needed
822    // BigQuery/Spark/Presto/Hive/etc: need explicit NULLS FIRST
823    // MySQL/TSQL: no NULLS FIRST syntax -> use CASE WHEN IS NULL
824    enum NullsMode {
825        None,       // Default NULLS FIRST behavior (Oracle, Redshift, Snowflake)
826        NullsFirst, // Add explicit NULLS FIRST (BigQuery, Spark, Presto, Hive, etc.)
827        CaseExpr,   // Use CASE WHEN IS NULL for NULLS FIRST simulation (MySQL, StarRocks, TSQL)
828    }
829
830    let nulls_mode = match target {
831        Some(DialectType::MySQL)
832        | Some(DialectType::SingleStore)
833        | Some(DialectType::TSQL)
834        | Some(DialectType::Fabric) => NullsMode::CaseExpr,
835        Some(DialectType::Oracle) | Some(DialectType::Redshift) | Some(DialectType::Snowflake) => {
836            NullsMode::None
837        }
838        Some(DialectType::StarRocks) => {
839            if matches!(source, Some(DialectType::Redshift)) {
840                NullsMode::CaseExpr
841            } else {
842                NullsMode::None
843            }
844        }
845        // All other dialects that don't support DISTINCT ON: use NULLS FIRST
846        _ => NullsMode::NullsFirst,
847    };
848
849    match expr {
850        Expression::Select(mut select) => {
851            if let Some(distinct_cols) = select.distinct_on.take() {
852                if !distinct_cols.is_empty() {
853                    // Create ROW_NUMBER() OVER (PARTITION BY distinct_cols ORDER BY ...)
854                    let row_number_alias = Identifier::new("_row_number".to_string());
855
856                    // Get order_by expressions, or use distinct_cols as default order
857                    let order_exprs = if let Some(ref order_by) = select.order_by {
858                        let mut exprs = order_by.expressions.clone();
859                        // Add NULL ordering based on target dialect
860                        match nulls_mode {
861                            NullsMode::NullsFirst => {
862                                for ord in &mut exprs {
863                                    if ord.desc && ord.nulls_first.is_none() {
864                                        ord.nulls_first = Some(true);
865                                    }
866                                }
867                            }
868                            NullsMode::CaseExpr => {
869                                // For each DESC column without explicit nulls ordering,
870                                // prepend: CASE WHEN col IS NULL THEN 1 ELSE 0 END DESC
871                                let mut new_exprs = Vec::new();
872                                for ord in &exprs {
873                                    if ord.desc && ord.nulls_first.is_none() {
874                                        // Add CASE WHEN col IS NULL THEN 1 ELSE 0 END DESC
875                                        let null_check = Expression::Case(Box::new(Case {
876                                            operand: None,
877                                            whens: vec![(
878                                                Expression::IsNull(Box::new(
879                                                    crate::expressions::IsNull {
880                                                        this: ord.this.clone(),
881                                                        not: false,
882                                                        postfix_form: false,
883                                                    },
884                                                )),
885                                                Expression::Literal(Box::new(Literal::Number(
886                                                    "1".to_string(),
887                                                ))),
888                                            )],
889                                            else_: Some(Expression::Literal(Box::new(
890                                                Literal::Number("0".to_string()),
891                                            ))),
892                                            comments: Vec::new(),
893                                            inferred_type: None,
894                                        }));
895                                        new_exprs.push(crate::expressions::Ordered {
896                                            this: null_check,
897                                            desc: true,
898                                            nulls_first: None,
899                                            explicit_asc: false,
900                                            with_fill: None,
901                                        });
902                                    }
903                                    new_exprs.push(ord.clone());
904                                }
905                                exprs = new_exprs;
906                            }
907                            NullsMode::None => {}
908                        }
909                        exprs
910                    } else {
911                        distinct_cols
912                            .iter()
913                            .map(|e| crate::expressions::Ordered {
914                                this: e.clone(),
915                                desc: false,
916                                nulls_first: None,
917                                explicit_asc: false,
918                                with_fill: None,
919                            })
920                            .collect()
921                    };
922
923                    // Create window function: ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)
924                    let row_number_func =
925                        Expression::WindowFunction(Box::new(crate::expressions::WindowFunction {
926                            this: Expression::RowNumber(crate::expressions::RowNumber),
927                            over: Over {
928                                partition_by: distinct_cols,
929                                order_by: order_exprs,
930                                frame: None,
931                                window_name: None,
932                                alias: None,
933                            },
934                            keep: None,
935                            inferred_type: None,
936                        }));
937
938                    // Build aliased inner expressions and outer column references
939                    // Inner: SELECT a AS a, b AS b, ROW_NUMBER() OVER (...) AS _row_number
940                    // Outer: SELECT a, b FROM (...)
941                    let mut inner_aliased_exprs = Vec::new();
942                    let mut outer_select_exprs = Vec::new();
943                    for orig_expr in &select.expressions {
944                        match orig_expr {
945                            Expression::Alias(alias) => {
946                                // Already aliased - keep as-is in inner, reference alias in outer
947                                inner_aliased_exprs.push(orig_expr.clone());
948                                outer_select_exprs.push(Expression::Column(Box::new(
949                                    crate::expressions::Column {
950                                        name: alias.alias.clone(),
951                                        table: None,
952                                        join_mark: false,
953                                        trailing_comments: vec![],
954                                        span: None,
955                                        inferred_type: None,
956                                    },
957                                )));
958                            }
959                            Expression::Column(col) => {
960                                // Wrap in alias: a AS a in inner, just a in outer
961                                inner_aliased_exprs.push(Expression::Alias(Box::new(
962                                    crate::expressions::Alias {
963                                        this: orig_expr.clone(),
964                                        alias: col.name.clone(),
965                                        column_aliases: vec![],
966                                        alias_explicit_as: false,
967                                        alias_keyword: None,
968                                        pre_alias_comments: vec![],
969                                        trailing_comments: vec![],
970                                        inferred_type: None,
971                                    },
972                                )));
973                                outer_select_exprs.push(Expression::Column(Box::new(
974                                    crate::expressions::Column {
975                                        name: col.name.clone(),
976                                        table: None,
977                                        join_mark: false,
978                                        trailing_comments: vec![],
979                                        span: None,
980                                        inferred_type: None,
981                                    },
982                                )));
983                            }
984                            _ => {
985                                // Complex expression without alias - include as-is in both
986                                inner_aliased_exprs.push(orig_expr.clone());
987                                outer_select_exprs.push(orig_expr.clone());
988                            }
989                        }
990                    }
991
992                    // Add ROW_NUMBER as aliased expression to inner select list
993                    let row_number_alias_expr =
994                        Expression::Alias(Box::new(crate::expressions::Alias {
995                            this: row_number_func,
996                            alias: row_number_alias.clone(),
997                            column_aliases: vec![],
998                            alias_explicit_as: false,
999                            alias_keyword: None,
1000                            pre_alias_comments: vec![],
1001                            trailing_comments: vec![],
1002                            inferred_type: None,
1003                        }));
1004                    inner_aliased_exprs.push(row_number_alias_expr);
1005
1006                    // Replace inner select's expressions with aliased versions
1007                    select.expressions = inner_aliased_exprs;
1008
1009                    // Remove ORDER BY from inner query (it's now in the window function)
1010                    let _inner_order_by = select.order_by.take();
1011
1012                    // Clear DISTINCT from inner select (DISTINCT ON is replaced by ROW_NUMBER)
1013                    select.distinct = false;
1014
1015                    // Create inner subquery
1016                    let inner_select = Expression::Select(select);
1017                    let subquery = Subquery {
1018                        this: inner_select,
1019                        alias: Some(Identifier::new("_t".to_string())),
1020                        column_aliases: vec![],
1021                        alias_explicit_as: false,
1022                        alias_keyword: None,
1023                        order_by: None,
1024                        limit: None,
1025                        offset: None,
1026                        distribute_by: None,
1027                        sort_by: None,
1028                        cluster_by: None,
1029                        lateral: false,
1030                        modifiers_inside: false,
1031                        trailing_comments: vec![],
1032                        inferred_type: None,
1033                    };
1034
1035                    // Create outer SELECT with WHERE _row_number = 1
1036                    // No ORDER BY on outer query
1037                    let outer_select = Select {
1038                        expressions: outer_select_exprs,
1039                        from: Some(From {
1040                            expressions: vec![Expression::Subquery(Box::new(subquery))],
1041                        }),
1042                        where_clause: Some(Where {
1043                            this: Expression::Eq(Box::new(BinaryOp {
1044                                left: Expression::Column(Box::new(crate::expressions::Column {
1045                                    name: row_number_alias,
1046                                    table: None,
1047                                    join_mark: false,
1048                                    trailing_comments: vec![],
1049                                    span: None,
1050                                    inferred_type: None,
1051                                })),
1052                                right: Expression::Literal(Box::new(Literal::Number(
1053                                    "1".to_string(),
1054                                ))),
1055                                left_comments: vec![],
1056                                operator_comments: vec![],
1057                                trailing_comments: vec![],
1058                                inferred_type: None,
1059                            })),
1060                        }),
1061                        ..Select::new()
1062                    };
1063
1064                    return Ok(Expression::Select(Box::new(outer_select)));
1065                }
1066            }
1067            Ok(Expression::Select(select))
1068        }
1069        other => Ok(other),
1070    }
1071}
1072
1073/// Convert SEMI and ANTI joins into equivalent forms that use EXISTS instead.
1074///
1075/// For dialects that don't support SEMI/ANTI join syntax, this converts:
1076/// - `SELECT * FROM a SEMI JOIN b ON a.x = b.x` → `SELECT * FROM a WHERE EXISTS (SELECT 1 FROM b WHERE a.x = b.x)`
1077/// - `SELECT * FROM a ANTI JOIN b ON a.x = b.x` → `SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a.x = b.x)`
1078///
1079/// Reference: `transforms.py:607-621`
1080pub fn eliminate_semi_and_anti_joins(expr: Expression) -> Result<Expression> {
1081    match expr {
1082        Expression::Select(mut select) => {
1083            let mut new_joins = Vec::new();
1084            let mut extra_where_conditions = Vec::new();
1085
1086            for join in select.joins.drain(..) {
1087                match join.kind {
1088                    JoinKind::Semi | JoinKind::LeftSemi => {
1089                        if let Some(on_condition) = join.on {
1090                            // Create: EXISTS (SELECT 1 FROM join_table WHERE on_condition)
1091                            let subquery_select = Select {
1092                                expressions: vec![Expression::Literal(Box::new(Literal::Number(
1093                                    "1".to_string(),
1094                                )))],
1095                                from: Some(From {
1096                                    expressions: vec![join.this],
1097                                }),
1098                                where_clause: Some(Where { this: on_condition }),
1099                                ..Select::new()
1100                            };
1101
1102                            let exists = Expression::Exists(Box::new(Exists {
1103                                this: Expression::Subquery(Box::new(Subquery {
1104                                    this: Expression::Select(Box::new(subquery_select)),
1105                                    alias: None,
1106                                    column_aliases: vec![],
1107                                    alias_explicit_as: false,
1108                                    alias_keyword: None,
1109                                    order_by: None,
1110                                    limit: None,
1111                                    offset: None,
1112                                    distribute_by: None,
1113                                    sort_by: None,
1114                                    cluster_by: None,
1115                                    lateral: false,
1116                                    modifiers_inside: false,
1117                                    trailing_comments: vec![],
1118                                    inferred_type: None,
1119                                })),
1120                                not: false,
1121                            }));
1122
1123                            extra_where_conditions.push(exists);
1124                        }
1125                    }
1126                    JoinKind::Anti | JoinKind::LeftAnti => {
1127                        if let Some(on_condition) = join.on {
1128                            // Create: NOT EXISTS (SELECT 1 FROM join_table WHERE on_condition)
1129                            let subquery_select = Select {
1130                                expressions: vec![Expression::Literal(Box::new(Literal::Number(
1131                                    "1".to_string(),
1132                                )))],
1133                                from: Some(From {
1134                                    expressions: vec![join.this],
1135                                }),
1136                                where_clause: Some(Where { this: on_condition }),
1137                                ..Select::new()
1138                            };
1139
1140                            // Use Exists with not: true for NOT EXISTS
1141                            let not_exists = Expression::Exists(Box::new(Exists {
1142                                this: Expression::Subquery(Box::new(Subquery {
1143                                    this: Expression::Select(Box::new(subquery_select)),
1144                                    alias: None,
1145                                    column_aliases: vec![],
1146                                    alias_explicit_as: false,
1147                                    alias_keyword: None,
1148                                    order_by: None,
1149                                    limit: None,
1150                                    offset: None,
1151                                    distribute_by: None,
1152                                    sort_by: None,
1153                                    cluster_by: None,
1154                                    lateral: false,
1155                                    modifiers_inside: false,
1156                                    trailing_comments: vec![],
1157                                    inferred_type: None,
1158                                })),
1159                                not: true,
1160                            }));
1161
1162                            extra_where_conditions.push(not_exists);
1163                        }
1164                    }
1165                    _ => {
1166                        // Keep other join types as-is
1167                        new_joins.push(join);
1168                    }
1169                }
1170            }
1171
1172            select.joins = new_joins;
1173
1174            // Add EXISTS conditions to WHERE clause
1175            if !extra_where_conditions.is_empty() {
1176                let combined = extra_where_conditions
1177                    .into_iter()
1178                    .reduce(|acc, cond| {
1179                        Expression::And(Box::new(BinaryOp {
1180                            left: acc,
1181                            right: cond,
1182                            left_comments: vec![],
1183                            operator_comments: vec![],
1184                            trailing_comments: vec![],
1185                            inferred_type: None,
1186                        }))
1187                    })
1188                    .unwrap();
1189
1190                select.where_clause = match select.where_clause {
1191                    Some(Where { this: existing }) => Some(Where {
1192                        this: Expression::And(Box::new(BinaryOp {
1193                            left: existing,
1194                            right: combined,
1195                            left_comments: vec![],
1196                            operator_comments: vec![],
1197                            trailing_comments: vec![],
1198                            inferred_type: None,
1199                        })),
1200                    }),
1201                    None => Some(Where { this: combined }),
1202                };
1203            }
1204
1205            Ok(Expression::Select(select))
1206        }
1207        other => Ok(other),
1208    }
1209}
1210
1211/// Convert FULL OUTER JOIN to a UNION of LEFT and RIGHT OUTER joins.
1212///
1213/// For dialects that don't support FULL OUTER JOIN, this converts:
1214/// ```sql
1215/// SELECT * FROM a FULL OUTER JOIN b ON a.x = b.x
1216/// ```
1217/// To:
1218/// ```sql
1219/// SELECT * FROM a LEFT OUTER JOIN b ON a.x = b.x
1220/// UNION ALL
1221/// SELECT * FROM a RIGHT OUTER JOIN b ON a.x = b.x
1222/// WHERE NOT EXISTS (SELECT 1 FROM a WHERE a.x = b.x)
1223/// ```
1224///
1225/// Note: This transformation currently only works for queries with a single FULL OUTER join.
1226///
1227/// Reference: `transforms.py:624-661`
1228pub fn eliminate_full_outer_join(expr: Expression) -> Result<Expression> {
1229    match expr {
1230        Expression::Select(mut select) => {
1231            // Find FULL OUTER joins
1232            let full_outer_join_idx = select.joins.iter().position(|j| j.kind == JoinKind::Full);
1233
1234            if let Some(idx) = full_outer_join_idx {
1235                // We only handle queries with a single FULL OUTER join
1236                let full_join_count = select
1237                    .joins
1238                    .iter()
1239                    .filter(|j| j.kind == JoinKind::Full)
1240                    .count();
1241                if full_join_count != 1 {
1242                    return Ok(Expression::Select(select));
1243                }
1244
1245                // Clone the query for the right side of the UNION
1246                let mut right_select = select.clone();
1247
1248                // Get the join condition from the FULL OUTER join
1249                let full_join = &select.joins[idx];
1250                let join_condition = full_join.on.clone();
1251
1252                // Left side: convert FULL to LEFT
1253                select.joins[idx].kind = JoinKind::Left;
1254
1255                // Right side: convert FULL to RIGHT and add NOT EXISTS condition
1256                right_select.joins[idx].kind = JoinKind::Right;
1257
1258                // Build NOT EXISTS for the right side to exclude rows that matched
1259                if let (Some(ref from), Some(ref join_cond)) = (&select.from, &join_condition) {
1260                    if !from.expressions.is_empty() {
1261                        let anti_subquery = Expression::Select(Box::new(Select {
1262                            expressions: vec![Expression::Literal(Box::new(Literal::Number(
1263                                "1".to_string(),
1264                            )))],
1265                            from: Some(from.clone()),
1266                            where_clause: Some(Where {
1267                                this: join_cond.clone(),
1268                            }),
1269                            ..Select::new()
1270                        }));
1271
1272                        let not_exists = Expression::Not(Box::new(crate::expressions::UnaryOp {
1273                            inferred_type: None,
1274                            this: Expression::Exists(Box::new(Exists {
1275                                this: Expression::Subquery(Box::new(Subquery {
1276                                    this: anti_subquery,
1277                                    alias: None,
1278                                    column_aliases: vec![],
1279                                    alias_explicit_as: false,
1280                                    alias_keyword: None,
1281                                    order_by: None,
1282                                    limit: None,
1283                                    offset: None,
1284                                    distribute_by: None,
1285                                    sort_by: None,
1286                                    cluster_by: None,
1287                                    lateral: false,
1288                                    modifiers_inside: false,
1289                                    trailing_comments: vec![],
1290                                    inferred_type: None,
1291                                })),
1292                                not: false,
1293                            })),
1294                        }));
1295
1296                        // Add NOT EXISTS to the WHERE clause
1297                        right_select.where_clause = Some(Where {
1298                            this: match right_select.where_clause {
1299                                Some(w) => Expression::And(Box::new(BinaryOp {
1300                                    left: w.this,
1301                                    right: not_exists,
1302                                    left_comments: vec![],
1303                                    operator_comments: vec![],
1304                                    trailing_comments: vec![],
1305                                    inferred_type: None,
1306                                })),
1307                                None => not_exists,
1308                            },
1309                        });
1310                    }
1311                }
1312
1313                // Remove WITH clause from right side (CTEs should only be on left)
1314                right_select.with = None;
1315
1316                // Remove ORDER BY from left side (will be applied after UNION)
1317                let order_by = select.order_by.take();
1318
1319                // Create UNION ALL of left and right
1320                let union = crate::expressions::Union {
1321                    left: Expression::Select(select),
1322                    right: Expression::Select(right_select),
1323                    all: true, // UNION ALL
1324                    distinct: false,
1325                    with: None,
1326                    order_by,
1327                    limit: None,
1328                    offset: None,
1329                    distribute_by: None,
1330                    sort_by: None,
1331                    cluster_by: None,
1332                    by_name: false,
1333                    side: None,
1334                    kind: None,
1335                    corresponding: false,
1336                    strict: false,
1337                    on_columns: Vec::new(),
1338                };
1339
1340                return Ok(Expression::Union(Box::new(union)));
1341            }
1342
1343            Ok(Expression::Select(select))
1344        }
1345        other => Ok(other),
1346    }
1347}
1348
1349/// Move CTEs to the top level of the query.
1350///
1351/// Some dialects (e.g., Hive, T-SQL, Spark prior to version 3) only allow CTEs to be
1352/// defined at the top-level, so for example queries like:
1353///
1354/// ```sql
1355/// SELECT * FROM (WITH t(c) AS (SELECT 1) SELECT * FROM t) AS subq
1356/// ```
1357///
1358/// are invalid in those dialects. This transformation moves all CTEs to the top level.
1359///
1360/// Reference: `transforms.py:664-700`
1361pub fn move_ctes_to_top_level(expr: Expression) -> Result<Expression> {
1362    match expr {
1363        Expression::Select(mut select) => {
1364            // Phase 1: Collect CTEs from nested subqueries (not inside CTE definitions)
1365            let mut collected_ctes: Vec<crate::expressions::Cte> = Vec::new();
1366            let mut has_recursive = false;
1367
1368            collect_nested_ctes(
1369                &Expression::Select(select.clone()),
1370                &mut collected_ctes,
1371                &mut has_recursive,
1372                true,
1373            );
1374
1375            // Phase 2: Flatten CTEs nested inside top-level CTE definitions
1376            // This handles: WITH c AS (WITH b AS (...) SELECT ...) -> WITH b AS (...), c AS (SELECT ...)
1377            let mut cte_body_collected: Vec<(String, Vec<crate::expressions::Cte>)> = Vec::new();
1378            if let Some(ref with) = select.with {
1379                for cte in &with.ctes {
1380                    let mut body_ctes: Vec<crate::expressions::Cte> = Vec::new();
1381                    collect_ctes_from_cte_body(&cte.this, &mut body_ctes, &mut has_recursive);
1382                    if !body_ctes.is_empty() {
1383                        cte_body_collected.push((cte.alias.name.clone(), body_ctes));
1384                    }
1385                }
1386            }
1387
1388            let has_subquery_ctes = !collected_ctes.is_empty();
1389            let has_body_ctes = !cte_body_collected.is_empty();
1390
1391            if has_subquery_ctes || has_body_ctes {
1392                // Strip WITH clauses from inner subqueries
1393                strip_nested_with_clauses(&mut select, true);
1394
1395                // Strip WITH clauses from CTE body definitions
1396                if has_body_ctes {
1397                    if let Some(ref mut with) = select.with {
1398                        for cte in with.ctes.iter_mut() {
1399                            strip_with_from_cte_body(&mut cte.this);
1400                        }
1401                    }
1402                }
1403
1404                let top_with = select.with.get_or_insert_with(|| crate::expressions::With {
1405                    ctes: Vec::new(),
1406                    recursive: false,
1407                    leading_comments: vec![],
1408                    search: None,
1409                });
1410
1411                if has_recursive {
1412                    top_with.recursive = true;
1413                }
1414
1415                // Insert body CTEs before their parent CTE (Python sqlglot behavior)
1416                if has_body_ctes {
1417                    let mut new_ctes: Vec<crate::expressions::Cte> = Vec::new();
1418                    for mut cte in top_with.ctes.drain(..) {
1419                        // Check if this CTE has nested CTEs to insert before it
1420                        if let Some(pos) = cte_body_collected
1421                            .iter()
1422                            .position(|(name, _)| *name == cte.alias.name)
1423                        {
1424                            let (_, mut nested) = cte_body_collected.remove(pos);
1425                            // Strip WITH from each nested CTE's body too
1426                            for nested_cte in nested.iter_mut() {
1427                                strip_with_from_cte_body(&mut nested_cte.this);
1428                            }
1429                            new_ctes.extend(nested);
1430                        }
1431                        // Also strip WITH from the parent CTE's body
1432                        strip_with_from_cte_body(&mut cte.this);
1433                        new_ctes.push(cte);
1434                    }
1435                    top_with.ctes = new_ctes;
1436                }
1437
1438                // Append collected subquery CTEs after existing ones
1439                top_with.ctes.extend(collected_ctes);
1440            }
1441
1442            Ok(Expression::Select(select))
1443        }
1444        other => Ok(other),
1445    }
1446}
1447
1448/// Recursively collect CTEs from within CTE body expressions (for deep nesting)
1449fn collect_ctes_from_cte_body(
1450    expr: &Expression,
1451    collected: &mut Vec<crate::expressions::Cte>,
1452    has_recursive: &mut bool,
1453) {
1454    if let Expression::Select(select) = expr {
1455        if let Some(ref with) = select.with {
1456            if with.recursive {
1457                *has_recursive = true;
1458            }
1459            for cte in &with.ctes {
1460                // Recursively collect from this CTE's body first (depth-first)
1461                collect_ctes_from_cte_body(&cte.this, collected, has_recursive);
1462                // Then add this CTE itself
1463                collected.push(cte.clone());
1464            }
1465        }
1466    }
1467}
1468
1469/// Strip WITH clauses from CTE body expressions
1470fn strip_with_from_cte_body(expr: &mut Expression) {
1471    if let Expression::Select(ref mut select) = expr {
1472        select.with = None;
1473    }
1474}
1475
1476/// Strip WITH clauses from nested subqueries (after hoisting to top level)
1477fn strip_nested_with_clauses(select: &mut Select, _is_top_level: bool) {
1478    // Strip WITH from FROM subqueries
1479    if let Some(ref mut from) = select.from {
1480        for expr in from.expressions.iter_mut() {
1481            strip_with_from_expr(expr);
1482        }
1483    }
1484    // Strip from JOINs
1485    for join in select.joins.iter_mut() {
1486        strip_with_from_expr(&mut join.this);
1487    }
1488    // Strip from select expressions
1489    for expr in select.expressions.iter_mut() {
1490        strip_with_from_expr(expr);
1491    }
1492    // Strip from WHERE
1493    if let Some(ref mut w) = select.where_clause {
1494        strip_with_from_expr(&mut w.this);
1495    }
1496}
1497
1498fn strip_with_from_expr(expr: &mut Expression) {
1499    match expr {
1500        Expression::Subquery(ref mut subquery) => {
1501            strip_with_from_inner_query(&mut subquery.this);
1502        }
1503        Expression::Alias(ref mut alias) => {
1504            strip_with_from_expr(&mut alias.this);
1505        }
1506        Expression::Select(ref mut select) => {
1507            // Strip WITH from this SELECT (it's nested)
1508            select.with = None;
1509            // Recurse into its subqueries
1510            strip_nested_with_clauses(select, false);
1511        }
1512        _ => {}
1513    }
1514}
1515
1516fn strip_with_from_inner_query(expr: &mut Expression) {
1517    if let Expression::Select(ref mut select) = expr {
1518        select.with = None;
1519        strip_nested_with_clauses(select, false);
1520    }
1521}
1522
1523/// Helper to recursively collect CTEs from nested subqueries
1524fn collect_nested_ctes(
1525    expr: &Expression,
1526    collected: &mut Vec<crate::expressions::Cte>,
1527    has_recursive: &mut bool,
1528    is_top_level: bool,
1529) {
1530    match expr {
1531        Expression::Select(select) => {
1532            // If this is not the top level and has a WITH clause, collect its CTEs
1533            if !is_top_level {
1534                if let Some(ref with) = select.with {
1535                    if with.recursive {
1536                        *has_recursive = true;
1537                    }
1538                    collected.extend(with.ctes.clone());
1539                }
1540            }
1541
1542            // Recurse into FROM clause
1543            if let Some(ref from) = select.from {
1544                for expr in &from.expressions {
1545                    collect_nested_ctes(expr, collected, has_recursive, false);
1546                }
1547            }
1548
1549            // Recurse into JOINs
1550            for join in &select.joins {
1551                collect_nested_ctes(&join.this, collected, has_recursive, false);
1552            }
1553
1554            // Recurse into select expressions (for subqueries in SELECT)
1555            for sel_expr in &select.expressions {
1556                collect_nested_ctes(sel_expr, collected, has_recursive, false);
1557            }
1558
1559            // Recurse into WHERE
1560            if let Some(ref where_clause) = select.where_clause {
1561                collect_nested_ctes(&where_clause.this, collected, has_recursive, false);
1562            }
1563        }
1564        Expression::Subquery(subquery) => {
1565            // Process the inner query
1566            collect_nested_ctes(&subquery.this, collected, has_recursive, false);
1567        }
1568        Expression::Alias(alias) => {
1569            collect_nested_ctes(&alias.this, collected, has_recursive, false);
1570        }
1571        // Add more expression types as needed
1572        _ => {}
1573    }
1574}
1575
1576/// Inline window definitions from WINDOW clause.
1577///
1578/// Some dialects don't support named windows. This transform inlines them:
1579///
1580/// ```sql
1581/// SELECT SUM(a) OVER w FROM t WINDOW w AS (PARTITION BY b)
1582/// ```
1583///
1584/// To:
1585///
1586/// ```sql
1587/// SELECT SUM(a) OVER (PARTITION BY b) FROM t
1588/// ```
1589///
1590/// Reference: `transforms.py:975-1003`
1591pub fn eliminate_window_clause(expr: Expression) -> Result<Expression> {
1592    match expr {
1593        Expression::Select(mut select) => {
1594            if let Some(named_windows) = select.windows.take() {
1595                // Build a map of window name -> window spec
1596                let window_map: std::collections::HashMap<String, &Over> = named_windows
1597                    .iter()
1598                    .map(|nw| (nw.name.name.to_lowercase(), &nw.spec))
1599                    .collect();
1600
1601                // Inline window references in the select expressions
1602                select.expressions = select
1603                    .expressions
1604                    .into_iter()
1605                    .map(|e| inline_window_refs(e, &window_map))
1606                    .collect();
1607            }
1608            Ok(Expression::Select(select))
1609        }
1610        other => Ok(other),
1611    }
1612}
1613
1614/// Helper function to inline window references in an expression
1615fn inline_window_refs(
1616    expr: Expression,
1617    window_map: &std::collections::HashMap<String, &Over>,
1618) -> Expression {
1619    match expr {
1620        Expression::WindowFunction(mut wf) => {
1621            // Check if this window references a named window
1622            if let Some(ref name) = wf.over.window_name {
1623                let key = name.name.to_lowercase();
1624                if let Some(named_spec) = window_map.get(&key) {
1625                    // Inherit properties from the named window
1626                    if wf.over.partition_by.is_empty() && !named_spec.partition_by.is_empty() {
1627                        wf.over.partition_by = named_spec.partition_by.clone();
1628                    }
1629                    if wf.over.order_by.is_empty() && !named_spec.order_by.is_empty() {
1630                        wf.over.order_by = named_spec.order_by.clone();
1631                    }
1632                    if wf.over.frame.is_none() && named_spec.frame.is_some() {
1633                        wf.over.frame = named_spec.frame.clone();
1634                    }
1635                    // Clear the window name reference
1636                    wf.over.window_name = None;
1637                }
1638            }
1639            Expression::WindowFunction(wf)
1640        }
1641        Expression::Alias(mut alias) => {
1642            // Recurse into aliased expressions
1643            alias.this = inline_window_refs(alias.this, window_map);
1644            Expression::Alias(alias)
1645        }
1646        // For a complete implementation, we would need to recursively visit all expressions
1647        // that can contain window functions (CASE, subqueries, etc.)
1648        other => other,
1649    }
1650}
1651
1652/// Eliminate Oracle-style (+) join marks by converting to standard JOINs.
1653///
1654/// Oracle uses (+) syntax for outer joins:
1655/// ```sql
1656/// SELECT * FROM a, b WHERE a.x = b.x(+)
1657/// ```
1658///
1659/// This is converted to standard LEFT OUTER JOIN:
1660/// ```sql
1661/// SELECT * FROM a LEFT OUTER JOIN b ON a.x = b.x
1662/// ```
1663///
1664/// Reference: `transforms.py:828-945`
1665pub fn eliminate_join_marks(expr: Expression) -> Result<Expression> {
1666    match expr {
1667        Expression::Select(mut select) => {
1668            // Check if there are any join marks in the WHERE clause
1669            let has_join_marks = select
1670                .where_clause
1671                .as_ref()
1672                .map_or(false, |w| contains_join_mark(&w.this));
1673
1674            if !has_join_marks {
1675                return Ok(Expression::Select(select));
1676            }
1677
1678            // Collect tables from FROM clause
1679            let from_tables: Vec<String> = select
1680                .from
1681                .as_ref()
1682                .map(|f| {
1683                    f.expressions
1684                        .iter()
1685                        .filter_map(|e| get_table_name(e))
1686                        .collect()
1687                })
1688                .unwrap_or_default();
1689
1690            // Extract join conditions and their marked tables from WHERE
1691            let mut join_conditions: std::collections::HashMap<String, Vec<Expression>> =
1692                std::collections::HashMap::new();
1693            let mut remaining_conditions: Vec<Expression> = Vec::new();
1694
1695            if let Some(ref where_clause) = select.where_clause {
1696                extract_join_mark_conditions(
1697                    &where_clause.this,
1698                    &mut join_conditions,
1699                    &mut remaining_conditions,
1700                );
1701            }
1702
1703            // Build new JOINs for each marked table
1704            let mut new_joins = select.joins.clone();
1705            for (table_name, conditions) in join_conditions {
1706                // Find if this table is in FROM or existing JOINs
1707                let table_in_from = from_tables.contains(&table_name);
1708
1709                if table_in_from && !conditions.is_empty() {
1710                    // Create LEFT JOIN with combined conditions
1711                    let combined_condition = conditions.into_iter().reduce(|a, b| {
1712                        Expression::And(Box::new(BinaryOp {
1713                            left: a,
1714                            right: b,
1715                            left_comments: vec![],
1716                            operator_comments: vec![],
1717                            trailing_comments: vec![],
1718                            inferred_type: None,
1719                        }))
1720                    });
1721
1722                    // Find the table in FROM and move it to a JOIN
1723                    if let Some(ref mut from) = select.from {
1724                        if let Some(pos) = from
1725                            .expressions
1726                            .iter()
1727                            .position(|e| get_table_name(e).map_or(false, |n| n == table_name))
1728                        {
1729                            if from.expressions.len() > 1 {
1730                                let join_table = from.expressions.remove(pos);
1731                                new_joins.push(crate::expressions::Join {
1732                                    this: join_table,
1733                                    kind: JoinKind::Left,
1734                                    on: combined_condition,
1735                                    using: vec![],
1736                                    use_inner_keyword: false,
1737                                    use_outer_keyword: true,
1738                                    deferred_condition: false,
1739                                    join_hint: None,
1740                                    match_condition: None,
1741                                    pivots: Vec::new(),
1742                                    comments: Vec::new(),
1743                                    nesting_group: 0,
1744                                    directed: false,
1745                                });
1746                            }
1747                        }
1748                    }
1749                }
1750            }
1751
1752            select.joins = new_joins;
1753
1754            // Update WHERE with remaining conditions
1755            if remaining_conditions.is_empty() {
1756                select.where_clause = None;
1757            } else {
1758                let combined = remaining_conditions.into_iter().reduce(|a, b| {
1759                    Expression::And(Box::new(BinaryOp {
1760                        left: a,
1761                        right: b,
1762                        left_comments: vec![],
1763                        operator_comments: vec![],
1764                        trailing_comments: vec![],
1765                        inferred_type: None,
1766                    }))
1767                });
1768                select.where_clause = combined.map(|c| Where { this: c });
1769            }
1770
1771            // Clear join marks from all columns
1772            clear_join_marks(&mut Expression::Select(select.clone()));
1773
1774            Ok(Expression::Select(select))
1775        }
1776        other => Ok(other),
1777    }
1778}
1779
1780/// Check if an expression contains any columns with join marks
1781fn contains_join_mark(expr: &Expression) -> bool {
1782    match expr {
1783        Expression::Column(col) => col.join_mark,
1784        Expression::And(op) | Expression::Or(op) => {
1785            contains_join_mark(&op.left) || contains_join_mark(&op.right)
1786        }
1787        Expression::Eq(op)
1788        | Expression::Neq(op)
1789        | Expression::Lt(op)
1790        | Expression::Lte(op)
1791        | Expression::Gt(op)
1792        | Expression::Gte(op) => contains_join_mark(&op.left) || contains_join_mark(&op.right),
1793        Expression::Not(op) => contains_join_mark(&op.this),
1794        _ => false,
1795    }
1796}
1797
1798/// Get table name from a table expression
1799fn get_table_name(expr: &Expression) -> Option<String> {
1800    match expr {
1801        Expression::Table(t) => Some(t.name.name.clone()),
1802        Expression::Alias(a) => Some(a.alias.name.clone()),
1803        _ => None,
1804    }
1805}
1806
1807/// Extract join mark conditions from WHERE clause
1808fn extract_join_mark_conditions(
1809    expr: &Expression,
1810    join_conditions: &mut std::collections::HashMap<String, Vec<Expression>>,
1811    remaining: &mut Vec<Expression>,
1812) {
1813    match expr {
1814        Expression::And(op) => {
1815            extract_join_mark_conditions(&op.left, join_conditions, remaining);
1816            extract_join_mark_conditions(&op.right, join_conditions, remaining);
1817        }
1818        _ => {
1819            if let Some(table) = get_join_mark_table(expr) {
1820                join_conditions
1821                    .entry(table)
1822                    .or_insert_with(Vec::new)
1823                    .push(expr.clone());
1824            } else {
1825                remaining.push(expr.clone());
1826            }
1827        }
1828    }
1829}
1830
1831/// Get the table name of a column with join mark in an expression
1832fn get_join_mark_table(expr: &Expression) -> Option<String> {
1833    match expr {
1834        Expression::Eq(op)
1835        | Expression::Neq(op)
1836        | Expression::Lt(op)
1837        | Expression::Lte(op)
1838        | Expression::Gt(op)
1839        | Expression::Gte(op) => {
1840            // Check both sides for join mark columns
1841            if let Expression::Column(col) = &op.left {
1842                if col.join_mark {
1843                    return col.table.as_ref().map(|t| t.name.clone());
1844                }
1845            }
1846            if let Expression::Column(col) = &op.right {
1847                if col.join_mark {
1848                    return col.table.as_ref().map(|t| t.name.clone());
1849                }
1850            }
1851            None
1852        }
1853        _ => None,
1854    }
1855}
1856
1857/// Clear join marks from all columns in an expression
1858fn clear_join_marks(expr: &mut Expression) {
1859    match expr {
1860        Expression::Column(col) => col.join_mark = false,
1861        Expression::Select(select) => {
1862            if let Some(ref mut w) = select.where_clause {
1863                clear_join_marks(&mut w.this);
1864            }
1865            for sel_expr in &mut select.expressions {
1866                clear_join_marks(sel_expr);
1867            }
1868        }
1869        Expression::And(op) | Expression::Or(op) => {
1870            clear_join_marks(&mut op.left);
1871            clear_join_marks(&mut op.right);
1872        }
1873        Expression::Eq(op)
1874        | Expression::Neq(op)
1875        | Expression::Lt(op)
1876        | Expression::Lte(op)
1877        | Expression::Gt(op)
1878        | Expression::Gte(op) => {
1879            clear_join_marks(&mut op.left);
1880            clear_join_marks(&mut op.right);
1881        }
1882        _ => {}
1883    }
1884}
1885
1886/// Add column names to recursive CTE definitions.
1887///
1888/// Uses projection output names in recursive CTE definitions to define the CTEs' columns.
1889/// This is required by some dialects that need explicit column names in recursive CTEs.
1890///
1891/// Reference: `transforms.py:576-592`
1892pub fn add_recursive_cte_column_names(expr: Expression) -> Result<Expression> {
1893    match expr {
1894        Expression::Select(mut select) => {
1895            if let Some(ref mut with) = select.with {
1896                if with.recursive {
1897                    let mut counter = 0;
1898                    for cte in &mut with.ctes {
1899                        if cte.columns.is_empty() {
1900                            // Try to get column names from the CTE's SELECT
1901                            if let Expression::Select(ref cte_select) = cte.this {
1902                                let names: Vec<Identifier> = cte_select
1903                                    .expressions
1904                                    .iter()
1905                                    .map(|e| match e {
1906                                        Expression::Alias(a) => a.alias.clone(),
1907                                        Expression::Column(c) => c.name.clone(),
1908                                        _ => {
1909                                            counter += 1;
1910                                            Identifier::new(format!("_c_{}", counter))
1911                                        }
1912                                    })
1913                                    .collect();
1914                                cte.columns = names;
1915                            }
1916                        }
1917                    }
1918                }
1919            }
1920            Ok(Expression::Select(select))
1921        }
1922        other => Ok(other),
1923    }
1924}
1925
1926/// Convert epoch string in CAST to timestamp literal.
1927///
1928/// Replaces `CAST('epoch' AS TIMESTAMP)` with `CAST('1970-01-01 00:00:00' AS TIMESTAMP)`
1929/// for dialects that don't support the 'epoch' keyword.
1930///
1931/// Reference: `transforms.py:595-604`
1932pub fn epoch_cast_to_ts(expr: Expression) -> Result<Expression> {
1933    match expr {
1934        Expression::Cast(mut cast) => {
1935            if let Expression::Literal(ref lit) = cast.this {
1936                if let Literal::String(ref s) = lit.as_ref() {
1937                    if s.to_lowercase() == "epoch" {
1938                        if is_temporal_type(&cast.to) {
1939                            cast.this = Expression::Literal(Box::new(Literal::String(
1940                                "1970-01-01 00:00:00".to_string(),
1941                            )));
1942                        }
1943                    }
1944                }
1945            }
1946            Ok(Expression::Cast(cast))
1947        }
1948        Expression::TryCast(mut try_cast) => {
1949            if let Expression::Literal(ref lit) = try_cast.this {
1950                if let Literal::String(ref s) = lit.as_ref() {
1951                    if s.to_lowercase() == "epoch" {
1952                        if is_temporal_type(&try_cast.to) {
1953                            try_cast.this = Expression::Literal(Box::new(Literal::String(
1954                                "1970-01-01 00:00:00".to_string(),
1955                            )));
1956                        }
1957                    }
1958                }
1959            }
1960            Ok(Expression::TryCast(try_cast))
1961        }
1962        other => Ok(other),
1963    }
1964}
1965
1966/// Check if a DataType is a temporal type (DATE, TIMESTAMP, etc.)
1967fn is_temporal_type(dt: &DataType) -> bool {
1968    matches!(
1969        dt,
1970        DataType::Date | DataType::Timestamp { .. } | DataType::Time { .. }
1971    )
1972}
1973
1974/// Ensure boolean values in conditions.
1975///
1976/// Converts numeric values used in conditions into explicit boolean expressions.
1977/// For dialects that require explicit booleans in WHERE clauses.
1978///
1979/// Converts:
1980/// ```sql
1981/// WHERE column
1982/// ```
1983/// To:
1984/// ```sql
1985/// WHERE column <> 0
1986/// ```
1987///
1988/// And:
1989/// ```sql
1990/// WHERE 1
1991/// ```
1992/// To:
1993/// ```sql
1994/// WHERE 1 <> 0
1995/// ```
1996///
1997/// Reference: `transforms.py:703-721`
1998pub fn ensure_bools(expr: Expression) -> Result<Expression> {
1999    let expr = ensure_bools_in_value_context(expr);
2000
2001    Ok(match expr {
2002        // Top-level AND/OR/NOT expressions also need ensure_bools processing
2003        Expression::And(_) | Expression::Or(_) | Expression::Not(_) => ensure_bool_condition(expr),
2004        other => other,
2005    })
2006}
2007
2008/// Recursively walk the expression tree to find Case expressions and apply
2009/// ensure_bool_condition to their WHEN conditions. This ensures that
2010/// `CASE WHEN TRUE` becomes `CASE WHEN (1 = 1)` etc.
2011fn ensure_bools_in_value_context(expr: Expression) -> Expression {
2012    match expr {
2013        Expression::Case(mut case) => {
2014            case.whens = case
2015                .whens
2016                .into_iter()
2017                .map(|(condition, result)| {
2018                    let new_condition =
2019                        ensure_bool_condition(ensure_bools_in_value_context(condition));
2020                    let new_result = ensure_bools_in_value_context(result);
2021                    (new_condition, new_result)
2022                })
2023                .collect();
2024            if let Some(else_expr) = case.else_ {
2025                case.else_ = Some(ensure_bools_in_value_context(else_expr));
2026            }
2027            Expression::Case(Box::new(*case))
2028        }
2029        Expression::Select(select) => Expression::Select(Box::new(ensure_bools_in_select(*select))),
2030        Expression::Subquery(mut subquery) => {
2031            subquery.this = ensure_bools_in_value_context(subquery.this);
2032            Expression::Subquery(subquery)
2033        }
2034        Expression::Union(mut union) => {
2035            let left = std::mem::replace(&mut union.left, Expression::null());
2036            let right = std::mem::replace(&mut union.right, Expression::null());
2037            union.left = ensure_bools_in_value_context(left);
2038            union.right = ensure_bools_in_value_context(right);
2039            if let Some(with) = union.with.take() {
2040                union.with = Some(ensure_bools_in_with(with));
2041            }
2042            Expression::Union(union)
2043        }
2044        Expression::Intersect(mut intersect) => {
2045            let left = std::mem::replace(&mut intersect.left, Expression::null());
2046            let right = std::mem::replace(&mut intersect.right, Expression::null());
2047            intersect.left = ensure_bools_in_value_context(left);
2048            intersect.right = ensure_bools_in_value_context(right);
2049            if let Some(with) = intersect.with.take() {
2050                intersect.with = Some(ensure_bools_in_with(with));
2051            }
2052            Expression::Intersect(intersect)
2053        }
2054        Expression::Except(mut except) => {
2055            let left = std::mem::replace(&mut except.left, Expression::null());
2056            let right = std::mem::replace(&mut except.right, Expression::null());
2057            except.left = ensure_bools_in_value_context(left);
2058            except.right = ensure_bools_in_value_context(right);
2059            if let Some(with) = except.with.take() {
2060                except.with = Some(ensure_bools_in_with(with));
2061            }
2062            Expression::Except(except)
2063        }
2064        Expression::Alias(mut alias) => {
2065            alias.this = ensure_bools_in_value_context(alias.this);
2066            Expression::Alias(alias)
2067        }
2068        Expression::Paren(mut paren) => {
2069            paren.this = ensure_bools_in_value_context(paren.this);
2070            Expression::Paren(paren)
2071        }
2072        other => other,
2073    }
2074}
2075
2076fn ensure_bools_in_select(mut select: Select) -> Select {
2077    select.expressions = select
2078        .expressions
2079        .into_iter()
2080        .map(ensure_bools_in_value_context)
2081        .collect();
2082
2083    if let Some(from) = select.from.take() {
2084        select.from = Some(crate::expressions::From {
2085            expressions: from
2086                .expressions
2087                .into_iter()
2088                .map(ensure_bools_in_value_context)
2089                .collect(),
2090        });
2091    }
2092
2093    select.joins = select.joins.into_iter().map(ensure_bools_in_join).collect();
2094
2095    if let Some(mut where_clause) = select.where_clause.take() {
2096        where_clause.this = ensure_bool_condition(ensure_bools_in_value_context(where_clause.this));
2097        select.where_clause = Some(where_clause);
2098    }
2099
2100    if let Some(mut having) = select.having.take() {
2101        having.this = ensure_bool_condition(ensure_bools_in_value_context(having.this));
2102        select.having = Some(having);
2103    }
2104
2105    if let Some(with) = select.with.take() {
2106        select.with = Some(ensure_bools_in_with(with));
2107    }
2108
2109    select
2110}
2111
2112fn ensure_bools_in_join(mut join: Join) -> Join {
2113    join.this = ensure_bools_in_value_context(join.this);
2114
2115    if let Some(on) = join.on.take() {
2116        join.on = Some(ensure_bool_condition(ensure_bools_in_value_context(on)));
2117    }
2118
2119    join.pivots = join
2120        .pivots
2121        .into_iter()
2122        .map(ensure_bools_in_value_context)
2123        .collect();
2124
2125    join
2126}
2127
2128fn ensure_bools_in_with(mut with: With) -> With {
2129    with.ctes = with
2130        .ctes
2131        .into_iter()
2132        .map(|mut cte| {
2133            cte.this = ensure_bools_in_value_context(cte.this);
2134            cte
2135        })
2136        .collect();
2137    with
2138}
2139
2140/// Helper to check if an expression is inherently boolean (returns a boolean value).
2141/// Inherently boolean expressions include comparisons, predicates, logical operators, etc.
2142fn is_boolean_expression(expr: &Expression) -> bool {
2143    matches!(
2144        expr,
2145        Expression::Eq(_)
2146            | Expression::Neq(_)
2147            | Expression::Lt(_)
2148            | Expression::Lte(_)
2149            | Expression::Gt(_)
2150            | Expression::Gte(_)
2151            | Expression::Is(_)
2152            | Expression::IsNull(_)
2153            | Expression::IsTrue(_)
2154            | Expression::IsFalse(_)
2155            | Expression::Like(_)
2156            | Expression::ILike(_)
2157            | Expression::SimilarTo(_)
2158            | Expression::Glob(_)
2159            | Expression::RegexpLike(_)
2160            | Expression::In(_)
2161            | Expression::Between(_)
2162            | Expression::Exists(_)
2163            | Expression::And(_)
2164            | Expression::Or(_)
2165            | Expression::Not(_)
2166            | Expression::Any(_)
2167            | Expression::All(_)
2168            | Expression::EqualNull(_)
2169    )
2170}
2171
2172/// Helper to wrap a non-boolean expression with `<> 0`
2173fn wrap_neq_zero(expr: Expression) -> Expression {
2174    Expression::Neq(Box::new(BinaryOp {
2175        left: expr,
2176        right: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
2177        left_comments: vec![],
2178        operator_comments: vec![],
2179        trailing_comments: vec![],
2180        inferred_type: None,
2181    }))
2182}
2183
2184/// Helper to convert a condition expression to ensure it's boolean.
2185///
2186/// In TSQL, conditions in WHERE/HAVING must be boolean expressions.
2187/// Non-boolean expressions (columns, literals, casts, function calls, etc.)
2188/// are wrapped with `<> 0`. Boolean literals are converted to `(1 = 1)` or `(1 = 0)`.
2189fn ensure_bool_condition(expr: Expression) -> Expression {
2190    match expr {
2191        // For AND/OR, recursively process children
2192        Expression::And(op) => {
2193            let new_op = BinaryOp {
2194                left: ensure_bool_condition(op.left.clone()),
2195                right: ensure_bool_condition(op.right.clone()),
2196                left_comments: op.left_comments.clone(),
2197                operator_comments: op.operator_comments.clone(),
2198                trailing_comments: op.trailing_comments.clone(),
2199                inferred_type: None,
2200            };
2201            Expression::And(Box::new(new_op))
2202        }
2203        Expression::Or(op) => {
2204            let new_op = BinaryOp {
2205                left: ensure_bool_condition(op.left.clone()),
2206                right: ensure_bool_condition(op.right.clone()),
2207                left_comments: op.left_comments.clone(),
2208                operator_comments: op.operator_comments.clone(),
2209                trailing_comments: op.trailing_comments.clone(),
2210                inferred_type: None,
2211            };
2212            Expression::Or(Box::new(new_op))
2213        }
2214        // For NOT, recursively process the inner expression
2215        Expression::Not(op) => Expression::Not(Box::new(crate::expressions::UnaryOp {
2216            this: ensure_bool_condition(op.this.clone()),
2217            inferred_type: None,
2218        })),
2219        // For Paren, recurse into inner expression
2220        Expression::Paren(paren) => Expression::Paren(Box::new(crate::expressions::Paren {
2221            this: ensure_bool_condition(paren.this.clone()),
2222            trailing_comments: paren.trailing_comments.clone(),
2223        })),
2224        // Boolean literals: true -> (1 = 1), false -> (1 = 0)
2225        Expression::Boolean(BooleanLiteral { value: true }) => {
2226            Expression::Paren(Box::new(crate::expressions::Paren {
2227                this: Expression::Eq(Box::new(BinaryOp {
2228                    left: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2229                    right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2230                    left_comments: vec![],
2231                    operator_comments: vec![],
2232                    trailing_comments: vec![],
2233                    inferred_type: None,
2234                })),
2235                trailing_comments: vec![],
2236            }))
2237        }
2238        Expression::Boolean(BooleanLiteral { value: false }) => {
2239            Expression::Paren(Box::new(crate::expressions::Paren {
2240                this: Expression::Eq(Box::new(BinaryOp {
2241                    left: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2242                    right: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
2243                    left_comments: vec![],
2244                    operator_comments: vec![],
2245                    trailing_comments: vec![],
2246                    inferred_type: None,
2247                })),
2248                trailing_comments: vec![],
2249            }))
2250        }
2251        // Already boolean expressions pass through unchanged
2252        ref e if is_boolean_expression(e) => expr,
2253        // Everything else (Column, Identifier, Cast, Literal::Number, function calls, etc.)
2254        // gets wrapped with <> 0
2255        _ => wrap_neq_zero(expr),
2256    }
2257}
2258
2259/// Remove table qualifiers from column references.
2260///
2261/// Converts `table.column` to just `column` throughout the expression tree.
2262///
2263/// Reference: `transforms.py:724-730`
2264pub fn unqualify_columns(expr: Expression) -> Result<Expression> {
2265    Ok(unqualify_columns_recursive(expr))
2266}
2267
2268/// Recursively remove table qualifiers from column references
2269fn unqualify_columns_recursive(expr: Expression) -> Expression {
2270    match expr {
2271        Expression::Column(mut col) => {
2272            col.table = None;
2273            Expression::Column(col)
2274        }
2275        Expression::Select(mut select) => {
2276            select.expressions = select
2277                .expressions
2278                .into_iter()
2279                .map(unqualify_columns_recursive)
2280                .collect();
2281            if let Some(ref mut where_clause) = select.where_clause {
2282                where_clause.this = unqualify_columns_recursive(where_clause.this.clone());
2283            }
2284            if let Some(ref mut having) = select.having {
2285                having.this = unqualify_columns_recursive(having.this.clone());
2286            }
2287            if let Some(ref mut group_by) = select.group_by {
2288                group_by.expressions = group_by
2289                    .expressions
2290                    .iter()
2291                    .cloned()
2292                    .map(unqualify_columns_recursive)
2293                    .collect();
2294            }
2295            if let Some(ref mut order_by) = select.order_by {
2296                order_by.expressions = order_by
2297                    .expressions
2298                    .iter()
2299                    .map(|o| crate::expressions::Ordered {
2300                        this: unqualify_columns_recursive(o.this.clone()),
2301                        desc: o.desc,
2302                        nulls_first: o.nulls_first,
2303                        explicit_asc: o.explicit_asc,
2304                        with_fill: o.with_fill.clone(),
2305                    })
2306                    .collect();
2307            }
2308            for join in &mut select.joins {
2309                if let Some(ref mut on) = join.on {
2310                    *on = unqualify_columns_recursive(on.clone());
2311                }
2312            }
2313            Expression::Select(select)
2314        }
2315        Expression::Alias(mut alias) => {
2316            alias.this = unqualify_columns_recursive(alias.this);
2317            Expression::Alias(alias)
2318        }
2319        // Binary operations
2320        Expression::And(op) => Expression::And(Box::new(unqualify_binary_op(*op))),
2321        Expression::Or(op) => Expression::Or(Box::new(unqualify_binary_op(*op))),
2322        Expression::Eq(op) => Expression::Eq(Box::new(unqualify_binary_op(*op))),
2323        Expression::Neq(op) => Expression::Neq(Box::new(unqualify_binary_op(*op))),
2324        Expression::Lt(op) => Expression::Lt(Box::new(unqualify_binary_op(*op))),
2325        Expression::Lte(op) => Expression::Lte(Box::new(unqualify_binary_op(*op))),
2326        Expression::Gt(op) => Expression::Gt(Box::new(unqualify_binary_op(*op))),
2327        Expression::Gte(op) => Expression::Gte(Box::new(unqualify_binary_op(*op))),
2328        Expression::Add(op) => Expression::Add(Box::new(unqualify_binary_op(*op))),
2329        Expression::Sub(op) => Expression::Sub(Box::new(unqualify_binary_op(*op))),
2330        Expression::Mul(op) => Expression::Mul(Box::new(unqualify_binary_op(*op))),
2331        Expression::Div(op) => Expression::Div(Box::new(unqualify_binary_op(*op))),
2332        // Functions
2333        Expression::Function(mut func) => {
2334            func.args = func
2335                .args
2336                .into_iter()
2337                .map(unqualify_columns_recursive)
2338                .collect();
2339            Expression::Function(func)
2340        }
2341        Expression::AggregateFunction(mut func) => {
2342            func.args = func
2343                .args
2344                .into_iter()
2345                .map(unqualify_columns_recursive)
2346                .collect();
2347            Expression::AggregateFunction(func)
2348        }
2349        Expression::Case(mut case) => {
2350            case.whens = case
2351                .whens
2352                .into_iter()
2353                .map(|(cond, result)| {
2354                    (
2355                        unqualify_columns_recursive(cond),
2356                        unqualify_columns_recursive(result),
2357                    )
2358                })
2359                .collect();
2360            if let Some(else_expr) = case.else_ {
2361                case.else_ = Some(unqualify_columns_recursive(else_expr));
2362            }
2363            Expression::Case(case)
2364        }
2365        // Other expressions pass through unchanged
2366        other => other,
2367    }
2368}
2369
2370/// Helper to unqualify columns in a binary operation
2371fn unqualify_binary_op(mut op: BinaryOp) -> BinaryOp {
2372    op.left = unqualify_columns_recursive(op.left);
2373    op.right = unqualify_columns_recursive(op.right);
2374    op
2375}
2376
2377/// Convert UNNEST(GENERATE_DATE_ARRAY(...)) to recursive CTE.
2378///
2379/// For dialects that don't support GENERATE_DATE_ARRAY, this converts:
2380/// ```sql
2381/// SELECT * FROM UNNEST(GENERATE_DATE_ARRAY('2024-01-01', '2024-01-31', INTERVAL 1 DAY)) AS d(date_value)
2382/// ```
2383/// To a recursive CTE:
2384/// ```sql
2385/// WITH RECURSIVE _generated_dates(date_value) AS (
2386///     SELECT CAST('2024-01-01' AS DATE) AS date_value
2387///     UNION ALL
2388///     SELECT CAST(DATE_ADD(date_value, 1, DAY) AS DATE)
2389///     FROM _generated_dates
2390///     WHERE CAST(DATE_ADD(date_value, 1, DAY) AS DATE) <= CAST('2024-01-31' AS DATE)
2391/// )
2392/// SELECT date_value FROM _generated_dates
2393/// ```
2394///
2395/// Reference: `transforms.py:68-122`
2396pub fn unnest_generate_date_array_using_recursive_cte(expr: Expression) -> Result<Expression> {
2397    match expr {
2398        Expression::Select(mut select) => {
2399            let mut cte_count = 0;
2400            let mut new_ctes: Vec<crate::expressions::Cte> = Vec::new();
2401
2402            // Process existing CTE bodies first (to handle CTE-wrapped GENERATE_DATE_ARRAY)
2403            if let Some(ref mut with) = select.with {
2404                for cte in &mut with.ctes {
2405                    process_expression_for_gda(&mut cte.this, &mut cte_count, &mut new_ctes);
2406                }
2407            }
2408
2409            // Process FROM clause
2410            if let Some(ref mut from) = select.from {
2411                for table_expr in &mut from.expressions {
2412                    if let Some((cte, replacement)) =
2413                        try_convert_generate_date_array(table_expr, &mut cte_count)
2414                    {
2415                        new_ctes.push(cte);
2416                        *table_expr = replacement;
2417                    }
2418                }
2419            }
2420
2421            // Process JOINs
2422            for join in &mut select.joins {
2423                if let Some((cte, replacement)) =
2424                    try_convert_generate_date_array(&join.this, &mut cte_count)
2425                {
2426                    new_ctes.push(cte);
2427                    join.this = replacement;
2428                }
2429            }
2430
2431            // Add collected CTEs to the WITH clause
2432            if !new_ctes.is_empty() {
2433                let with_clause = select.with.get_or_insert_with(|| crate::expressions::With {
2434                    ctes: Vec::new(),
2435                    recursive: true, // Recursive CTEs
2436                    leading_comments: vec![],
2437                    search: None,
2438                });
2439                with_clause.recursive = true;
2440
2441                // Prepend new CTEs before existing ones
2442                let mut all_ctes = new_ctes;
2443                all_ctes.append(&mut with_clause.ctes);
2444                with_clause.ctes = all_ctes;
2445            }
2446
2447            Ok(Expression::Select(select))
2448        }
2449        other => Ok(other),
2450    }
2451}
2452
2453/// Recursively process an expression tree to find and convert UNNEST(GENERATE_DATE_ARRAY)
2454/// inside CTE bodies, subqueries, etc.
2455fn process_expression_for_gda(
2456    expr: &mut Expression,
2457    cte_count: &mut usize,
2458    new_ctes: &mut Vec<crate::expressions::Cte>,
2459) {
2460    match expr {
2461        Expression::Select(ref mut select) => {
2462            // Process FROM clause
2463            if let Some(ref mut from) = select.from {
2464                for table_expr in &mut from.expressions {
2465                    if let Some((cte, replacement)) =
2466                        try_convert_generate_date_array(table_expr, cte_count)
2467                    {
2468                        new_ctes.push(cte);
2469                        *table_expr = replacement;
2470                    }
2471                }
2472            }
2473            // Process JOINs
2474            for join in &mut select.joins {
2475                if let Some((cte, replacement)) =
2476                    try_convert_generate_date_array(&join.this, cte_count)
2477                {
2478                    new_ctes.push(cte);
2479                    join.this = replacement;
2480                }
2481            }
2482        }
2483        Expression::Union(ref mut u) => {
2484            process_expression_for_gda(&mut u.left, cte_count, new_ctes);
2485            process_expression_for_gda(&mut u.right, cte_count, new_ctes);
2486        }
2487        Expression::Subquery(ref mut sq) => {
2488            process_expression_for_gda(&mut sq.this, cte_count, new_ctes);
2489        }
2490        _ => {}
2491    }
2492}
2493
2494/// Try to convert an UNNEST(GENERATE_DATE_ARRAY(...)) to a recursive CTE reference.
2495/// `column_name_override` allows the caller to specify a custom column name (from alias).
2496fn try_convert_generate_date_array(
2497    expr: &Expression,
2498    cte_count: &mut usize,
2499) -> Option<(crate::expressions::Cte, Expression)> {
2500    try_convert_generate_date_array_with_name(expr, cte_count, None)
2501}
2502
2503fn try_convert_generate_date_array_with_name(
2504    expr: &Expression,
2505    cte_count: &mut usize,
2506    column_name_override: Option<&str>,
2507) -> Option<(crate::expressions::Cte, Expression)> {
2508    // Helper: extract (start, end, step) from GENERATE_DATE_ARRAY/GenerateSeries variants
2509    fn extract_gda_args(
2510        inner: &Expression,
2511    ) -> Option<(&Expression, &Expression, Option<&Expression>)> {
2512        match inner {
2513            Expression::GenerateDateArray(gda) => {
2514                let start = gda.start.as_ref()?;
2515                let end = gda.end.as_ref()?;
2516                let step = gda.step.as_deref();
2517                Some((start, end, step))
2518            }
2519            Expression::GenerateSeries(gs) => {
2520                let start = gs.start.as_deref()?;
2521                let end = gs.end.as_deref()?;
2522                let step = gs.step.as_deref();
2523                Some((start, end, step))
2524            }
2525            Expression::Function(f) if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") => {
2526                if f.args.len() >= 2 {
2527                    let start = &f.args[0];
2528                    let end = &f.args[1];
2529                    let step = f.args.get(2);
2530                    Some((start, end, step))
2531                } else {
2532                    None
2533                }
2534            }
2535            _ => None,
2536        }
2537    }
2538
2539    // Look for UNNEST containing GENERATE_DATE_ARRAY
2540    if let Expression::Unnest(unnest) = expr {
2541        if let Some((start, end, step_opt)) = extract_gda_args(&unnest.this) {
2542            let start = start;
2543            let end = end;
2544            let step: Option<&Expression> = step_opt;
2545
2546            // Generate CTE name
2547            let cte_name = if *cte_count == 0 {
2548                "_generated_dates".to_string()
2549            } else {
2550                format!("_generated_dates_{}", cte_count)
2551            };
2552            *cte_count += 1;
2553
2554            let column_name =
2555                Identifier::new(column_name_override.unwrap_or("date_value").to_string());
2556
2557            // Helper: wrap expression in CAST(... AS DATE) unless already a date literal or CAST to DATE
2558            let cast_to_date = |expr: &Expression| -> Expression {
2559                match expr {
2560                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Date(_)) => {
2561                        // DATE '...' -> convert to CAST('...' AS DATE) to match expected output
2562                        if let Expression::Literal(lit) = expr {
2563                            if let Literal::Date(d) = lit.as_ref() {
2564                                Expression::Cast(Box::new(Cast {
2565                                    this: Expression::Literal(Box::new(Literal::String(d.clone()))),
2566                                    to: DataType::Date,
2567                                    trailing_comments: vec![],
2568                                    double_colon_syntax: false,
2569                                    format: None,
2570                                    default: None,
2571                                    inferred_type: None,
2572                                }))
2573                            } else {
2574                                expr.clone()
2575                            }
2576                        } else {
2577                            unreachable!()
2578                        }
2579                    }
2580                    Expression::Cast(c) if matches!(c.to, DataType::Date) => expr.clone(),
2581                    _ => Expression::Cast(Box::new(Cast {
2582                        this: expr.clone(),
2583                        to: DataType::Date,
2584                        trailing_comments: vec![],
2585                        double_colon_syntax: false,
2586                        format: None,
2587                        default: None,
2588                        inferred_type: None,
2589                    })),
2590                }
2591            };
2592
2593            // Build base case: SELECT CAST(start AS DATE) AS date_value
2594            let base_select = Select {
2595                expressions: vec![Expression::Alias(Box::new(crate::expressions::Alias {
2596                    this: cast_to_date(start),
2597                    alias: column_name.clone(),
2598                    column_aliases: vec![],
2599                    alias_explicit_as: false,
2600                    alias_keyword: None,
2601                    pre_alias_comments: vec![],
2602                    trailing_comments: vec![],
2603                    inferred_type: None,
2604                }))],
2605                ..Select::new()
2606            };
2607
2608            // Normalize interval: convert String("1") -> Number("1") so it generates without quotes
2609            let normalize_interval = |expr: &Expression| -> Expression {
2610                if let Expression::Interval(ref iv) = expr {
2611                    let mut iv_clone = iv.as_ref().clone();
2612                    if let Some(Expression::Literal(ref lit)) = iv_clone.this {
2613                        if let Literal::String(ref s) = lit.as_ref() {
2614                            // Convert numeric strings to Number literals for unquoted output
2615                            if s.parse::<f64>().is_ok() {
2616                                iv_clone.this =
2617                                    Some(Expression::Literal(Box::new(Literal::Number(s.clone()))));
2618                            }
2619                        }
2620                    }
2621                    Expression::Interval(Box::new(iv_clone))
2622                } else {
2623                    expr.clone()
2624                }
2625            };
2626
2627            // Build recursive case: DateAdd(date_value, count, unit) from CTE where result <= end
2628            // Extract interval unit and count from step expression
2629            let normalized_step = step.map(|s| normalize_interval(s)).unwrap_or_else(|| {
2630                Expression::Interval(Box::new(crate::expressions::Interval {
2631                    this: Some(Expression::Literal(Box::new(Literal::Number(
2632                        "1".to_string(),
2633                    )))),
2634                    unit: Some(crate::expressions::IntervalUnitSpec::Simple {
2635                        unit: crate::expressions::IntervalUnit::Day,
2636                        use_plural: false,
2637                    }),
2638                }))
2639            });
2640
2641            // Extract unit and count from interval expression to build DateAddFunc
2642            let (add_unit, add_count) = extract_interval_unit_and_count(&normalized_step);
2643
2644            let date_add_expr = Expression::DateAdd(Box::new(crate::expressions::DateAddFunc {
2645                this: Expression::Column(Box::new(crate::expressions::Column {
2646                    name: column_name.clone(),
2647                    table: None,
2648                    join_mark: false,
2649                    trailing_comments: vec![],
2650                    span: None,
2651                    inferred_type: None,
2652                })),
2653                interval: add_count,
2654                unit: add_unit,
2655            }));
2656
2657            let cast_date_add = Expression::Cast(Box::new(Cast {
2658                this: date_add_expr.clone(),
2659                to: DataType::Date,
2660                trailing_comments: vec![],
2661                double_colon_syntax: false,
2662                format: None,
2663                default: None,
2664                inferred_type: None,
2665            }));
2666
2667            let recursive_select = Select {
2668                expressions: vec![cast_date_add.clone()],
2669                from: Some(From {
2670                    expressions: vec![Expression::Table(Box::new(
2671                        crate::expressions::TableRef::new(&cte_name),
2672                    ))],
2673                }),
2674                where_clause: Some(Where {
2675                    this: Expression::Lte(Box::new(BinaryOp {
2676                        left: cast_date_add,
2677                        right: cast_to_date(end),
2678                        left_comments: vec![],
2679                        operator_comments: vec![],
2680                        trailing_comments: vec![],
2681                        inferred_type: None,
2682                    })),
2683                }),
2684                ..Select::new()
2685            };
2686
2687            // Build UNION ALL of base and recursive
2688            let union = crate::expressions::Union {
2689                left: Expression::Select(Box::new(base_select)),
2690                right: Expression::Select(Box::new(recursive_select)),
2691                all: true, // UNION ALL
2692                distinct: false,
2693                with: None,
2694                order_by: None,
2695                limit: None,
2696                offset: None,
2697                distribute_by: None,
2698                sort_by: None,
2699                cluster_by: None,
2700                by_name: false,
2701                side: None,
2702                kind: None,
2703                corresponding: false,
2704                strict: false,
2705                on_columns: Vec::new(),
2706            };
2707
2708            // Create CTE
2709            let cte = crate::expressions::Cte {
2710                this: Expression::Union(Box::new(union)),
2711                alias: Identifier::new(cte_name.clone()),
2712                columns: vec![column_name.clone()],
2713                materialized: None,
2714                key_expressions: Vec::new(),
2715                alias_first: true,
2716                comments: Vec::new(),
2717            };
2718
2719            // Create replacement: SELECT date_value FROM cte_name
2720            let replacement_select = Select {
2721                expressions: vec![Expression::Column(Box::new(crate::expressions::Column {
2722                    name: column_name,
2723                    table: None,
2724                    join_mark: false,
2725                    trailing_comments: vec![],
2726                    span: None,
2727                    inferred_type: None,
2728                }))],
2729                from: Some(From {
2730                    expressions: vec![Expression::Table(Box::new(
2731                        crate::expressions::TableRef::new(&cte_name),
2732                    ))],
2733                }),
2734                ..Select::new()
2735            };
2736
2737            let replacement = Expression::Subquery(Box::new(Subquery {
2738                this: Expression::Select(Box::new(replacement_select)),
2739                alias: Some(Identifier::new(cte_name)),
2740                column_aliases: vec![],
2741                alias_explicit_as: false,
2742                alias_keyword: None,
2743                order_by: None,
2744                limit: None,
2745                offset: None,
2746                distribute_by: None,
2747                sort_by: None,
2748                cluster_by: None,
2749                lateral: false,
2750                modifiers_inside: false,
2751                trailing_comments: vec![],
2752                inferred_type: None,
2753            }));
2754
2755            return Some((cte, replacement));
2756        }
2757    }
2758
2759    // Also check for aliased UNNEST like UNNEST(...) AS _q(date_week)
2760    if let Expression::Alias(alias) = expr {
2761        // Extract column name from alias column_aliases if present
2762        let col_name = alias.column_aliases.first().map(|id| id.name.as_str());
2763        if let Some((cte, replacement)) =
2764            try_convert_generate_date_array_with_name(&alias.this, cte_count, col_name)
2765        {
2766            // If we extracted a column name from the alias, don't preserve the outer alias
2767            // since the CTE now uses that column name directly
2768            if col_name.is_some() {
2769                return Some((cte, replacement));
2770            }
2771            let new_alias = Expression::Alias(Box::new(crate::expressions::Alias {
2772                this: replacement,
2773                alias: alias.alias.clone(),
2774                column_aliases: alias.column_aliases.clone(),
2775                alias_explicit_as: false,
2776                alias_keyword: None,
2777                pre_alias_comments: alias.pre_alias_comments.clone(),
2778                trailing_comments: alias.trailing_comments.clone(),
2779                inferred_type: None,
2780            }));
2781            return Some((cte, new_alias));
2782        }
2783    }
2784
2785    None
2786}
2787
2788/// Extract interval unit and count from an interval expression.
2789/// Handles both structured intervals (with separate unit field) and
2790/// string-encoded intervals like `INTERVAL '1 WEEK'` where unit is None
2791/// and the value contains both count and unit.
2792fn extract_interval_unit_and_count(
2793    expr: &Expression,
2794) -> (crate::expressions::IntervalUnit, Expression) {
2795    use crate::expressions::{IntervalUnit, IntervalUnitSpec, Literal};
2796
2797    if let Expression::Interval(ref iv) = expr {
2798        // First try: structured unit field
2799        if let Some(ref unit_spec) = iv.unit {
2800            if let IntervalUnitSpec::Simple { unit, .. } = unit_spec {
2801                let count = match &iv.this {
2802                    Some(e) => e.clone(),
2803                    None => Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2804                };
2805                return (unit.clone(), count);
2806            }
2807        }
2808
2809        // Second try: parse from string value like "1 WEEK" or "1"
2810        if let Some(ref val_expr) = iv.this {
2811            match val_expr {
2812                Expression::Literal(lit)
2813                    if matches!(lit.as_ref(), Literal::String(_) | Literal::Number(_)) =>
2814                {
2815                    let s = match lit.as_ref() {
2816                        Literal::String(s) | Literal::Number(s) => s,
2817                        _ => unreachable!(),
2818                    };
2819                    // Try to parse "count unit" format like "1 WEEK", "1 MONTH"
2820                    let parts: Vec<&str> = s.trim().splitn(2, char::is_whitespace).collect();
2821                    if parts.len() == 2 {
2822                        let count_str = parts[0].trim();
2823                        let unit_str = parts[1].trim().to_uppercase();
2824                        let unit = match unit_str.as_str() {
2825                            "YEAR" | "YEARS" => IntervalUnit::Year,
2826                            "QUARTER" | "QUARTERS" => IntervalUnit::Quarter,
2827                            "MONTH" | "MONTHS" => IntervalUnit::Month,
2828                            "WEEK" | "WEEKS" => IntervalUnit::Week,
2829                            "DAY" | "DAYS" => IntervalUnit::Day,
2830                            "HOUR" | "HOURS" => IntervalUnit::Hour,
2831                            "MINUTE" | "MINUTES" => IntervalUnit::Minute,
2832                            "SECOND" | "SECONDS" => IntervalUnit::Second,
2833                            "MILLISECOND" | "MILLISECONDS" => IntervalUnit::Millisecond,
2834                            "MICROSECOND" | "MICROSECONDS" => IntervalUnit::Microsecond,
2835                            _ => IntervalUnit::Day,
2836                        };
2837                        return (
2838                            unit,
2839                            Expression::Literal(Box::new(Literal::Number(count_str.to_string()))),
2840                        );
2841                    }
2842                    // Just a number with no unit - default to Day
2843                    if s.parse::<f64>().is_ok() {
2844                        return (
2845                            IntervalUnit::Day,
2846                            Expression::Literal(Box::new(Literal::Number(s.clone()))),
2847                        );
2848                    }
2849                }
2850                _ => {}
2851            }
2852        }
2853
2854        // Fallback
2855        (
2856            IntervalUnit::Day,
2857            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2858        )
2859    } else {
2860        (
2861            IntervalUnit::Day,
2862            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
2863        )
2864    }
2865}
2866
2867/// Convert ILIKE to LOWER(x) LIKE LOWER(y).
2868///
2869/// For dialects that don't support ILIKE (case-insensitive LIKE), this converts:
2870/// ```sql
2871/// SELECT * FROM t WHERE x ILIKE '%pattern%'
2872/// ```
2873/// To:
2874/// ```sql
2875/// SELECT * FROM t WHERE LOWER(x) LIKE LOWER('%pattern%')
2876/// ```
2877///
2878/// Reference: `generator.py:no_ilike_sql()`
2879pub fn no_ilike_sql(expr: Expression) -> Result<Expression> {
2880    match expr {
2881        Expression::ILike(ilike) => {
2882            // Create LOWER(left) LIKE LOWER(right)
2883            let lower_left = Expression::Function(Box::new(crate::expressions::Function {
2884                name: "LOWER".to_string(),
2885                args: vec![ilike.left],
2886                distinct: false,
2887                trailing_comments: vec![],
2888                use_bracket_syntax: false,
2889                no_parens: false,
2890                quoted: false,
2891                span: None,
2892                inferred_type: None,
2893            }));
2894
2895            let lower_right = Expression::Function(Box::new(crate::expressions::Function {
2896                name: "LOWER".to_string(),
2897                args: vec![ilike.right],
2898                distinct: false,
2899                trailing_comments: vec![],
2900                use_bracket_syntax: false,
2901                no_parens: false,
2902                quoted: false,
2903                span: None,
2904                inferred_type: None,
2905            }));
2906
2907            Ok(Expression::Like(Box::new(crate::expressions::LikeOp {
2908                left: lower_left,
2909                right: lower_right,
2910                escape: ilike.escape,
2911                quantifier: ilike.quantifier,
2912                inferred_type: None,
2913            })))
2914        }
2915        other => Ok(other),
2916    }
2917}
2918
2919/// Convert TryCast to Cast.
2920///
2921/// For dialects that don't support TRY_CAST (safe cast that returns NULL on error),
2922/// this converts TRY_CAST to regular CAST. Note: This may cause runtime errors
2923/// for invalid casts that TRY_CAST would handle gracefully.
2924///
2925/// Reference: `generator.py:no_trycast_sql()`
2926pub fn no_trycast_sql(expr: Expression) -> Result<Expression> {
2927    match expr {
2928        Expression::TryCast(try_cast) => Ok(Expression::Cast(try_cast)),
2929        other => Ok(other),
2930    }
2931}
2932
2933/// Convert SafeCast to Cast.
2934///
2935/// For dialects that don't support SAFE_CAST (BigQuery's safe cast syntax),
2936/// this converts SAFE_CAST to regular CAST.
2937pub fn no_safe_cast_sql(expr: Expression) -> Result<Expression> {
2938    match expr {
2939        Expression::SafeCast(safe_cast) => Ok(Expression::Cast(safe_cast)),
2940        other => Ok(other),
2941    }
2942}
2943
2944/// Convert COMMENT ON statements to inline comments.
2945///
2946/// For dialects that don't support COMMENT ON syntax, this can transform
2947/// comment statements into inline comments or skip them entirely.
2948///
2949/// Reference: `generator.py:no_comment_column_constraint_sql()`
2950pub fn no_comment_column_constraint(expr: Expression) -> Result<Expression> {
2951    // For now, just pass through - comment handling is done in generator
2952    Ok(expr)
2953}
2954
2955/// Convert TABLE GENERATE_SERIES to UNNEST(GENERATE_SERIES(...)).
2956///
2957/// Some dialects use GENERATE_SERIES as a table-valued function, while others
2958/// prefer the UNNEST syntax. This converts:
2959/// ```sql
2960/// SELECT * FROM GENERATE_SERIES(1, 10) AS t(n)
2961/// ```
2962/// To:
2963/// ```sql
2964/// SELECT * FROM UNNEST(GENERATE_SERIES(1, 10)) AS _u(n)
2965/// ```
2966///
2967/// Reference: `transforms.py:125-135`
2968pub fn unnest_generate_series(expr: Expression) -> Result<Expression> {
2969    // Convert TABLE GENERATE_SERIES to UNNEST(GENERATE_SERIES(...))
2970    // This handles the case where GENERATE_SERIES is used as a table-valued function
2971    match expr {
2972        Expression::Table(ref table) => {
2973            // Check if the table name matches GENERATE_SERIES pattern
2974            // In practice, this would be Expression::GenerateSeries wrapped in a Table context
2975            if table.name.name.to_uppercase() == "GENERATE_SERIES" {
2976                // Create UNNEST wrapper
2977                let unnest = Expression::Unnest(Box::new(UnnestFunc {
2978                    this: expr.clone(),
2979                    expressions: Vec::new(),
2980                    with_ordinality: false,
2981                    alias: None,
2982                    offset_alias: None,
2983                }));
2984
2985                // If there's an alias, wrap in alias
2986                return Ok(Expression::Alias(Box::new(crate::expressions::Alias {
2987                    this: unnest,
2988                    alias: Identifier::new("_u".to_string()),
2989                    column_aliases: vec![],
2990                    alias_explicit_as: false,
2991                    alias_keyword: None,
2992                    pre_alias_comments: vec![],
2993                    trailing_comments: vec![],
2994                    inferred_type: None,
2995                })));
2996            }
2997            Ok(expr)
2998        }
2999        Expression::GenerateSeries(gs) => {
3000            // Wrap GenerateSeries directly in UNNEST
3001            let unnest = Expression::Unnest(Box::new(UnnestFunc {
3002                this: Expression::GenerateSeries(gs),
3003                expressions: Vec::new(),
3004                with_ordinality: false,
3005                alias: None,
3006                offset_alias: None,
3007            }));
3008            Ok(unnest)
3009        }
3010        other => Ok(other),
3011    }
3012}
3013
3014/// Convert UNNEST(GENERATE_SERIES(start, end, step)) to a subquery for PostgreSQL.
3015///
3016/// PostgreSQL's GENERATE_SERIES returns rows directly, so UNNEST wrapping is unnecessary.
3017/// Instead, convert to:
3018/// ```sql
3019/// (SELECT CAST(value AS DATE) FROM GENERATE_SERIES(start, end, step) AS _t(value)) AS _unnested_generate_series
3020/// ```
3021///
3022/// This handles the case where GENERATE_DATE_ARRAY was converted to GENERATE_SERIES
3023/// during cross-dialect normalization, but the original had UNNEST wrapping.
3024pub fn unwrap_unnest_generate_series_for_postgres(expr: Expression) -> Result<Expression> {
3025    use crate::dialects::transform_recursive;
3026    transform_recursive(expr, &unwrap_unnest_generate_series_single)
3027}
3028
3029fn unwrap_unnest_generate_series_single(expr: Expression) -> Result<Expression> {
3030    use crate::expressions::*;
3031    // Match UNNEST(GENERATE_SERIES(...)) patterns in FROM clauses
3032    match expr {
3033        Expression::Select(mut select) => {
3034            // Process FROM clause
3035            if let Some(ref mut from) = select.from {
3036                for table_expr in &mut from.expressions {
3037                    if let Some(replacement) = try_unwrap_unnest_gen_series(table_expr) {
3038                        *table_expr = replacement;
3039                    }
3040                }
3041            }
3042            // Process JOINs
3043            for join in &mut select.joins {
3044                if let Some(replacement) = try_unwrap_unnest_gen_series(&join.this) {
3045                    join.this = replacement;
3046                }
3047            }
3048            Ok(Expression::Select(select))
3049        }
3050        other => Ok(other),
3051    }
3052}
3053
3054/// Try to convert an UNNEST(GENERATE_SERIES(...)) to a PostgreSQL subquery.
3055/// Returns the replacement expression if applicable.
3056fn try_unwrap_unnest_gen_series(expr: &Expression) -> Option<Expression> {
3057    use crate::expressions::*;
3058
3059    // Match Unnest containing GenerateSeries
3060    let gen_series = match expr {
3061        Expression::Unnest(unnest) => {
3062            if let Expression::GenerateSeries(ref gs) = unnest.this {
3063                Some(gs.as_ref().clone())
3064            } else {
3065                None
3066            }
3067        }
3068        Expression::Alias(alias) => {
3069            if let Expression::Unnest(ref unnest) = alias.this {
3070                if let Expression::GenerateSeries(ref gs) = unnest.this {
3071                    Some(gs.as_ref().clone())
3072                } else {
3073                    None
3074                }
3075            } else {
3076                None
3077            }
3078        }
3079        _ => None,
3080    };
3081
3082    let gs = gen_series?;
3083
3084    // Build: (SELECT CAST(value AS DATE) FROM GENERATE_SERIES(start, end, step) AS _t(value)) AS _unnested_generate_series
3085    let value_col = Expression::boxed_column(Column {
3086        name: Identifier::new("value".to_string()),
3087        table: None,
3088        join_mark: false,
3089        trailing_comments: vec![],
3090        span: None,
3091        inferred_type: None,
3092    });
3093
3094    let cast_value = Expression::Cast(Box::new(Cast {
3095        this: value_col,
3096        to: DataType::Date,
3097        trailing_comments: vec![],
3098        double_colon_syntax: false,
3099        format: None,
3100        default: None,
3101        inferred_type: None,
3102    }));
3103
3104    let gen_series_expr = Expression::GenerateSeries(Box::new(gs));
3105
3106    // GENERATE_SERIES(...) AS _t(value)
3107    let gen_series_aliased = Expression::Alias(Box::new(Alias {
3108        this: gen_series_expr,
3109        alias: Identifier::new("_t".to_string()),
3110        column_aliases: vec![Identifier::new("value".to_string())],
3111        alias_explicit_as: false,
3112        alias_keyword: None,
3113        pre_alias_comments: vec![],
3114        trailing_comments: vec![],
3115        inferred_type: None,
3116    }));
3117
3118    let mut inner_select = Select::new();
3119    inner_select.expressions = vec![cast_value];
3120    inner_select.from = Some(From {
3121        expressions: vec![gen_series_aliased],
3122    });
3123
3124    let inner_select_expr = Expression::Select(Box::new(inner_select));
3125
3126    let subquery = Expression::Subquery(Box::new(Subquery {
3127        this: inner_select_expr,
3128        alias: None,
3129        column_aliases: vec![],
3130        alias_explicit_as: false,
3131        alias_keyword: None,
3132        order_by: None,
3133        limit: None,
3134        offset: None,
3135        distribute_by: None,
3136        sort_by: None,
3137        cluster_by: None,
3138        lateral: false,
3139        modifiers_inside: false,
3140        trailing_comments: vec![],
3141        inferred_type: None,
3142    }));
3143
3144    // Wrap in alias AS _unnested_generate_series
3145    Some(Expression::Alias(Box::new(Alias {
3146        this: subquery,
3147        alias: Identifier::new("_unnested_generate_series".to_string()),
3148        column_aliases: vec![],
3149        alias_explicit_as: false,
3150        alias_keyword: None,
3151        pre_alias_comments: vec![],
3152        trailing_comments: vec![],
3153        inferred_type: None,
3154    })))
3155}
3156
3157/// Expand BETWEEN expressions in DELETE statements to >= AND <=
3158///
3159/// Some dialects (like StarRocks) don't support BETWEEN in DELETE statements
3160/// or prefer the expanded form. This transforms:
3161///   `DELETE FROM t WHERE a BETWEEN b AND c`
3162/// to:
3163///   `DELETE FROM t WHERE a >= b AND a <= c`
3164pub fn expand_between_in_delete(expr: Expression) -> Result<Expression> {
3165    match expr {
3166        Expression::Delete(mut delete) => {
3167            // If there's a WHERE clause, expand any BETWEEN expressions in it
3168            if let Some(ref mut where_clause) = delete.where_clause {
3169                where_clause.this = expand_between_recursive(where_clause.this.clone());
3170            }
3171            Ok(Expression::Delete(delete))
3172        }
3173        other => Ok(other),
3174    }
3175}
3176
3177/// Recursively expand BETWEEN expressions to >= AND <=
3178fn expand_between_recursive(expr: Expression) -> Expression {
3179    match expr {
3180        // Expand: a BETWEEN b AND c -> a >= b AND a <= c
3181        // Expand: a NOT BETWEEN b AND c -> a < b OR a > c
3182        Expression::Between(between) => {
3183            let this = expand_between_recursive(between.this.clone());
3184            let low = expand_between_recursive(between.low);
3185            let high = expand_between_recursive(between.high);
3186
3187            if between.not {
3188                // NOT BETWEEN: a < b OR a > c
3189                Expression::Or(Box::new(BinaryOp::new(
3190                    Expression::Lt(Box::new(BinaryOp::new(this.clone(), low))),
3191                    Expression::Gt(Box::new(BinaryOp::new(this, high))),
3192                )))
3193            } else {
3194                // BETWEEN: a >= b AND a <= c
3195                Expression::And(Box::new(BinaryOp::new(
3196                    Expression::Gte(Box::new(BinaryOp::new(this.clone(), low))),
3197                    Expression::Lte(Box::new(BinaryOp::new(this, high))),
3198                )))
3199            }
3200        }
3201
3202        // Recursively process AND/OR expressions
3203        Expression::And(mut op) => {
3204            op.left = expand_between_recursive(op.left);
3205            op.right = expand_between_recursive(op.right);
3206            Expression::And(op)
3207        }
3208        Expression::Or(mut op) => {
3209            op.left = expand_between_recursive(op.left);
3210            op.right = expand_between_recursive(op.right);
3211            Expression::Or(op)
3212        }
3213        Expression::Not(mut op) => {
3214            op.this = expand_between_recursive(op.this);
3215            Expression::Not(op)
3216        }
3217
3218        // Recursively process parenthesized expressions
3219        Expression::Paren(mut paren) => {
3220            paren.this = expand_between_recursive(paren.this);
3221            Expression::Paren(paren)
3222        }
3223
3224        // Pass through everything else unchanged
3225        other => other,
3226    }
3227}
3228
3229/// Push down CTE column names into SELECT expressions.
3230///
3231/// BigQuery doesn't support column names when defining a CTE, e.g.:
3232/// `WITH vartab(v) AS (SELECT ...)` is not valid.
3233/// Instead, it expects: `WITH vartab AS (SELECT ... AS v)`.
3234///
3235/// This transform removes the CTE column aliases and adds them as
3236/// aliases on the SELECT expressions.
3237pub fn pushdown_cte_column_names(expr: Expression) -> Result<Expression> {
3238    match expr {
3239        Expression::Select(mut select) => {
3240            if let Some(ref mut with) = select.with {
3241                for cte in &mut with.ctes {
3242                    if !cte.columns.is_empty() {
3243                        // Check if the CTE body is a star query - if so, just strip column names
3244                        let is_star = matches!(&cte.this, Expression::Select(s) if
3245                            s.expressions.len() == 1 && matches!(&s.expressions[0], Expression::Star(_)));
3246
3247                        if is_star {
3248                            // Can't push down column names for star queries, just remove them
3249                            cte.columns.clear();
3250                            continue;
3251                        }
3252
3253                        // Extract column names
3254                        let column_names: Vec<Identifier> = cte.columns.drain(..).collect();
3255
3256                        // Push column names down into the SELECT expressions
3257                        if let Expression::Select(ref mut inner_select) = cte.this {
3258                            let new_exprs: Vec<Expression> = inner_select
3259                                .expressions
3260                                .drain(..)
3261                                .zip(
3262                                    column_names
3263                                        .into_iter()
3264                                        .chain(std::iter::repeat_with(|| Identifier::new(""))),
3265                                )
3266                                .map(|(expr, col_name)| {
3267                                    if col_name.name.is_empty() {
3268                                        return expr;
3269                                    }
3270                                    // If already aliased, replace the alias
3271                                    match expr {
3272                                        Expression::Alias(mut a) => {
3273                                            a.alias = col_name;
3274                                            Expression::Alias(a)
3275                                        }
3276                                        other => {
3277                                            Expression::Alias(Box::new(crate::expressions::Alias {
3278                                                this: other,
3279                                                alias: col_name,
3280                                                column_aliases: Vec::new(),
3281                                                alias_explicit_as: false,
3282                                                alias_keyword: None,
3283                                                pre_alias_comments: Vec::new(),
3284                                                trailing_comments: Vec::new(),
3285                                                inferred_type: None,
3286                                            }))
3287                                        }
3288                                    }
3289                                })
3290                                .collect();
3291                            inner_select.expressions = new_exprs;
3292                        }
3293                    }
3294                }
3295            }
3296            Ok(Expression::Select(select))
3297        }
3298        other => Ok(other),
3299    }
3300}
3301
3302/// Simplify nested parentheses around VALUES in FROM clause.
3303/// Converts `FROM ((VALUES (1)))` to `FROM (VALUES (1))` by stripping redundant wrapping.
3304/// Handles various nesting patterns: Subquery(Paren(Values)), Paren(Paren(Values)), etc.
3305pub fn simplify_nested_paren_values(expr: Expression) -> Result<Expression> {
3306    match expr {
3307        Expression::Select(mut select) => {
3308            if let Some(ref mut from) = select.from {
3309                for from_item in from.expressions.iter_mut() {
3310                    simplify_paren_values_in_from(from_item);
3311                }
3312            }
3313            Ok(Expression::Select(select))
3314        }
3315        other => Ok(other),
3316    }
3317}
3318
3319fn simplify_paren_values_in_from(expr: &mut Expression) {
3320    // Check various patterns and build replacement if needed
3321    let replacement = match expr {
3322        // Subquery(Paren(Values)) -> Subquery with Values directly
3323        Expression::Subquery(ref subquery) => {
3324            if let Expression::Paren(ref paren) = subquery.this {
3325                if matches!(&paren.this, Expression::Values(_)) {
3326                    let mut new_sub = subquery.as_ref().clone();
3327                    new_sub.this = paren.this.clone();
3328                    Some(Expression::Subquery(Box::new(new_sub)))
3329                } else {
3330                    None
3331                }
3332            } else {
3333                None
3334            }
3335        }
3336        // Paren(Subquery(Values)) -> Subquery(Values) - strip the Paren wrapper
3337        // Paren(Paren(Values)) -> Paren(Values) - strip one layer
3338        Expression::Paren(ref outer_paren) => {
3339            if let Expression::Subquery(ref subquery) = outer_paren.this {
3340                // Paren(Subquery(Values)) -> Subquery(Values) - strip outer Paren
3341                if matches!(&subquery.this, Expression::Values(_)) {
3342                    Some(outer_paren.this.clone())
3343                }
3344                // Paren(Subquery(Paren(Values))) -> Subquery(Values)
3345                else if let Expression::Paren(ref paren) = subquery.this {
3346                    if matches!(&paren.this, Expression::Values(_)) {
3347                        let mut new_sub = subquery.as_ref().clone();
3348                        new_sub.this = paren.this.clone();
3349                        Some(Expression::Subquery(Box::new(new_sub)))
3350                    } else {
3351                        None
3352                    }
3353                } else {
3354                    None
3355                }
3356            } else if let Expression::Paren(ref inner_paren) = outer_paren.this {
3357                if matches!(&inner_paren.this, Expression::Values(_)) {
3358                    Some(outer_paren.this.clone())
3359                } else {
3360                    None
3361                }
3362            } else {
3363                None
3364            }
3365        }
3366        _ => None,
3367    };
3368    if let Some(new_expr) = replacement {
3369        *expr = new_expr;
3370    }
3371}
3372
3373/// Add auto-generated table aliases (like `_t0`) for POSEXPLODE/EXPLODE in FROM clause
3374/// when the alias has column_aliases but no alias name.
3375/// This is needed for Spark target: `FROM POSEXPLODE(x) AS (a, b)` -> `FROM POSEXPLODE(x) AS _t0(a, b)`
3376pub fn add_auto_table_alias(expr: Expression) -> Result<Expression> {
3377    match expr {
3378        Expression::Select(mut select) => {
3379            // Process FROM expressions
3380            if let Some(ref mut from) = select.from {
3381                let mut counter = 0usize;
3382                for from_item in from.expressions.iter_mut() {
3383                    add_auto_alias_to_from_item(from_item, &mut counter);
3384                }
3385            }
3386            Ok(Expression::Select(select))
3387        }
3388        other => Ok(other),
3389    }
3390}
3391
3392fn add_auto_alias_to_from_item(expr: &mut Expression, counter: &mut usize) {
3393    use crate::expressions::Identifier;
3394
3395    match expr {
3396        Expression::Alias(ref mut alias) => {
3397            // If the alias name is empty and there are column_aliases, add auto-generated name
3398            if alias.alias.name.is_empty() && !alias.column_aliases.is_empty() {
3399                alias.alias = Identifier::new(format!("_t{}", counter));
3400                *counter += 1;
3401            }
3402        }
3403        _ => {}
3404    }
3405}
3406
3407/// Convert BigQuery-style UNNEST aliases to column-alias format for DuckDB/Presto/Spark.
3408///
3409/// BigQuery uses: `UNNEST(arr) AS x` where x is a column alias.
3410/// DuckDB/Presto/Spark need: `UNNEST(arr) AS _t0(x)` where _t0 is a table alias and x is the column alias.
3411///
3412/// Propagate struct field names from the first named struct in an array to subsequent unnamed structs.
3413///
3414/// In BigQuery, `[STRUCT('Alice' AS name, 85 AS score), STRUCT('Bob', 92)]` means the second struct
3415/// should inherit field names from the first: `[STRUCT('Alice' AS name, 85 AS score), STRUCT('Bob' AS name, 92 AS score)]`.
3416pub fn propagate_struct_field_names(expr: Expression) -> Result<Expression> {
3417    use crate::dialects::transform_recursive;
3418    transform_recursive(expr, &propagate_struct_names_in_expr)
3419}
3420
3421fn propagate_struct_names_in_expr(expr: Expression) -> Result<Expression> {
3422    use crate::expressions::{Alias, ArrayConstructor, Function, Identifier};
3423
3424    /// Helper to propagate struct field names within an array of expressions
3425    fn propagate_in_elements(elements: &[Expression]) -> Option<Vec<Expression>> {
3426        if elements.len() <= 1 {
3427            return None;
3428        }
3429        // Check if first element is a named STRUCT function
3430        if let Some(Expression::Function(ref first_struct)) = elements.first() {
3431            if first_struct.name.eq_ignore_ascii_case("STRUCT") {
3432                // Extract field names from first struct
3433                let field_names: Vec<Option<String>> = first_struct
3434                    .args
3435                    .iter()
3436                    .map(|arg| {
3437                        if let Expression::Alias(a) = arg {
3438                            Some(a.alias.name.clone())
3439                        } else {
3440                            None
3441                        }
3442                    })
3443                    .collect();
3444
3445                // Only propagate if first struct has at least one named field
3446                if field_names.iter().any(|n| n.is_some()) {
3447                    let mut new_elements = Vec::with_capacity(elements.len());
3448                    new_elements.push(elements[0].clone());
3449
3450                    for elem in &elements[1..] {
3451                        if let Expression::Function(ref s) = elem {
3452                            if s.name.eq_ignore_ascii_case("STRUCT")
3453                                && s.args.len() == field_names.len()
3454                            {
3455                                // Check if this struct has NO names (all unnamed)
3456                                let all_unnamed =
3457                                    s.args.iter().all(|a| !matches!(a, Expression::Alias(_)));
3458                                if all_unnamed {
3459                                    // Apply names from first struct
3460                                    let new_args: Vec<Expression> = s
3461                                        .args
3462                                        .iter()
3463                                        .zip(field_names.iter())
3464                                        .map(|(val, name)| {
3465                                            if let Some(n) = name {
3466                                                Expression::Alias(Box::new(Alias::new(
3467                                                    val.clone(),
3468                                                    Identifier::new(n.clone()),
3469                                                )))
3470                                            } else {
3471                                                val.clone()
3472                                            }
3473                                        })
3474                                        .collect();
3475                                    new_elements.push(Expression::Function(Box::new(
3476                                        Function::new("STRUCT".to_string(), new_args),
3477                                    )));
3478                                    continue;
3479                                }
3480                            }
3481                        }
3482                        new_elements.push(elem.clone());
3483                    }
3484
3485                    return Some(new_elements);
3486                }
3487            }
3488        }
3489        None
3490    }
3491
3492    // Look for Array expressions containing STRUCT function calls
3493    if let Expression::Array(ref arr) = expr {
3494        if let Some(new_elements) = propagate_in_elements(&arr.expressions) {
3495            return Ok(Expression::Array(Box::new(crate::expressions::Array {
3496                expressions: new_elements,
3497            })));
3498        }
3499    }
3500
3501    // Also handle ArrayFunc (ArrayConstructor) - bracket notation [STRUCT(...), ...]
3502    if let Expression::ArrayFunc(ref arr) = expr {
3503        if let Some(new_elements) = propagate_in_elements(&arr.expressions) {
3504            return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
3505                expressions: new_elements,
3506                bracket_notation: arr.bracket_notation,
3507                use_list_keyword: arr.use_list_keyword,
3508            })));
3509        }
3510    }
3511
3512    Ok(expr)
3513}
3514
3515/// This walks the entire expression tree to find SELECT statements and converts UNNEST aliases
3516/// in their FROM clauses and JOINs.
3517pub fn unnest_alias_to_column_alias(expr: Expression) -> Result<Expression> {
3518    use crate::dialects::transform_recursive;
3519    transform_recursive(expr, &unnest_alias_transform_single_select)
3520}
3521
3522/// Move UNNEST items from FROM clause to CROSS JOINs without changing alias format.
3523/// Used for BigQuery -> BigQuery/Redshift where we want CROSS JOIN but not _t0(col) aliases.
3524pub fn unnest_from_to_cross_join(expr: Expression) -> Result<Expression> {
3525    use crate::dialects::transform_recursive;
3526    transform_recursive(expr, &unnest_from_to_cross_join_single_select)
3527}
3528
3529fn unnest_from_to_cross_join_single_select(expr: Expression) -> Result<Expression> {
3530    if let Expression::Select(mut select) = expr {
3531        if let Some(ref mut from) = select.from {
3532            if from.expressions.len() > 1 {
3533                let mut new_from_exprs = Vec::new();
3534                let mut new_cross_joins = Vec::new();
3535
3536                for (idx, from_item) in from.expressions.drain(..).enumerate() {
3537                    if idx == 0 {
3538                        new_from_exprs.push(from_item);
3539                    } else {
3540                        let is_unnest = match &from_item {
3541                            Expression::Unnest(_) => true,
3542                            Expression::Alias(a) => matches!(a.this, Expression::Unnest(_)),
3543                            _ => false,
3544                        };
3545
3546                        if is_unnest {
3547                            new_cross_joins.push(crate::expressions::Join {
3548                                this: from_item,
3549                                on: None,
3550                                using: Vec::new(),
3551                                kind: JoinKind::Cross,
3552                                use_inner_keyword: false,
3553                                use_outer_keyword: false,
3554                                deferred_condition: false,
3555                                join_hint: None,
3556                                match_condition: None,
3557                                pivots: Vec::new(),
3558                                comments: Vec::new(),
3559                                nesting_group: 0,
3560                                directed: false,
3561                            });
3562                        } else {
3563                            new_from_exprs.push(from_item);
3564                        }
3565                    }
3566                }
3567
3568                from.expressions = new_from_exprs;
3569                new_cross_joins.append(&mut select.joins);
3570                select.joins = new_cross_joins;
3571            }
3572        }
3573
3574        Ok(Expression::Select(select))
3575    } else {
3576        Ok(expr)
3577    }
3578}
3579
3580/// Wrap UNNEST function aliases in JOIN items from `AS name` to `AS _u(name)`
3581/// Used for PostgreSQL → Presto/Trino transpilation where GENERATE_SERIES is
3582/// converted to UNNEST(SEQUENCE) and the alias needs the column-alias format.
3583pub fn wrap_unnest_join_aliases(expr: Expression) -> Result<Expression> {
3584    use crate::dialects::transform_recursive;
3585    transform_recursive(expr, &wrap_unnest_join_aliases_single)
3586}
3587
3588fn wrap_unnest_join_aliases_single(expr: Expression) -> Result<Expression> {
3589    if let Expression::Select(mut select) = expr {
3590        // Process JOIN items
3591        for join in &mut select.joins {
3592            wrap_unnest_alias_in_join_item(&mut join.this);
3593        }
3594        Ok(Expression::Select(select))
3595    } else {
3596        Ok(expr)
3597    }
3598}
3599
3600/// If a join item is an Alias wrapping an UNNEST function, convert alias to _u(alias_name) format
3601fn wrap_unnest_alias_in_join_item(expr: &mut Expression) {
3602    use crate::expressions::Identifier;
3603    if let Expression::Alias(alias) = expr {
3604        // Check if the inner expression is a function call to UNNEST
3605        let is_unnest = match &alias.this {
3606            Expression::Function(f) => f.name.eq_ignore_ascii_case("UNNEST"),
3607            _ => false,
3608        };
3609
3610        if is_unnest && alias.column_aliases.is_empty() {
3611            // Simple alias like `AS s` -> wrap to `AS _u(s)`
3612            let original_alias_name = alias.alias.name.clone();
3613            alias.alias = Identifier {
3614                name: "_u".to_string(),
3615                quoted: false,
3616                trailing_comments: Vec::new(),
3617                span: None,
3618            };
3619            alias.column_aliases = vec![Identifier {
3620                name: original_alias_name,
3621                quoted: false,
3622                trailing_comments: Vec::new(),
3623                span: None,
3624            }];
3625        }
3626    }
3627}
3628
3629fn unnest_alias_transform_single_select(expr: Expression) -> Result<Expression> {
3630    if let Expression::Select(mut select) = expr {
3631        let mut counter = 0usize;
3632
3633        // Process FROM expressions: convert aliases AND move UNNEST items to CROSS JOIN
3634        if let Some(ref mut from) = select.from {
3635            // First pass: convert aliases in-place
3636            for from_item in from.expressions.iter_mut() {
3637                convert_unnest_alias_in_from(from_item, &mut counter);
3638            }
3639
3640            // Second pass: move UNNEST items from FROM to CROSS JOINs
3641            if from.expressions.len() > 1 {
3642                let mut new_from_exprs = Vec::new();
3643                let mut new_cross_joins = Vec::new();
3644
3645                for (idx, from_item) in from.expressions.drain(..).enumerate() {
3646                    if idx == 0 {
3647                        // First expression always stays in FROM
3648                        new_from_exprs.push(from_item);
3649                    } else {
3650                        // Check if this is UNNEST or Alias(UNNEST)
3651                        let is_unnest = match &from_item {
3652                            Expression::Unnest(_) => true,
3653                            Expression::Alias(a) => matches!(a.this, Expression::Unnest(_)),
3654                            _ => false,
3655                        };
3656
3657                        if is_unnest {
3658                            // Convert to CROSS JOIN
3659                            new_cross_joins.push(crate::expressions::Join {
3660                                this: from_item,
3661                                on: None,
3662                                using: Vec::new(),
3663                                kind: JoinKind::Cross,
3664                                use_inner_keyword: false,
3665                                use_outer_keyword: false,
3666                                deferred_condition: false,
3667                                join_hint: None,
3668                                match_condition: None,
3669                                pivots: Vec::new(),
3670                                comments: Vec::new(),
3671                                nesting_group: 0,
3672                                directed: false,
3673                            });
3674                        } else {
3675                            // Keep non-UNNEST items in FROM
3676                            new_from_exprs.push(from_item);
3677                        }
3678                    }
3679                }
3680
3681                from.expressions = new_from_exprs;
3682                // Prepend cross joins before existing joins
3683                new_cross_joins.append(&mut select.joins);
3684                select.joins = new_cross_joins;
3685            }
3686        }
3687
3688        // Process JOINs (existing joins that may have UNNEST aliases)
3689        for join in select.joins.iter_mut() {
3690            convert_unnest_alias_in_from(&mut join.this, &mut counter);
3691        }
3692
3693        Ok(Expression::Select(select))
3694    } else {
3695        Ok(expr)
3696    }
3697}
3698
3699fn convert_unnest_alias_in_from(expr: &mut Expression, counter: &mut usize) {
3700    use crate::expressions::Identifier;
3701
3702    if let Expression::Alias(ref mut alias) = expr {
3703        // Check if the inner expression is UNNEST (or EXPLODE)
3704        let is_unnest = matches!(&alias.this, Expression::Unnest(_))
3705            || matches!(&alias.this, Expression::Function(f) if f.name.eq_ignore_ascii_case("EXPLODE"));
3706
3707        if is_unnest && alias.column_aliases.is_empty() {
3708            // Convert: UNNEST(arr) AS x -> UNNEST(arr) AS _tN(x)
3709            let col_alias = alias.alias.clone();
3710            alias.column_aliases = vec![col_alias];
3711            alias.alias = Identifier::new(format!("_t{}", counter));
3712            *counter += 1;
3713        }
3714    }
3715}
3716
3717/// Expand POSEXPLODE in SELECT expressions for DuckDB.
3718///
3719/// Converts `SELECT POSEXPLODE(x)` to `SELECT GENERATE_SUBSCRIPTS(x, 1) - 1 AS pos, UNNEST(x) AS col`
3720/// Handles both aliased and unaliased forms:
3721/// - `SELECT POSEXPLODE(x) AS (a, b)` -> `SELECT GENERATE_SUBSCRIPTS(x, 1) - 1 AS a, UNNEST(x) AS b`
3722/// - `SELECT * FROM POSEXPLODE(x) AS (a, b)` -> `SELECT * FROM (SELECT GENERATE_SUBSCRIPTS(x, 1) - 1 AS a, UNNEST(x) AS b)`
3723pub fn expand_posexplode_duckdb(expr: Expression) -> Result<Expression> {
3724    use crate::expressions::{Alias, Function};
3725
3726    match expr {
3727        Expression::Select(mut select) => {
3728            // Check if any SELECT expression is a POSEXPLODE function
3729            let mut new_expressions = Vec::new();
3730            let mut changed = false;
3731
3732            for sel_expr in select.expressions.drain(..) {
3733                // Check for POSEXPLODE(x) AS (a, b) - aliased form
3734                if let Expression::Alias(ref alias_box) = sel_expr {
3735                    if let Expression::Function(ref func) = alias_box.this {
3736                        if func.name.eq_ignore_ascii_case("POSEXPLODE") && func.args.len() == 1 {
3737                            let arg = func.args[0].clone();
3738                            // Get alias names: default pos, col
3739                            let (pos_name, col_name) = if alias_box.column_aliases.len() == 2 {
3740                                (
3741                                    alias_box.column_aliases[0].name.clone(),
3742                                    alias_box.column_aliases[1].name.clone(),
3743                                )
3744                            } else if !alias_box.alias.is_empty() {
3745                                // Single alias like AS x - use as col name, "pos" for position
3746                                ("pos".to_string(), alias_box.alias.name.clone())
3747                            } else {
3748                                ("pos".to_string(), "col".to_string())
3749                            };
3750
3751                            // GENERATE_SUBSCRIPTS(x, 1) - 1 AS pos_name
3752                            let gen_subscripts = Expression::Function(Box::new(Function::new(
3753                                "GENERATE_SUBSCRIPTS".to_string(),
3754                                vec![
3755                                    arg.clone(),
3756                                    Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3757                                ],
3758                            )));
3759                            let sub_one = Expression::Sub(Box::new(BinaryOp::new(
3760                                gen_subscripts,
3761                                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3762                            )));
3763                            let pos_alias = Expression::Alias(Box::new(Alias {
3764                                this: sub_one,
3765                                alias: Identifier::new(pos_name),
3766                                column_aliases: Vec::new(),
3767                                alias_explicit_as: false,
3768                                alias_keyword: None,
3769                                pre_alias_comments: Vec::new(),
3770                                trailing_comments: Vec::new(),
3771                                inferred_type: None,
3772                            }));
3773
3774                            // UNNEST(x) AS col_name
3775                            let unnest = Expression::Unnest(Box::new(UnnestFunc {
3776                                this: arg,
3777                                expressions: Vec::new(),
3778                                with_ordinality: false,
3779                                alias: None,
3780                                offset_alias: None,
3781                            }));
3782                            let col_alias = Expression::Alias(Box::new(Alias {
3783                                this: unnest,
3784                                alias: Identifier::new(col_name),
3785                                column_aliases: Vec::new(),
3786                                alias_explicit_as: false,
3787                                alias_keyword: None,
3788                                pre_alias_comments: Vec::new(),
3789                                trailing_comments: Vec::new(),
3790                                inferred_type: None,
3791                            }));
3792
3793                            new_expressions.push(pos_alias);
3794                            new_expressions.push(col_alias);
3795                            changed = true;
3796                            continue;
3797                        }
3798                    }
3799                }
3800
3801                // Check for bare POSEXPLODE(x) - unaliased form
3802                if let Expression::Function(ref func) = sel_expr {
3803                    if func.name.eq_ignore_ascii_case("POSEXPLODE") && func.args.len() == 1 {
3804                        let arg = func.args[0].clone();
3805                        let pos_name = "pos";
3806                        let col_name = "col";
3807
3808                        // GENERATE_SUBSCRIPTS(x, 1) - 1 AS pos
3809                        let gen_subscripts = Expression::Function(Box::new(Function::new(
3810                            "GENERATE_SUBSCRIPTS".to_string(),
3811                            vec![
3812                                arg.clone(),
3813                                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3814                            ],
3815                        )));
3816                        let sub_one = Expression::Sub(Box::new(BinaryOp::new(
3817                            gen_subscripts,
3818                            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3819                        )));
3820                        let pos_alias = Expression::Alias(Box::new(Alias {
3821                            this: sub_one,
3822                            alias: Identifier::new(pos_name),
3823                            column_aliases: Vec::new(),
3824                            alias_explicit_as: false,
3825                            alias_keyword: None,
3826                            pre_alias_comments: Vec::new(),
3827                            trailing_comments: Vec::new(),
3828                            inferred_type: None,
3829                        }));
3830
3831                        // UNNEST(x) AS col
3832                        let unnest = Expression::Unnest(Box::new(UnnestFunc {
3833                            this: arg,
3834                            expressions: Vec::new(),
3835                            with_ordinality: false,
3836                            alias: None,
3837                            offset_alias: None,
3838                        }));
3839                        let col_alias = Expression::Alias(Box::new(Alias {
3840                            this: unnest,
3841                            alias: Identifier::new(col_name),
3842                            column_aliases: Vec::new(),
3843                            alias_explicit_as: false,
3844                            alias_keyword: None,
3845                            pre_alias_comments: Vec::new(),
3846                            trailing_comments: Vec::new(),
3847                            inferred_type: None,
3848                        }));
3849
3850                        new_expressions.push(pos_alias);
3851                        new_expressions.push(col_alias);
3852                        changed = true;
3853                        continue;
3854                    }
3855                }
3856
3857                // Not a POSEXPLODE, keep as-is
3858                new_expressions.push(sel_expr);
3859            }
3860
3861            if changed {
3862                select.expressions = new_expressions;
3863            } else {
3864                select.expressions = new_expressions;
3865            }
3866
3867            // Also handle POSEXPLODE in FROM clause:
3868            // SELECT * FROM POSEXPLODE(x) AS (a, b) -> SELECT * FROM (SELECT ...)
3869            if let Some(ref mut from) = select.from {
3870                expand_posexplode_in_from_duckdb(from)?;
3871            }
3872
3873            Ok(Expression::Select(select))
3874        }
3875        other => Ok(other),
3876    }
3877}
3878
3879/// Helper to expand POSEXPLODE in FROM clause for DuckDB
3880fn expand_posexplode_in_from_duckdb(from: &mut From) -> Result<()> {
3881    use crate::expressions::{Alias, Function};
3882
3883    let mut new_expressions = Vec::new();
3884    let mut _changed = false;
3885
3886    for table_expr in from.expressions.drain(..) {
3887        // Check for POSEXPLODE(x) AS (a, b) in FROM
3888        if let Expression::Alias(ref alias_box) = table_expr {
3889            if let Expression::Function(ref func) = alias_box.this {
3890                if func.name.eq_ignore_ascii_case("POSEXPLODE") && func.args.len() == 1 {
3891                    let arg = func.args[0].clone();
3892                    let (pos_name, col_name) = if alias_box.column_aliases.len() == 2 {
3893                        (
3894                            alias_box.column_aliases[0].name.clone(),
3895                            alias_box.column_aliases[1].name.clone(),
3896                        )
3897                    } else {
3898                        ("pos".to_string(), "col".to_string())
3899                    };
3900
3901                    // Create subquery: (SELECT GENERATE_SUBSCRIPTS(x, 1) - 1 AS a, UNNEST(x) AS b)
3902                    let gen_subscripts = Expression::Function(Box::new(Function::new(
3903                        "GENERATE_SUBSCRIPTS".to_string(),
3904                        vec![
3905                            arg.clone(),
3906                            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3907                        ],
3908                    )));
3909                    let sub_one = Expression::Sub(Box::new(BinaryOp::new(
3910                        gen_subscripts,
3911                        Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3912                    )));
3913                    let pos_alias = Expression::Alias(Box::new(Alias {
3914                        this: sub_one,
3915                        alias: Identifier::new(&pos_name),
3916                        column_aliases: Vec::new(),
3917                        alias_explicit_as: false,
3918                        alias_keyword: None,
3919                        pre_alias_comments: Vec::new(),
3920                        trailing_comments: Vec::new(),
3921                        inferred_type: None,
3922                    }));
3923                    let unnest = Expression::Unnest(Box::new(UnnestFunc {
3924                        this: arg,
3925                        expressions: Vec::new(),
3926                        with_ordinality: false,
3927                        alias: None,
3928                        offset_alias: None,
3929                    }));
3930                    let col_alias = Expression::Alias(Box::new(Alias {
3931                        this: unnest,
3932                        alias: Identifier::new(&col_name),
3933                        column_aliases: Vec::new(),
3934                        alias_explicit_as: false,
3935                        alias_keyword: None,
3936                        pre_alias_comments: Vec::new(),
3937                        trailing_comments: Vec::new(),
3938                        inferred_type: None,
3939                    }));
3940
3941                    let mut inner_select = Select::new();
3942                    inner_select.expressions = vec![pos_alias, col_alias];
3943
3944                    let subquery = Expression::Subquery(Box::new(Subquery {
3945                        this: Expression::Select(Box::new(inner_select)),
3946                        alias: None,
3947                        column_aliases: Vec::new(),
3948                        alias_explicit_as: false,
3949                        alias_keyword: None,
3950                        order_by: None,
3951                        limit: None,
3952                        offset: None,
3953                        distribute_by: None,
3954                        sort_by: None,
3955                        cluster_by: None,
3956                        lateral: false,
3957                        modifiers_inside: false,
3958                        trailing_comments: Vec::new(),
3959                        inferred_type: None,
3960                    }));
3961                    new_expressions.push(subquery);
3962                    _changed = true;
3963                    continue;
3964                }
3965            }
3966        }
3967
3968        // Also check for bare POSEXPLODE(x) in FROM (no alias)
3969        if let Expression::Function(ref func) = table_expr {
3970            if func.name.eq_ignore_ascii_case("POSEXPLODE") && func.args.len() == 1 {
3971                let arg = func.args[0].clone();
3972
3973                // Create subquery: (SELECT GENERATE_SUBSCRIPTS(x, 1) - 1 AS pos, UNNEST(x) AS col)
3974                let gen_subscripts = Expression::Function(Box::new(Function::new(
3975                    "GENERATE_SUBSCRIPTS".to_string(),
3976                    vec![
3977                        arg.clone(),
3978                        Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3979                    ],
3980                )));
3981                let sub_one = Expression::Sub(Box::new(BinaryOp::new(
3982                    gen_subscripts,
3983                    Expression::Literal(Box::new(Literal::Number("1".to_string()))),
3984                )));
3985                let pos_alias = Expression::Alias(Box::new(Alias {
3986                    this: sub_one,
3987                    alias: Identifier::new("pos"),
3988                    column_aliases: Vec::new(),
3989                    alias_explicit_as: false,
3990                    alias_keyword: None,
3991                    pre_alias_comments: Vec::new(),
3992                    trailing_comments: Vec::new(),
3993                    inferred_type: None,
3994                }));
3995                let unnest = Expression::Unnest(Box::new(UnnestFunc {
3996                    this: arg,
3997                    expressions: Vec::new(),
3998                    with_ordinality: false,
3999                    alias: None,
4000                    offset_alias: None,
4001                }));
4002                let col_alias = Expression::Alias(Box::new(Alias {
4003                    this: unnest,
4004                    alias: Identifier::new("col"),
4005                    column_aliases: Vec::new(),
4006                    alias_explicit_as: false,
4007                    alias_keyword: None,
4008                    pre_alias_comments: Vec::new(),
4009                    trailing_comments: Vec::new(),
4010                    inferred_type: None,
4011                }));
4012
4013                let mut inner_select = Select::new();
4014                inner_select.expressions = vec![pos_alias, col_alias];
4015
4016                let subquery = Expression::Subquery(Box::new(Subquery {
4017                    this: Expression::Select(Box::new(inner_select)),
4018                    alias: None,
4019                    column_aliases: Vec::new(),
4020                    alias_explicit_as: false,
4021                    alias_keyword: None,
4022                    order_by: None,
4023                    limit: None,
4024                    offset: None,
4025                    distribute_by: None,
4026                    sort_by: None,
4027                    cluster_by: None,
4028                    lateral: false,
4029                    modifiers_inside: false,
4030                    trailing_comments: Vec::new(),
4031                    inferred_type: None,
4032                }));
4033                new_expressions.push(subquery);
4034                _changed = true;
4035                continue;
4036            }
4037        }
4038
4039        new_expressions.push(table_expr);
4040    }
4041
4042    from.expressions = new_expressions;
4043    Ok(())
4044}
4045
4046/// Convert EXPLODE/POSEXPLODE in SELECT projections into CROSS JOIN UNNEST patterns.
4047///
4048/// This implements the `explode_projection_to_unnest` transform from Python sqlglot.
4049/// It restructures queries like:
4050///   `SELECT EXPLODE(x) FROM tbl`
4051/// into:
4052///   `SELECT IF(pos = pos_2, col, NULL) AS col FROM tbl CROSS JOIN UNNEST(...) AS pos CROSS JOIN UNNEST(x) AS col WITH OFFSET AS pos_2 WHERE ...`
4053///
4054/// The transform handles:
4055/// - EXPLODE(x) and POSEXPLODE(x) functions
4056/// - Name collision avoidance (_u, _u_2, ... and col, col_2, ...)
4057/// - Multiple EXPLODE/POSEXPLODE in one SELECT
4058/// - Queries with or without FROM clause
4059/// - Presto (index_offset=1) and BigQuery (index_offset=0) variants
4060pub fn explode_projection_to_unnest(expr: Expression, target: DialectType) -> Result<Expression> {
4061    match expr {
4062        Expression::Select(select) => explode_projection_to_unnest_impl(*select, target),
4063        other => Ok(other),
4064    }
4065}
4066
4067/// Snowflake-specific rewrite to mirror Python sqlglot's explode_projection_to_unnest behavior
4068/// when FLATTEN appears in a nested LATERAL within a SELECT projection.
4069///
4070/// This intentionally rewrites:
4071/// - `LATERAL FLATTEN(INPUT => x) alias`
4072/// into:
4073/// - `LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS alias(SEQ, KEY, PATH, INDEX, VALUE, THIS)`
4074/// and appends CROSS JOIN TABLE(FLATTEN(...)) range/entity joins plus alignment predicates
4075/// to the containing SELECT.
4076pub fn snowflake_flatten_projection_to_unnest(expr: Expression) -> Result<Expression> {
4077    match expr {
4078        Expression::Select(select) => snowflake_flatten_projection_to_unnest_impl(*select),
4079        other => Ok(other),
4080    }
4081}
4082
4083fn snowflake_flatten_projection_to_unnest_impl(mut select: Select) -> Result<Expression> {
4084    let mut flattened_inputs: Vec<Expression> = Vec::new();
4085    let mut new_selects: Vec<Expression> = Vec::with_capacity(select.expressions.len());
4086
4087    for sel_expr in select.expressions.into_iter() {
4088        let found_input: RefCell<Option<Expression>> = RefCell::new(None);
4089
4090        let rewritten = transform_recursive(sel_expr, &|e| {
4091            if let Expression::Lateral(lat) = e {
4092                if let Some(input_expr) = extract_flatten_input(&lat) {
4093                    if found_input.borrow().is_none() {
4094                        *found_input.borrow_mut() = Some(input_expr);
4095                    }
4096                    return Ok(Expression::Lateral(Box::new(rewrite_flatten_lateral(*lat))));
4097                }
4098                return Ok(Expression::Lateral(lat));
4099            }
4100            Ok(e)
4101        })?;
4102
4103        if let Some(input) = found_input.into_inner() {
4104            flattened_inputs.push(input);
4105        }
4106        new_selects.push(rewritten);
4107    }
4108
4109    if flattened_inputs.is_empty() {
4110        select.expressions = new_selects;
4111        return Ok(Expression::Select(Box::new(select)));
4112    }
4113
4114    select.expressions = new_selects;
4115
4116    for (idx, input_expr) in flattened_inputs.into_iter().enumerate() {
4117        // Match sqlglot naming: first pair is _u/_u_2 with pos/pos_2 and entity.
4118        let is_first = idx == 0;
4119        let series_alias = if is_first {
4120            "pos".to_string()
4121        } else {
4122            format!("pos_{}", idx + 1)
4123        };
4124        let series_source_alias = if is_first {
4125            "_u".to_string()
4126        } else {
4127            format!("_u_{}", idx * 2 + 1)
4128        };
4129        let unnest_source_alias = if is_first {
4130            "_u_2".to_string()
4131        } else {
4132            format!("_u_{}", idx * 2 + 2)
4133        };
4134        let pos2_alias = if is_first {
4135            "pos_2".to_string()
4136        } else {
4137            format!("{}_2", series_alias)
4138        };
4139        let entity_alias = if is_first {
4140            "entity".to_string()
4141        } else {
4142            format!("entity_{}", idx + 1)
4143        };
4144
4145        let array_size_call = Expression::Function(Box::new(Function::new(
4146            "ARRAY_SIZE".to_string(),
4147            vec![Expression::NamedArgument(Box::new(NamedArgument {
4148                name: Identifier::new("INPUT"),
4149                value: input_expr.clone(),
4150                separator: NamedArgSeparator::DArrow,
4151            }))],
4152        )));
4153
4154        let greatest = Expression::Function(Box::new(Function::new(
4155            "GREATEST".to_string(),
4156            vec![array_size_call.clone()],
4157        )));
4158
4159        let series_end = Expression::Add(Box::new(BinaryOp::new(
4160            Expression::Paren(Box::new(crate::expressions::Paren {
4161                this: Expression::Sub(Box::new(BinaryOp::new(
4162                    greatest,
4163                    Expression::Literal(Box::new(Literal::Number("1".to_string()))),
4164                ))),
4165                trailing_comments: Vec::new(),
4166            })),
4167            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
4168        )));
4169
4170        let series_range = Expression::Function(Box::new(Function::new(
4171            "ARRAY_GENERATE_RANGE".to_string(),
4172            vec![
4173                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
4174                series_end,
4175            ],
4176        )));
4177
4178        let series_flatten = Expression::Function(Box::new(Function::new(
4179            "FLATTEN".to_string(),
4180            vec![Expression::NamedArgument(Box::new(NamedArgument {
4181                name: Identifier::new("INPUT"),
4182                value: series_range,
4183                separator: NamedArgSeparator::DArrow,
4184            }))],
4185        )));
4186
4187        let series_table = Expression::Function(Box::new(Function::new(
4188            "TABLE".to_string(),
4189            vec![series_flatten],
4190        )));
4191
4192        let series_alias_expr = Expression::Alias(Box::new(Alias {
4193            this: series_table,
4194            alias: Identifier::new(series_source_alias.clone()),
4195            column_aliases: vec![
4196                Identifier::new("seq"),
4197                Identifier::new("key"),
4198                Identifier::new("path"),
4199                Identifier::new("index"),
4200                Identifier::new(series_alias.clone()),
4201                Identifier::new("this"),
4202            ],
4203            alias_explicit_as: false,
4204            alias_keyword: None,
4205            pre_alias_comments: Vec::new(),
4206            trailing_comments: Vec::new(),
4207            inferred_type: None,
4208        }));
4209
4210        select.joins.push(Join {
4211            this: series_alias_expr,
4212            on: None,
4213            using: Vec::new(),
4214            kind: JoinKind::Cross,
4215            use_inner_keyword: false,
4216            use_outer_keyword: false,
4217            deferred_condition: false,
4218            join_hint: None,
4219            match_condition: None,
4220            pivots: Vec::new(),
4221            comments: Vec::new(),
4222            nesting_group: 0,
4223            directed: false,
4224        });
4225
4226        let entity_flatten = Expression::Function(Box::new(Function::new(
4227            "FLATTEN".to_string(),
4228            vec![Expression::NamedArgument(Box::new(NamedArgument {
4229                name: Identifier::new("INPUT"),
4230                value: input_expr.clone(),
4231                separator: NamedArgSeparator::DArrow,
4232            }))],
4233        )));
4234
4235        let entity_table = Expression::Function(Box::new(Function::new(
4236            "TABLE".to_string(),
4237            vec![entity_flatten],
4238        )));
4239
4240        let entity_alias_expr = Expression::Alias(Box::new(Alias {
4241            this: entity_table,
4242            alias: Identifier::new(unnest_source_alias.clone()),
4243            column_aliases: vec![
4244                Identifier::new("seq"),
4245                Identifier::new("key"),
4246                Identifier::new("path"),
4247                Identifier::new(pos2_alias.clone()),
4248                Identifier::new(entity_alias.clone()),
4249                Identifier::new("this"),
4250            ],
4251            alias_explicit_as: false,
4252            alias_keyword: None,
4253            pre_alias_comments: Vec::new(),
4254            trailing_comments: Vec::new(),
4255            inferred_type: None,
4256        }));
4257
4258        select.joins.push(Join {
4259            this: entity_alias_expr,
4260            on: None,
4261            using: Vec::new(),
4262            kind: JoinKind::Cross,
4263            use_inner_keyword: false,
4264            use_outer_keyword: false,
4265            deferred_condition: false,
4266            join_hint: None,
4267            match_condition: None,
4268            pivots: Vec::new(),
4269            comments: Vec::new(),
4270            nesting_group: 0,
4271            directed: false,
4272        });
4273
4274        let pos_col =
4275            Expression::qualified_column(series_source_alias.clone(), series_alias.clone());
4276        let pos2_col =
4277            Expression::qualified_column(unnest_source_alias.clone(), pos2_alias.clone());
4278
4279        let eq = Expression::Eq(Box::new(BinaryOp::new(pos_col.clone(), pos2_col.clone())));
4280        let size_minus_1 = Expression::Paren(Box::new(crate::expressions::Paren {
4281            this: Expression::Sub(Box::new(BinaryOp::new(
4282                array_size_call,
4283                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
4284            ))),
4285            trailing_comments: Vec::new(),
4286        }));
4287        let gt = Expression::Gt(Box::new(BinaryOp::new(pos_col, size_minus_1.clone())));
4288        let pos2_eq_size = Expression::Eq(Box::new(BinaryOp::new(pos2_col, size_minus_1)));
4289        let and_cond = Expression::And(Box::new(BinaryOp::new(gt, pos2_eq_size)));
4290        let or_cond = Expression::Or(Box::new(BinaryOp::new(
4291            eq,
4292            Expression::Paren(Box::new(crate::expressions::Paren {
4293                this: and_cond,
4294                trailing_comments: Vec::new(),
4295            })),
4296        )));
4297
4298        select.where_clause = Some(match select.where_clause.take() {
4299            Some(existing) => Where {
4300                this: Expression::And(Box::new(BinaryOp::new(existing.this, or_cond))),
4301            },
4302            None => Where { this: or_cond },
4303        });
4304    }
4305
4306    Ok(Expression::Select(Box::new(select)))
4307}
4308
4309fn extract_flatten_input(lat: &Lateral) -> Option<Expression> {
4310    let Expression::Function(f) = lat.this.as_ref() else {
4311        return None;
4312    };
4313    if !f.name.eq_ignore_ascii_case("FLATTEN") {
4314        return None;
4315    }
4316
4317    for arg in &f.args {
4318        if let Expression::NamedArgument(na) = arg {
4319            if na.name.name.eq_ignore_ascii_case("INPUT") {
4320                return Some(na.value.clone());
4321            }
4322        }
4323    }
4324    f.args.first().cloned()
4325}
4326
4327fn rewrite_flatten_lateral(mut lat: Lateral) -> Lateral {
4328    let cond = Expression::Eq(Box::new(BinaryOp::new(
4329        Expression::qualified_column("_u", "pos"),
4330        Expression::qualified_column("_u_2", "pos_2"),
4331    )));
4332    let true_expr = Expression::qualified_column("_u_2", "entity");
4333    let iff_expr = Expression::Function(Box::new(Function::new(
4334        "IFF".to_string(),
4335        vec![cond, true_expr, Expression::Null(crate::expressions::Null)],
4336    )));
4337
4338    lat.this = Box::new(iff_expr);
4339    if lat.column_aliases.is_empty() {
4340        lat.column_aliases = vec![
4341            "SEQ".to_string(),
4342            "KEY".to_string(),
4343            "PATH".to_string(),
4344            "INDEX".to_string(),
4345            "VALUE".to_string(),
4346            "THIS".to_string(),
4347        ];
4348    }
4349    lat
4350}
4351
4352/// Info about an EXPLODE/POSEXPLODE found in a SELECT projection
4353struct ExplodeInfo {
4354    /// The argument to EXPLODE/POSEXPLODE (the array expression)
4355    arg_sql: String,
4356    /// The alias for the exploded column
4357    explode_alias: String,
4358    /// The alias for the position column
4359    pos_alias: String,
4360    /// Source alias for this unnest (e.g., _u_2)
4361    unnest_source_alias: String,
4362}
4363
4364fn explode_projection_to_unnest_impl(select: Select, target: DialectType) -> Result<Expression> {
4365    let is_presto = matches!(
4366        target,
4367        DialectType::Presto | DialectType::Trino | DialectType::Athena
4368    );
4369    let is_bigquery = matches!(target, DialectType::BigQuery);
4370
4371    if !is_presto && !is_bigquery {
4372        return Ok(Expression::Select(Box::new(select)));
4373    }
4374
4375    // Check if any SELECT projection contains EXPLODE or POSEXPLODE
4376    let has_explode = select.expressions.iter().any(|e| expr_contains_explode(e));
4377    if !has_explode {
4378        return Ok(Expression::Select(Box::new(select)));
4379    }
4380
4381    // Collect taken names from existing SELECT expressions and FROM sources
4382    let mut taken_select_names = std::collections::HashSet::new();
4383    let mut taken_source_names = std::collections::HashSet::new();
4384
4385    // Collect names from existing SELECT expressions (output names)
4386    for sel in &select.expressions {
4387        if let Some(name) = get_output_name(sel) {
4388            taken_select_names.insert(name);
4389        }
4390    }
4391
4392    // Also add the explode arg name if it's a column reference
4393    for sel in &select.expressions {
4394        let explode_expr = find_explode_in_expr(sel);
4395        if let Some(arg) = explode_expr {
4396            if let Some(name) = get_output_name(&arg) {
4397                taken_select_names.insert(name);
4398            }
4399        }
4400    }
4401
4402    // Collect source names from FROM clause
4403    if let Some(ref from) = select.from {
4404        for from_expr in &from.expressions {
4405            collect_source_names(from_expr, &mut taken_source_names);
4406        }
4407    }
4408    // Also collect from JOINs
4409    for join in &select.joins {
4410        collect_source_names(&join.this, &mut taken_source_names);
4411    }
4412
4413    // Generate series alias
4414    let series_alias = new_name(&mut taken_select_names, "pos");
4415
4416    // Generate series source alias
4417    let series_source_alias = new_name(&mut taken_source_names, "_u");
4418
4419    // Get the target dialect for generating expression SQL
4420    let target_dialect = Dialect::get(target);
4421
4422    // Process each SELECT expression, collecting explode info
4423    let mut explode_infos: Vec<ExplodeInfo> = Vec::new();
4424    let mut new_projections: Vec<String> = Vec::new();
4425
4426    for (_idx, sel_expr) in select.expressions.iter().enumerate() {
4427        let explode_data = extract_explode_data(sel_expr);
4428
4429        if let Some((is_posexplode, arg_expr, explicit_alias, explicit_pos_alias)) = explode_data {
4430            // Generate the argument SQL in target dialect
4431            let arg_sql = target_dialect
4432                .generate(&arg_expr)
4433                .unwrap_or_else(|_| "NULL".to_string());
4434
4435            let unnest_source_alias = new_name(&mut taken_source_names, "_u");
4436
4437            let explode_alias = if let Some(ref ea) = explicit_alias {
4438                // Use the explicit alias directly (it was explicitly specified by the user)
4439                // Remove from taken_select_names first to avoid false collision with itself
4440                taken_select_names.remove(ea.as_str());
4441                // Now check for collision with other names
4442                let name = new_name(&mut taken_select_names, ea);
4443                name
4444            } else {
4445                new_name(&mut taken_select_names, "col")
4446            };
4447
4448            let pos_alias = if let Some(ref pa) = explicit_pos_alias {
4449                // Use the explicit pos alias directly
4450                taken_select_names.remove(pa.as_str());
4451                let name = new_name(&mut taken_select_names, pa);
4452                name
4453            } else {
4454                new_name(&mut taken_select_names, "pos")
4455            };
4456
4457            // Build the IF projection
4458            if is_presto {
4459                // Presto: IF(_u.pos = _u_2.pos_2, _u_2.col) AS col
4460                let if_col = format!(
4461                    "IF({}.{} = {}.{}, {}.{}) AS {}",
4462                    series_source_alias,
4463                    series_alias,
4464                    unnest_source_alias,
4465                    pos_alias,
4466                    unnest_source_alias,
4467                    explode_alias,
4468                    explode_alias
4469                );
4470                new_projections.push(if_col);
4471
4472                // For POSEXPLODE, also add the position projection
4473                if is_posexplode {
4474                    let if_pos = format!(
4475                        "IF({}.{} = {}.{}, {}.{}) AS {}",
4476                        series_source_alias,
4477                        series_alias,
4478                        unnest_source_alias,
4479                        pos_alias,
4480                        unnest_source_alias,
4481                        pos_alias,
4482                        pos_alias
4483                    );
4484                    new_projections.push(if_pos);
4485                }
4486            } else {
4487                // BigQuery: IF(pos = pos_2, col, NULL) AS col
4488                let if_col = format!(
4489                    "IF({} = {}, {}, NULL) AS {}",
4490                    series_alias, pos_alias, explode_alias, explode_alias
4491                );
4492                new_projections.push(if_col);
4493
4494                // For POSEXPLODE, also add the position projection
4495                if is_posexplode {
4496                    let if_pos = format!(
4497                        "IF({} = {}, {}, NULL) AS {}",
4498                        series_alias, pos_alias, pos_alias, pos_alias
4499                    );
4500                    new_projections.push(if_pos);
4501                }
4502            }
4503
4504            explode_infos.push(ExplodeInfo {
4505                arg_sql,
4506                explode_alias,
4507                pos_alias,
4508                unnest_source_alias,
4509            });
4510        } else {
4511            // Not an EXPLODE expression, generate as-is
4512            let sel_sql = target_dialect
4513                .generate(sel_expr)
4514                .unwrap_or_else(|_| "*".to_string());
4515            new_projections.push(sel_sql);
4516        }
4517    }
4518
4519    if explode_infos.is_empty() {
4520        return Ok(Expression::Select(Box::new(select)));
4521    }
4522
4523    // Build the FROM clause
4524    let mut from_parts: Vec<String> = Vec::new();
4525
4526    // Existing FROM sources
4527    if let Some(ref from) = select.from {
4528        for from_expr in &from.expressions {
4529            let from_sql = target_dialect.generate(from_expr).unwrap_or_default();
4530            from_parts.push(from_sql);
4531        }
4532    }
4533
4534    // Build the size expressions for the series generator
4535    let size_exprs: Vec<String> = explode_infos
4536        .iter()
4537        .map(|info| {
4538            if is_presto {
4539                format!("CARDINALITY({})", info.arg_sql)
4540            } else {
4541                format!("ARRAY_LENGTH({})", info.arg_sql)
4542            }
4543        })
4544        .collect();
4545
4546    let greatest_arg = if size_exprs.len() == 1 {
4547        size_exprs[0].clone()
4548    } else {
4549        format!("GREATEST({})", size_exprs.join(", "))
4550    };
4551
4552    // Build the series source
4553    // greatest_arg is already "GREATEST(...)" when multiple, or "CARDINALITY(x)" / "ARRAY_LENGTH(x)" when single
4554    let series_sql = if is_presto {
4555        // SEQUENCE(1, GREATEST(CARDINALITY(x))) for single, SEQUENCE(1, GREATEST(C(a), C(b))) for multiple
4556        if size_exprs.len() == 1 {
4557            format!(
4558                "UNNEST(SEQUENCE(1, GREATEST({}))) AS {}({})",
4559                greatest_arg, series_source_alias, series_alias
4560            )
4561        } else {
4562            // greatest_arg already has GREATEST(...) wrapper
4563            format!(
4564                "UNNEST(SEQUENCE(1, {})) AS {}({})",
4565                greatest_arg, series_source_alias, series_alias
4566            )
4567        }
4568    } else {
4569        // GENERATE_ARRAY(0, GREATEST(ARRAY_LENGTH(x)) - 1) for single
4570        if size_exprs.len() == 1 {
4571            format!(
4572                "UNNEST(GENERATE_ARRAY(0, GREATEST({}) - 1)) AS {}",
4573                greatest_arg, series_alias
4574            )
4575        } else {
4576            // greatest_arg already has GREATEST(...) wrapper
4577            format!(
4578                "UNNEST(GENERATE_ARRAY(0, {} - 1)) AS {}",
4579                greatest_arg, series_alias
4580            )
4581        }
4582    };
4583
4584    // Build CROSS JOIN UNNEST clauses
4585    // Always use Presto-style (WITH ORDINALITY) for the SQL string to parse,
4586    // then convert to BigQuery-style AST after parsing if needed
4587    let mut cross_joins: Vec<String> = Vec::new();
4588
4589    for info in &explode_infos {
4590        // Always use WITH ORDINALITY syntax (which our parser handles)
4591        cross_joins.push(format!(
4592            "CROSS JOIN UNNEST({}) WITH ORDINALITY AS {}({}, {})",
4593            info.arg_sql, info.unnest_source_alias, info.explode_alias, info.pos_alias
4594        ));
4595    }
4596
4597    // Build WHERE clause
4598    let mut where_conditions: Vec<String> = Vec::new();
4599
4600    for info in &explode_infos {
4601        let size_expr = if is_presto {
4602            format!("CARDINALITY({})", info.arg_sql)
4603        } else {
4604            format!("ARRAY_LENGTH({})", info.arg_sql)
4605        };
4606
4607        let cond = if is_presto {
4608            format!(
4609                "{series_src}.{series_al} = {unnest_src}.{pos_al} OR ({series_src}.{series_al} > {size} AND {unnest_src}.{pos_al} = {size})",
4610                series_src = series_source_alias,
4611                series_al = series_alias,
4612                unnest_src = info.unnest_source_alias,
4613                pos_al = info.pos_alias,
4614                size = size_expr
4615            )
4616        } else {
4617            format!(
4618                "{series_al} = {pos_al} OR ({series_al} > ({size} - 1) AND {pos_al} = ({size} - 1))",
4619                series_al = series_alias,
4620                pos_al = info.pos_alias,
4621                size = size_expr
4622            )
4623        };
4624
4625        where_conditions.push(cond);
4626    }
4627
4628    // Combine WHERE conditions with AND (wrapped in parens if multiple)
4629    let where_sql = if where_conditions.len() == 1 {
4630        where_conditions[0].clone()
4631    } else {
4632        where_conditions
4633            .iter()
4634            .map(|c| format!("({})", c))
4635            .collect::<Vec<_>>()
4636            .join(" AND ")
4637    };
4638
4639    // Build the complete SQL
4640    let select_part = new_projections.join(", ");
4641
4642    // FROM part: if there was no original FROM, the series becomes the FROM source
4643    let from_and_joins = if from_parts.is_empty() {
4644        // No original FROM: series is the FROM source, everything else is CROSS JOIN
4645        format!("FROM {} {}", series_sql, cross_joins.join(" "))
4646    } else {
4647        format!(
4648            "FROM {} {} {}",
4649            from_parts.join(", "),
4650            format!("CROSS JOIN {}", series_sql),
4651            cross_joins.join(" ")
4652        )
4653    };
4654
4655    let full_sql = format!(
4656        "SELECT {} {} WHERE {}",
4657        select_part, from_and_joins, where_sql
4658    );
4659
4660    // Parse the constructed SQL using the Generic dialect (which handles all SQL syntax)
4661    // We use Generic instead of the target dialect to avoid parser limitations
4662    let generic_dialect = Dialect::get(DialectType::Generic);
4663    let parsed = generic_dialect.parse(&full_sql);
4664    match parsed {
4665        Ok(mut stmts) if !stmts.is_empty() => {
4666            let mut result = stmts.remove(0);
4667
4668            // For BigQuery, convert Presto-style UNNEST AST to BigQuery-style
4669            // Presto: Alias(Unnest(with_ordinality=true), alias=_u_N, column_aliases=[col, pos])
4670            // BigQuery: Unnest(with_ordinality=true, alias=col, offset_alias=pos) [no outer Alias]
4671            if is_bigquery {
4672                convert_unnest_presto_to_bigquery(&mut result);
4673            }
4674
4675            Ok(result)
4676        }
4677        _ => {
4678            // If parsing fails, return the original expression unchanged
4679            Ok(Expression::Select(Box::new(select)))
4680        }
4681    }
4682}
4683
4684/// Convert Presto-style UNNEST WITH ORDINALITY to BigQuery-style UNNEST WITH OFFSET in the AST.
4685/// Presto: Alias(Unnest(with_ordinality=true), alias=_u_N, column_aliases=[col, pos_N])
4686/// BigQuery: Unnest(with_ordinality=true, alias=col, offset_alias=pos_N)
4687fn convert_unnest_presto_to_bigquery(expr: &mut Expression) {
4688    match expr {
4689        Expression::Select(ref mut select) => {
4690            // Convert in FROM clause
4691            if let Some(ref mut from) = select.from {
4692                for from_item in from.expressions.iter_mut() {
4693                    convert_unnest_presto_to_bigquery(from_item);
4694                }
4695            }
4696            // Convert in JOINs
4697            for join in select.joins.iter_mut() {
4698                convert_unnest_presto_to_bigquery(&mut join.this);
4699            }
4700        }
4701        Expression::Alias(ref alias) => {
4702            // Check if this is Alias(Unnest(with_ordinality=true), ..., column_aliases=[col, pos])
4703            if let Expression::Unnest(ref unnest) = alias.this {
4704                if unnest.with_ordinality && alias.column_aliases.len() >= 2 {
4705                    let col_alias = alias.column_aliases[0].clone();
4706                    let pos_alias = alias.column_aliases[1].clone();
4707                    let mut new_unnest = unnest.as_ref().clone();
4708                    new_unnest.alias = Some(col_alias);
4709                    new_unnest.offset_alias = Some(pos_alias);
4710                    // Replace the Alias(Unnest) with just Unnest
4711                    *expr = Expression::Unnest(Box::new(new_unnest));
4712                }
4713            }
4714        }
4715        _ => {}
4716    }
4717}
4718
4719/// Find a new name that doesn't conflict with existing names.
4720/// Tries `base`, then `base_2`, `base_3`, etc.
4721fn new_name(names: &mut std::collections::HashSet<String>, base: &str) -> String {
4722    if !names.contains(base) {
4723        names.insert(base.to_string());
4724        return base.to_string();
4725    }
4726    let mut i = 2;
4727    loop {
4728        let candidate = format!("{}_{}", base, i);
4729        if !names.contains(&candidate) {
4730            names.insert(candidate.clone());
4731            return candidate;
4732        }
4733        i += 1;
4734    }
4735}
4736
4737/// Check if an expression contains EXPLODE or POSEXPLODE
4738fn expr_contains_explode(expr: &Expression) -> bool {
4739    match expr {
4740        Expression::Explode(_) => true,
4741        Expression::ExplodeOuter(_) => true,
4742        Expression::Function(f) => {
4743            let name = f.name.to_uppercase();
4744            name == "POSEXPLODE" || name == "POSEXPLODE_OUTER"
4745        }
4746        Expression::Alias(a) => expr_contains_explode(&a.this),
4747        _ => false,
4748    }
4749}
4750
4751/// Find the EXPLODE/POSEXPLODE expression within a select item, return the arg
4752fn find_explode_in_expr(expr: &Expression) -> Option<Expression> {
4753    match expr {
4754        Expression::Explode(uf) => Some(uf.this.clone()),
4755        Expression::ExplodeOuter(uf) => Some(uf.this.clone()),
4756        Expression::Function(f) => {
4757            let name = f.name.to_uppercase();
4758            if (name == "POSEXPLODE" || name == "POSEXPLODE_OUTER") && !f.args.is_empty() {
4759                Some(f.args[0].clone())
4760            } else {
4761                None
4762            }
4763        }
4764        Expression::Alias(a) => find_explode_in_expr(&a.this),
4765        _ => None,
4766    }
4767}
4768
4769/// Extract explode data from a SELECT expression.
4770/// Returns (is_posexplode, arg_expression, explicit_col_alias, explicit_pos_alias)
4771fn extract_explode_data(
4772    expr: &Expression,
4773) -> Option<(bool, Expression, Option<String>, Option<String>)> {
4774    match expr {
4775        // Bare EXPLODE(x) without alias
4776        Expression::Explode(uf) => Some((false, uf.this.clone(), None, None)),
4777        Expression::ExplodeOuter(uf) => Some((false, uf.this.clone(), None, None)),
4778        // Bare POSEXPLODE(x) without alias
4779        Expression::Function(f) => {
4780            let name = f.name.to_uppercase();
4781            if (name == "POSEXPLODE" || name == "POSEXPLODE_OUTER") && !f.args.is_empty() {
4782                Some((true, f.args[0].clone(), None, None))
4783            } else {
4784                None
4785            }
4786        }
4787        // Aliased: EXPLODE(x) AS col, or POSEXPLODE(x) AS (a, b)
4788        Expression::Alias(a) => {
4789            match &a.this {
4790                Expression::Explode(uf) => {
4791                    let alias = if !a.alias.is_empty() {
4792                        Some(a.alias.name.clone())
4793                    } else {
4794                        None
4795                    };
4796                    Some((false, uf.this.clone(), alias, None))
4797                }
4798                Expression::ExplodeOuter(uf) => {
4799                    let alias = if !a.alias.is_empty() {
4800                        Some(a.alias.name.clone())
4801                    } else {
4802                        None
4803                    };
4804                    Some((false, uf.this.clone(), alias, None))
4805                }
4806                Expression::Function(f) => {
4807                    let name = f.name.to_uppercase();
4808                    if (name == "POSEXPLODE" || name == "POSEXPLODE_OUTER") && !f.args.is_empty() {
4809                        // Check for column aliases: AS (a, b)
4810                        if a.column_aliases.len() == 2 {
4811                            let pos_alias = a.column_aliases[0].name.clone();
4812                            let col_alias = a.column_aliases[1].name.clone();
4813                            Some((true, f.args[0].clone(), Some(col_alias), Some(pos_alias)))
4814                        } else if !a.alias.is_empty() {
4815                            // Single alias: AS x
4816                            Some((true, f.args[0].clone(), Some(a.alias.name.clone()), None))
4817                        } else {
4818                            Some((true, f.args[0].clone(), None, None))
4819                        }
4820                    } else {
4821                        None
4822                    }
4823                }
4824                _ => None,
4825            }
4826        }
4827        _ => None,
4828    }
4829}
4830
4831/// Get the output name of a SELECT expression
4832fn get_output_name(expr: &Expression) -> Option<String> {
4833    match expr {
4834        Expression::Alias(a) => {
4835            if !a.alias.is_empty() {
4836                Some(a.alias.name.clone())
4837            } else {
4838                None
4839            }
4840        }
4841        Expression::Column(c) => Some(c.name.name.clone()),
4842        Expression::Identifier(id) => Some(id.name.clone()),
4843        _ => None,
4844    }
4845}
4846
4847/// Collect source names from a FROM/JOIN expression
4848fn collect_source_names(expr: &Expression, names: &mut std::collections::HashSet<String>) {
4849    match expr {
4850        Expression::Alias(a) => {
4851            if !a.alias.is_empty() {
4852                names.insert(a.alias.name.clone());
4853            }
4854        }
4855        Expression::Subquery(s) => {
4856            if let Some(ref alias) = s.alias {
4857                names.insert(alias.name.clone());
4858            }
4859        }
4860        Expression::Table(t) => {
4861            if let Some(ref alias) = t.alias {
4862                names.insert(alias.name.clone());
4863            } else {
4864                names.insert(t.name.name.clone());
4865            }
4866        }
4867        Expression::Column(c) => {
4868            names.insert(c.name.name.clone());
4869        }
4870        Expression::Identifier(id) => {
4871            names.insert(id.name.clone());
4872        }
4873        _ => {}
4874    }
4875}
4876
4877/// Strip UNNEST wrapping from column reference arguments for Redshift target.
4878/// BigQuery UNNEST(column_ref) -> Redshift: just column_ref
4879pub fn strip_unnest_column_refs(expr: Expression) -> Result<Expression> {
4880    use crate::dialects::transform_recursive;
4881    transform_recursive(expr, &strip_unnest_column_refs_single)
4882}
4883
4884fn strip_unnest_column_refs_single(expr: Expression) -> Result<Expression> {
4885    if let Expression::Select(mut select) = expr {
4886        // Process JOINs (UNNEST items have been moved to joins by unnest_from_to_cross_join)
4887        for join in select.joins.iter_mut() {
4888            strip_unnest_from_expr(&mut join.this);
4889        }
4890        // Process FROM items too
4891        if let Some(ref mut from) = select.from {
4892            for from_item in from.expressions.iter_mut() {
4893                strip_unnest_from_expr(from_item);
4894            }
4895        }
4896        Ok(Expression::Select(select))
4897    } else {
4898        Ok(expr)
4899    }
4900}
4901
4902/// If expr is Alias(UNNEST(column_ref), alias) where UNNEST arg is a column/dot path,
4903/// replace with Alias(column_ref, alias) to strip the UNNEST.
4904fn strip_unnest_from_expr(expr: &mut Expression) {
4905    if let Expression::Alias(ref mut alias) = expr {
4906        if let Expression::Unnest(ref unnest) = alias.this {
4907            let is_column_ref = matches!(&unnest.this, Expression::Column(_) | Expression::Dot(_));
4908            if is_column_ref {
4909                // Replace UNNEST(col_ref) with just col_ref
4910                let inner = unnest.this.clone();
4911                alias.this = inner;
4912            }
4913        }
4914    }
4915}
4916
4917/// Wrap DuckDB UNNEST of struct arrays in (SELECT UNNEST(..., max_depth => 2)) subquery.
4918/// BigQuery UNNEST of struct arrays needs this wrapping for DuckDB to properly expand struct fields.
4919pub fn wrap_duckdb_unnest_struct(expr: Expression) -> Result<Expression> {
4920    use crate::dialects::transform_recursive;
4921    transform_recursive(expr, &wrap_duckdb_unnest_struct_single)
4922}
4923
4924fn wrap_duckdb_unnest_struct_single(expr: Expression) -> Result<Expression> {
4925    if let Expression::Select(mut select) = expr {
4926        // Process FROM items
4927        if let Some(ref mut from) = select.from {
4928            for from_item in from.expressions.iter_mut() {
4929                try_wrap_unnest_in_subquery(from_item);
4930            }
4931        }
4932
4933        // Process JOINs
4934        for join in select.joins.iter_mut() {
4935            try_wrap_unnest_in_subquery(&mut join.this);
4936        }
4937
4938        Ok(Expression::Select(select))
4939    } else {
4940        Ok(expr)
4941    }
4942}
4943
4944/// Check if an expression contains struct array elements that need DuckDB UNNEST wrapping.
4945fn is_struct_array_unnest_arg(expr: &Expression) -> bool {
4946    match expr {
4947        // Array literal containing struct elements
4948        Expression::Array(arr) => arr
4949            .expressions
4950            .iter()
4951            .any(|e| matches!(e, Expression::Struct(_))),
4952        Expression::ArrayFunc(arr) => arr
4953            .expressions
4954            .iter()
4955            .any(|e| matches!(e, Expression::Struct(_))),
4956        // CAST to struct array type, e.g. CAST([] AS STRUCT(x BIGINT)[])
4957        Expression::Cast(c) => {
4958            matches!(&c.to, DataType::Array { element_type, .. } if matches!(**element_type, DataType::Struct { .. }))
4959        }
4960        _ => false,
4961    }
4962}
4963
4964/// Try to wrap an UNNEST expression in a (SELECT UNNEST(..., max_depth => 2)) subquery.
4965/// Handles both bare UNNEST and Alias(UNNEST).
4966fn try_wrap_unnest_in_subquery(expr: &mut Expression) {
4967    // Check for Alias wrapping UNNEST
4968    if let Expression::Alias(ref alias) = expr {
4969        if let Expression::Unnest(ref unnest) = alias.this {
4970            if is_struct_array_unnest_arg(&unnest.this) {
4971                let unnest_clone = (**unnest).clone();
4972                let alias_name = alias.alias.clone();
4973                let new_expr = make_unnest_subquery(unnest_clone, Some(alias_name));
4974                *expr = new_expr;
4975                return;
4976            }
4977        }
4978    }
4979
4980    // Check for bare UNNEST
4981    if let Expression::Unnest(ref unnest) = expr {
4982        if is_struct_array_unnest_arg(&unnest.this) {
4983            let unnest_clone = (**unnest).clone();
4984            let new_expr = make_unnest_subquery(unnest_clone, None);
4985            *expr = new_expr;
4986        }
4987    }
4988}
4989
4990/// Create (SELECT UNNEST(arg, max_depth => 2)) [AS alias] subquery.
4991fn make_unnest_subquery(unnest: UnnestFunc, alias: Option<Identifier>) -> Expression {
4992    // Build UNNEST function call with max_depth => 2 named argument
4993    let max_depth_arg = Expression::NamedArgument(Box::new(NamedArgument {
4994        name: Identifier::new("max_depth".to_string()),
4995        value: Expression::Literal(Box::new(Literal::Number("2".to_string()))),
4996        separator: NamedArgSeparator::DArrow,
4997    }));
4998
4999    let mut unnest_args = vec![unnest.this];
5000    unnest_args.extend(unnest.expressions);
5001    unnest_args.push(max_depth_arg);
5002
5003    let unnest_func =
5004        Expression::Function(Box::new(Function::new("UNNEST".to_string(), unnest_args)));
5005
5006    // Build SELECT UNNEST(...)
5007    let mut inner_select = Select::new();
5008    inner_select.expressions = vec![unnest_func];
5009    let inner_select = Expression::Select(Box::new(inner_select));
5010
5011    // Wrap in subquery
5012    let subquery = Subquery {
5013        this: inner_select,
5014        alias,
5015        column_aliases: Vec::new(),
5016        alias_explicit_as: false,
5017        alias_keyword: None,
5018        order_by: None,
5019        limit: None,
5020        offset: None,
5021        distribute_by: None,
5022        sort_by: None,
5023        cluster_by: None,
5024        lateral: false,
5025        modifiers_inside: false,
5026        trailing_comments: Vec::new(),
5027        inferred_type: None,
5028    };
5029
5030    Expression::Subquery(Box::new(subquery))
5031}
5032
5033/// Wrap UNION with ORDER BY/LIMIT in a subquery.
5034///
5035/// Some dialects (ClickHouse, TSQL) don't support ORDER BY/LIMIT directly on UNION.
5036/// This transform converts:
5037///   SELECT ... UNION SELECT ... ORDER BY x LIMIT n
5038/// to:
5039///   SELECT * FROM (SELECT ... UNION SELECT ...) AS _l_0 ORDER BY x LIMIT n
5040///
5041/// NOTE: Our parser may place ORDER BY/LIMIT on the right-hand SELECT rather than
5042/// the Union (unlike Python sqlglot). This function handles both cases by checking
5043/// the right-hand SELECT for trailing ORDER BY/LIMIT and moving them to the Union.
5044pub fn no_limit_order_by_union(expr: Expression) -> Result<Expression> {
5045    use crate::expressions::{Limit as LimitClause, Offset as OffsetClause, OrderBy, Star};
5046
5047    match expr {
5048        Expression::Union(mut u) => {
5049            // Check if ORDER BY/LIMIT are on the rightmost Select instead of the Union
5050            // (our parser may attach them to the right SELECT)
5051            if u.order_by.is_none() && u.limit.is_none() && u.offset.is_none() {
5052                // Find the rightmost Select and check for ORDER BY/LIMIT
5053                if let Expression::Select(ref mut right_select) = u.right {
5054                    if right_select.order_by.is_some()
5055                        || right_select.limit.is_some()
5056                        || right_select.offset.is_some()
5057                    {
5058                        // Move ORDER BY/LIMIT from right Select to Union
5059                        u.order_by = right_select.order_by.take();
5060                        u.limit = right_select.limit.take().map(|l| Box::new(l.this));
5061                        u.offset = right_select.offset.take().map(|o| Box::new(o.this));
5062                    }
5063                }
5064            }
5065
5066            let has_order_or_limit =
5067                u.order_by.is_some() || u.limit.is_some() || u.offset.is_some();
5068            if has_order_or_limit {
5069                // Extract ORDER BY, LIMIT, OFFSET from the Union
5070                let order_by: Option<OrderBy> = u.order_by.take();
5071                let union_limit: Option<Box<Expression>> = u.limit.take();
5072                let union_offset: Option<Box<Expression>> = u.offset.take();
5073
5074                // Convert Union's limit (Box<Expression>) to Select's limit (Limit struct)
5075                let select_limit: Option<LimitClause> = union_limit.map(|l| LimitClause {
5076                    this: *l,
5077                    percent: false,
5078                    comments: Vec::new(),
5079                });
5080
5081                // Convert Union's offset (Box<Expression>) to Select's offset (Offset struct)
5082                let select_offset: Option<OffsetClause> = union_offset.map(|o| OffsetClause {
5083                    this: *o,
5084                    rows: None,
5085                });
5086
5087                // Create a subquery from the Union
5088                let subquery = Subquery {
5089                    this: Expression::Union(u),
5090                    alias: Some(Identifier::new("_l_0")),
5091                    column_aliases: Vec::new(),
5092                    alias_explicit_as: true,
5093                    alias_keyword: None,
5094                    lateral: false,
5095                    modifiers_inside: false,
5096                    order_by: None,
5097                    limit: None,
5098                    offset: None,
5099                    distribute_by: None,
5100                    sort_by: None,
5101                    cluster_by: None,
5102                    trailing_comments: Vec::new(),
5103                    inferred_type: None,
5104                };
5105
5106                // Build SELECT * FROM (UNION) AS _l_0 ORDER BY ... LIMIT ...
5107                let mut select = Select::default();
5108                select.expressions = vec![Expression::Star(Star {
5109                    table: None,
5110                    except: None,
5111                    replace: None,
5112                    rename: None,
5113                    trailing_comments: Vec::new(),
5114                    span: None,
5115                })];
5116                select.from = Some(From {
5117                    expressions: vec![Expression::Subquery(Box::new(subquery))],
5118                });
5119                select.order_by = order_by;
5120                select.limit = select_limit;
5121                select.offset = select_offset;
5122
5123                Ok(Expression::Select(Box::new(select)))
5124            } else {
5125                Ok(Expression::Union(u))
5126            }
5127        }
5128        _ => Ok(expr),
5129    }
5130}
5131
5132/// Expand LIKE ANY / ILIKE ANY to OR chains.
5133///
5134/// For dialects that don't support quantifiers on LIKE/ILIKE (e.g. DuckDB),
5135/// expand `x LIKE ANY (('a', 'b'))` to `x LIKE 'a' OR x LIKE 'b'`.
5136///
5137/// Handles precedence: when LIKE ANY (→OR) is inside AND, wraps in parens.
5138/// When LIKE ALL (→AND) is inside OR, wraps in parens for readability.
5139pub fn expand_like_any(expr: Expression) -> Result<Expression> {
5140    use crate::expressions::{BinaryOp, LikeOp, Paren};
5141
5142    /// Sentinel comment used to mark Paren nodes created by LIKE ALL expansion.
5143    /// These markers are stripped in a cleanup pass unless they end up inside an OR parent.
5144    const LIKE_ALL_MARKER: &str = "__LIKE_ALL_EXPANSION__";
5145
5146    fn unwrap_parens(e: &Expression) -> &Expression {
5147        match e {
5148            Expression::Paren(p) => unwrap_parens(&p.this),
5149            _ => e,
5150        }
5151    }
5152
5153    fn extract_tuple_values(e: &Expression) -> Option<Vec<Expression>> {
5154        let inner = unwrap_parens(e);
5155        match inner {
5156            Expression::Tuple(t) => Some(t.expressions.clone()),
5157            // Single value in parens: treat as single-element list
5158            _ if !matches!(e, Expression::Tuple(_)) => Some(vec![inner.clone()]),
5159            _ => None,
5160        }
5161    }
5162
5163    /// Build a chain of LIKE/ILIKE conditions joined by a combiner (OR for ANY, AND for ALL).
5164    fn expand_like_quantifier(
5165        op: &LikeOp,
5166        values: Vec<Expression>,
5167        is_ilike: bool,
5168        combiner: fn(Expression, Expression) -> Expression,
5169        wrap_marker: bool,
5170    ) -> Expression {
5171        let num_values = values.len();
5172        let mut result: Option<Expression> = None;
5173        for val in values {
5174            let like = if is_ilike {
5175                Expression::ILike(Box::new(LikeOp {
5176                    left: op.left.clone(),
5177                    right: val,
5178                    escape: op.escape.clone(),
5179                    quantifier: None,
5180                    inferred_type: None,
5181                }))
5182            } else {
5183                Expression::Like(Box::new(LikeOp {
5184                    left: op.left.clone(),
5185                    right: val,
5186                    escape: op.escape.clone(),
5187                    quantifier: None,
5188                    inferred_type: None,
5189                }))
5190            };
5191            result = Some(match result {
5192                None => like,
5193                Some(prev) => combiner(prev, like),
5194            });
5195        }
5196        let expanded = result.unwrap_or_else(|| unreachable!("values is non-empty"));
5197        // For LIKE ALL (AND chain) with multiple values, wrap in a marker Paren.
5198        // The marker lets us distinguish expansion-created AND from parser-created AND
5199        // when deciding whether to keep parens inside OR.
5200        if wrap_marker && num_values > 1 {
5201            Expression::Paren(Box::new(Paren {
5202                this: expanded,
5203                trailing_comments: vec![LIKE_ALL_MARKER.to_string()],
5204            }))
5205        } else {
5206            expanded
5207        }
5208    }
5209
5210    fn or_combiner(a: Expression, b: Expression) -> Expression {
5211        Expression::Or(Box::new(BinaryOp::new(a, b)))
5212    }
5213
5214    fn and_combiner(a: Expression, b: Expression) -> Expression {
5215        Expression::And(Box::new(BinaryOp::new(a, b)))
5216    }
5217
5218    fn is_like_all_marker(p: &Paren) -> bool {
5219        p.trailing_comments.len() == 1 && p.trailing_comments[0] == LIKE_ALL_MARKER
5220    }
5221
5222    // Phase 1: Expand LIKE ANY/ALL and fix precedence in a single bottom-up pass.
5223    //
5224    // - LIKE ANY → bare Or chain
5225    // - LIKE ALL → Paren(And chain) with marker comment
5226    // - And handler: wraps bare Or children in Paren (from LIKE ANY expansion;
5227    //   parser never creates bare Or inside And)
5228    // - Or handler: converts marker Paren to clean Paren (keeps the wrapping)
5229    let result = transform_recursive(expr, &|e| {
5230        match e {
5231            // LIKE ANY -> OR chain (bare)
5232            Expression::Like(ref op) if op.quantifier.as_deref() == Some("ANY") => {
5233                if let Some(values) = extract_tuple_values(&op.right) {
5234                    if values.is_empty() {
5235                        return Ok(e);
5236                    }
5237                    Ok(expand_like_quantifier(
5238                        op,
5239                        values,
5240                        false,
5241                        or_combiner,
5242                        false,
5243                    ))
5244                } else {
5245                    Ok(e)
5246                }
5247            }
5248            // LIKE ALL -> AND chain (with marker Paren)
5249            Expression::Like(ref op) if op.quantifier.as_deref() == Some("ALL") => {
5250                if let Some(values) = extract_tuple_values(&op.right) {
5251                    if values.is_empty() {
5252                        return Ok(e);
5253                    }
5254                    Ok(expand_like_quantifier(
5255                        op,
5256                        values,
5257                        false,
5258                        and_combiner,
5259                        true,
5260                    ))
5261                } else {
5262                    Ok(e)
5263                }
5264            }
5265            // ILIKE ANY -> OR chain (bare)
5266            Expression::ILike(ref op) if op.quantifier.as_deref() == Some("ANY") => {
5267                if let Some(values) = extract_tuple_values(&op.right) {
5268                    if values.is_empty() {
5269                        return Ok(e);
5270                    }
5271                    Ok(expand_like_quantifier(op, values, true, or_combiner, false))
5272                } else {
5273                    Ok(e)
5274                }
5275            }
5276            // ILIKE ALL -> AND chain (with marker Paren)
5277            Expression::ILike(ref op) if op.quantifier.as_deref() == Some("ALL") => {
5278                if let Some(values) = extract_tuple_values(&op.right) {
5279                    if values.is_empty() {
5280                        return Ok(e);
5281                    }
5282                    Ok(expand_like_quantifier(op, values, true, and_combiner, true))
5283                } else {
5284                    Ok(e)
5285                }
5286            }
5287            // After children are expanded (bottom-up), fix And nodes:
5288            // Wrap bare Or children in Paren (from LIKE ANY expansion).
5289            // The parser never produces bare Or inside And (AND binds tighter than OR,
5290            // so explicit parens in SQL like "(a OR b) AND c" create Paren(Or(...)) in the AST).
5291            Expression::And(mut op) => {
5292                if matches!(&op.left, Expression::Or(_)) {
5293                    op.left = Expression::Paren(Box::new(Paren {
5294                        this: op.left,
5295                        trailing_comments: vec![],
5296                    }));
5297                }
5298                if matches!(&op.right, Expression::Or(_)) {
5299                    op.right = Expression::Paren(Box::new(Paren {
5300                        this: op.right,
5301                        trailing_comments: vec![],
5302                    }));
5303                }
5304                Ok(Expression::And(op))
5305            }
5306            // After children are expanded (bottom-up), fix Or nodes:
5307            // Convert marker Paren(And) to clean Paren(And) so the cleanup pass won't strip it.
5308            Expression::Or(mut op) => {
5309                if let Expression::Paren(ref mut p) = op.left {
5310                    if is_like_all_marker(p) {
5311                        p.trailing_comments.clear();
5312                    }
5313                }
5314                if let Expression::Paren(ref mut p) = op.right {
5315                    if is_like_all_marker(p) {
5316                        p.trailing_comments.clear();
5317                    }
5318                }
5319                Ok(Expression::Or(op))
5320            }
5321            _ => Ok(e),
5322        }
5323    })?;
5324
5325    // Phase 2: Strip remaining marker Paren(And) nodes that weren't inside an Or parent.
5326    // These are standalone LIKE ALL expansions (e.g., in SELECT expressions) that don't
5327    // need parentheses.
5328    transform_recursive(result, &|e| {
5329        if let Expression::Paren(p) = &e {
5330            if is_like_all_marker(p) {
5331                let Expression::Paren(p) = e else {
5332                    unreachable!()
5333                };
5334                return Ok(p.this);
5335            }
5336        }
5337        Ok(e)
5338    })
5339}
5340
5341/// Ensures all unaliased column outputs in subqueries and CTEs get self-aliases.
5342///
5343/// This is needed for TSQL which requires derived table outputs to be aliased.
5344/// For example: `SELECT c FROM t` inside a subquery becomes `SELECT c AS c FROM t`.
5345///
5346/// Mirrors Python sqlglot's `qualify_derived_table_outputs` function which is applied
5347/// as a TRANSFORMS preprocessor for Subquery and CTE expressions in the TSQL dialect.
5348pub fn qualify_derived_table_outputs(expr: Expression) -> Result<Expression> {
5349    use crate::expressions::Alias;
5350
5351    fn add_self_aliases_to_select(select: &mut Select) {
5352        let new_expressions: Vec<Expression> = select
5353            .expressions
5354            .iter()
5355            .map(|e| {
5356                match e {
5357                    // Column reference without alias -> add self-alias
5358                    Expression::Column(col) => {
5359                        let alias_name = col.name.clone();
5360                        Expression::Alias(Box::new(Alias {
5361                            this: e.clone(),
5362                            alias: alias_name,
5363                            column_aliases: Vec::new(),
5364                            alias_explicit_as: false,
5365                            alias_keyword: None,
5366                            pre_alias_comments: Vec::new(),
5367                            trailing_comments: Vec::new(),
5368                            inferred_type: None,
5369                        }))
5370                    }
5371                    // Already aliased or star or other -> keep as is
5372                    _ => e.clone(),
5373                }
5374            })
5375            .collect();
5376        select.expressions = new_expressions;
5377    }
5378
5379    fn walk_and_qualify(expr: &mut Expression) {
5380        match expr {
5381            Expression::Select(ref mut select) => {
5382                // Qualify subqueries in FROM
5383                if let Some(ref mut from) = select.from {
5384                    for e in from.expressions.iter_mut() {
5385                        qualify_subquery_expr(e);
5386                        walk_and_qualify(e);
5387                    }
5388                }
5389                // Qualify subqueries in JOINs
5390                for join in select.joins.iter_mut() {
5391                    qualify_subquery_expr(&mut join.this);
5392                    walk_and_qualify(&mut join.this);
5393                }
5394                // Recurse into expressions (for correlated subqueries etc.)
5395                for e in select.expressions.iter_mut() {
5396                    walk_and_qualify(e);
5397                }
5398                // Recurse into WHERE
5399                if let Some(ref mut w) = select.where_clause {
5400                    walk_and_qualify(&mut w.this);
5401                }
5402            }
5403            Expression::Subquery(ref mut subquery) => {
5404                walk_and_qualify(&mut subquery.this);
5405            }
5406            Expression::Union(ref mut u) => {
5407                walk_and_qualify(&mut u.left);
5408                walk_and_qualify(&mut u.right);
5409            }
5410            Expression::Intersect(ref mut i) => {
5411                walk_and_qualify(&mut i.left);
5412                walk_and_qualify(&mut i.right);
5413            }
5414            Expression::Except(ref mut e) => {
5415                walk_and_qualify(&mut e.left);
5416                walk_and_qualify(&mut e.right);
5417            }
5418            Expression::Cte(ref mut cte) => {
5419                walk_and_qualify(&mut cte.this);
5420            }
5421            _ => {}
5422        }
5423    }
5424
5425    fn qualify_subquery_expr(expr: &mut Expression) {
5426        match expr {
5427            Expression::Subquery(ref mut subquery) => {
5428                // Only qualify if the subquery has a table alias but no column aliases
5429                if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
5430                    if let Expression::Select(ref mut inner_select) = subquery.this {
5431                        // Check the inner select doesn't use *
5432                        let has_star = inner_select
5433                            .expressions
5434                            .iter()
5435                            .any(|e| matches!(e, Expression::Star(_)));
5436                        if !has_star {
5437                            add_self_aliases_to_select(inner_select);
5438                        }
5439                    }
5440                }
5441                // Recurse into the subquery's inner query
5442                walk_and_qualify(&mut subquery.this);
5443            }
5444            Expression::Alias(ref mut alias) => {
5445                qualify_subquery_expr(&mut alias.this);
5446            }
5447            _ => {}
5448        }
5449    }
5450
5451    let mut result = expr;
5452    walk_and_qualify(&mut result);
5453
5454    // Also qualify CTE inner queries at the top level
5455    if let Expression::Select(ref mut select) = result {
5456        if let Some(ref mut with) = select.with {
5457            for cte in with.ctes.iter_mut() {
5458                // CTE with column names -> no need to qualify
5459                if cte.columns.is_empty() {
5460                    // Walk into the CTE's inner query for nested subqueries
5461                    walk_and_qualify(&mut cte.this);
5462                }
5463            }
5464        }
5465    }
5466
5467    Ok(result)
5468}
5469
5470#[cfg(test)]
5471mod tests {
5472    use super::*;
5473    use crate::dialects::{Dialect, DialectType};
5474    use crate::expressions::Column;
5475
5476    fn gen(expr: &Expression) -> String {
5477        let dialect = Dialect::get(DialectType::Generic);
5478        dialect.generate(expr).unwrap()
5479    }
5480
5481    #[test]
5482    fn test_preprocess() {
5483        let expr = Expression::Boolean(BooleanLiteral { value: true });
5484        let result = preprocess(expr, &[replace_bool_with_int]).unwrap();
5485        assert!(
5486            matches!(result, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)))
5487        );
5488    }
5489
5490    #[test]
5491    fn test_preprocess_chain() {
5492        // Test chaining multiple transforms using function pointers
5493        let expr = Expression::Boolean(BooleanLiteral { value: true });
5494        // Create array of function pointers (all same type)
5495        let transforms: Vec<fn(Expression) -> Result<Expression>> =
5496            vec![replace_bool_with_int, replace_int_with_bool];
5497        let result = preprocess(expr, &transforms).unwrap();
5498        // After replace_bool_with_int: 1
5499        // After replace_int_with_bool: true
5500        if let Expression::Boolean(b) = result {
5501            assert!(b.value);
5502        } else {
5503            panic!("Expected boolean literal");
5504        }
5505    }
5506
5507    #[test]
5508    fn test_unnest_to_explode() {
5509        let unnest = Expression::Unnest(Box::new(UnnestFunc {
5510            this: Expression::boxed_column(Column {
5511                name: Identifier::new("arr".to_string()),
5512                table: None,
5513                join_mark: false,
5514                trailing_comments: vec![],
5515                span: None,
5516                inferred_type: None,
5517            }),
5518            expressions: Vec::new(),
5519            with_ordinality: false,
5520            alias: None,
5521            offset_alias: None,
5522        }));
5523
5524        let result = unnest_to_explode(unnest).unwrap();
5525        assert!(matches!(result, Expression::Explode(_)));
5526    }
5527
5528    #[test]
5529    fn test_explode_to_unnest() {
5530        let explode = Expression::Explode(Box::new(UnaryFunc {
5531            this: Expression::boxed_column(Column {
5532                name: Identifier::new("arr".to_string()),
5533                table: None,
5534                join_mark: false,
5535                trailing_comments: vec![],
5536                span: None,
5537                inferred_type: None,
5538            }),
5539            original_name: None,
5540            inferred_type: None,
5541        }));
5542
5543        let result = explode_to_unnest(explode).unwrap();
5544        assert!(matches!(result, Expression::Unnest(_)));
5545    }
5546
5547    #[test]
5548    fn test_replace_bool_with_int() {
5549        let true_expr = Expression::Boolean(BooleanLiteral { value: true });
5550        let result = replace_bool_with_int(true_expr).unwrap();
5551        if let Expression::Literal(lit) = result {
5552            if let Literal::Number(n) = lit.as_ref() {
5553                assert_eq!(n, "1");
5554            }
5555        } else {
5556            panic!("Expected number literal");
5557        }
5558
5559        let false_expr = Expression::Boolean(BooleanLiteral { value: false });
5560        let result = replace_bool_with_int(false_expr).unwrap();
5561        if let Expression::Literal(lit) = result {
5562            if let Literal::Number(n) = lit.as_ref() {
5563                assert_eq!(n, "0");
5564            }
5565        } else {
5566            panic!("Expected number literal");
5567        }
5568    }
5569
5570    #[test]
5571    fn test_replace_int_with_bool() {
5572        let one_expr = Expression::Literal(Box::new(Literal::Number("1".to_string())));
5573        let result = replace_int_with_bool(one_expr).unwrap();
5574        if let Expression::Boolean(b) = result {
5575            assert!(b.value);
5576        } else {
5577            panic!("Expected boolean true");
5578        }
5579
5580        let zero_expr = Expression::Literal(Box::new(Literal::Number("0".to_string())));
5581        let result = replace_int_with_bool(zero_expr).unwrap();
5582        if let Expression::Boolean(b) = result {
5583            assert!(!b.value);
5584        } else {
5585            panic!("Expected boolean false");
5586        }
5587
5588        // Test that other numbers are not converted
5589        let two_expr = Expression::Literal(Box::new(Literal::Number("2".to_string())));
5590        let result = replace_int_with_bool(two_expr).unwrap();
5591        assert!(
5592            matches!(result, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)))
5593        );
5594    }
5595
5596    #[test]
5597    fn test_strip_data_type_params() {
5598        // Test Decimal
5599        let decimal = DataType::Decimal {
5600            precision: Some(10),
5601            scale: Some(2),
5602        };
5603        let stripped = strip_data_type_params(decimal);
5604        assert_eq!(
5605            stripped,
5606            DataType::Decimal {
5607                precision: None,
5608                scale: None
5609            }
5610        );
5611
5612        // Test VarChar
5613        let varchar = DataType::VarChar {
5614            length: Some(255),
5615            parenthesized_length: false,
5616        };
5617        let stripped = strip_data_type_params(varchar);
5618        assert_eq!(
5619            stripped,
5620            DataType::VarChar {
5621                length: None,
5622                parenthesized_length: false
5623            }
5624        );
5625
5626        // Test Char
5627        let char_type = DataType::Char { length: Some(10) };
5628        let stripped = strip_data_type_params(char_type);
5629        assert_eq!(stripped, DataType::Char { length: None });
5630
5631        // Test Timestamp (preserve timezone)
5632        let timestamp = DataType::Timestamp {
5633            precision: Some(6),
5634            timezone: true,
5635        };
5636        let stripped = strip_data_type_params(timestamp);
5637        assert_eq!(
5638            stripped,
5639            DataType::Timestamp {
5640                precision: None,
5641                timezone: true
5642            }
5643        );
5644
5645        // Test Array (recursive)
5646        let array = DataType::Array {
5647            element_type: Box::new(DataType::VarChar {
5648                length: Some(100),
5649                parenthesized_length: false,
5650            }),
5651            dimension: None,
5652        };
5653        let stripped = strip_data_type_params(array);
5654        assert_eq!(
5655            stripped,
5656            DataType::Array {
5657                element_type: Box::new(DataType::VarChar {
5658                    length: None,
5659                    parenthesized_length: false
5660                }),
5661                dimension: None,
5662            }
5663        );
5664
5665        // Test types without params are unchanged
5666        let text = DataType::Text;
5667        let stripped = strip_data_type_params(text);
5668        assert_eq!(stripped, DataType::Text);
5669    }
5670
5671    #[test]
5672    fn test_remove_precision_parameterized_types_cast() {
5673        // Create a CAST(1 AS DECIMAL(10, 2)) expression
5674        let cast_expr = Expression::Cast(Box::new(Cast {
5675            this: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
5676            to: DataType::Decimal {
5677                precision: Some(10),
5678                scale: Some(2),
5679            },
5680            trailing_comments: vec![],
5681            double_colon_syntax: false,
5682            format: None,
5683            default: None,
5684            inferred_type: None,
5685        }));
5686
5687        let result = remove_precision_parameterized_types(cast_expr).unwrap();
5688        if let Expression::Cast(cast) = result {
5689            assert_eq!(
5690                cast.to,
5691                DataType::Decimal {
5692                    precision: None,
5693                    scale: None
5694                }
5695            );
5696        } else {
5697            panic!("Expected Cast expression");
5698        }
5699    }
5700
5701    #[test]
5702    fn test_remove_precision_parameterized_types_varchar() {
5703        // Create a CAST('hello' AS VARCHAR(10)) expression
5704        let cast_expr = Expression::Cast(Box::new(Cast {
5705            this: Expression::Literal(Box::new(Literal::String("hello".to_string()))),
5706            to: DataType::VarChar {
5707                length: Some(10),
5708                parenthesized_length: false,
5709            },
5710            trailing_comments: vec![],
5711            double_colon_syntax: false,
5712            format: None,
5713            default: None,
5714            inferred_type: None,
5715        }));
5716
5717        let result = remove_precision_parameterized_types(cast_expr).unwrap();
5718        if let Expression::Cast(cast) = result {
5719            assert_eq!(
5720                cast.to,
5721                DataType::VarChar {
5722                    length: None,
5723                    parenthesized_length: false
5724                }
5725            );
5726        } else {
5727            panic!("Expected Cast expression");
5728        }
5729    }
5730
5731    #[test]
5732    fn test_remove_precision_direct_cast() {
5733        // Test transform on a direct Cast expression (not nested in Select)
5734        // The current implementation handles top-level Cast expressions;
5735        // a full implementation would need recursive AST traversal
5736        let cast = Expression::Cast(Box::new(Cast {
5737            this: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
5738            to: DataType::Decimal {
5739                precision: Some(10),
5740                scale: Some(2),
5741            },
5742            trailing_comments: vec![],
5743            double_colon_syntax: false,
5744            format: None,
5745            default: None,
5746            inferred_type: None,
5747        }));
5748
5749        let transformed = remove_precision_parameterized_types(cast).unwrap();
5750        let generated = gen(&transformed);
5751
5752        // Should now be DECIMAL without precision
5753        assert!(generated.contains("DECIMAL"));
5754        assert!(!generated.contains("(10"));
5755    }
5756
5757    #[test]
5758    fn test_epoch_cast_to_ts() {
5759        // Test CAST('epoch' AS TIMESTAMP) → CAST('1970-01-01 00:00:00' AS TIMESTAMP)
5760        let cast_expr = Expression::Cast(Box::new(Cast {
5761            this: Expression::Literal(Box::new(Literal::String("epoch".to_string()))),
5762            to: DataType::Timestamp {
5763                precision: None,
5764                timezone: false,
5765            },
5766            trailing_comments: vec![],
5767            double_colon_syntax: false,
5768            format: None,
5769            default: None,
5770            inferred_type: None,
5771        }));
5772
5773        let result = epoch_cast_to_ts(cast_expr).unwrap();
5774        if let Expression::Cast(cast) = result {
5775            if let Expression::Literal(lit) = cast.this {
5776                if let Literal::String(s) = lit.as_ref() {
5777                    assert_eq!(s, "1970-01-01 00:00:00");
5778                }
5779            } else {
5780                panic!("Expected string literal");
5781            }
5782        } else {
5783            panic!("Expected Cast expression");
5784        }
5785    }
5786
5787    #[test]
5788    fn test_epoch_cast_to_ts_preserves_non_epoch() {
5789        // Test that non-epoch strings are preserved
5790        let cast_expr = Expression::Cast(Box::new(Cast {
5791            this: Expression::Literal(Box::new(Literal::String("2024-01-15".to_string()))),
5792            to: DataType::Timestamp {
5793                precision: None,
5794                timezone: false,
5795            },
5796            trailing_comments: vec![],
5797            double_colon_syntax: false,
5798            format: None,
5799            default: None,
5800            inferred_type: None,
5801        }));
5802
5803        let result = epoch_cast_to_ts(cast_expr).unwrap();
5804        if let Expression::Cast(cast) = result {
5805            if let Expression::Literal(lit) = cast.this {
5806                if let Literal::String(s) = lit.as_ref() {
5807                    assert_eq!(s, "2024-01-15");
5808                }
5809            } else {
5810                panic!("Expected string literal");
5811            }
5812        } else {
5813            panic!("Expected Cast expression");
5814        }
5815    }
5816
5817    #[test]
5818    fn test_unqualify_columns() {
5819        // Test that table qualifiers are removed
5820        let col = Expression::boxed_column(Column {
5821            name: Identifier::new("id".to_string()),
5822            table: Some(Identifier::new("users".to_string())),
5823            join_mark: false,
5824            trailing_comments: vec![],
5825            span: None,
5826            inferred_type: None,
5827        });
5828
5829        let result = unqualify_columns(col).unwrap();
5830        if let Expression::Column(c) = result {
5831            assert!(c.table.is_none());
5832            assert_eq!(c.name.name, "id");
5833        } else {
5834            panic!("Expected Column expression");
5835        }
5836    }
5837
5838    #[test]
5839    fn test_is_temporal_type() {
5840        assert!(is_temporal_type(&DataType::Date));
5841        assert!(is_temporal_type(&DataType::Timestamp {
5842            precision: None,
5843            timezone: false
5844        }));
5845        assert!(is_temporal_type(&DataType::Time {
5846            precision: None,
5847            timezone: false
5848        }));
5849        assert!(!is_temporal_type(&DataType::Int {
5850            length: None,
5851            integer_spelling: false
5852        }));
5853        assert!(!is_temporal_type(&DataType::VarChar {
5854            length: None,
5855            parenthesized_length: false
5856        }));
5857    }
5858
5859    #[test]
5860    fn test_eliminate_semi_join_basic() {
5861        use crate::expressions::{Join, TableRef};
5862
5863        // Test that semi joins are converted to EXISTS
5864        let select = Expression::Select(Box::new(Select {
5865            expressions: vec![Expression::boxed_column(Column {
5866                name: Identifier::new("a".to_string()),
5867                table: None,
5868                join_mark: false,
5869                trailing_comments: vec![],
5870                span: None,
5871                inferred_type: None,
5872            })],
5873            from: Some(From {
5874                expressions: vec![Expression::Table(Box::new(TableRef::new("t1")))],
5875            }),
5876            joins: vec![Join {
5877                this: Expression::Table(Box::new(TableRef::new("t2"))),
5878                kind: JoinKind::Semi,
5879                on: Some(Expression::Eq(Box::new(BinaryOp {
5880                    left: Expression::boxed_column(Column {
5881                        name: Identifier::new("x".to_string()),
5882                        table: None,
5883                        join_mark: false,
5884                        trailing_comments: vec![],
5885                        span: None,
5886                        inferred_type: None,
5887                    }),
5888                    right: Expression::boxed_column(Column {
5889                        name: Identifier::new("y".to_string()),
5890                        table: None,
5891                        join_mark: false,
5892                        trailing_comments: vec![],
5893                        span: None,
5894                        inferred_type: None,
5895                    }),
5896                    left_comments: vec![],
5897                    operator_comments: vec![],
5898                    trailing_comments: vec![],
5899                    inferred_type: None,
5900                }))),
5901                using: vec![],
5902                use_inner_keyword: false,
5903                use_outer_keyword: false,
5904                deferred_condition: false,
5905                join_hint: None,
5906                match_condition: None,
5907                pivots: Vec::new(),
5908                comments: Vec::new(),
5909                nesting_group: 0,
5910                directed: false,
5911            }],
5912            ..Select::new()
5913        }));
5914
5915        let result = eliminate_semi_and_anti_joins(select).unwrap();
5916        if let Expression::Select(s) = result {
5917            // Semi join should be removed
5918            assert!(s.joins.is_empty());
5919            // WHERE clause should have EXISTS
5920            assert!(s.where_clause.is_some());
5921        } else {
5922            panic!("Expected Select expression");
5923        }
5924    }
5925
5926    #[test]
5927    fn test_no_ilike_sql() {
5928        use crate::expressions::LikeOp;
5929
5930        // Test ILIKE conversion to LOWER+LIKE
5931        let ilike_expr = Expression::ILike(Box::new(LikeOp {
5932            left: Expression::boxed_column(Column {
5933                name: Identifier::new("name".to_string()),
5934                table: None,
5935                join_mark: false,
5936                trailing_comments: vec![],
5937                span: None,
5938                inferred_type: None,
5939            }),
5940            right: Expression::Literal(Box::new(Literal::String("%test%".to_string()))),
5941            escape: None,
5942            quantifier: None,
5943            inferred_type: None,
5944        }));
5945
5946        let result = no_ilike_sql(ilike_expr).unwrap();
5947        if let Expression::Like(like) = result {
5948            // Left should be LOWER(name)
5949            if let Expression::Function(f) = &like.left {
5950                assert_eq!(f.name, "LOWER");
5951            } else {
5952                panic!("Expected LOWER function on left");
5953            }
5954            // Right should be LOWER('%test%')
5955            if let Expression::Function(f) = &like.right {
5956                assert_eq!(f.name, "LOWER");
5957            } else {
5958                panic!("Expected LOWER function on right");
5959            }
5960        } else {
5961            panic!("Expected Like expression");
5962        }
5963    }
5964
5965    #[test]
5966    fn test_no_trycast_sql() {
5967        // Test TryCast conversion to Cast
5968        let trycast_expr = Expression::TryCast(Box::new(Cast {
5969            this: Expression::Literal(Box::new(Literal::String("123".to_string()))),
5970            to: DataType::Int {
5971                length: None,
5972                integer_spelling: false,
5973            },
5974            trailing_comments: vec![],
5975            double_colon_syntax: false,
5976            format: None,
5977            default: None,
5978            inferred_type: None,
5979        }));
5980
5981        let result = no_trycast_sql(trycast_expr).unwrap();
5982        assert!(matches!(result, Expression::Cast(_)));
5983    }
5984
5985    #[test]
5986    fn test_no_safe_cast_sql() {
5987        // Test SafeCast conversion to Cast
5988        let safe_cast_expr = Expression::SafeCast(Box::new(Cast {
5989            this: Expression::Literal(Box::new(Literal::String("123".to_string()))),
5990            to: DataType::Int {
5991                length: None,
5992                integer_spelling: false,
5993            },
5994            trailing_comments: vec![],
5995            double_colon_syntax: false,
5996            format: None,
5997            default: None,
5998            inferred_type: None,
5999        }));
6000
6001        let result = no_safe_cast_sql(safe_cast_expr).unwrap();
6002        assert!(matches!(result, Expression::Cast(_)));
6003    }
6004
6005    #[test]
6006    fn test_explode_to_unnest_presto() {
6007        let spark = Dialect::get(DialectType::Spark);
6008        let result = spark
6009            .transpile("SELECT EXPLODE(x) FROM tbl", DialectType::Presto)
6010            .unwrap();
6011        assert_eq!(
6012            result[0],
6013            "SELECT IF(_u.pos = _u_2.pos_2, _u_2.col) AS col FROM tbl CROSS JOIN UNNEST(SEQUENCE(1, GREATEST(CARDINALITY(x)))) AS _u(pos) CROSS JOIN UNNEST(x) WITH ORDINALITY AS _u_2(col, pos_2) WHERE _u.pos = _u_2.pos_2 OR (_u.pos > CARDINALITY(x) AND _u_2.pos_2 = CARDINALITY(x))"
6014        );
6015    }
6016
6017    #[test]
6018    fn test_explode_to_unnest_bigquery() {
6019        let spark = Dialect::get(DialectType::Spark);
6020        let result = spark
6021            .transpile("SELECT EXPLODE(x) FROM tbl", DialectType::BigQuery)
6022            .unwrap();
6023        assert_eq!(
6024            result[0],
6025            "SELECT IF(pos = pos_2, col, NULL) AS col FROM tbl CROSS JOIN UNNEST(GENERATE_ARRAY(0, GREATEST(ARRAY_LENGTH(x)) - 1)) AS pos CROSS JOIN UNNEST(x) AS col WITH OFFSET AS pos_2 WHERE pos = pos_2 OR (pos > (ARRAY_LENGTH(x) - 1) AND pos_2 = (ARRAY_LENGTH(x) - 1))"
6026        );
6027    }
6028}