Skip to main content

polyglot_sql/
transforms.rs

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