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