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