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