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