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