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