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