Skip to main content

polyglot_sql/
transforms.rs

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