Skip to main content

polyglot_sql/
transforms.rs

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