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