Skip to main content

polyglot_sql/dialects/
mod.rs

1//! SQL Dialect System
2//!
3//! This module implements the dialect abstraction layer that enables SQL transpilation
4//! between more than 30 SQL dialects. Each dialect encapsulates three concerns:
5//!
6//! - **Tokenization**: Dialect-specific lexing rules (e.g., BigQuery uses backtick quoting,
7//!   MySQL uses backtick for identifiers, TSQL uses square brackets).
8//! - **Generation**: How AST nodes are rendered back to SQL text, including identifier quoting
9//!   style, function name casing, and syntax variations.
10//! - **Transformation**: AST-level rewrites that convert dialect-specific constructs to/from
11//!   a normalized form (e.g., Snowflake `SQUARE(x)` becomes `POWER(x, 2)`).
12//!
13//! The primary entry point is [`Dialect::get`], which returns a configured [`Dialect`] instance
14//! for a given [`DialectType`]. From there, callers can [`parse`](Dialect::parse),
15//! [`generate`](Dialect::generate), [`transform`](Dialect::transform), or
16//! [`transpile`](Dialect::transpile) to another dialect in a single call.
17//!
18//! Each concrete dialect (e.g., `PostgresDialect`, `BigQueryDialect`) implements the
19//! [`DialectImpl`] trait, which provides configuration hooks and expression-level transforms.
20//! Dialect modules live in submodules of this module and are re-exported here.
21
22mod generic; // Always compiled
23#[cfg(feature = "transpile")]
24mod normalization;
25
26#[cfg(feature = "dialect-athena")]
27mod athena;
28#[cfg(feature = "dialect-bigquery")]
29mod bigquery;
30#[cfg(feature = "dialect-clickhouse")]
31mod clickhouse;
32#[cfg(feature = "dialect-cockroachdb")]
33mod cockroachdb;
34#[cfg(feature = "dialect-databricks")]
35mod databricks;
36#[cfg(feature = "dialect-datafusion")]
37mod datafusion;
38#[cfg(feature = "dialect-doris")]
39mod doris;
40#[cfg(feature = "dialect-dremio")]
41mod dremio;
42#[cfg(feature = "dialect-drill")]
43mod drill;
44#[cfg(feature = "dialect-druid")]
45mod druid;
46#[cfg(feature = "dialect-duckdb")]
47mod duckdb;
48#[cfg(feature = "dialect-dune")]
49mod dune;
50#[cfg(feature = "dialect-exasol")]
51mod exasol;
52#[cfg(feature = "dialect-fabric")]
53mod fabric;
54#[cfg(feature = "dialect-hive")]
55mod hive;
56#[cfg(feature = "dialect-materialize")]
57mod materialize;
58#[cfg(feature = "dialect-mysql")]
59mod mysql;
60#[cfg(feature = "dialect-oracle")]
61mod oracle;
62#[cfg(feature = "dialect-postgresql")]
63mod postgres;
64#[cfg(feature = "dialect-presto")]
65mod presto;
66#[cfg(feature = "dialect-redshift")]
67mod redshift;
68#[cfg(feature = "dialect-risingwave")]
69mod risingwave;
70#[cfg(feature = "dialect-singlestore")]
71mod singlestore;
72#[cfg(feature = "dialect-snowflake")]
73mod snowflake;
74#[cfg(feature = "dialect-solr")]
75mod solr;
76#[cfg(feature = "dialect-spark")]
77mod spark;
78#[cfg(feature = "dialect-sqlite")]
79mod sqlite;
80#[cfg(feature = "dialect-starrocks")]
81mod starrocks;
82#[cfg(feature = "dialect-tableau")]
83mod tableau;
84#[cfg(feature = "dialect-teradata")]
85mod teradata;
86#[cfg(feature = "dialect-tidb")]
87mod tidb;
88#[cfg(feature = "dialect-trino")]
89mod trino;
90#[cfg(feature = "dialect-tsql")]
91mod tsql;
92
93pub use generic::GenericDialect; // Always available
94
95#[cfg(feature = "dialect-athena")]
96pub use athena::AthenaDialect;
97#[cfg(feature = "dialect-bigquery")]
98pub use bigquery::BigQueryDialect;
99#[cfg(feature = "dialect-clickhouse")]
100pub use clickhouse::ClickHouseDialect;
101#[cfg(feature = "dialect-cockroachdb")]
102pub use cockroachdb::CockroachDBDialect;
103#[cfg(feature = "dialect-databricks")]
104pub use databricks::DatabricksDialect;
105#[cfg(feature = "dialect-datafusion")]
106pub use datafusion::DataFusionDialect;
107#[cfg(feature = "dialect-doris")]
108pub use doris::DorisDialect;
109#[cfg(feature = "dialect-dremio")]
110pub use dremio::DremioDialect;
111#[cfg(feature = "dialect-drill")]
112pub use drill::DrillDialect;
113#[cfg(feature = "dialect-druid")]
114pub use druid::DruidDialect;
115#[cfg(feature = "dialect-duckdb")]
116pub use duckdb::DuckDBDialect;
117#[cfg(feature = "dialect-dune")]
118pub use dune::DuneDialect;
119#[cfg(feature = "dialect-exasol")]
120pub use exasol::ExasolDialect;
121#[cfg(feature = "dialect-fabric")]
122pub use fabric::FabricDialect;
123#[cfg(feature = "dialect-hive")]
124pub use hive::HiveDialect;
125#[cfg(feature = "dialect-materialize")]
126pub use materialize::MaterializeDialect;
127#[cfg(feature = "dialect-mysql")]
128pub use mysql::MySQLDialect;
129#[cfg(feature = "dialect-oracle")]
130pub use oracle::OracleDialect;
131#[cfg(feature = "dialect-postgresql")]
132pub use postgres::PostgresDialect;
133#[cfg(feature = "dialect-presto")]
134pub use presto::PrestoDialect;
135#[cfg(feature = "dialect-redshift")]
136pub use redshift::RedshiftDialect;
137#[cfg(feature = "dialect-risingwave")]
138pub use risingwave::RisingWaveDialect;
139#[cfg(feature = "dialect-singlestore")]
140pub use singlestore::SingleStoreDialect;
141#[cfg(feature = "dialect-snowflake")]
142pub use snowflake::SnowflakeDialect;
143#[cfg(feature = "dialect-solr")]
144pub use solr::SolrDialect;
145#[cfg(feature = "dialect-spark")]
146pub use spark::SparkDialect;
147#[cfg(feature = "dialect-sqlite")]
148pub use sqlite::SQLiteDialect;
149#[cfg(feature = "dialect-starrocks")]
150pub use starrocks::StarRocksDialect;
151#[cfg(feature = "dialect-tableau")]
152pub use tableau::TableauDialect;
153#[cfg(feature = "dialect-teradata")]
154pub use teradata::TeradataDialect;
155#[cfg(feature = "dialect-tidb")]
156pub use tidb::TiDBDialect;
157#[cfg(feature = "dialect-trino")]
158pub use trino::TrinoDialect;
159#[cfg(feature = "dialect-tsql")]
160pub use tsql::TSQLDialect;
161
162use crate::error::Result;
163#[cfg(feature = "transpile")]
164use crate::expressions::{
165    BinaryOp, Case, Cast, ColumnConstraint, DateBin, Fetch, Function, Identifier, Interval,
166    IntervalUnit, IntervalUnitSpec, Literal, Offset, Over, Top, Var, WindowFrame, WindowFrameBound,
167    WindowFrameKind,
168};
169use crate::expressions::{DataType, Expression};
170#[cfg(any(
171    feature = "transpile",
172    feature = "ast-tools",
173    feature = "generate",
174    feature = "semantic"
175))]
176use crate::expressions::{From, FunctionBody, Join, Null, OrderBy, OutputClause, TableRef, With};
177#[cfg(feature = "transpile")]
178use crate::generator::UnsupportedLevel;
179#[cfg(feature = "generate")]
180use crate::generator::{Generator, GeneratorConfig};
181#[cfg(feature = "transpile")]
182use crate::guard::enforce_generate_ast;
183use crate::guard::{enforce_input, ComplexityGuardOptions};
184use crate::parser::Parser;
185#[cfg(feature = "transpile")]
186use crate::tokens::TokenType;
187use crate::tokens::{Token, Tokenizer, TokenizerConfig};
188#[cfg(feature = "transpile")]
189use crate::traversal::ExpressionWalk;
190use serde::{Deserialize, Serialize};
191use std::collections::HashMap;
192use std::sync::{Arc, LazyLock, RwLock};
193
194/// Enumeration of all supported SQL dialects.
195///
196/// Each variant corresponds to a specific SQL database engine or query language.
197/// The `Generic` variant represents standard SQL with no dialect-specific behavior,
198/// and is used as the default when no dialect is specified.
199///
200/// Dialect names are case-insensitive when parsed from strings via [`FromStr`].
201/// Some dialects accept aliases (e.g., "mssql" and "sqlserver" both resolve to [`TSQL`](DialectType::TSQL)).
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
203#[serde(rename_all = "lowercase")]
204pub enum DialectType {
205    /// Standard SQL with no dialect-specific behavior (default).
206    Generic,
207    /// PostgreSQL -- advanced open-source relational database.
208    PostgreSQL,
209    /// MySQL -- widely-used open-source relational database (also accepts "mysql").
210    MySQL,
211    /// Google BigQuery -- serverless cloud data warehouse with unique syntax (backtick quoting, STRUCT types, QUALIFY).
212    BigQuery,
213    /// Snowflake -- cloud data platform with QUALIFY clause, FLATTEN, and variant types.
214    Snowflake,
215    /// DuckDB -- in-process analytical database with modern SQL extensions.
216    DuckDB,
217    /// SQLite -- lightweight embedded relational database.
218    SQLite,
219    /// Apache Hive -- data warehouse on Hadoop with HiveQL syntax.
220    Hive,
221    /// Apache Spark SQL -- distributed query engine (also accepts "spark2").
222    Spark,
223    /// Trino -- distributed SQL query engine (formerly PrestoSQL).
224    Trino,
225    /// PrestoDB -- distributed SQL query engine for big data.
226    Presto,
227    /// Amazon Redshift -- cloud data warehouse based on PostgreSQL.
228    Redshift,
229    /// Transact-SQL (T-SQL) -- Microsoft SQL Server and Azure SQL (also accepts "mssql", "sqlserver").
230    TSQL,
231    /// Oracle Database -- commercial relational database with PL/SQL extensions.
232    Oracle,
233    /// ClickHouse -- column-oriented OLAP database for real-time analytics.
234    ClickHouse,
235    /// Databricks SQL -- Spark-based lakehouse platform with QUALIFY support.
236    Databricks,
237    /// Amazon Athena -- serverless query service (hybrid Trino/Hive engine).
238    Athena,
239    /// Teradata -- enterprise data warehouse with proprietary SQL extensions.
240    Teradata,
241    /// Apache Doris -- real-time analytical database (MySQL-compatible).
242    Doris,
243    /// StarRocks -- sub-second OLAP database (MySQL-compatible).
244    StarRocks,
245    /// Materialize -- streaming SQL database built on differential dataflow.
246    Materialize,
247    /// RisingWave -- distributed streaming database with PostgreSQL compatibility.
248    RisingWave,
249    /// SingleStore (formerly MemSQL) -- distributed SQL database (also accepts "memsql").
250    SingleStore,
251    /// CockroachDB -- distributed SQL database with PostgreSQL compatibility (also accepts "cockroach").
252    CockroachDB,
253    /// TiDB -- distributed HTAP database with MySQL compatibility.
254    TiDB,
255    /// Apache Druid -- real-time analytics database.
256    Druid,
257    /// Apache Solr -- search platform with SQL interface.
258    Solr,
259    /// Tableau -- data visualization platform with its own SQL dialect.
260    Tableau,
261    /// Dune Analytics -- blockchain analytics SQL engine.
262    Dune,
263    /// Microsoft Fabric -- unified analytics platform (T-SQL based).
264    Fabric,
265    /// Apache Drill -- schema-free SQL query engine for big data.
266    Drill,
267    /// Dremio -- data lakehouse platform with Arrow-based query engine.
268    Dremio,
269    /// Exasol -- in-memory analytic database.
270    Exasol,
271    /// Apache DataFusion -- Arrow-based query engine with modern SQL extensions.
272    DataFusion,
273}
274
275impl Default for DialectType {
276    fn default() -> Self {
277        DialectType::Generic
278    }
279}
280
281impl std::fmt::Display for DialectType {
282    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283        match self {
284            DialectType::Generic => write!(f, "generic"),
285            DialectType::PostgreSQL => write!(f, "postgresql"),
286            DialectType::MySQL => write!(f, "mysql"),
287            DialectType::BigQuery => write!(f, "bigquery"),
288            DialectType::Snowflake => write!(f, "snowflake"),
289            DialectType::DuckDB => write!(f, "duckdb"),
290            DialectType::SQLite => write!(f, "sqlite"),
291            DialectType::Hive => write!(f, "hive"),
292            DialectType::Spark => write!(f, "spark"),
293            DialectType::Trino => write!(f, "trino"),
294            DialectType::Presto => write!(f, "presto"),
295            DialectType::Redshift => write!(f, "redshift"),
296            DialectType::TSQL => write!(f, "tsql"),
297            DialectType::Oracle => write!(f, "oracle"),
298            DialectType::ClickHouse => write!(f, "clickhouse"),
299            DialectType::Databricks => write!(f, "databricks"),
300            DialectType::Athena => write!(f, "athena"),
301            DialectType::Teradata => write!(f, "teradata"),
302            DialectType::Doris => write!(f, "doris"),
303            DialectType::StarRocks => write!(f, "starrocks"),
304            DialectType::Materialize => write!(f, "materialize"),
305            DialectType::RisingWave => write!(f, "risingwave"),
306            DialectType::SingleStore => write!(f, "singlestore"),
307            DialectType::CockroachDB => write!(f, "cockroachdb"),
308            DialectType::TiDB => write!(f, "tidb"),
309            DialectType::Druid => write!(f, "druid"),
310            DialectType::Solr => write!(f, "solr"),
311            DialectType::Tableau => write!(f, "tableau"),
312            DialectType::Dune => write!(f, "dune"),
313            DialectType::Fabric => write!(f, "fabric"),
314            DialectType::Drill => write!(f, "drill"),
315            DialectType::Dremio => write!(f, "dremio"),
316            DialectType::Exasol => write!(f, "exasol"),
317            DialectType::DataFusion => write!(f, "datafusion"),
318        }
319    }
320}
321
322impl std::str::FromStr for DialectType {
323    type Err = crate::error::Error;
324
325    fn from_str(s: &str) -> Result<Self> {
326        match s.to_ascii_lowercase().as_str() {
327            "generic" | "" => Ok(DialectType::Generic),
328            "postgres" | "postgresql" => Ok(DialectType::PostgreSQL),
329            "mysql" => Ok(DialectType::MySQL),
330            "bigquery" => Ok(DialectType::BigQuery),
331            "snowflake" => Ok(DialectType::Snowflake),
332            "duckdb" => Ok(DialectType::DuckDB),
333            "sqlite" => Ok(DialectType::SQLite),
334            "hive" => Ok(DialectType::Hive),
335            "spark" | "spark2" => Ok(DialectType::Spark),
336            "trino" => Ok(DialectType::Trino),
337            "presto" => Ok(DialectType::Presto),
338            "redshift" => Ok(DialectType::Redshift),
339            "tsql" | "mssql" | "sqlserver" => Ok(DialectType::TSQL),
340            "oracle" => Ok(DialectType::Oracle),
341            "clickhouse" => Ok(DialectType::ClickHouse),
342            "databricks" => Ok(DialectType::Databricks),
343            "athena" => Ok(DialectType::Athena),
344            "teradata" => Ok(DialectType::Teradata),
345            "doris" => Ok(DialectType::Doris),
346            "starrocks" => Ok(DialectType::StarRocks),
347            "materialize" => Ok(DialectType::Materialize),
348            "risingwave" => Ok(DialectType::RisingWave),
349            "singlestore" | "memsql" => Ok(DialectType::SingleStore),
350            "cockroachdb" | "cockroach" => Ok(DialectType::CockroachDB),
351            "tidb" => Ok(DialectType::TiDB),
352            "druid" => Ok(DialectType::Druid),
353            "solr" => Ok(DialectType::Solr),
354            "tableau" => Ok(DialectType::Tableau),
355            "dune" => Ok(DialectType::Dune),
356            "fabric" => Ok(DialectType::Fabric),
357            "drill" => Ok(DialectType::Drill),
358            "dremio" => Ok(DialectType::Dremio),
359            "exasol" => Ok(DialectType::Exasol),
360            "datafusion" | "arrow-datafusion" | "arrow_datafusion" => Ok(DialectType::DataFusion),
361            _ => Err(crate::error::Error::parse(
362                format!("Unknown dialect: {}", s),
363                0,
364                0,
365                0,
366                0,
367            )),
368        }
369    }
370}
371
372/// Trait that each concrete SQL dialect must implement.
373///
374/// `DialectImpl` provides the configuration hooks and per-expression transform logic
375/// that distinguish one dialect from another. Implementors supply:
376///
377/// - A [`DialectType`] identifier.
378/// - Optional overrides for tokenizer and generator configuration (defaults to generic SQL).
379/// - An expression-level transform function ([`transform_expr`](DialectImpl::transform_expr))
380///   that rewrites individual AST nodes for this dialect (e.g., converting `NVL` to `COALESCE`).
381/// - An optional preprocessing step ([`preprocess`](DialectImpl::preprocess)) for whole-tree
382///   rewrites that must run before the recursive per-node transform (e.g., eliminating QUALIFY).
383///
384/// The default implementations are no-ops, so a minimal dialect only needs to provide
385/// [`dialect_type`](DialectImpl::dialect_type) and override the methods that differ from
386/// standard SQL.
387pub trait DialectImpl {
388    /// Returns the [`DialectType`] that identifies this dialect.
389    fn dialect_type(&self) -> DialectType;
390
391    /// Returns the tokenizer configuration for this dialect.
392    ///
393    /// Override to customize identifier quoting characters, string escape rules,
394    /// comment styles, and other lexing behavior.
395    fn tokenizer_config(&self) -> TokenizerConfig {
396        TokenizerConfig::default()
397    }
398
399    /// Returns the generator configuration for this dialect.
400    ///
401    /// Override to customize identifier quoting style, function name casing,
402    /// keyword casing, and other SQL generation behavior.
403    #[cfg(feature = "generate")]
404    fn generator_config(&self) -> GeneratorConfig {
405        GeneratorConfig::default()
406    }
407
408    /// Returns a generator configuration tailored to a specific expression.
409    ///
410    /// Override this for hybrid dialects like Athena that route to different SQL engines
411    /// based on expression type (e.g., Hive-style generation for DDL, Trino-style for DML).
412    /// The default delegates to [`generator_config`](DialectImpl::generator_config).
413    #[cfg(feature = "generate")]
414    fn generator_config_for_expr(&self, _expr: &Expression) -> GeneratorConfig {
415        self.generator_config()
416    }
417
418    /// Transforms a single expression node for this dialect, without recursing into children.
419    ///
420    /// This is the per-node rewrite hook invoked by [`transform_recursive`]. Return the
421    /// expression unchanged if no dialect-specific rewrite is needed. Transformations
422    /// typically include function renaming, operator substitution, and type mapping.
423    #[cfg(feature = "transpile")]
424    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
425        Ok(expr)
426    }
427
428    /// Applies whole-tree preprocessing transforms before the recursive per-node pass.
429    ///
430    /// Override this to apply structural rewrites that must see the entire tree at once,
431    /// such as `eliminate_qualify`, `eliminate_distinct_on`, `ensure_bools`, or
432    /// `explode_projection_to_unnest`. The default is a no-op pass-through.
433    #[cfg(feature = "transpile")]
434    fn preprocess(&self, expr: Expression) -> Result<Expression> {
435        Ok(expr)
436    }
437}
438
439/// Recursively transforms a [`DataType`](crate::expressions::DataType), handling nested
440/// parametric types such as `ARRAY<INT>`, `STRUCT<a INT, b TEXT>`, and `MAP<STRING, INT>`.
441///
442/// The outer type is first passed through `transform_fn` as an `Expression::DataType`,
443/// and then nested element/field types are recursed into. This ensures that dialect-level
444/// type mappings (e.g., `INT` to `INTEGER`) propagate into complex nested types.
445#[cfg(any(
446    feature = "transpile",
447    feature = "ast-tools",
448    feature = "generate",
449    feature = "semantic"
450))]
451fn transform_data_type_recursive<F>(
452    dt: crate::expressions::DataType,
453    transform_fn: &F,
454) -> Result<crate::expressions::DataType>
455where
456    F: Fn(Expression) -> Result<Expression>,
457{
458    use crate::expressions::DataType;
459    // First, transform the outermost type through the expression system
460    let dt_expr = transform_fn(Expression::DataType(dt))?;
461    let dt = match dt_expr {
462        Expression::DataType(d) => d,
463        _ => {
464            return Ok(match dt_expr {
465                _ => DataType::Custom {
466                    name: "UNKNOWN".to_string(),
467                },
468            })
469        }
470    };
471    // Then recurse into nested types
472    match dt {
473        DataType::Array {
474            element_type,
475            dimension,
476        } => {
477            let inner = transform_data_type_recursive(*element_type, transform_fn)?;
478            Ok(DataType::Array {
479                element_type: Box::new(inner),
480                dimension,
481            })
482        }
483        DataType::List { element_type } => {
484            let inner = transform_data_type_recursive(*element_type, transform_fn)?;
485            Ok(DataType::List {
486                element_type: Box::new(inner),
487            })
488        }
489        DataType::Struct { fields, nested } => {
490            let mut new_fields = Vec::new();
491            for mut field in fields {
492                field.data_type = transform_data_type_recursive(field.data_type, transform_fn)?;
493                new_fields.push(field);
494            }
495            Ok(DataType::Struct {
496                fields: new_fields,
497                nested,
498            })
499        }
500        DataType::Map {
501            key_type,
502            value_type,
503        } => {
504            let k = transform_data_type_recursive(*key_type, transform_fn)?;
505            let v = transform_data_type_recursive(*value_type, transform_fn)?;
506            Ok(DataType::Map {
507                key_type: Box::new(k),
508                value_type: Box::new(v),
509            })
510        }
511        other => Ok(other),
512    }
513}
514
515/// Convert DuckDB C-style format strings to Presto C-style format strings.
516/// DuckDB and Presto both use C-style % directives but with different specifiers for some cases.
517#[cfg(feature = "transpile")]
518fn duckdb_to_presto_format(fmt: &str) -> String {
519    // Order matters: handle longer patterns first to avoid partial replacements
520    let mut result = fmt.to_string();
521    // First pass: mark multi-char patterns with placeholders
522    result = result.replace("%-m", "\x01NOPADM\x01");
523    result = result.replace("%-d", "\x01NOPADD\x01");
524    result = result.replace("%-I", "\x01NOPADI\x01");
525    result = result.replace("%-H", "\x01NOPADH\x01");
526    result = result.replace("%H:%M:%S", "\x01HMS\x01");
527    result = result.replace("%Y-%m-%d", "\x01YMD\x01");
528    // Now convert individual specifiers
529    result = result.replace("%M", "%i");
530    result = result.replace("%S", "%s");
531    // Restore multi-char patterns with Presto equivalents
532    result = result.replace("\x01NOPADM\x01", "%c");
533    result = result.replace("\x01NOPADD\x01", "%e");
534    result = result.replace("\x01NOPADI\x01", "%l");
535    result = result.replace("\x01NOPADH\x01", "%k");
536    result = result.replace("\x01HMS\x01", "%T");
537    result = result.replace("\x01YMD\x01", "%Y-%m-%d");
538    result
539}
540
541/// Convert DuckDB C-style format strings to BigQuery format strings.
542/// BigQuery uses a mix of strftime-like directives.
543#[cfg(feature = "transpile")]
544fn duckdb_to_bigquery_format(fmt: &str) -> String {
545    let mut result = fmt.to_string();
546    // Handle longer patterns first
547    result = result.replace("%-d", "%e");
548    result = result.replace("%Y-%m-%d %H:%M:%S", "%F %T");
549    result = result.replace("%Y-%m-%d", "%F");
550    result = result.replace("%H:%M:%S", "%T");
551    result
552}
553
554#[cfg(feature = "transpile")]
555fn presto_to_java_format(fmt: &str) -> String {
556    fmt.replace("%Y", "yyyy")
557        .replace("%m", "MM")
558        .replace("%d", "dd")
559        .replace("%H", "HH")
560        .replace("%i", "mm")
561        .replace("%S", "ss")
562        .replace("%s", "ss")
563        .replace("%y", "yy")
564        .replace("%T", "HH:mm:ss")
565        .replace("%F", "yyyy-MM-dd")
566        .replace("%M", "MMMM")
567}
568
569#[cfg(feature = "transpile")]
570fn normalize_presto_format(fmt: &str) -> String {
571    fmt.replace("%H:%i:%S", "%T").replace("%H:%i:%s", "%T")
572}
573
574#[cfg(feature = "transpile")]
575fn presto_to_duckdb_format(fmt: &str) -> String {
576    fmt.replace("%i", "%M")
577        .replace("%s", "%S")
578        .replace("%T", "%H:%M:%S")
579}
580
581#[cfg(feature = "transpile")]
582fn presto_to_bigquery_format(fmt: &str) -> String {
583    fmt.replace("%Y-%m-%d", "%F")
584        .replace("%H:%i:%S", "%T")
585        .replace("%H:%i:%s", "%T")
586        .replace("%i", "%M")
587        .replace("%s", "%S")
588}
589
590#[cfg(feature = "transpile")]
591fn is_default_presto_timestamp_format(fmt: &str) -> bool {
592    let normalized = normalize_presto_format(fmt);
593    normalized == "%Y-%m-%d %T"
594        || normalized == "%Y-%m-%d %H:%i:%S"
595        || fmt == "%Y-%m-%d %H:%i:%S"
596        || fmt == "%Y-%m-%d %T"
597}
598
599#[cfg(feature = "transpile")]
600fn is_default_presto_date_format(fmt: &str) -> bool {
601    fmt == "%Y-%m-%d" || fmt == "%F"
602}
603
604/// Applies a transform function bottom-up through an entire expression tree.
605///
606/// The public entrypoint uses an explicit task stack for the recursion-heavy shapes
607/// that dominate deeply nested SQL (nested SELECT/FROM/SUBQUERY chains, set-operation
608/// trees, and common binary/unary expression chains). Less common shapes currently
609/// reuse the reference recursive implementation so semantics stay identical while
610/// the hot path avoids stack growth.
611#[cfg(any(
612    feature = "transpile",
613    feature = "ast-tools",
614    feature = "generate",
615    feature = "semantic"
616))]
617pub fn transform_recursive<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
618where
619    F: Fn(Expression) -> Result<Expression>,
620{
621    #[cfg(feature = "stacker")]
622    {
623        let red_zone = if cfg!(debug_assertions) {
624            4 * 1024 * 1024
625        } else {
626            1024 * 1024
627        };
628        stacker::maybe_grow(red_zone, 8 * 1024 * 1024, move || {
629            transform_recursive_inner(expr, transform_fn)
630        })
631    }
632    #[cfg(not(feature = "stacker"))]
633    {
634        transform_recursive_inner(expr, transform_fn)
635    }
636}
637
638#[cfg(any(
639    feature = "transpile",
640    feature = "ast-tools",
641    feature = "generate",
642    feature = "semantic"
643))]
644fn transform_recursive_inner<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
645where
646    F: Fn(Expression) -> Result<Expression>,
647{
648    enum Task {
649        Visit(Expression),
650        Finish {
651            shell: Expression,
652            child_count: usize,
653        },
654    }
655
656    // These are the shapes handled by the former explicit-stack fast path. Other
657    // nodes retain the reference transformer's selective and wrapper-aware child
658    // semantics even though all physical children are visible to traversal APIs.
659    fn uses_generated_dispatch(expression: &Expression) -> bool {
660        match expression {
661            Expression::Select(select) => {
662                select.joins.is_empty()
663                    && select.with.is_none()
664                    && select.order_by.is_none()
665                    && select.windows.is_none()
666                    && select.settings.is_none()
667            }
668            Expression::Union(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
669            Expression::Intersect(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
670            Expression::Except(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
671            Expression::Literal(_)
672            | Expression::Boolean(_)
673            | Expression::Null(_)
674            | Expression::Identifier(_)
675            | Expression::Star(_)
676            | Expression::Parameter(_)
677            | Expression::Placeholder(_)
678            | Expression::SessionParameter(_)
679            | Expression::Alias(_)
680            | Expression::Paren(_)
681            | Expression::Not(_)
682            | Expression::Neg(_)
683            | Expression::IsNull(_)
684            | Expression::IsTrue(_)
685            | Expression::IsFalse(_)
686            | Expression::Subquery(_)
687            | Expression::Exists(_)
688            | Expression::TableArgument(_)
689            | Expression::And(_)
690            | Expression::Or(_)
691            | Expression::Add(_)
692            | Expression::Sub(_)
693            | Expression::Mul(_)
694            | Expression::Div(_)
695            | Expression::Eq(_)
696            | Expression::Lt(_)
697            | Expression::Gt(_)
698            | Expression::Neq(_)
699            | Expression::Lte(_)
700            | Expression::Gte(_)
701            | Expression::Mod(_)
702            | Expression::Concat(_)
703            | Expression::BitwiseAnd(_)
704            | Expression::BitwiseOr(_)
705            | Expression::BitwiseXor(_)
706            | Expression::Is(_)
707            | Expression::MemberOf(_)
708            | Expression::ArrayContainsAll(_)
709            | Expression::ArrayContainedBy(_)
710            | Expression::ArrayOverlaps(_)
711            | Expression::TsMatch(_)
712            | Expression::Adjacent(_)
713            | Expression::Like(_)
714            | Expression::ILike(_)
715            | Expression::Function(_)
716            | Expression::Lead(_)
717            | Expression::Lag(_)
718            | Expression::Array(_)
719            | Expression::Tuple(_)
720            | Expression::ArrayFunc(_)
721            | Expression::Coalesce(_)
722            | Expression::Greatest(_)
723            | Expression::Least(_)
724            | Expression::ArrayConcat(_)
725            | Expression::ArrayIntersect(_)
726            | Expression::ArrayZip(_)
727            | Expression::MapConcat(_)
728            | Expression::JsonArray(_)
729            | Expression::From(_) => true,
730            _ => false,
731        }
732    }
733
734    let mut tasks = vec![Task::Visit(expr)];
735    let mut results = Vec::new();
736
737    while let Some(task) = tasks.pop() {
738        match task {
739            Task::Visit(mut expression) => {
740                if !uses_generated_dispatch(&expression) {
741                    results.push(transform_recursive_reference(expression, transform_fn)?);
742                    continue;
743                }
744
745                let mut children = Vec::new();
746                crate::ast_children::for_each_child_mut(&mut expression, |child| {
747                    children.push(std::mem::replace(child, Expression::Null(Null)));
748                });
749                let child_count = children.len();
750                tasks.push(Task::Finish {
751                    shell: expression,
752                    child_count,
753                });
754                for child in children.into_iter().rev() {
755                    tasks.push(Task::Visit(child));
756                }
757            }
758            Task::Finish {
759                mut shell,
760                child_count,
761            } => {
762                if results.len() < child_count {
763                    return Err(crate::error::Error::Internal(
764                        "transform result stack underflow".to_string(),
765                    ));
766                }
767                let transformed_children = results.split_off(results.len() - child_count);
768                let mut transformed_children = transformed_children.into_iter();
769                crate::ast_children::for_each_child_mut(&mut shell, |child| {
770                    *child = transformed_children
771                        .next()
772                        .expect("validated transform child count");
773                });
774                if transformed_children.next().is_some() {
775                    return Err(crate::error::Error::Internal(
776                        "transform child restoration mismatch".to_string(),
777                    ));
778                }
779                results.push(transform_fn(shell)?);
780            }
781        }
782    }
783
784    match results.len() {
785        1 => Ok(results.pop().expect("single transform result")),
786        _ => Err(crate::error::Error::Internal(
787            "unexpected transform result stack size".to_string(),
788        )),
789    }
790}
791
792#[cfg(any(
793    feature = "transpile",
794    feature = "ast-tools",
795    feature = "generate",
796    feature = "semantic"
797))]
798fn transform_table_ref_recursive<F>(table: TableRef, transform_fn: &F) -> Result<TableRef>
799where
800    F: Fn(Expression) -> Result<Expression>,
801{
802    match transform_recursive(Expression::Table(Box::new(table)), transform_fn)? {
803        Expression::Table(table) => Ok(*table),
804        _ => Err(crate::error::Error::parse(
805            "TableRef transformation returned non-table expression",
806            0,
807            0,
808            0,
809            0,
810        )),
811    }
812}
813
814#[cfg(any(
815    feature = "transpile",
816    feature = "ast-tools",
817    feature = "generate",
818    feature = "semantic"
819))]
820fn transform_from_recursive<F>(from: From, transform_fn: &F) -> Result<From>
821where
822    F: Fn(Expression) -> Result<Expression>,
823{
824    match transform_recursive(Expression::From(Box::new(from)), transform_fn)? {
825        Expression::From(from) => Ok(*from),
826        _ => Err(crate::error::Error::parse(
827            "FROM transformation returned non-FROM expression",
828            0,
829            0,
830            0,
831            0,
832        )),
833    }
834}
835
836#[cfg(any(
837    feature = "transpile",
838    feature = "ast-tools",
839    feature = "generate",
840    feature = "semantic"
841))]
842fn transform_join_recursive<F>(mut join: Join, transform_fn: &F) -> Result<Join>
843where
844    F: Fn(Expression) -> Result<Expression>,
845{
846    join.this = transform_recursive(join.this, transform_fn)?;
847    if let Some(on) = join.on.take() {
848        join.on = Some(transform_recursive(on, transform_fn)?);
849    }
850    if let Some(match_condition) = join.match_condition.take() {
851        join.match_condition = Some(transform_recursive(match_condition, transform_fn)?);
852    }
853    join.pivots = join
854        .pivots
855        .into_iter()
856        .map(|pivot| transform_recursive(pivot, transform_fn))
857        .collect::<Result<Vec<_>>>()?;
858
859    match transform_fn(Expression::Join(Box::new(join)))? {
860        Expression::Join(join) => Ok(*join),
861        _ => Err(crate::error::Error::parse(
862            "Join transformation returned non-join expression",
863            0,
864            0,
865            0,
866            0,
867        )),
868    }
869}
870
871#[cfg(any(
872    feature = "transpile",
873    feature = "ast-tools",
874    feature = "generate",
875    feature = "semantic"
876))]
877fn transform_output_clause_recursive<F>(
878    mut output: OutputClause,
879    transform_fn: &F,
880) -> Result<OutputClause>
881where
882    F: Fn(Expression) -> Result<Expression>,
883{
884    output.columns = output
885        .columns
886        .into_iter()
887        .map(|column| transform_recursive(column, transform_fn))
888        .collect::<Result<Vec<_>>>()?;
889    if let Some(into_table) = output.into_table.take() {
890        output.into_table = Some(transform_recursive(into_table, transform_fn)?);
891    }
892    Ok(output)
893}
894
895#[cfg(any(
896    feature = "transpile",
897    feature = "ast-tools",
898    feature = "generate",
899    feature = "semantic"
900))]
901fn transform_with_recursive<F>(mut with: With, transform_fn: &F) -> Result<With>
902where
903    F: Fn(Expression) -> Result<Expression>,
904{
905    with.ctes = with
906        .ctes
907        .into_iter()
908        .map(|mut cte| {
909            cte.this = transform_recursive(cte.this, transform_fn)?;
910            Ok(cte)
911        })
912        .collect::<Result<Vec<_>>>()?;
913    if let Some(search) = with.search.take() {
914        with.search = Some(Box::new(transform_recursive(*search, transform_fn)?));
915    }
916    Ok(with)
917}
918
919#[cfg(any(
920    feature = "transpile",
921    feature = "ast-tools",
922    feature = "generate",
923    feature = "semantic"
924))]
925fn transform_order_by_recursive<F>(mut order: OrderBy, transform_fn: &F) -> Result<OrderBy>
926where
927    F: Fn(Expression) -> Result<Expression>,
928{
929    order.expressions = order
930        .expressions
931        .into_iter()
932        .map(|mut ordered| {
933            let original = ordered.this.clone();
934            ordered.this = transform_recursive(ordered.this, transform_fn).unwrap_or(original);
935            match transform_fn(Expression::Ordered(Box::new(ordered.clone()))) {
936                Ok(Expression::Ordered(transformed)) => Ok(*transformed),
937                Ok(_) | Err(_) => Ok(ordered),
938            }
939        })
940        .collect::<Result<Vec<_>>>()?;
941    Ok(order)
942}
943
944#[cfg(any(
945    feature = "transpile",
946    feature = "ast-tools",
947    feature = "generate",
948    feature = "semantic"
949))]
950fn transform_recursive_reference<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
951where
952    F: Fn(Expression) -> Result<Expression>,
953{
954    use crate::expressions::BinaryOp;
955
956    // Helper macro to recurse into AggFunc-based expressions (this, filter, order_by, having_max, limit).
957    macro_rules! recurse_agg {
958        ($variant:ident, $f:expr) => {{
959            let mut f = $f;
960            f.this = transform_recursive(f.this, transform_fn)?;
961            if let Some(filter) = f.filter.take() {
962                f.filter = Some(transform_recursive(filter, transform_fn)?);
963            }
964            for ord in &mut f.order_by {
965                ord.this = transform_recursive(
966                    std::mem::replace(&mut ord.this, Expression::Null(crate::expressions::Null)),
967                    transform_fn,
968                )?;
969            }
970            if let Some((ref mut expr, _)) = f.having_max {
971                *expr = Box::new(transform_recursive(
972                    std::mem::replace(expr.as_mut(), Expression::Null(crate::expressions::Null)),
973                    transform_fn,
974                )?);
975            }
976            if let Some(limit) = f.limit.take() {
977                f.limit = Some(Box::new(transform_recursive(*limit, transform_fn)?));
978            }
979            Expression::$variant(f)
980        }};
981    }
982
983    // Helper macro to transform binary ops with Box<BinaryOp>
984    macro_rules! transform_binary {
985        ($variant:ident, $op:expr) => {{
986            let left = transform_recursive($op.left, transform_fn)?;
987            let right = transform_recursive($op.right, transform_fn)?;
988            Expression::$variant(Box::new(BinaryOp {
989                left,
990                right,
991                left_comments: $op.left_comments,
992                operator_comments: $op.operator_comments,
993                trailing_comments: $op.trailing_comments,
994                inferred_type: $op.inferred_type,
995            }))
996        }};
997    }
998
999    // Fast path: leaf nodes never need child traversal, apply transform directly
1000    if matches!(
1001        &expr,
1002        Expression::Literal(_)
1003            | Expression::Boolean(_)
1004            | Expression::Null(_)
1005            | Expression::Identifier(_)
1006            | Expression::Star(_)
1007            | Expression::Parameter(_)
1008            | Expression::Placeholder(_)
1009            | Expression::SessionParameter(_)
1010    ) {
1011        return transform_fn(expr);
1012    }
1013
1014    // First recursively transform children, then apply the transform function
1015    let expr = match expr {
1016        Expression::Select(mut select) => {
1017            select.expressions = select
1018                .expressions
1019                .into_iter()
1020                .map(|e| transform_recursive(e, transform_fn))
1021                .collect::<Result<Vec<_>>>()?;
1022
1023            // Transform FROM clause
1024            if let Some(mut from) = select.from.take() {
1025                from.expressions = from
1026                    .expressions
1027                    .into_iter()
1028                    .map(|e| transform_recursive(e, transform_fn))
1029                    .collect::<Result<Vec<_>>>()?;
1030                select.from = Some(from);
1031            }
1032
1033            // Transform JOINs - important for CROSS APPLY / LATERAL transformations
1034            select.joins = select
1035                .joins
1036                .into_iter()
1037                .map(|mut join| {
1038                    join.this = transform_recursive(join.this, transform_fn)?;
1039                    if let Some(on) = join.on.take() {
1040                        join.on = Some(transform_recursive(on, transform_fn)?);
1041                    }
1042                    // Wrap join in Expression::Join to allow transform_fn to transform it
1043                    match transform_fn(Expression::Join(Box::new(join)))? {
1044                        Expression::Join(j) => Ok(*j),
1045                        _ => Err(crate::error::Error::parse(
1046                            "Join transformation returned non-join expression",
1047                            0,
1048                            0,
1049                            0,
1050                            0,
1051                        )),
1052                    }
1053                })
1054                .collect::<Result<Vec<_>>>()?;
1055
1056            // Transform LATERAL VIEW expressions (Hive/Spark)
1057            select.lateral_views = select
1058                .lateral_views
1059                .into_iter()
1060                .map(|mut lv| {
1061                    lv.this = transform_recursive(lv.this, transform_fn)?;
1062                    Ok(lv)
1063                })
1064                .collect::<Result<Vec<_>>>()?;
1065
1066            // Transform WHERE clause
1067            if let Some(mut where_clause) = select.where_clause.take() {
1068                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1069                select.where_clause = Some(where_clause);
1070            }
1071
1072            // Transform GROUP BY
1073            if let Some(mut group_by) = select.group_by.take() {
1074                group_by.expressions = group_by
1075                    .expressions
1076                    .into_iter()
1077                    .map(|e| transform_recursive(e, transform_fn))
1078                    .collect::<Result<Vec<_>>>()?;
1079                select.group_by = Some(group_by);
1080            }
1081
1082            // Transform HAVING
1083            if let Some(mut having) = select.having.take() {
1084                having.this = transform_recursive(having.this, transform_fn)?;
1085                select.having = Some(having);
1086            }
1087
1088            // Transform WITH (CTEs)
1089            if let Some(mut with) = select.with.take() {
1090                with.ctes = with
1091                    .ctes
1092                    .into_iter()
1093                    .map(|mut cte| {
1094                        let original = cte.this.clone();
1095                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1096                        cte
1097                    })
1098                    .collect();
1099                select.with = Some(with);
1100            }
1101
1102            // Transform ORDER BY
1103            if let Some(mut order) = select.order_by.take() {
1104                order.expressions = order
1105                    .expressions
1106                    .into_iter()
1107                    .map(|o| {
1108                        let mut o = o;
1109                        let original = o.this.clone();
1110                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1111                        // Also apply transform to the Ordered wrapper itself (for NULLS FIRST etc.)
1112                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1113                            Ok(Expression::Ordered(transformed)) => *transformed,
1114                            Ok(_) | Err(_) => o,
1115                        }
1116                    })
1117                    .collect();
1118                select.order_by = Some(order);
1119            }
1120
1121            // Transform WINDOW clause order_by
1122            if let Some(ref mut windows) = select.windows {
1123                for nw in windows.iter_mut() {
1124                    nw.spec.order_by = std::mem::take(&mut nw.spec.order_by)
1125                        .into_iter()
1126                        .map(|o| {
1127                            let mut o = o;
1128                            let original = o.this.clone();
1129                            o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1130                            match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1131                                Ok(Expression::Ordered(transformed)) => *transformed,
1132                                Ok(_) | Err(_) => o,
1133                            }
1134                        })
1135                        .collect();
1136                }
1137            }
1138
1139            // Transform QUALIFY
1140            if let Some(mut qual) = select.qualify.take() {
1141                qual.this = transform_recursive(qual.this, transform_fn)?;
1142                select.qualify = Some(qual);
1143            }
1144
1145            Expression::Select(select)
1146        }
1147        Expression::Function(mut f) => {
1148            f.args = f
1149                .args
1150                .into_iter()
1151                .map(|e| transform_recursive(e, transform_fn))
1152                .collect::<Result<Vec<_>>>()?;
1153            Expression::Function(f)
1154        }
1155        Expression::AggregateFunction(mut f) => {
1156            f.args = f
1157                .args
1158                .into_iter()
1159                .map(|e| transform_recursive(e, transform_fn))
1160                .collect::<Result<Vec<_>>>()?;
1161            if let Some(filter) = f.filter {
1162                f.filter = Some(transform_recursive(filter, transform_fn)?);
1163            }
1164            Expression::AggregateFunction(f)
1165        }
1166        Expression::WindowFunction(mut wf) => {
1167            wf.this = transform_recursive(wf.this, transform_fn)?;
1168            wf.over.partition_by = wf
1169                .over
1170                .partition_by
1171                .into_iter()
1172                .map(|e| transform_recursive(e, transform_fn))
1173                .collect::<Result<Vec<_>>>()?;
1174            // Transform order_by items through Expression::Ordered wrapper
1175            wf.over.order_by = wf
1176                .over
1177                .order_by
1178                .into_iter()
1179                .map(|o| {
1180                    let mut o = o;
1181                    o.this = transform_recursive(o.this, transform_fn)?;
1182                    match transform_fn(Expression::Ordered(Box::new(o)))? {
1183                        Expression::Ordered(transformed) => Ok(*transformed),
1184                        _ => Err(crate::error::Error::parse(
1185                            "Ordered transformation returned non-Ordered expression",
1186                            0,
1187                            0,
1188                            0,
1189                            0,
1190                        )),
1191                    }
1192                })
1193                .collect::<Result<Vec<_>>>()?;
1194            Expression::WindowFunction(wf)
1195        }
1196        Expression::Alias(mut a) => {
1197            a.this = transform_recursive(a.this, transform_fn)?;
1198            Expression::Alias(a)
1199        }
1200        Expression::Cast(mut c) => {
1201            c.this = transform_recursive(c.this, transform_fn)?;
1202            // Also transform the target data type (recursively for nested types like ARRAY<INT>, STRUCT<a INT>)
1203            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1204            Expression::Cast(c)
1205        }
1206        Expression::And(op) => transform_binary!(And, *op),
1207        Expression::Or(op) => transform_binary!(Or, *op),
1208        Expression::Add(op) => transform_binary!(Add, *op),
1209        Expression::Sub(op) => transform_binary!(Sub, *op),
1210        Expression::Mul(op) => transform_binary!(Mul, *op),
1211        Expression::Div(op) => transform_binary!(Div, *op),
1212        Expression::Eq(op) => transform_binary!(Eq, *op),
1213        Expression::Lt(op) => transform_binary!(Lt, *op),
1214        Expression::Gt(op) => transform_binary!(Gt, *op),
1215        Expression::Paren(mut p) => {
1216            p.this = transform_recursive(p.this, transform_fn)?;
1217            Expression::Paren(p)
1218        }
1219        Expression::Coalesce(mut f) => {
1220            f.expressions = f
1221                .expressions
1222                .into_iter()
1223                .map(|e| transform_recursive(e, transform_fn))
1224                .collect::<Result<Vec<_>>>()?;
1225            Expression::Coalesce(f)
1226        }
1227        Expression::IfNull(mut f) => {
1228            f.this = transform_recursive(f.this, transform_fn)?;
1229            f.expression = transform_recursive(f.expression, transform_fn)?;
1230            Expression::IfNull(f)
1231        }
1232        Expression::Nvl(mut f) => {
1233            f.this = transform_recursive(f.this, transform_fn)?;
1234            f.expression = transform_recursive(f.expression, transform_fn)?;
1235            Expression::Nvl(f)
1236        }
1237        Expression::In(mut i) => {
1238            i.this = transform_recursive(i.this, transform_fn)?;
1239            i.expressions = i
1240                .expressions
1241                .into_iter()
1242                .map(|e| transform_recursive(e, transform_fn))
1243                .collect::<Result<Vec<_>>>()?;
1244            if let Some(query) = i.query {
1245                i.query = Some(transform_recursive(query, transform_fn)?);
1246            }
1247            Expression::In(i)
1248        }
1249        Expression::Not(mut n) => {
1250            n.this = transform_recursive(n.this, transform_fn)?;
1251            Expression::Not(n)
1252        }
1253        Expression::ArraySlice(mut s) => {
1254            s.this = transform_recursive(s.this, transform_fn)?;
1255            if let Some(start) = s.start {
1256                s.start = Some(transform_recursive(start, transform_fn)?);
1257            }
1258            if let Some(end) = s.end {
1259                s.end = Some(transform_recursive(end, transform_fn)?);
1260            }
1261            Expression::ArraySlice(s)
1262        }
1263        Expression::Subscript(mut s) => {
1264            s.this = transform_recursive(s.this, transform_fn)?;
1265            s.index = transform_recursive(s.index, transform_fn)?;
1266            Expression::Subscript(s)
1267        }
1268        Expression::Array(mut a) => {
1269            a.expressions = a
1270                .expressions
1271                .into_iter()
1272                .map(|e| transform_recursive(e, transform_fn))
1273                .collect::<Result<Vec<_>>>()?;
1274            Expression::Array(a)
1275        }
1276        Expression::Struct(mut s) => {
1277            let mut new_fields = Vec::new();
1278            for (name, expr) in s.fields {
1279                let transformed = transform_recursive(expr, transform_fn)?;
1280                new_fields.push((name, transformed));
1281            }
1282            s.fields = new_fields;
1283            Expression::Struct(s)
1284        }
1285        Expression::NamedArgument(mut na) => {
1286            na.value = transform_recursive(na.value, transform_fn)?;
1287            Expression::NamedArgument(na)
1288        }
1289        Expression::MapFunc(mut m) => {
1290            m.keys = m
1291                .keys
1292                .into_iter()
1293                .map(|e| transform_recursive(e, transform_fn))
1294                .collect::<Result<Vec<_>>>()?;
1295            m.values = m
1296                .values
1297                .into_iter()
1298                .map(|e| transform_recursive(e, transform_fn))
1299                .collect::<Result<Vec<_>>>()?;
1300            Expression::MapFunc(m)
1301        }
1302        Expression::ArrayFunc(mut a) => {
1303            a.expressions = a
1304                .expressions
1305                .into_iter()
1306                .map(|e| transform_recursive(e, transform_fn))
1307                .collect::<Result<Vec<_>>>()?;
1308            Expression::ArrayFunc(a)
1309        }
1310        Expression::Lambda(mut l) => {
1311            l.body = transform_recursive(l.body, transform_fn)?;
1312            Expression::Lambda(l)
1313        }
1314        Expression::JsonExtract(mut f) => {
1315            f.this = transform_recursive(f.this, transform_fn)?;
1316            f.path = transform_recursive(f.path, transform_fn)?;
1317            Expression::JsonExtract(f)
1318        }
1319        Expression::JsonExtractScalar(mut f) => {
1320            f.this = transform_recursive(f.this, transform_fn)?;
1321            f.path = transform_recursive(f.path, transform_fn)?;
1322            Expression::JsonExtractScalar(f)
1323        }
1324
1325        // ===== UnaryFunc-based expressions =====
1326        // These all have a single `this: Expression` child
1327        Expression::Length(mut f) => {
1328            f.this = transform_recursive(f.this, transform_fn)?;
1329            Expression::Length(f)
1330        }
1331        Expression::Upper(mut f) => {
1332            f.this = transform_recursive(f.this, transform_fn)?;
1333            Expression::Upper(f)
1334        }
1335        Expression::Lower(mut f) => {
1336            f.this = transform_recursive(f.this, transform_fn)?;
1337            Expression::Lower(f)
1338        }
1339        Expression::LTrim(mut f) => {
1340            f.this = transform_recursive(f.this, transform_fn)?;
1341            Expression::LTrim(f)
1342        }
1343        Expression::RTrim(mut f) => {
1344            f.this = transform_recursive(f.this, transform_fn)?;
1345            Expression::RTrim(f)
1346        }
1347        Expression::Reverse(mut f) => {
1348            f.this = transform_recursive(f.this, transform_fn)?;
1349            Expression::Reverse(f)
1350        }
1351        Expression::Abs(mut f) => {
1352            f.this = transform_recursive(f.this, transform_fn)?;
1353            Expression::Abs(f)
1354        }
1355        Expression::Ceil(mut f) => {
1356            f.this = transform_recursive(f.this, transform_fn)?;
1357            Expression::Ceil(f)
1358        }
1359        Expression::Floor(mut f) => {
1360            f.this = transform_recursive(f.this, transform_fn)?;
1361            Expression::Floor(f)
1362        }
1363        Expression::Sign(mut f) => {
1364            f.this = transform_recursive(f.this, transform_fn)?;
1365            Expression::Sign(f)
1366        }
1367        Expression::Sqrt(mut f) => {
1368            f.this = transform_recursive(f.this, transform_fn)?;
1369            Expression::Sqrt(f)
1370        }
1371        Expression::Cbrt(mut f) => {
1372            f.this = transform_recursive(f.this, transform_fn)?;
1373            Expression::Cbrt(f)
1374        }
1375        Expression::Ln(mut f) => {
1376            f.this = transform_recursive(f.this, transform_fn)?;
1377            Expression::Ln(f)
1378        }
1379        Expression::Log(mut f) => {
1380            f.this = transform_recursive(f.this, transform_fn)?;
1381            if let Some(base) = f.base {
1382                f.base = Some(transform_recursive(base, transform_fn)?);
1383            }
1384            Expression::Log(f)
1385        }
1386        Expression::Exp(mut f) => {
1387            f.this = transform_recursive(f.this, transform_fn)?;
1388            Expression::Exp(f)
1389        }
1390        Expression::Date(mut f) => {
1391            f.this = transform_recursive(f.this, transform_fn)?;
1392            Expression::Date(f)
1393        }
1394        Expression::Stddev(f) => recurse_agg!(Stddev, f),
1395        Expression::StddevSamp(f) => recurse_agg!(StddevSamp, f),
1396        Expression::Variance(f) => recurse_agg!(Variance, f),
1397
1398        // ===== BinaryFunc-based expressions =====
1399        Expression::ModFunc(mut f) => {
1400            f.this = transform_recursive(f.this, transform_fn)?;
1401            f.expression = transform_recursive(f.expression, transform_fn)?;
1402            Expression::ModFunc(f)
1403        }
1404        Expression::Power(mut f) => {
1405            f.this = transform_recursive(f.this, transform_fn)?;
1406            f.expression = transform_recursive(f.expression, transform_fn)?;
1407            Expression::Power(f)
1408        }
1409        Expression::MapFromArrays(mut f) => {
1410            f.this = transform_recursive(f.this, transform_fn)?;
1411            f.expression = transform_recursive(f.expression, transform_fn)?;
1412            Expression::MapFromArrays(f)
1413        }
1414        Expression::ElementAt(mut f) => {
1415            f.this = transform_recursive(f.this, transform_fn)?;
1416            f.expression = transform_recursive(f.expression, transform_fn)?;
1417            Expression::ElementAt(f)
1418        }
1419        Expression::MapContainsKey(mut f) => {
1420            f.this = transform_recursive(f.this, transform_fn)?;
1421            f.expression = transform_recursive(f.expression, transform_fn)?;
1422            Expression::MapContainsKey(f)
1423        }
1424        Expression::Left(mut f) => {
1425            f.this = transform_recursive(f.this, transform_fn)?;
1426            f.length = transform_recursive(f.length, transform_fn)?;
1427            Expression::Left(f)
1428        }
1429        Expression::Right(mut f) => {
1430            f.this = transform_recursive(f.this, transform_fn)?;
1431            f.length = transform_recursive(f.length, transform_fn)?;
1432            Expression::Right(f)
1433        }
1434        Expression::Repeat(mut f) => {
1435            f.this = transform_recursive(f.this, transform_fn)?;
1436            f.times = transform_recursive(f.times, transform_fn)?;
1437            Expression::Repeat(f)
1438        }
1439
1440        // ===== Complex function expressions =====
1441        Expression::Substring(mut f) => {
1442            f.this = transform_recursive(f.this, transform_fn)?;
1443            f.start = transform_recursive(f.start, transform_fn)?;
1444            if let Some(len) = f.length {
1445                f.length = Some(transform_recursive(len, transform_fn)?);
1446            }
1447            Expression::Substring(f)
1448        }
1449        Expression::Replace(mut f) => {
1450            f.this = transform_recursive(f.this, transform_fn)?;
1451            f.old = transform_recursive(f.old, transform_fn)?;
1452            f.new = transform_recursive(f.new, transform_fn)?;
1453            Expression::Replace(f)
1454        }
1455        Expression::ConcatWs(mut f) => {
1456            f.separator = transform_recursive(f.separator, transform_fn)?;
1457            f.expressions = f
1458                .expressions
1459                .into_iter()
1460                .map(|e| transform_recursive(e, transform_fn))
1461                .collect::<Result<Vec<_>>>()?;
1462            Expression::ConcatWs(f)
1463        }
1464        Expression::Trim(mut f) => {
1465            f.this = transform_recursive(f.this, transform_fn)?;
1466            if let Some(chars) = f.characters {
1467                f.characters = Some(transform_recursive(chars, transform_fn)?);
1468            }
1469            Expression::Trim(f)
1470        }
1471        Expression::Split(mut f) => {
1472            f.this = transform_recursive(f.this, transform_fn)?;
1473            f.delimiter = transform_recursive(f.delimiter, transform_fn)?;
1474            Expression::Split(f)
1475        }
1476        Expression::Lpad(mut f) => {
1477            f.this = transform_recursive(f.this, transform_fn)?;
1478            f.length = transform_recursive(f.length, transform_fn)?;
1479            if let Some(fill) = f.fill {
1480                f.fill = Some(transform_recursive(fill, transform_fn)?);
1481            }
1482            Expression::Lpad(f)
1483        }
1484        Expression::Rpad(mut f) => {
1485            f.this = transform_recursive(f.this, transform_fn)?;
1486            f.length = transform_recursive(f.length, transform_fn)?;
1487            if let Some(fill) = f.fill {
1488                f.fill = Some(transform_recursive(fill, transform_fn)?);
1489            }
1490            Expression::Rpad(f)
1491        }
1492
1493        // ===== Conditional expressions =====
1494        Expression::Case(mut c) => {
1495            if let Some(operand) = c.operand {
1496                c.operand = Some(transform_recursive(operand, transform_fn)?);
1497            }
1498            c.whens = c
1499                .whens
1500                .into_iter()
1501                .map(|(cond, then)| {
1502                    let new_cond = transform_recursive(cond.clone(), transform_fn).unwrap_or(cond);
1503                    let new_then = transform_recursive(then.clone(), transform_fn).unwrap_or(then);
1504                    (new_cond, new_then)
1505                })
1506                .collect();
1507            if let Some(else_expr) = c.else_ {
1508                c.else_ = Some(transform_recursive(else_expr, transform_fn)?);
1509            }
1510            Expression::Case(c)
1511        }
1512        Expression::IfFunc(mut f) => {
1513            f.condition = transform_recursive(f.condition, transform_fn)?;
1514            f.true_value = transform_recursive(f.true_value, transform_fn)?;
1515            if let Some(false_val) = f.false_value {
1516                f.false_value = Some(transform_recursive(false_val, transform_fn)?);
1517            }
1518            Expression::IfFunc(f)
1519        }
1520
1521        // ===== Date/Time expressions =====
1522        Expression::DateAdd(mut f) => {
1523            f.this = transform_recursive(f.this, transform_fn)?;
1524            f.interval = transform_recursive(f.interval, transform_fn)?;
1525            Expression::DateAdd(f)
1526        }
1527        Expression::DateSub(mut f) => {
1528            f.this = transform_recursive(f.this, transform_fn)?;
1529            f.interval = transform_recursive(f.interval, transform_fn)?;
1530            Expression::DateSub(f)
1531        }
1532        Expression::DateDiff(mut f) => {
1533            f.this = transform_recursive(f.this, transform_fn)?;
1534            f.expression = transform_recursive(f.expression, transform_fn)?;
1535            Expression::DateDiff(f)
1536        }
1537        Expression::DateTrunc(mut f) => {
1538            f.this = transform_recursive(f.this, transform_fn)?;
1539            Expression::DateTrunc(f)
1540        }
1541        Expression::Extract(mut f) => {
1542            f.this = transform_recursive(f.this, transform_fn)?;
1543            Expression::Extract(f)
1544        }
1545
1546        // ===== JSON expressions =====
1547        Expression::JsonObject(mut f) => {
1548            f.pairs = f
1549                .pairs
1550                .into_iter()
1551                .map(|(k, v)| {
1552                    let new_k = transform_recursive(k, transform_fn)?;
1553                    let new_v = transform_recursive(v, transform_fn)?;
1554                    Ok((new_k, new_v))
1555                })
1556                .collect::<Result<Vec<_>>>()?;
1557            Expression::JsonObject(f)
1558        }
1559
1560        // ===== Subquery expressions =====
1561        Expression::Subquery(mut s) => {
1562            s.this = transform_recursive(s.this, transform_fn)?;
1563            Expression::Subquery(s)
1564        }
1565        Expression::Exists(mut e) => {
1566            e.this = transform_recursive(e.this, transform_fn)?;
1567            Expression::Exists(e)
1568        }
1569        Expression::Describe(mut d) => {
1570            d.target = transform_recursive(d.target, transform_fn)?;
1571            Expression::Describe(d)
1572        }
1573
1574        // ===== Set operations =====
1575        Expression::Union(mut u) => {
1576            let left = std::mem::replace(&mut u.left, Expression::Null(Null));
1577            u.left = transform_recursive(left, transform_fn)?;
1578            let right = std::mem::replace(&mut u.right, Expression::Null(Null));
1579            u.right = transform_recursive(right, transform_fn)?;
1580            if let Some(mut order) = u.order_by.take() {
1581                order.expressions = order
1582                    .expressions
1583                    .into_iter()
1584                    .map(|o| {
1585                        let mut o = o;
1586                        let original = o.this.clone();
1587                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1588                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1589                            Ok(Expression::Ordered(transformed)) => *transformed,
1590                            Ok(_) | Err(_) => o,
1591                        }
1592                    })
1593                    .collect();
1594                u.order_by = Some(order);
1595            }
1596            if let Some(mut with) = u.with.take() {
1597                with.ctes = with
1598                    .ctes
1599                    .into_iter()
1600                    .map(|mut cte| {
1601                        let original = cte.this.clone();
1602                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1603                        cte
1604                    })
1605                    .collect();
1606                u.with = Some(with);
1607            }
1608            Expression::Union(u)
1609        }
1610        Expression::Intersect(mut i) => {
1611            let left = std::mem::replace(&mut i.left, Expression::Null(Null));
1612            i.left = transform_recursive(left, transform_fn)?;
1613            let right = std::mem::replace(&mut i.right, Expression::Null(Null));
1614            i.right = transform_recursive(right, transform_fn)?;
1615            if let Some(mut order) = i.order_by.take() {
1616                order.expressions = order
1617                    .expressions
1618                    .into_iter()
1619                    .map(|o| {
1620                        let mut o = o;
1621                        let original = o.this.clone();
1622                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1623                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1624                            Ok(Expression::Ordered(transformed)) => *transformed,
1625                            Ok(_) | Err(_) => o,
1626                        }
1627                    })
1628                    .collect();
1629                i.order_by = Some(order);
1630            }
1631            if let Some(mut with) = i.with.take() {
1632                with.ctes = with
1633                    .ctes
1634                    .into_iter()
1635                    .map(|mut cte| {
1636                        let original = cte.this.clone();
1637                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1638                        cte
1639                    })
1640                    .collect();
1641                i.with = Some(with);
1642            }
1643            Expression::Intersect(i)
1644        }
1645        Expression::Except(mut e) => {
1646            let left = std::mem::replace(&mut e.left, Expression::Null(Null));
1647            e.left = transform_recursive(left, transform_fn)?;
1648            let right = std::mem::replace(&mut e.right, Expression::Null(Null));
1649            e.right = transform_recursive(right, transform_fn)?;
1650            if let Some(mut order) = e.order_by.take() {
1651                order.expressions = order
1652                    .expressions
1653                    .into_iter()
1654                    .map(|o| {
1655                        let mut o = o;
1656                        let original = o.this.clone();
1657                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1658                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1659                            Ok(Expression::Ordered(transformed)) => *transformed,
1660                            Ok(_) | Err(_) => o,
1661                        }
1662                    })
1663                    .collect();
1664                e.order_by = Some(order);
1665            }
1666            if let Some(mut with) = e.with.take() {
1667                with.ctes = with
1668                    .ctes
1669                    .into_iter()
1670                    .map(|mut cte| {
1671                        let original = cte.this.clone();
1672                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1673                        cte
1674                    })
1675                    .collect();
1676                e.with = Some(with);
1677            }
1678            Expression::Except(e)
1679        }
1680
1681        // ===== DML expressions =====
1682        Expression::Insert(mut ins) => {
1683            // Transform VALUES clause expressions
1684            let mut new_values = Vec::new();
1685            for row in ins.values {
1686                let mut new_row = Vec::new();
1687                for e in row {
1688                    new_row.push(transform_recursive(e, transform_fn)?);
1689                }
1690                new_values.push(new_row);
1691            }
1692            ins.values = new_values;
1693
1694            // Transform query (for INSERT ... SELECT)
1695            if let Some(query) = ins.query {
1696                ins.query = Some(transform_recursive(query, transform_fn)?);
1697            }
1698
1699            // Transform RETURNING clause
1700            let mut new_returning = Vec::new();
1701            for e in ins.returning {
1702                new_returning.push(transform_recursive(e, transform_fn)?);
1703            }
1704            ins.returning = new_returning;
1705
1706            // Transform ON CONFLICT clause
1707            if let Some(on_conflict) = ins.on_conflict {
1708                ins.on_conflict = Some(Box::new(transform_recursive(*on_conflict, transform_fn)?));
1709            }
1710
1711            Expression::Insert(ins)
1712        }
1713        Expression::Update(mut upd) => {
1714            upd.table = transform_table_ref_recursive(upd.table, transform_fn)?;
1715            upd.extra_tables = upd
1716                .extra_tables
1717                .into_iter()
1718                .map(|table| transform_table_ref_recursive(table, transform_fn))
1719                .collect::<Result<Vec<_>>>()?;
1720            upd.table_joins = upd
1721                .table_joins
1722                .into_iter()
1723                .map(|join| transform_join_recursive(join, transform_fn))
1724                .collect::<Result<Vec<_>>>()?;
1725            upd.set = upd
1726                .set
1727                .into_iter()
1728                .map(|(id, val)| {
1729                    let new_val = transform_recursive(val.clone(), transform_fn).unwrap_or(val);
1730                    (id, new_val)
1731                })
1732                .collect();
1733            if let Some(from_clause) = upd.from_clause.take() {
1734                upd.from_clause = Some(transform_from_recursive(from_clause, transform_fn)?);
1735            }
1736            upd.from_joins = upd
1737                .from_joins
1738                .into_iter()
1739                .map(|join| transform_join_recursive(join, transform_fn))
1740                .collect::<Result<Vec<_>>>()?;
1741            if let Some(mut where_clause) = upd.where_clause.take() {
1742                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1743                upd.where_clause = Some(where_clause);
1744            }
1745            upd.returning = upd
1746                .returning
1747                .into_iter()
1748                .map(|expr| transform_recursive(expr, transform_fn))
1749                .collect::<Result<Vec<_>>>()?;
1750            if let Some(output) = upd.output.take() {
1751                upd.output = Some(transform_output_clause_recursive(output, transform_fn)?);
1752            }
1753            if let Some(with) = upd.with.take() {
1754                upd.with = Some(transform_with_recursive(with, transform_fn)?);
1755            }
1756            if let Some(limit) = upd.limit.take() {
1757                upd.limit = Some(transform_recursive(limit, transform_fn)?);
1758            }
1759            if let Some(order_by) = upd.order_by.take() {
1760                upd.order_by = Some(transform_order_by_recursive(order_by, transform_fn)?);
1761            }
1762            Expression::Update(upd)
1763        }
1764        Expression::Delete(mut del) => {
1765            del.table = transform_table_ref_recursive(del.table, transform_fn)?;
1766            del.using = del
1767                .using
1768                .into_iter()
1769                .map(|table| transform_table_ref_recursive(table, transform_fn))
1770                .collect::<Result<Vec<_>>>()?;
1771            if let Some(mut where_clause) = del.where_clause.take() {
1772                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1773                del.where_clause = Some(where_clause);
1774            }
1775            if let Some(output) = del.output.take() {
1776                del.output = Some(transform_output_clause_recursive(output, transform_fn)?);
1777            }
1778            if let Some(with) = del.with.take() {
1779                del.with = Some(transform_with_recursive(with, transform_fn)?);
1780            }
1781            if let Some(limit) = del.limit.take() {
1782                del.limit = Some(transform_recursive(limit, transform_fn)?);
1783            }
1784            if let Some(order_by) = del.order_by.take() {
1785                del.order_by = Some(transform_order_by_recursive(order_by, transform_fn)?);
1786            }
1787            del.returning = del
1788                .returning
1789                .into_iter()
1790                .map(|expr| transform_recursive(expr, transform_fn))
1791                .collect::<Result<Vec<_>>>()?;
1792            del.tables = del
1793                .tables
1794                .into_iter()
1795                .map(|table| transform_table_ref_recursive(table, transform_fn))
1796                .collect::<Result<Vec<_>>>()?;
1797            del.joins = del
1798                .joins
1799                .into_iter()
1800                .map(|join| transform_join_recursive(join, transform_fn))
1801                .collect::<Result<Vec<_>>>()?;
1802            Expression::Delete(del)
1803        }
1804
1805        // ===== CTE expressions =====
1806        Expression::With(mut w) => {
1807            w.ctes = w
1808                .ctes
1809                .into_iter()
1810                .map(|mut cte| {
1811                    let original = cte.this.clone();
1812                    cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1813                    cte
1814                })
1815                .collect();
1816            Expression::With(w)
1817        }
1818        Expression::Cte(mut c) => {
1819            c.this = transform_recursive(c.this, transform_fn)?;
1820            Expression::Cte(c)
1821        }
1822
1823        // ===== Order expressions =====
1824        Expression::Ordered(mut o) => {
1825            o.this = transform_recursive(o.this, transform_fn)?;
1826            Expression::Ordered(o)
1827        }
1828
1829        // ===== Negation =====
1830        Expression::Neg(mut n) => {
1831            n.this = transform_recursive(n.this, transform_fn)?;
1832            Expression::Neg(n)
1833        }
1834
1835        // ===== Between =====
1836        Expression::Between(mut b) => {
1837            b.this = transform_recursive(b.this, transform_fn)?;
1838            b.low = transform_recursive(b.low, transform_fn)?;
1839            b.high = transform_recursive(b.high, transform_fn)?;
1840            Expression::Between(b)
1841        }
1842        Expression::IsNull(mut i) => {
1843            i.this = transform_recursive(i.this, transform_fn)?;
1844            Expression::IsNull(i)
1845        }
1846        Expression::IsTrue(mut i) => {
1847            i.this = transform_recursive(i.this, transform_fn)?;
1848            Expression::IsTrue(i)
1849        }
1850        Expression::IsFalse(mut i) => {
1851            i.this = transform_recursive(i.this, transform_fn)?;
1852            Expression::IsFalse(i)
1853        }
1854
1855        // ===== Like expressions =====
1856        Expression::Like(mut l) => {
1857            l.left = transform_recursive(l.left, transform_fn)?;
1858            l.right = transform_recursive(l.right, transform_fn)?;
1859            Expression::Like(l)
1860        }
1861        Expression::ILike(mut l) => {
1862            l.left = transform_recursive(l.left, transform_fn)?;
1863            l.right = transform_recursive(l.right, transform_fn)?;
1864            Expression::ILike(l)
1865        }
1866
1867        // ===== Additional binary ops not covered by macro =====
1868        Expression::Neq(op) => transform_binary!(Neq, *op),
1869        Expression::Lte(op) => transform_binary!(Lte, *op),
1870        Expression::Gte(op) => transform_binary!(Gte, *op),
1871        Expression::Mod(op) => transform_binary!(Mod, *op),
1872        Expression::Concat(op) => transform_binary!(Concat, *op),
1873        Expression::BitwiseAnd(op) => transform_binary!(BitwiseAnd, *op),
1874        Expression::BitwiseOr(op) => transform_binary!(BitwiseOr, *op),
1875        Expression::BitwiseXor(op) => transform_binary!(BitwiseXor, *op),
1876        Expression::Is(op) => transform_binary!(Is, *op),
1877
1878        // ===== TryCast / SafeCast =====
1879        Expression::TryCast(mut c) => {
1880            c.this = transform_recursive(c.this, transform_fn)?;
1881            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1882            Expression::TryCast(c)
1883        }
1884        Expression::SafeCast(mut c) => {
1885            c.this = transform_recursive(c.this, transform_fn)?;
1886            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1887            Expression::SafeCast(c)
1888        }
1889
1890        // ===== Misc =====
1891        Expression::Unnest(mut f) => {
1892            f.this = transform_recursive(f.this, transform_fn)?;
1893            f.expressions = f
1894                .expressions
1895                .into_iter()
1896                .map(|e| transform_recursive(e, transform_fn))
1897                .collect::<Result<Vec<_>>>()?;
1898            Expression::Unnest(f)
1899        }
1900        Expression::Explode(mut f) => {
1901            f.this = transform_recursive(f.this, transform_fn)?;
1902            Expression::Explode(f)
1903        }
1904        Expression::GroupConcat(mut f) => {
1905            f.this = transform_recursive(f.this, transform_fn)?;
1906            Expression::GroupConcat(f)
1907        }
1908        Expression::StringAgg(mut f) => {
1909            f.this = transform_recursive(f.this, transform_fn)?;
1910            if let Some(order_by) = f.order_by.take() {
1911                f.order_by = Some(
1912                    order_by
1913                        .into_iter()
1914                        .map(|mut ordered| {
1915                            let original = ordered.this.clone();
1916                            ordered.this =
1917                                transform_recursive(ordered.this, transform_fn).unwrap_or(original);
1918                            match transform_fn(Expression::Ordered(Box::new(ordered.clone()))) {
1919                                Ok(Expression::Ordered(transformed)) => Ok(*transformed),
1920                                Ok(_) | Err(_) => Ok(ordered),
1921                            }
1922                        })
1923                        .collect::<Result<Vec<_>>>()?,
1924                );
1925            }
1926            Expression::StringAgg(f)
1927        }
1928        Expression::ListAgg(mut f) => {
1929            f.this = transform_recursive(f.this, transform_fn)?;
1930            Expression::ListAgg(f)
1931        }
1932        Expression::ArrayAgg(mut f) => {
1933            f.this = transform_recursive(f.this, transform_fn)?;
1934            Expression::ArrayAgg(f)
1935        }
1936        Expression::ParseJson(mut f) => {
1937            f.this = transform_recursive(f.this, transform_fn)?;
1938            Expression::ParseJson(f)
1939        }
1940        Expression::ToJson(mut f) => {
1941            f.this = transform_recursive(f.this, transform_fn)?;
1942            Expression::ToJson(f)
1943        }
1944        Expression::JSONExtract(mut e) => {
1945            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1946            e.expression = Box::new(transform_recursive(*e.expression, transform_fn)?);
1947            Expression::JSONExtract(e)
1948        }
1949        Expression::JSONExtractScalar(mut e) => {
1950            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1951            e.expression = Box::new(transform_recursive(*e.expression, transform_fn)?);
1952            Expression::JSONExtractScalar(e)
1953        }
1954
1955        // StrToTime: recurse into this
1956        Expression::StrToTime(mut e) => {
1957            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1958            Expression::StrToTime(e)
1959        }
1960
1961        // UnixToTime: recurse into this
1962        Expression::UnixToTime(mut e) => {
1963            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1964            Expression::UnixToTime(e)
1965        }
1966
1967        // CreateTable: recurse into column defaults, on_update expressions, and data types
1968        Expression::CreateTable(mut ct) => {
1969            for col in &mut ct.columns {
1970                if let Some(default_expr) = col.default.take() {
1971                    col.default = Some(transform_recursive(default_expr, transform_fn)?);
1972                }
1973                if let Some(on_update_expr) = col.on_update.take() {
1974                    col.on_update = Some(transform_recursive(on_update_expr, transform_fn)?);
1975                }
1976                // Note: Column data type transformations (INT -> INT64 for BigQuery, etc.)
1977                // are NOT applied here because per-dialect transforms are designed for CAST/expression
1978                // contexts and may not produce correct results for DDL column definitions.
1979                // The DDL type mappings would need dedicated handling per source/target pair.
1980            }
1981            if let Some(as_select) = ct.as_select.take() {
1982                ct.as_select = Some(transform_recursive(as_select, transform_fn)?);
1983            }
1984            Expression::CreateTable(ct)
1985        }
1986
1987        // CreateView: recurse into the view body query
1988        Expression::CreateView(mut cv) => {
1989            cv.query = transform_recursive(cv.query, transform_fn)?;
1990            Expression::CreateView(cv)
1991        }
1992
1993        // CreateTask: recurse into the task body
1994        Expression::CreateTask(mut ct) => {
1995            ct.body = transform_recursive(ct.body, transform_fn)?;
1996            Expression::CreateTask(ct)
1997        }
1998
1999        // Prepare: recurse into the prepared statement body
2000        Expression::Prepare(mut prepare) => {
2001            prepare.statement = transform_recursive(prepare.statement, transform_fn)?;
2002            Expression::Prepare(prepare)
2003        }
2004
2005        // Execute: recurse into procedure/prepared name and argument values
2006        Expression::Execute(mut execute) => {
2007            execute.this = transform_recursive(execute.this, transform_fn)?;
2008            execute.arguments = execute
2009                .arguments
2010                .into_iter()
2011                .map(|argument| transform_recursive(argument, transform_fn))
2012                .collect::<Result<Vec<_>>>()?;
2013            execute.parameters = execute
2014                .parameters
2015                .into_iter()
2016                .map(|mut parameter| {
2017                    parameter.value = transform_recursive(parameter.value, transform_fn)?;
2018                    Ok(parameter)
2019                })
2020                .collect::<Result<Vec<_>>>()?;
2021            Expression::Execute(execute)
2022        }
2023
2024        // CreateProcedure: recurse into body expressions
2025        Expression::CreateProcedure(mut cp) => {
2026            if let Some(body) = cp.body.take() {
2027                cp.body = Some(match body {
2028                    FunctionBody::Expression(expr) => {
2029                        FunctionBody::Expression(transform_recursive(expr, transform_fn)?)
2030                    }
2031                    FunctionBody::Return(expr) => {
2032                        FunctionBody::Return(transform_recursive(expr, transform_fn)?)
2033                    }
2034                    FunctionBody::Statements(stmts) => {
2035                        let transformed_stmts = stmts
2036                            .into_iter()
2037                            .map(|s| transform_recursive(s, transform_fn))
2038                            .collect::<Result<Vec<_>>>()?;
2039                        FunctionBody::Statements(transformed_stmts)
2040                    }
2041                    other => other,
2042                });
2043            }
2044            Expression::CreateProcedure(cp)
2045        }
2046
2047        // CreateFunction: recurse into body expressions
2048        Expression::CreateFunction(mut cf) => {
2049            if let Some(body) = cf.body.take() {
2050                cf.body = Some(match body {
2051                    FunctionBody::Expression(expr) => {
2052                        FunctionBody::Expression(transform_recursive(expr, transform_fn)?)
2053                    }
2054                    FunctionBody::Return(expr) => {
2055                        FunctionBody::Return(transform_recursive(expr, transform_fn)?)
2056                    }
2057                    FunctionBody::Statements(stmts) => {
2058                        let transformed_stmts = stmts
2059                            .into_iter()
2060                            .map(|s| transform_recursive(s, transform_fn))
2061                            .collect::<Result<Vec<_>>>()?;
2062                        FunctionBody::Statements(transformed_stmts)
2063                    }
2064                    other => other,
2065                });
2066            }
2067            Expression::CreateFunction(cf)
2068        }
2069
2070        // MemberOf: recurse into left and right operands
2071        Expression::MemberOf(op) => transform_binary!(MemberOf, *op),
2072        // ArrayContainsAll (@>): recurse into left and right operands
2073        Expression::ArrayContainsAll(op) => transform_binary!(ArrayContainsAll, *op),
2074        // ArrayContainedBy (<@): recurse into left and right operands
2075        Expression::ArrayContainedBy(op) => transform_binary!(ArrayContainedBy, *op),
2076        // ArrayOverlaps (&&): recurse into left and right operands
2077        Expression::ArrayOverlaps(op) => transform_binary!(ArrayOverlaps, *op),
2078        // TsMatch (@@): recurse into left and right operands
2079        Expression::TsMatch(op) => transform_binary!(TsMatch, *op),
2080        // Adjacent (-|-): recurse into left and right operands
2081        Expression::Adjacent(op) => transform_binary!(Adjacent, *op),
2082
2083        // Table: recurse into when (HistoricalData) and changes fields
2084        Expression::Table(mut t) => {
2085            if let Some(when) = t.when.take() {
2086                let transformed =
2087                    transform_recursive(Expression::HistoricalData(when), transform_fn)?;
2088                if let Expression::HistoricalData(hd) = transformed {
2089                    t.when = Some(hd);
2090                }
2091            }
2092            if let Some(changes) = t.changes.take() {
2093                let transformed = transform_recursive(Expression::Changes(changes), transform_fn)?;
2094                if let Expression::Changes(c) = transformed {
2095                    t.changes = Some(c);
2096                }
2097            }
2098            Expression::Table(t)
2099        }
2100
2101        // HistoricalData (Snowflake time travel): recurse into expression
2102        Expression::HistoricalData(mut hd) => {
2103            *hd.expression = transform_recursive(*hd.expression, transform_fn)?;
2104            Expression::HistoricalData(hd)
2105        }
2106
2107        // Changes (Snowflake CHANGES clause): recurse into at_before and end
2108        Expression::Changes(mut c) => {
2109            if let Some(at_before) = c.at_before.take() {
2110                c.at_before = Some(Box::new(transform_recursive(*at_before, transform_fn)?));
2111            }
2112            if let Some(end) = c.end.take() {
2113                c.end = Some(Box::new(transform_recursive(*end, transform_fn)?));
2114            }
2115            Expression::Changes(c)
2116        }
2117
2118        // TableArgument: TABLE(expr) or MODEL(expr)
2119        Expression::TableArgument(mut ta) => {
2120            ta.this = transform_recursive(ta.this, transform_fn)?;
2121            Expression::TableArgument(ta)
2122        }
2123
2124        // JoinedTable: (tbl1 JOIN tbl2 ON ...) - recurse into left and join tables
2125        Expression::JoinedTable(mut jt) => {
2126            jt.left = transform_recursive(jt.left, transform_fn)?;
2127            jt.joins = jt
2128                .joins
2129                .into_iter()
2130                .map(|mut join| {
2131                    join.this = transform_recursive(join.this, transform_fn)?;
2132                    if let Some(on) = join.on.take() {
2133                        join.on = Some(transform_recursive(on, transform_fn)?);
2134                    }
2135                    match transform_fn(Expression::Join(Box::new(join)))? {
2136                        Expression::Join(j) => Ok(*j),
2137                        _ => Err(crate::error::Error::parse(
2138                            "Join transformation returned non-join expression",
2139                            0,
2140                            0,
2141                            0,
2142                            0,
2143                        )),
2144                    }
2145                })
2146                .collect::<Result<Vec<_>>>()?;
2147            jt.lateral_views = jt
2148                .lateral_views
2149                .into_iter()
2150                .map(|mut lv| {
2151                    lv.this = transform_recursive(lv.this, transform_fn)?;
2152                    Ok(lv)
2153                })
2154                .collect::<Result<Vec<_>>>()?;
2155            Expression::JoinedTable(jt)
2156        }
2157
2158        // Lateral: LATERAL func() - recurse into the function expression
2159        Expression::Lateral(mut lat) => {
2160            *lat.this = transform_recursive(*lat.this, transform_fn)?;
2161            Expression::Lateral(lat)
2162        }
2163
2164        // WithinGroup: recurse into order_by items (for NULLS FIRST/LAST etc.)
2165        // but NOT into wg.this - the inner function is handled by StringAggConvert/GroupConcatConvert
2166        // as a unit together with the WithinGroup wrapper
2167        Expression::WithinGroup(mut wg) => {
2168            wg.order_by = wg
2169                .order_by
2170                .into_iter()
2171                .map(|mut o| {
2172                    let original = o.this.clone();
2173                    o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
2174                    match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
2175                        Ok(Expression::Ordered(transformed)) => *transformed,
2176                        Ok(_) | Err(_) => o,
2177                    }
2178                })
2179                .collect();
2180            Expression::WithinGroup(wg)
2181        }
2182
2183        // Filter: recurse into both the aggregate and the filter condition
2184        Expression::Filter(mut f) => {
2185            f.this = Box::new(transform_recursive(*f.this, transform_fn)?);
2186            f.expression = Box::new(transform_recursive(*f.expression, transform_fn)?);
2187            Expression::Filter(f)
2188        }
2189
2190        // Aggregate functions (AggFunc-based): recurse into the aggregate argument,
2191        // filter, order_by, having_max, and limit.
2192        // Stddev, StddevSamp, Variance, and ArrayAgg are handled earlier in this match.
2193        Expression::Sum(f) => recurse_agg!(Sum, f),
2194        Expression::Avg(f) => recurse_agg!(Avg, f),
2195        Expression::Min(f) => recurse_agg!(Min, f),
2196        Expression::Max(f) => recurse_agg!(Max, f),
2197        Expression::CountIf(f) => recurse_agg!(CountIf, f),
2198        Expression::StddevPop(f) => recurse_agg!(StddevPop, f),
2199        Expression::VarPop(f) => recurse_agg!(VarPop, f),
2200        Expression::VarSamp(f) => recurse_agg!(VarSamp, f),
2201        Expression::Median(f) => recurse_agg!(Median, f),
2202        Expression::Mode(f) => recurse_agg!(Mode, f),
2203        Expression::First(f) => recurse_agg!(First, f),
2204        Expression::Last(f) => recurse_agg!(Last, f),
2205        Expression::AnyValue(f) => recurse_agg!(AnyValue, f),
2206        Expression::ApproxDistinct(f) => recurse_agg!(ApproxDistinct, f),
2207        Expression::ApproxCountDistinct(f) => recurse_agg!(ApproxCountDistinct, f),
2208        Expression::LogicalAnd(f) => recurse_agg!(LogicalAnd, f),
2209        Expression::LogicalOr(f) => recurse_agg!(LogicalOr, f),
2210        Expression::Skewness(f) => recurse_agg!(Skewness, f),
2211        Expression::ArrayConcatAgg(f) => recurse_agg!(ArrayConcatAgg, f),
2212        Expression::ArrayUniqueAgg(f) => recurse_agg!(ArrayUniqueAgg, f),
2213        Expression::BoolXorAgg(f) => recurse_agg!(BoolXorAgg, f),
2214        Expression::BitwiseOrAgg(f) => recurse_agg!(BitwiseOrAgg, f),
2215        Expression::BitwiseAndAgg(f) => recurse_agg!(BitwiseAndAgg, f),
2216        Expression::BitwiseXorAgg(f) => recurse_agg!(BitwiseXorAgg, f),
2217
2218        // Count has its own struct with an Option<Expression> `this` field
2219        Expression::Count(mut c) => {
2220            if let Some(this) = c.this.take() {
2221                c.this = Some(transform_recursive(this, transform_fn)?);
2222            }
2223            if let Some(filter) = c.filter.take() {
2224                c.filter = Some(transform_recursive(filter, transform_fn)?);
2225            }
2226            Expression::Count(c)
2227        }
2228
2229        Expression::PipeOperator(mut pipe) => {
2230            pipe.this = transform_recursive(pipe.this, transform_fn)?;
2231            pipe.expression = transform_recursive(pipe.expression, transform_fn)?;
2232            Expression::PipeOperator(pipe)
2233        }
2234
2235        // ArrayExcept/ArrayContains/ArrayDistinct: recurse into children
2236        Expression::ArrayExcept(mut f) => {
2237            f.this = transform_recursive(f.this, transform_fn)?;
2238            f.expression = transform_recursive(f.expression, transform_fn)?;
2239            Expression::ArrayExcept(f)
2240        }
2241        Expression::ArrayContains(mut f) => {
2242            f.this = transform_recursive(f.this, transform_fn)?;
2243            f.expression = transform_recursive(f.expression, transform_fn)?;
2244            Expression::ArrayContains(f)
2245        }
2246        Expression::ArrayDistinct(mut f) => {
2247            f.this = transform_recursive(f.this, transform_fn)?;
2248            Expression::ArrayDistinct(f)
2249        }
2250        Expression::ArrayPosition(mut f) => {
2251            f.this = transform_recursive(f.this, transform_fn)?;
2252            f.expression = transform_recursive(f.expression, transform_fn)?;
2253            Expression::ArrayPosition(f)
2254        }
2255
2256        // Pass through leaf nodes unchanged
2257        other => other,
2258    };
2259
2260    // Then apply the transform function
2261    transform_fn(expr)
2262}
2263
2264/// Returns the tokenizer config, generator config, and expression transform closure
2265/// for a built-in dialect type. This is the shared implementation used by both
2266/// `Dialect::get()` and custom dialect construction.
2267// ---------------------------------------------------------------------------
2268// Cached dialect configurations
2269// ---------------------------------------------------------------------------
2270
2271/// Pre-computed tokenizer + generator configs for a dialect, cached via `LazyLock`.
2272/// Transform closures are cheap (unit-struct method calls) and created fresh each time.
2273struct CachedDialectConfig {
2274    tokenizer_config: Arc<TokenizerConfig>,
2275    #[cfg(feature = "generate")]
2276    generator_config: Arc<GeneratorConfig>,
2277}
2278
2279struct DialectConfigs {
2280    tokenizer_config: Arc<TokenizerConfig>,
2281    #[cfg(feature = "generate")]
2282    generator_config: Arc<GeneratorConfig>,
2283    #[cfg(feature = "transpile")]
2284    transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2285}
2286
2287/// Declare a per-dialect `LazyLock<CachedDialectConfig>` static.
2288macro_rules! cached_dialect {
2289    ($static_name:ident, $dialect_struct:expr, $feature:literal) => {
2290        #[cfg(feature = $feature)]
2291        static $static_name: LazyLock<CachedDialectConfig> = LazyLock::new(|| {
2292            let d = $dialect_struct;
2293            CachedDialectConfig {
2294                tokenizer_config: Arc::new(d.tokenizer_config()),
2295                #[cfg(feature = "generate")]
2296                generator_config: Arc::new(d.generator_config()),
2297            }
2298        });
2299    };
2300}
2301
2302static CACHED_GENERIC: LazyLock<CachedDialectConfig> = LazyLock::new(|| {
2303    let d = GenericDialect;
2304    CachedDialectConfig {
2305        tokenizer_config: Arc::new(d.tokenizer_config()),
2306        #[cfg(feature = "generate")]
2307        generator_config: Arc::new(d.generator_config()),
2308    }
2309});
2310
2311cached_dialect!(CACHED_POSTGRESQL, PostgresDialect, "dialect-postgresql");
2312cached_dialect!(CACHED_MYSQL, MySQLDialect, "dialect-mysql");
2313cached_dialect!(CACHED_BIGQUERY, BigQueryDialect, "dialect-bigquery");
2314cached_dialect!(CACHED_SNOWFLAKE, SnowflakeDialect, "dialect-snowflake");
2315cached_dialect!(CACHED_DUCKDB, DuckDBDialect, "dialect-duckdb");
2316cached_dialect!(CACHED_TSQL, TSQLDialect, "dialect-tsql");
2317cached_dialect!(CACHED_ORACLE, OracleDialect, "dialect-oracle");
2318cached_dialect!(CACHED_HIVE, HiveDialect, "dialect-hive");
2319cached_dialect!(CACHED_SPARK, SparkDialect, "dialect-spark");
2320cached_dialect!(CACHED_SQLITE, SQLiteDialect, "dialect-sqlite");
2321cached_dialect!(CACHED_PRESTO, PrestoDialect, "dialect-presto");
2322cached_dialect!(CACHED_TRINO, TrinoDialect, "dialect-trino");
2323cached_dialect!(CACHED_REDSHIFT, RedshiftDialect, "dialect-redshift");
2324cached_dialect!(CACHED_CLICKHOUSE, ClickHouseDialect, "dialect-clickhouse");
2325cached_dialect!(CACHED_DATABRICKS, DatabricksDialect, "dialect-databricks");
2326cached_dialect!(CACHED_ATHENA, AthenaDialect, "dialect-athena");
2327cached_dialect!(CACHED_TERADATA, TeradataDialect, "dialect-teradata");
2328cached_dialect!(CACHED_DORIS, DorisDialect, "dialect-doris");
2329cached_dialect!(CACHED_STARROCKS, StarRocksDialect, "dialect-starrocks");
2330cached_dialect!(
2331    CACHED_MATERIALIZE,
2332    MaterializeDialect,
2333    "dialect-materialize"
2334);
2335cached_dialect!(CACHED_RISINGWAVE, RisingWaveDialect, "dialect-risingwave");
2336cached_dialect!(
2337    CACHED_SINGLESTORE,
2338    SingleStoreDialect,
2339    "dialect-singlestore"
2340);
2341cached_dialect!(
2342    CACHED_COCKROACHDB,
2343    CockroachDBDialect,
2344    "dialect-cockroachdb"
2345);
2346cached_dialect!(CACHED_TIDB, TiDBDialect, "dialect-tidb");
2347cached_dialect!(CACHED_DRUID, DruidDialect, "dialect-druid");
2348cached_dialect!(CACHED_SOLR, SolrDialect, "dialect-solr");
2349cached_dialect!(CACHED_TABLEAU, TableauDialect, "dialect-tableau");
2350cached_dialect!(CACHED_DUNE, DuneDialect, "dialect-dune");
2351cached_dialect!(CACHED_FABRIC, FabricDialect, "dialect-fabric");
2352cached_dialect!(CACHED_DRILL, DrillDialect, "dialect-drill");
2353cached_dialect!(CACHED_DREMIO, DremioDialect, "dialect-dremio");
2354cached_dialect!(CACHED_EXASOL, ExasolDialect, "dialect-exasol");
2355cached_dialect!(CACHED_DATAFUSION, DataFusionDialect, "dialect-datafusion");
2356
2357fn configs_for_dialect_type(dt: DialectType) -> DialectConfigs {
2358    /// Clone configs from a cached static and pair with a fresh transform closure.
2359    macro_rules! from_cache {
2360        ($cache:expr, $dialect_struct:expr) => {{
2361            let c = &*$cache;
2362            DialectConfigs {
2363                tokenizer_config: c.tokenizer_config.clone(),
2364                #[cfg(feature = "generate")]
2365                generator_config: c.generator_config.clone(),
2366                #[cfg(feature = "transpile")]
2367                transformer: Box::new(move |e| $dialect_struct.transform_expr(e)),
2368            }
2369        }};
2370    }
2371    match dt {
2372        #[cfg(feature = "dialect-postgresql")]
2373        DialectType::PostgreSQL => from_cache!(CACHED_POSTGRESQL, PostgresDialect),
2374        #[cfg(feature = "dialect-mysql")]
2375        DialectType::MySQL => from_cache!(CACHED_MYSQL, MySQLDialect),
2376        #[cfg(feature = "dialect-bigquery")]
2377        DialectType::BigQuery => from_cache!(CACHED_BIGQUERY, BigQueryDialect),
2378        #[cfg(feature = "dialect-snowflake")]
2379        DialectType::Snowflake => from_cache!(CACHED_SNOWFLAKE, SnowflakeDialect),
2380        #[cfg(feature = "dialect-duckdb")]
2381        DialectType::DuckDB => from_cache!(CACHED_DUCKDB, DuckDBDialect),
2382        #[cfg(feature = "dialect-tsql")]
2383        DialectType::TSQL => from_cache!(CACHED_TSQL, TSQLDialect),
2384        #[cfg(feature = "dialect-oracle")]
2385        DialectType::Oracle => from_cache!(CACHED_ORACLE, OracleDialect),
2386        #[cfg(feature = "dialect-hive")]
2387        DialectType::Hive => from_cache!(CACHED_HIVE, HiveDialect),
2388        #[cfg(feature = "dialect-spark")]
2389        DialectType::Spark => from_cache!(CACHED_SPARK, SparkDialect),
2390        #[cfg(feature = "dialect-sqlite")]
2391        DialectType::SQLite => from_cache!(CACHED_SQLITE, SQLiteDialect),
2392        #[cfg(feature = "dialect-presto")]
2393        DialectType::Presto => from_cache!(CACHED_PRESTO, PrestoDialect),
2394        #[cfg(feature = "dialect-trino")]
2395        DialectType::Trino => from_cache!(CACHED_TRINO, TrinoDialect),
2396        #[cfg(feature = "dialect-redshift")]
2397        DialectType::Redshift => from_cache!(CACHED_REDSHIFT, RedshiftDialect),
2398        #[cfg(feature = "dialect-clickhouse")]
2399        DialectType::ClickHouse => from_cache!(CACHED_CLICKHOUSE, ClickHouseDialect),
2400        #[cfg(feature = "dialect-databricks")]
2401        DialectType::Databricks => from_cache!(CACHED_DATABRICKS, DatabricksDialect),
2402        #[cfg(feature = "dialect-athena")]
2403        DialectType::Athena => from_cache!(CACHED_ATHENA, AthenaDialect),
2404        #[cfg(feature = "dialect-teradata")]
2405        DialectType::Teradata => from_cache!(CACHED_TERADATA, TeradataDialect),
2406        #[cfg(feature = "dialect-doris")]
2407        DialectType::Doris => from_cache!(CACHED_DORIS, DorisDialect),
2408        #[cfg(feature = "dialect-starrocks")]
2409        DialectType::StarRocks => from_cache!(CACHED_STARROCKS, StarRocksDialect),
2410        #[cfg(feature = "dialect-materialize")]
2411        DialectType::Materialize => from_cache!(CACHED_MATERIALIZE, MaterializeDialect),
2412        #[cfg(feature = "dialect-risingwave")]
2413        DialectType::RisingWave => from_cache!(CACHED_RISINGWAVE, RisingWaveDialect),
2414        #[cfg(feature = "dialect-singlestore")]
2415        DialectType::SingleStore => from_cache!(CACHED_SINGLESTORE, SingleStoreDialect),
2416        #[cfg(feature = "dialect-cockroachdb")]
2417        DialectType::CockroachDB => from_cache!(CACHED_COCKROACHDB, CockroachDBDialect),
2418        #[cfg(feature = "dialect-tidb")]
2419        DialectType::TiDB => from_cache!(CACHED_TIDB, TiDBDialect),
2420        #[cfg(feature = "dialect-druid")]
2421        DialectType::Druid => from_cache!(CACHED_DRUID, DruidDialect),
2422        #[cfg(feature = "dialect-solr")]
2423        DialectType::Solr => from_cache!(CACHED_SOLR, SolrDialect),
2424        #[cfg(feature = "dialect-tableau")]
2425        DialectType::Tableau => from_cache!(CACHED_TABLEAU, TableauDialect),
2426        #[cfg(feature = "dialect-dune")]
2427        DialectType::Dune => from_cache!(CACHED_DUNE, DuneDialect),
2428        #[cfg(feature = "dialect-fabric")]
2429        DialectType::Fabric => from_cache!(CACHED_FABRIC, FabricDialect),
2430        #[cfg(feature = "dialect-drill")]
2431        DialectType::Drill => from_cache!(CACHED_DRILL, DrillDialect),
2432        #[cfg(feature = "dialect-dremio")]
2433        DialectType::Dremio => from_cache!(CACHED_DREMIO, DremioDialect),
2434        #[cfg(feature = "dialect-exasol")]
2435        DialectType::Exasol => from_cache!(CACHED_EXASOL, ExasolDialect),
2436        #[cfg(feature = "dialect-datafusion")]
2437        DialectType::DataFusion => from_cache!(CACHED_DATAFUSION, DataFusionDialect),
2438        _ => from_cache!(CACHED_GENERIC, GenericDialect),
2439    }
2440}
2441
2442// ---------------------------------------------------------------------------
2443// Custom dialect registry
2444// ---------------------------------------------------------------------------
2445
2446static CUSTOM_DIALECT_REGISTRY: LazyLock<RwLock<HashMap<String, Arc<CustomDialectConfig>>>> =
2447    LazyLock::new(|| RwLock::new(HashMap::new()));
2448
2449struct CustomDialectConfig {
2450    name: String,
2451    base_dialect: DialectType,
2452    tokenizer_config: Arc<TokenizerConfig>,
2453    #[cfg(feature = "generate")]
2454    generator_config: GeneratorConfig,
2455    #[cfg(feature = "transpile")]
2456    transform: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2457    #[cfg(feature = "transpile")]
2458    preprocess: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2459}
2460
2461/// Fluent builder for creating and registering custom SQL dialects.
2462///
2463/// A custom dialect is based on an existing built-in dialect and allows selective
2464/// overrides of tokenizer configuration, generator configuration, and expression
2465/// transforms.
2466///
2467/// # Example
2468///
2469/// ```rust,ignore
2470/// use polyglot_sql::dialects::{CustomDialectBuilder, DialectType, Dialect};
2471/// use polyglot_sql::generator::NormalizeFunctions;
2472///
2473/// CustomDialectBuilder::new("my_postgres")
2474///     .based_on(DialectType::PostgreSQL)
2475///     .generator_config_modifier(|gc| {
2476///         gc.normalize_functions = NormalizeFunctions::Lower;
2477///     })
2478///     .register()
2479///     .unwrap();
2480///
2481/// let d = Dialect::get_by_name("my_postgres").unwrap();
2482/// let exprs = d.parse("SELECT COUNT(*)").unwrap();
2483/// let sql = d.generate(&exprs[0]).unwrap();
2484/// assert_eq!(sql, "select count(*)");
2485///
2486/// polyglot_sql::unregister_custom_dialect("my_postgres");
2487/// ```
2488pub struct CustomDialectBuilder {
2489    name: String,
2490    base_dialect: DialectType,
2491    tokenizer_modifier: Option<Box<dyn FnOnce(&mut TokenizerConfig)>>,
2492    #[cfg(feature = "generate")]
2493    generator_modifier: Option<Box<dyn FnOnce(&mut GeneratorConfig)>>,
2494    #[cfg(feature = "transpile")]
2495    transform: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2496    #[cfg(feature = "transpile")]
2497    preprocess: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2498}
2499
2500impl CustomDialectBuilder {
2501    /// Create a new builder with the given name. Defaults to `Generic` as the base dialect.
2502    pub fn new(name: impl Into<String>) -> Self {
2503        Self {
2504            name: name.into(),
2505            base_dialect: DialectType::Generic,
2506            tokenizer_modifier: None,
2507            #[cfg(feature = "generate")]
2508            generator_modifier: None,
2509            #[cfg(feature = "transpile")]
2510            transform: None,
2511            #[cfg(feature = "transpile")]
2512            preprocess: None,
2513        }
2514    }
2515
2516    /// Set the base built-in dialect to inherit configuration from.
2517    pub fn based_on(mut self, dialect: DialectType) -> Self {
2518        self.base_dialect = dialect;
2519        self
2520    }
2521
2522    /// Provide a closure that modifies the tokenizer configuration inherited from the base dialect.
2523    pub fn tokenizer_config_modifier<F>(mut self, f: F) -> Self
2524    where
2525        F: FnOnce(&mut TokenizerConfig) + 'static,
2526    {
2527        self.tokenizer_modifier = Some(Box::new(f));
2528        self
2529    }
2530
2531    /// Provide a closure that modifies the generator configuration inherited from the base dialect.
2532    #[cfg(feature = "generate")]
2533    pub fn generator_config_modifier<F>(mut self, f: F) -> Self
2534    where
2535        F: FnOnce(&mut GeneratorConfig) + 'static,
2536    {
2537        self.generator_modifier = Some(Box::new(f));
2538        self
2539    }
2540
2541    /// Set a custom per-node expression transform function.
2542    ///
2543    /// This replaces the base dialect's transform. It is called on every expression
2544    /// node during the recursive transform pass.
2545    #[cfg(feature = "transpile")]
2546    pub fn transform_fn<F>(mut self, f: F) -> Self
2547    where
2548        F: Fn(Expression) -> Result<Expression> + Send + Sync + 'static,
2549    {
2550        self.transform = Some(Arc::new(f));
2551        self
2552    }
2553
2554    /// Set a custom whole-tree preprocessing function.
2555    ///
2556    /// This replaces the base dialect's built-in preprocessing. It is called once
2557    /// on the entire expression tree before the recursive per-node transform.
2558    #[cfg(feature = "transpile")]
2559    pub fn preprocess_fn<F>(mut self, f: F) -> Self
2560    where
2561        F: Fn(Expression) -> Result<Expression> + Send + Sync + 'static,
2562    {
2563        self.preprocess = Some(Arc::new(f));
2564        self
2565    }
2566
2567    /// Build the custom dialect configuration and register it in the global registry.
2568    ///
2569    /// Returns an error if:
2570    /// - The name collides with a built-in dialect name
2571    /// - A custom dialect with the same name is already registered
2572    pub fn register(self) -> Result<()> {
2573        // Reject names that collide with built-in dialects
2574        if DialectType::from_str(&self.name).is_ok() {
2575            return Err(crate::error::Error::parse(
2576                format!(
2577                    "Cannot register custom dialect '{}': name collides with built-in dialect",
2578                    self.name
2579                ),
2580                0,
2581                0,
2582                0,
2583                0,
2584            ));
2585        }
2586
2587        // Get base configs
2588        let base_configs = configs_for_dialect_type(self.base_dialect);
2589        let mut tok_config = (*base_configs.tokenizer_config).clone();
2590        #[cfg(feature = "generate")]
2591        let mut gen_config = (*base_configs.generator_config).clone();
2592
2593        // Apply modifiers
2594        if let Some(tok_mod) = self.tokenizer_modifier {
2595            tok_mod(&mut tok_config);
2596        }
2597        #[cfg(feature = "generate")]
2598        if let Some(gen_mod) = self.generator_modifier {
2599            gen_mod(&mut gen_config);
2600        }
2601
2602        let config = CustomDialectConfig {
2603            name: self.name.clone(),
2604            base_dialect: self.base_dialect,
2605            tokenizer_config: Arc::new(tok_config),
2606            #[cfg(feature = "generate")]
2607            generator_config: gen_config,
2608            #[cfg(feature = "transpile")]
2609            transform: self.transform,
2610            #[cfg(feature = "transpile")]
2611            preprocess: self.preprocess,
2612        };
2613
2614        register_custom_dialect(config)
2615    }
2616}
2617
2618use std::str::FromStr;
2619
2620fn register_custom_dialect(config: CustomDialectConfig) -> Result<()> {
2621    let mut registry = CUSTOM_DIALECT_REGISTRY.write().map_err(|e| {
2622        crate::error::Error::parse(format!("Registry lock poisoned: {}", e), 0, 0, 0, 0)
2623    })?;
2624
2625    if registry.contains_key(&config.name) {
2626        return Err(crate::error::Error::parse(
2627            format!("Custom dialect '{}' is already registered", config.name),
2628            0,
2629            0,
2630            0,
2631            0,
2632        ));
2633    }
2634
2635    registry.insert(config.name.clone(), Arc::new(config));
2636    Ok(())
2637}
2638
2639/// Remove a custom dialect from the global registry.
2640///
2641/// Returns `true` if a dialect with that name was found and removed,
2642/// `false` if no such custom dialect existed.
2643pub fn unregister_custom_dialect(name: &str) -> bool {
2644    if let Ok(mut registry) = CUSTOM_DIALECT_REGISTRY.write() {
2645        registry.remove(name).is_some()
2646    } else {
2647        false
2648    }
2649}
2650
2651fn get_custom_dialect_config(name: &str) -> Option<Arc<CustomDialectConfig>> {
2652    CUSTOM_DIALECT_REGISTRY
2653        .read()
2654        .ok()
2655        .and_then(|registry| registry.get(name).cloned())
2656}
2657
2658/// Main entry point for dialect-specific SQL operations.
2659///
2660/// A `Dialect` bundles together a tokenizer, generator configuration, and expression
2661/// transformer for a specific SQL database engine. It is the high-level API through
2662/// which callers parse, generate, transform, and transpile SQL.
2663///
2664/// # Usage
2665///
2666/// ```rust,ignore
2667/// use polyglot_sql::dialects::{Dialect, DialectType};
2668///
2669/// // Parse PostgreSQL SQL into an AST
2670/// let pg = Dialect::get(DialectType::PostgreSQL);
2671/// let exprs = pg.parse("SELECT id, name FROM users WHERE active")?;
2672///
2673/// // Transpile from PostgreSQL to BigQuery
2674/// let results = pg.transpile("SELECT NOW()", DialectType::BigQuery)?;
2675/// assert_eq!(results[0], "SELECT CURRENT_TIMESTAMP()");
2676/// ```
2677///
2678/// Obtain an instance via [`Dialect::get`] or [`Dialect::get_by_name`].
2679/// The struct is `Send + Sync` safe so it can be shared across threads.
2680pub struct Dialect {
2681    dialect_type: DialectType,
2682    tokenizer: Tokenizer,
2683    #[cfg(feature = "generate")]
2684    generator_config: Arc<GeneratorConfig>,
2685    #[cfg(feature = "transpile")]
2686    transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2687    /// Optional function to get expression-specific generator config (for hybrid dialects like Athena).
2688    #[cfg(feature = "generate")]
2689    generator_config_for_expr: Option<Box<dyn Fn(&Expression) -> GeneratorConfig + Send + Sync>>,
2690    /// Optional custom preprocessing function (overrides built-in preprocess for custom dialects).
2691    #[cfg(feature = "transpile")]
2692    custom_preprocess: Option<Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2693}
2694
2695/// Options for [`Dialect::transpile_with`].
2696///
2697/// Use [`TranspileOptions::default`] for defaults, then tweak the fields you need.
2698/// The struct is marked `#[non_exhaustive]` so new fields can be added without
2699/// breaking the API.
2700///
2701/// The struct derives `Serialize`/`Deserialize` using camelCase field names so
2702/// it can be round-tripped over JSON bridges (C FFI, WASM) without mapping.
2703#[cfg(feature = "transpile")]
2704#[derive(Debug, Clone, Serialize, Deserialize)]
2705#[serde(rename_all = "camelCase", default)]
2706#[non_exhaustive]
2707pub struct TranspileOptions {
2708    /// Whether to pretty-print the output SQL.
2709    pub pretty: bool,
2710    /// How unsupported target-dialect constructs should be handled.
2711    ///
2712    /// The default is [`UnsupportedLevel::Warn`], which preserves the current
2713    /// compatibility behavior and continues transpilation.
2714    pub unsupported_level: UnsupportedLevel,
2715    /// Maximum number of unsupported diagnostics to include in raised errors.
2716    pub max_unsupported: usize,
2717    /// Complexity guard limits used while parsing, transforming, and generating.
2718    pub complexity_guard: ComplexityGuardOptions,
2719}
2720
2721#[cfg(feature = "transpile")]
2722impl Default for TranspileOptions {
2723    fn default() -> Self {
2724        Self {
2725            pretty: false,
2726            unsupported_level: UnsupportedLevel::Warn,
2727            max_unsupported: 3,
2728            complexity_guard: ComplexityGuardOptions::default(),
2729        }
2730    }
2731}
2732
2733#[cfg(feature = "transpile")]
2734impl TranspileOptions {
2735    /// Construct options with pretty-printing enabled.
2736    pub fn pretty() -> Self {
2737        Self {
2738            pretty: true,
2739            ..Default::default()
2740        }
2741    }
2742
2743    /// Construct options that raise when known unsupported constructs remain.
2744    pub fn strict() -> Self {
2745        Self {
2746            unsupported_level: UnsupportedLevel::Raise,
2747            ..Default::default()
2748        }
2749    }
2750
2751    /// Set how unsupported target-dialect constructs should be handled.
2752    pub fn with_unsupported_level(mut self, level: UnsupportedLevel) -> Self {
2753        self.unsupported_level = level;
2754        self
2755    }
2756
2757    /// Set the maximum number of unsupported diagnostics to include in raised errors.
2758    pub fn with_max_unsupported(mut self, max: usize) -> Self {
2759        self.max_unsupported = max;
2760        self
2761    }
2762
2763    /// Set complexity guard limits for parse/transpile/generate recursion-heavy paths.
2764    pub fn with_complexity_guard(mut self, guard: ComplexityGuardOptions) -> Self {
2765        self.complexity_guard = guard;
2766        self
2767    }
2768}
2769
2770/// A value that can be used as the target dialect in [`Dialect::transpile`] /
2771/// [`Dialect::transpile_with`].
2772///
2773/// Implemented for [`DialectType`] (built-in dialect enum) and `&Dialect` (any
2774/// dialect handle, including custom ones). End users do not normally need to
2775/// implement this trait themselves.
2776#[cfg(feature = "transpile")]
2777pub trait TranspileTarget {
2778    /// Invoke `f` with a reference to the resolved target dialect.
2779    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R;
2780}
2781
2782#[cfg(feature = "transpile")]
2783impl TranspileTarget for DialectType {
2784    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R {
2785        f(&Dialect::get(self))
2786    }
2787}
2788
2789#[cfg(feature = "transpile")]
2790impl TranspileTarget for &Dialect {
2791    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R {
2792        f(self)
2793    }
2794}
2795
2796impl Dialect {
2797    /// Creates a fully configured [`Dialect`] instance for the given [`DialectType`].
2798    ///
2799    /// This is the primary constructor. It initializes the tokenizer, generator config,
2800    /// and expression transformer based on the dialect's [`DialectImpl`] implementation.
2801    /// For hybrid dialects like Athena, it also sets up expression-specific generator
2802    /// config routing.
2803    pub fn get(dialect_type: DialectType) -> Self {
2804        let configs = configs_for_dialect_type(dialect_type);
2805        let tokenizer_config = configs.tokenizer_config;
2806        #[cfg(feature = "generate")]
2807        let generator_config = configs.generator_config;
2808        #[cfg(feature = "transpile")]
2809        let transformer = configs.transformer;
2810
2811        // Set up expression-specific generator config for hybrid dialects
2812        #[cfg(feature = "generate")]
2813        let generator_config_for_expr: Option<
2814            Box<dyn Fn(&Expression) -> GeneratorConfig + Send + Sync>,
2815        > = match dialect_type {
2816            #[cfg(feature = "dialect-athena")]
2817            DialectType::Athena => Some(Box::new(|expr| {
2818                AthenaDialect.generator_config_for_expr(expr)
2819            })),
2820            _ => None,
2821        };
2822
2823        Self {
2824            dialect_type,
2825            tokenizer: Tokenizer::from_shared_config(tokenizer_config),
2826            #[cfg(feature = "generate")]
2827            generator_config,
2828            #[cfg(feature = "transpile")]
2829            transformer,
2830            #[cfg(feature = "generate")]
2831            generator_config_for_expr,
2832            #[cfg(feature = "transpile")]
2833            custom_preprocess: None,
2834        }
2835    }
2836
2837    /// Look up a dialect by string name.
2838    ///
2839    /// Checks built-in dialect names first (via [`DialectType::from_str`]), then
2840    /// falls back to the custom dialect registry. Returns `None` if no dialect
2841    /// with the given name exists.
2842    pub fn get_by_name(name: &str) -> Option<Self> {
2843        // Try built-in first
2844        if let Ok(dt) = DialectType::from_str(name) {
2845            return Some(Self::get(dt));
2846        }
2847
2848        // Try custom registry
2849        let config = get_custom_dialect_config(name)?;
2850        Some(Self::from_custom_config(&config))
2851    }
2852
2853    /// Construct a `Dialect` from a custom dialect configuration.
2854    fn from_custom_config(config: &CustomDialectConfig) -> Self {
2855        // Build the transformer: use custom if provided, else use base dialect's
2856        #[cfg(feature = "transpile")]
2857        let transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync> =
2858            if let Some(ref custom_transform) = config.transform {
2859                let t = Arc::clone(custom_transform);
2860                Box::new(move |e| t(e))
2861            } else {
2862                configs_for_dialect_type(config.base_dialect).transformer
2863            };
2864
2865        // Build the custom preprocess: use custom if provided
2866        #[cfg(feature = "transpile")]
2867        let custom_preprocess: Option<
2868            Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2869        > = config.preprocess.as_ref().map(|p| {
2870            let p = Arc::clone(p);
2871            Box::new(move |e: Expression| p(e))
2872                as Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>
2873        });
2874
2875        Self {
2876            dialect_type: config.base_dialect,
2877            tokenizer: Tokenizer::from_shared_config(config.tokenizer_config.clone()),
2878            #[cfg(feature = "generate")]
2879            generator_config: Arc::new(config.generator_config.clone()),
2880            #[cfg(feature = "transpile")]
2881            transformer,
2882            #[cfg(feature = "generate")]
2883            generator_config_for_expr: None,
2884            #[cfg(feature = "transpile")]
2885            custom_preprocess,
2886        }
2887    }
2888
2889    /// Get the dialect type
2890    pub fn dialect_type(&self) -> DialectType {
2891        self.dialect_type
2892    }
2893
2894    /// Get the generator configuration
2895    #[cfg(feature = "generate")]
2896    pub fn generator_config(&self) -> &GeneratorConfig {
2897        &self.generator_config
2898    }
2899
2900    /// Parses a SQL string into a list of [`Expression`] AST nodes.
2901    ///
2902    /// The input may contain multiple semicolon-separated statements; each one
2903    /// produces a separate element in the returned vector. Tokenization uses
2904    /// this dialect's configured tokenizer, and parsing uses the dialect-aware parser.
2905    pub fn parse(&self, sql: &str) -> Result<Vec<Expression>> {
2906        self.parse_with_guard(sql, self.default_complexity_guard())
2907    }
2908
2909    fn parse_with_guard(
2910        &self,
2911        sql: &str,
2912        complexity_guard: ComplexityGuardOptions,
2913    ) -> Result<Vec<Expression>> {
2914        enforce_input(sql, &complexity_guard)?;
2915        let source: Arc<str> = Arc::from(sql);
2916        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
2917        let config = crate::parser::ParserConfig {
2918            dialect: Some(self.dialect_type),
2919            complexity_guard,
2920            ..Default::default()
2921        };
2922        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
2923        parser.parse()
2924    }
2925
2926    fn default_complexity_guard(&self) -> ComplexityGuardOptions {
2927        let mut guard = ComplexityGuardOptions::default();
2928        if matches!(self.dialect_type, DialectType::ClickHouse) {
2929            guard.max_ast_depth = Some(4_096);
2930            guard.max_function_call_depth = Some(512);
2931        }
2932        guard
2933    }
2934
2935    #[cfg(feature = "transpile")]
2936    fn default_transpile_complexity_guard(
2937        &self,
2938        target_dialect: &Dialect,
2939        guard: ComplexityGuardOptions,
2940    ) -> ComplexityGuardOptions {
2941        if guard != ComplexityGuardOptions::default() {
2942            return guard;
2943        }
2944
2945        if matches!(self.dialect_type, DialectType::ClickHouse)
2946            || matches!(target_dialect.dialect_type, DialectType::ClickHouse)
2947        {
2948            let mut guard = guard;
2949            guard.max_ast_depth = Some(4_096);
2950            guard.max_function_call_depth = Some(512);
2951            guard
2952        } else {
2953            guard
2954        }
2955    }
2956
2957    /// Parse a standalone SQL data type using this dialect's tokenizer and parser.
2958    ///
2959    /// This accepts type strings such as `DECIMAL(10, 2)`, `INT[]`, or
2960    /// `STRUCT(a INT, b VARCHAR)` without requiring a surrounding statement.
2961    pub fn parse_data_type(&self, sql: &str) -> Result<DataType> {
2962        let complexity_guard = self.default_complexity_guard();
2963        enforce_input(sql, &complexity_guard)?;
2964        let source: Arc<str> = Arc::from(sql);
2965        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
2966        let config = crate::parser::ParserConfig {
2967            dialect: Some(self.dialect_type),
2968            complexity_guard,
2969            ..Default::default()
2970        };
2971        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
2972        parser.parse_standalone_data_type()
2973    }
2974
2975    /// Tokenize SQL using this dialect's tokenizer configuration.
2976    pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
2977        self.tokenizer.tokenize(sql)
2978    }
2979
2980    /// Get the generator config for a specific expression (supports hybrid dialects).
2981    /// Returns an owned `GeneratorConfig` suitable for mutation before generation.
2982    #[cfg(feature = "generate")]
2983    fn get_config_for_expr(&self, expr: &Expression) -> GeneratorConfig {
2984        if let Some(ref config_fn) = self.generator_config_for_expr {
2985            config_fn(expr)
2986        } else {
2987            (*self.generator_config).clone()
2988        }
2989    }
2990
2991    /// Generates a SQL string from an [`Expression`] AST node.
2992    ///
2993    /// The output uses this dialect's generator configuration for identifier quoting,
2994    /// keyword casing, function name normalization, and syntax style. The result is
2995    /// a single-line (non-pretty) SQL string.
2996    #[cfg(feature = "generate")]
2997    pub fn generate(&self, expr: &Expression) -> Result<String> {
2998        // Fast path: when no per-expression config override, share the Arc cheaply.
2999        if self.generator_config_for_expr.is_none() {
3000            let mut generator = Generator::with_arc_config(self.generator_config.clone());
3001            return generator.generate(expr);
3002        }
3003        let config = self.get_config_for_expr(expr);
3004        let mut generator = Generator::with_config(config);
3005        generator.generate(expr)
3006    }
3007
3008    /// Generate SQL from an expression with pretty printing enabled
3009    #[cfg(feature = "generate")]
3010    pub fn generate_pretty(&self, expr: &Expression) -> Result<String> {
3011        let mut config = self.get_config_for_expr(expr);
3012        config.pretty = true;
3013        let mut generator = Generator::with_config(config);
3014        generator.generate(expr)
3015    }
3016
3017    /// Generate SQL from an expression with source dialect info (for transpilation)
3018    #[cfg(feature = "generate")]
3019    pub fn generate_with_source(&self, expr: &Expression, source: DialectType) -> Result<String> {
3020        let mut config = self.get_config_for_expr(expr);
3021        config.source_dialect = Some(source);
3022        let mut generator = Generator::with_config(config);
3023        generator.generate(expr)
3024    }
3025
3026    /// Generate SQL from an expression with pretty printing and source dialect info
3027    #[cfg(feature = "generate")]
3028    pub fn generate_pretty_with_source(
3029        &self,
3030        expr: &Expression,
3031        source: DialectType,
3032    ) -> Result<String> {
3033        let mut config = self.get_config_for_expr(expr);
3034        config.pretty = true;
3035        config.source_dialect = Some(source);
3036        let mut generator = Generator::with_config(config);
3037        generator.generate(expr)
3038    }
3039
3040    /// Generate SQL from an expression with source dialect and transpile options.
3041    #[cfg(all(feature = "generate", feature = "transpile"))]
3042    fn generate_with_transpile_options(
3043        &self,
3044        expr: &Expression,
3045        source: DialectType,
3046        opts: &TranspileOptions,
3047    ) -> Result<String> {
3048        let mut config = self.get_config_for_expr(expr);
3049        config.source_dialect = Some(source);
3050        config.pretty = opts.pretty;
3051        config.unsupported_level = opts.unsupported_level;
3052        config.max_unsupported = opts.max_unsupported.max(1);
3053        config.complexity_guard = opts.complexity_guard;
3054        let mut generator = Generator::with_config(config);
3055        generator.generate(expr)
3056    }
3057
3058    /// Generate SQL from an expression with forced identifier quoting (identify=True)
3059    #[cfg(feature = "generate")]
3060    pub fn generate_with_identify(&self, expr: &Expression) -> Result<String> {
3061        let mut config = self.get_config_for_expr(expr);
3062        config.always_quote_identifiers = true;
3063        let mut generator = Generator::with_config(config);
3064        generator.generate(expr)
3065    }
3066
3067    /// Generate SQL from an expression with pretty printing and forced identifier quoting
3068    #[cfg(feature = "generate")]
3069    pub fn generate_pretty_with_identify(&self, expr: &Expression) -> Result<String> {
3070        let mut config = (*self.generator_config).clone();
3071        config.pretty = true;
3072        config.always_quote_identifiers = true;
3073        let mut generator = Generator::with_config(config);
3074        generator.generate(expr)
3075    }
3076
3077    /// Generate SQL from an expression with caller-specified config overrides
3078    #[cfg(feature = "generate")]
3079    pub fn generate_with_overrides(
3080        &self,
3081        expr: &Expression,
3082        overrides: impl FnOnce(&mut GeneratorConfig),
3083    ) -> Result<String> {
3084        let mut config = self.get_config_for_expr(expr);
3085        overrides(&mut config);
3086        let mut generator = Generator::with_config(config);
3087        generator.generate(expr)
3088    }
3089
3090    /// Transforms an expression tree to conform to this dialect's syntax and semantics.
3091    ///
3092    /// The transformation proceeds in two phases:
3093    /// 1. **Preprocessing** -- whole-tree structural rewrites such as eliminating QUALIFY,
3094    ///    ensuring boolean predicates, or converting DISTINCT ON to a window-function pattern.
3095    /// 2. **Recursive per-node transform** -- a bottom-up pass via [`transform_recursive`]
3096    ///    that applies this dialect's [`DialectImpl::transform_expr`] to every node.
3097    ///
3098    /// This method is used both during transpilation (to rewrite an AST for a target dialect)
3099    /// and for identity transforms (normalizing SQL within the same dialect).
3100    #[cfg(feature = "transpile")]
3101    pub fn transform(&self, expr: Expression) -> Result<Expression> {
3102        self.transform_with_guard(expr, self.default_complexity_guard())
3103    }
3104
3105    #[cfg(feature = "transpile")]
3106    fn transform_with_guard(
3107        &self,
3108        expr: Expression,
3109        complexity_guard: ComplexityGuardOptions,
3110    ) -> Result<Expression> {
3111        enforce_generate_ast(&expr, &complexity_guard)?;
3112        // Apply preprocessing transforms based on dialect
3113        let preprocessed = self.preprocess(expr)?;
3114        // Then apply recursive transformation
3115        transform_recursive(preprocessed, &self.transformer)
3116    }
3117
3118    /// Apply dialect-specific preprocessing transforms
3119    #[cfg(feature = "transpile")]
3120    fn preprocess(&self, expr: Expression) -> Result<Expression> {
3121        // If a custom preprocess function is set, use it instead of the built-in logic
3122        if let Some(ref custom_preprocess) = self.custom_preprocess {
3123            return custom_preprocess(expr);
3124        }
3125
3126        #[cfg(any(
3127            feature = "dialect-mysql",
3128            feature = "dialect-postgresql",
3129            feature = "dialect-bigquery",
3130            feature = "dialect-snowflake",
3131            feature = "dialect-tsql",
3132            feature = "dialect-spark",
3133            feature = "dialect-databricks",
3134            feature = "dialect-hive",
3135            feature = "dialect-sqlite",
3136            feature = "dialect-trino",
3137            feature = "dialect-presto",
3138            feature = "dialect-duckdb",
3139            feature = "dialect-redshift",
3140            feature = "dialect-starrocks",
3141            feature = "dialect-oracle",
3142            feature = "dialect-clickhouse",
3143            feature = "dialect-fabric",
3144        ))]
3145        use crate::transforms;
3146
3147        match self.dialect_type {
3148            // MySQL doesn't support QUALIFY, DISTINCT ON, FULL OUTER JOIN
3149            // MySQL doesn't natively support GENERATE_DATE_ARRAY (expand to recursive CTE)
3150            #[cfg(feature = "dialect-mysql")]
3151            DialectType::MySQL => {
3152                let expr = transforms::eliminate_qualify(expr)?;
3153                let expr = transforms::eliminate_full_outer_join(expr)?;
3154                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3155                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3156                Ok(expr)
3157            }
3158            // PostgreSQL doesn't support QUALIFY
3159            // PostgreSQL: UNNEST(GENERATE_SERIES) -> subquery wrapping
3160            // PostgreSQL: Normalize SET ... TO to SET ... = in CREATE FUNCTION
3161            #[cfg(feature = "dialect-postgresql")]
3162            DialectType::PostgreSQL => {
3163                let expr = transforms::eliminate_qualify(expr)?;
3164                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3165                let expr = transforms::unwrap_unnest_generate_series_for_postgres(expr)?;
3166                // Normalize SET ... TO to SET ... = in CREATE FUNCTION
3167                // Only normalize when sqlglot would fully parse (no body) —
3168                // sqlglot falls back to Command for complex function bodies,
3169                // preserving the original text including TO.
3170                let expr = if let Expression::CreateFunction(mut cf) = expr {
3171                    if cf.body.is_none() {
3172                        for opt in &mut cf.set_options {
3173                            if let crate::expressions::FunctionSetValue::Value { use_to, .. } =
3174                                &mut opt.value
3175                            {
3176                                *use_to = false;
3177                            }
3178                        }
3179                    }
3180                    Expression::CreateFunction(cf)
3181                } else {
3182                    expr
3183                };
3184                Ok(expr)
3185            }
3186            // BigQuery doesn't support DISTINCT ON or CTE column aliases
3187            #[cfg(feature = "dialect-bigquery")]
3188            DialectType::BigQuery => {
3189                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3190                let expr = transforms::pushdown_cte_column_names(expr)?;
3191                let expr = transforms::explode_projection_to_unnest(expr, DialectType::BigQuery)?;
3192                Ok(expr)
3193            }
3194            // Snowflake
3195            #[cfg(feature = "dialect-snowflake")]
3196            DialectType::Snowflake => {
3197                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3198                let expr = transforms::eliminate_window_clause(expr)?;
3199                let expr = transforms::snowflake_flatten_projection_to_unnest(expr)?;
3200                Ok(expr)
3201            }
3202            // TSQL doesn't support QUALIFY
3203            // TSQL requires boolean expressions in WHERE/HAVING (no implicit truthiness)
3204            // TSQL doesn't support CTEs in subqueries (hoist to top level)
3205            // NOTE: no_limit_order_by_union is handled in cross_dialect_normalize (not preprocess)
3206            // to avoid breaking TSQL identity tests where ORDER BY on UNION is valid
3207            #[cfg(feature = "dialect-tsql")]
3208            DialectType::TSQL => {
3209                let expr = transforms::eliminate_qualify(expr)?;
3210                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3211                let expr =
3212                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::TSQL)?;
3213                let expr = transforms::ensure_bools(expr)?;
3214                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3215                let expr = transforms::strip_cte_materialization(expr)?;
3216                let expr = transforms::move_ctes_to_top_level(expr)?;
3217                let expr = transforms::qualify_derived_table_outputs(expr)?;
3218                Ok(expr)
3219            }
3220            // Fabric shares T-SQL predicate rules and CTE placement restrictions,
3221            // but keeps Fabric-specific APPLY and derived-table behavior separate.
3222            #[cfg(feature = "dialect-fabric")]
3223            DialectType::Fabric => {
3224                let expr =
3225                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::Fabric)?;
3226                let expr = transforms::ensure_bools(expr)?;
3227                let expr = transforms::strip_cte_materialization(expr)?;
3228                let expr = transforms::move_ctes_to_top_level(expr)?;
3229                Ok(expr)
3230            }
3231            // Spark doesn't support QUALIFY (but Databricks does)
3232            // Spark doesn't support CTEs in subqueries (hoist to top level)
3233            #[cfg(feature = "dialect-spark")]
3234            DialectType::Spark => {
3235                let expr = transforms::eliminate_qualify(expr)?;
3236                let expr = transforms::add_auto_table_alias(expr)?;
3237                let expr = transforms::simplify_nested_paren_values(expr)?;
3238                let expr = transforms::move_ctes_to_top_level(expr)?;
3239                Ok(expr)
3240            }
3241            // Databricks supports QUALIFY natively
3242            // Databricks doesn't support CTEs in subqueries (hoist to top level)
3243            #[cfg(feature = "dialect-databricks")]
3244            DialectType::Databricks => {
3245                let expr = transforms::add_auto_table_alias(expr)?;
3246                let expr = transforms::simplify_nested_paren_values(expr)?;
3247                let expr = transforms::move_ctes_to_top_level(expr)?;
3248                Ok(expr)
3249            }
3250            // Hive doesn't support QUALIFY or CTEs in subqueries
3251            #[cfg(feature = "dialect-hive")]
3252            DialectType::Hive => {
3253                let expr = transforms::eliminate_qualify(expr)?;
3254                let expr = transforms::move_ctes_to_top_level(expr)?;
3255                Ok(expr)
3256            }
3257            // SQLite doesn't support QUALIFY
3258            #[cfg(feature = "dialect-sqlite")]
3259            DialectType::SQLite => {
3260                let expr = transforms::eliminate_qualify(expr)?;
3261                Ok(expr)
3262            }
3263            // Trino doesn't support QUALIFY
3264            #[cfg(feature = "dialect-trino")]
3265            DialectType::Trino => {
3266                let expr = transforms::eliminate_qualify(expr)?;
3267                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Trino)?;
3268                Ok(expr)
3269            }
3270            // Presto doesn't support QUALIFY or WINDOW clause
3271            #[cfg(feature = "dialect-presto")]
3272            DialectType::Presto => {
3273                let expr = transforms::eliminate_qualify(expr)?;
3274                let expr = transforms::eliminate_window_clause(expr)?;
3275                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Presto)?;
3276                Ok(expr)
3277            }
3278            // DuckDB supports QUALIFY - no elimination needed
3279            // Expand POSEXPLODE to GENERATE_SUBSCRIPTS + UNNEST
3280            // Expand LIKE ANY / ILIKE ANY to OR chains (DuckDB doesn't support quantifiers)
3281            #[cfg(feature = "dialect-duckdb")]
3282            DialectType::DuckDB => {
3283                let expr = transforms::expand_posexplode_duckdb(expr)?;
3284                let expr = transforms::expand_like_any(expr)?;
3285                Ok(expr)
3286            }
3287            // Redshift doesn't support QUALIFY, WINDOW clause, or GENERATE_DATE_ARRAY
3288            #[cfg(feature = "dialect-redshift")]
3289            DialectType::Redshift => {
3290                let expr = transforms::eliminate_qualify(expr)?;
3291                let expr = transforms::eliminate_window_clause(expr)?;
3292                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3293                Ok(expr)
3294            }
3295            // StarRocks doesn't support BETWEEN in DELETE statements or QUALIFY
3296            #[cfg(feature = "dialect-starrocks")]
3297            DialectType::StarRocks => {
3298                let expr = transforms::eliminate_qualify(expr)?;
3299                let expr = transforms::expand_between_in_delete(expr)?;
3300                let expr = transforms::eliminate_distinct_on_for_dialect(
3301                    expr,
3302                    Some(DialectType::StarRocks),
3303                    Some(DialectType::StarRocks),
3304                )?;
3305                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3306                Ok(expr)
3307            }
3308            // DataFusion supports QUALIFY and semi/anti joins natively
3309            #[cfg(feature = "dialect-datafusion")]
3310            DialectType::DataFusion => Ok(expr),
3311            // Oracle doesn't support QUALIFY
3312            #[cfg(feature = "dialect-oracle")]
3313            DialectType::Oracle => {
3314                let expr = transforms::eliminate_qualify(expr)?;
3315                Ok(expr)
3316            }
3317            // Drill - no special preprocessing needed
3318            #[cfg(feature = "dialect-drill")]
3319            DialectType::Drill => Ok(expr),
3320            // Teradata - no special preprocessing needed
3321            #[cfg(feature = "dialect-teradata")]
3322            DialectType::Teradata => Ok(expr),
3323            // ClickHouse doesn't support ORDER BY/LIMIT directly on UNION
3324            #[cfg(feature = "dialect-clickhouse")]
3325            DialectType::ClickHouse => {
3326                let expr = transforms::no_limit_order_by_union(expr)?;
3327                Ok(expr)
3328            }
3329            // Other dialects - no preprocessing
3330            _ => Ok(expr),
3331        }
3332    }
3333
3334    /// Transpile SQL from this dialect to the given target dialect.
3335    ///
3336    /// The target may be specified as either a built-in [`DialectType`] enum variant
3337    /// or as a reference to a [`Dialect`] handle (built-in or custom). Both work:
3338    ///
3339    /// ```rust,ignore
3340    /// let pg = Dialect::get(DialectType::PostgreSQL);
3341    /// pg.transpile("SELECT NOW()", DialectType::BigQuery)?;   // enum
3342    /// pg.transpile("SELECT NOW()", &custom_dialect)?;         // handle
3343    /// ```
3344    ///
3345    /// For pretty-printing or other options, use [`transpile_with`](Self::transpile_with).
3346    #[cfg(feature = "transpile")]
3347    pub fn transpile<T: TranspileTarget>(&self, sql: &str, target: T) -> Result<Vec<String>> {
3348        self.transpile_with(sql, target, TranspileOptions::default())
3349    }
3350
3351    /// Transpile SQL with configurable [`TranspileOptions`] (e.g. pretty-printing).
3352    #[cfg(feature = "transpile")]
3353    pub fn transpile_with<T: TranspileTarget>(
3354        &self,
3355        sql: &str,
3356        target: T,
3357        opts: TranspileOptions,
3358    ) -> Result<Vec<String>> {
3359        target.with_dialect(|td| self.transpile_inner(sql, td, &opts))
3360    }
3361
3362    #[cfg(feature = "transpile")]
3363    fn transpile_inner(
3364        &self,
3365        sql: &str,
3366        target_dialect: &Dialect,
3367        opts: &TranspileOptions,
3368    ) -> Result<Vec<String>> {
3369        let mut effective_opts = opts.clone();
3370        effective_opts.complexity_guard =
3371            self.default_transpile_complexity_guard(target_dialect, opts.complexity_guard);
3372        let opts = &effective_opts;
3373        let target = target_dialect.dialect_type;
3374        if matches!(self.dialect_type, DialectType::PostgreSQL)
3375            && matches!(target, DialectType::SQLite)
3376        {
3377            self.reject_pgvector_distance_operators_for_sqlite(sql)?;
3378        }
3379        let expressions = self.parse_with_guard(sql, opts.complexity_guard)?;
3380        let generic_identity =
3381            self.dialect_type == DialectType::Generic && target == DialectType::Generic;
3382
3383        if generic_identity {
3384            return expressions
3385                .into_iter()
3386                .map(|expr| {
3387                    Self::reject_strict_unsupported(&expr, self.dialect_type, target, opts)?;
3388                    target_dialect.generate_with_transpile_options(&expr, self.dialect_type, opts)
3389                })
3390                .collect();
3391        }
3392
3393        expressions
3394            .into_iter()
3395            .map(|expr| {
3396                // DuckDB source: normalize VARCHAR/CHAR to TEXT (DuckDB doesn't support
3397                // VARCHAR length constraints). This emulates Python sqlglot's DuckDB parser
3398                // where VARCHAR_LENGTH = None and VARCHAR maps to TEXT.
3399                let expr = if matches!(self.dialect_type, DialectType::DuckDB) {
3400                    use crate::expressions::DataType as DT;
3401                    transform_recursive(expr, &|e| match e {
3402                        Expression::DataType(DT::VarChar { .. }) => {
3403                            Ok(Expression::DataType(DT::Text))
3404                        }
3405                        Expression::DataType(DT::Char { .. }) => Ok(Expression::DataType(DT::Text)),
3406                        _ => Ok(e),
3407                    })?
3408                } else {
3409                    expr
3410                };
3411
3412                Self::reject_postgres_tsql_strict_regex_predicates(
3413                    &expr,
3414                    self.dialect_type,
3415                    target,
3416                    opts,
3417                )?;
3418
3419                // When source and target differ, first normalize the source dialect's
3420                // AST constructs to standard SQL, so that the target dialect can handle them.
3421                // This handles cases like Snowflake's SQUARE -> POWER, DIV0 -> CASE, etc.
3422                let normalized =
3423                    if self.dialect_type != target && self.dialect_type != DialectType::Generic {
3424                        self.transform_with_guard(expr, opts.complexity_guard)?
3425                    } else {
3426                        expr
3427                    };
3428
3429                // For TSQL source targeting non-TSQL: unwrap ISNULL(JSON_QUERY(...), JSON_VALUE(...))
3430                // to just JSON_QUERY(...) so cross_dialect_normalize can convert it cleanly.
3431                // The TSQL read transform wraps JsonQuery in ISNULL for identity, but for
3432                // cross-dialect transpilation we need the unwrapped JSON_QUERY.
3433                let normalized =
3434                    if matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3435                        && !matches!(target, DialectType::TSQL | DialectType::Fabric)
3436                    {
3437                        transform_recursive(normalized, &|e| {
3438                            if let Expression::Function(ref f) = e {
3439                                if f.name.eq_ignore_ascii_case("ISNULL") && f.args.len() == 2 {
3440                                    // Check if first arg is JSON_QUERY and second is JSON_VALUE
3441                                    if let (
3442                                        Expression::Function(ref jq),
3443                                        Expression::Function(ref jv),
3444                                    ) = (&f.args[0], &f.args[1])
3445                                    {
3446                                        if jq.name.eq_ignore_ascii_case("JSON_QUERY")
3447                                            && jv.name.eq_ignore_ascii_case("JSON_VALUE")
3448                                        {
3449                                            // Unwrap: return just JSON_QUERY(...)
3450                                            return Ok(f.args[0].clone());
3451                                        }
3452                                    }
3453                                }
3454                            }
3455                            Ok(e)
3456                        })?
3457                    } else {
3458                        normalized
3459                    };
3460
3461                // Snowflake source to non-Snowflake target: CURRENT_TIME -> LOCALTIME
3462                // Snowflake's CURRENT_TIME is equivalent to LOCALTIME in other dialects.
3463                // Python sqlglot parses Snowflake's CURRENT_TIME as Localtime expression.
3464                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3465                    && !matches!(target, DialectType::Snowflake)
3466                {
3467                    transform_recursive(normalized, &|e| {
3468                        if let Expression::Function(ref f) = e {
3469                            if f.name.eq_ignore_ascii_case("CURRENT_TIME") {
3470                                return Ok(Expression::Localtime(Box::new(
3471                                    crate::expressions::Localtime { this: None },
3472                                )));
3473                            }
3474                        }
3475                        Ok(e)
3476                    })?
3477                } else {
3478                    normalized
3479                };
3480
3481                // Snowflake source to DuckDB target: REPEAT(' ', n) -> REPEAT(' ', CAST(n AS BIGINT))
3482                // Snowflake's SPACE(n) is converted to REPEAT(' ', n) by the Snowflake source
3483                // transform. DuckDB requires the count argument to be BIGINT.
3484                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3485                    && matches!(target, DialectType::DuckDB)
3486                {
3487                    transform_recursive(normalized, &|e| {
3488                        if let Expression::Function(ref f) = e {
3489                            if f.name.eq_ignore_ascii_case("REPEAT") && f.args.len() == 2 {
3490                                // Check if first arg is space string literal
3491                                if let Expression::Literal(ref lit) = f.args[0] {
3492                                    if let crate::expressions::Literal::String(ref s) = lit.as_ref()
3493                                    {
3494                                        if s == " " {
3495                                            // Wrap second arg in CAST(... AS BIGINT) if not already
3496                                            if !matches!(f.args[1], Expression::Cast(_)) {
3497                                                let mut new_args = f.args.clone();
3498                                                new_args[1] = Expression::Cast(Box::new(
3499                                                    crate::expressions::Cast {
3500                                                        this: new_args[1].clone(),
3501                                                        to: crate::expressions::DataType::BigInt {
3502                                                            length: None,
3503                                                        },
3504                                                        trailing_comments: Vec::new(),
3505                                                        double_colon_syntax: false,
3506                                                        format: None,
3507                                                        default: None,
3508                                                        inferred_type: None,
3509                                                    },
3510                                                ));
3511                                                return Ok(Expression::Function(Box::new(
3512                                                    crate::expressions::Function {
3513                                                        name: f.name.clone(),
3514                                                        args: new_args,
3515                                                        distinct: f.distinct,
3516                                                        trailing_comments: f
3517                                                            .trailing_comments
3518                                                            .clone(),
3519                                                        use_bracket_syntax: f.use_bracket_syntax,
3520                                                        no_parens: f.no_parens,
3521                                                        quoted: f.quoted,
3522                                                        span: None,
3523                                                        inferred_type: None,
3524                                                    },
3525                                                )));
3526                                            }
3527                                        }
3528                                    }
3529                                }
3530                            }
3531                        }
3532                        Ok(e)
3533                    })?
3534                } else {
3535                    normalized
3536                };
3537
3538                // Propagate struct field names in arrays (for BigQuery source to non-BigQuery target)
3539                // BigQuery->BigQuery should NOT propagate names (BigQuery handles implicit inheritance)
3540                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3541                    && !matches!(target, DialectType::BigQuery)
3542                {
3543                    crate::transforms::propagate_struct_field_names(normalized)?
3544                } else {
3545                    normalized
3546                };
3547
3548                // Snowflake source to DuckDB target: RANDOM()/RANDOM(seed) -> scaled RANDOM()
3549                // Snowflake RANDOM() returns integer in [-2^63, 2^63-1], DuckDB RANDOM() returns float [0, 1)
3550                // Skip RANDOM inside UNIFORM/NORMAL/ZIPF/RANDSTR generator args since those
3551                // functions handle their generator args differently (as float seeds).
3552                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3553                    && matches!(target, DialectType::DuckDB)
3554                {
3555                    fn make_scaled_random() -> Expression {
3556                        let lower =
3557                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3558                                "-9.223372036854776E+18".to_string(),
3559                            )));
3560                        let upper =
3561                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3562                                "9.223372036854776e+18".to_string(),
3563                            )));
3564                        let random_call = Expression::Random(crate::expressions::Random);
3565                        let range_size = Expression::Paren(Box::new(crate::expressions::Paren {
3566                            this: Expression::Sub(Box::new(crate::expressions::BinaryOp {
3567                                left: upper,
3568                                right: lower.clone(),
3569                                left_comments: vec![],
3570                                operator_comments: vec![],
3571                                trailing_comments: vec![],
3572                                inferred_type: None,
3573                            })),
3574                            trailing_comments: vec![],
3575                        }));
3576                        let scaled = Expression::Mul(Box::new(crate::expressions::BinaryOp {
3577                            left: random_call,
3578                            right: range_size,
3579                            left_comments: vec![],
3580                            operator_comments: vec![],
3581                            trailing_comments: vec![],
3582                            inferred_type: None,
3583                        }));
3584                        let shifted = Expression::Add(Box::new(crate::expressions::BinaryOp {
3585                            left: lower,
3586                            right: scaled,
3587                            left_comments: vec![],
3588                            operator_comments: vec![],
3589                            trailing_comments: vec![],
3590                            inferred_type: None,
3591                        }));
3592                        Expression::Cast(Box::new(crate::expressions::Cast {
3593                            this: shifted,
3594                            to: crate::expressions::DataType::BigInt { length: None },
3595                            trailing_comments: vec![],
3596                            double_colon_syntax: false,
3597                            format: None,
3598                            default: None,
3599                            inferred_type: None,
3600                        }))
3601                    }
3602
3603                    // Pre-process: protect seeded RANDOM(seed) inside UNIFORM/NORMAL/ZIPF/RANDSTR
3604                    // by converting Rand{seed: Some(s)} to Function{name:"RANDOM", args:[s]}.
3605                    // This prevents transform_recursive (which is bottom-up) from expanding
3606                    // seeded RANDOM into make_scaled_random() and losing the seed value.
3607                    // Unseeded RANDOM()/Rand{seed:None} is left as-is so it gets expanded
3608                    // and then un-expanded back to Expression::Random by the code below.
3609                    let normalized = transform_recursive(normalized, &|e| {
3610                        if let Expression::Function(ref f) = e {
3611                            let n = f.name.to_ascii_uppercase();
3612                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" || n == "RANDSTR" {
3613                                if let Expression::Function(mut f) = e {
3614                                    for arg in f.args.iter_mut() {
3615                                        if let Expression::Rand(ref r) = arg {
3616                                            if r.lower.is_none() && r.upper.is_none() {
3617                                                if let Some(ref seed) = r.seed {
3618                                                    // Convert Rand{seed: Some(s)} to Function("RANDOM", [s])
3619                                                    // so it won't be expanded by the RANDOM expansion below
3620                                                    *arg = Expression::Function(Box::new(
3621                                                        crate::expressions::Function::new(
3622                                                            "RANDOM".to_string(),
3623                                                            vec![*seed.clone()],
3624                                                        ),
3625                                                    ));
3626                                                }
3627                                            }
3628                                        }
3629                                    }
3630                                    return Ok(Expression::Function(f));
3631                                }
3632                            }
3633                        }
3634                        Ok(e)
3635                    })?;
3636
3637                    // transform_recursive processes bottom-up, so RANDOM() (unseeded) inside
3638                    // generator functions (UNIFORM, NORMAL, ZIPF) gets expanded before
3639                    // we see the parent. We detect this and undo the expansion by replacing
3640                    // the expanded pattern back with Expression::Random.
3641                    // Seeded RANDOM(seed) was already protected above as Function("RANDOM", [seed]).
3642                    // Note: RANDSTR is NOT included here — it needs the expanded form for unseeded
3643                    // RANDOM() since the DuckDB handler uses the expanded SQL as-is in the hash.
3644                    transform_recursive(normalized, &|e| {
3645                        if let Expression::Function(ref f) = e {
3646                            let n = f.name.to_ascii_uppercase();
3647                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" {
3648                                if let Expression::Function(mut f) = e {
3649                                    for arg in f.args.iter_mut() {
3650                                        // Detect expanded RANDOM pattern: CAST(-9.22... + RANDOM() * (...) AS BIGINT)
3651                                        if let Expression::Cast(ref cast) = arg {
3652                                            if matches!(
3653                                                cast.to,
3654                                                crate::expressions::DataType::BigInt { .. }
3655                                            ) {
3656                                                if let Expression::Add(ref add) = cast.this {
3657                                                    if let Expression::Literal(ref lit) = add.left {
3658                                                        if let crate::expressions::Literal::Number(
3659                                                            ref num,
3660                                                        ) = lit.as_ref()
3661                                                        {
3662                                                            if num == "-9.223372036854776E+18" {
3663                                                                *arg = Expression::Random(
3664                                                                    crate::expressions::Random,
3665                                                                );
3666                                                            }
3667                                                        }
3668                                                    }
3669                                                }
3670                                            }
3671                                        }
3672                                    }
3673                                    return Ok(Expression::Function(f));
3674                                }
3675                                return Ok(e);
3676                            }
3677                        }
3678                        match e {
3679                            Expression::Random(_) => Ok(make_scaled_random()),
3680                            // Rand(seed) with no bounds: drop seed and expand
3681                            // (DuckDB RANDOM doesn't support seeds)
3682                            Expression::Rand(ref r) if r.lower.is_none() && r.upper.is_none() => {
3683                                Ok(make_scaled_random())
3684                            }
3685                            _ => Ok(e),
3686                        }
3687                    })?
3688                } else {
3689                    normalized
3690                };
3691
3692                // Apply cross-dialect semantic normalizations
3693                let normalized = normalization::normalize(normalized, self.dialect_type, target)?;
3694
3695                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3696                    Self::normalize_tsql_fetch_overlaps_date_bin(normalized)?
3697                } else {
3698                    normalized
3699                };
3700
3701                let normalized =
3702                    if matches!(
3703                        self.dialect_type,
3704                        DialectType::PostgreSQL | DialectType::CockroachDB
3705                    ) && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
3706                    {
3707                        Self::normalize_postgres_type_function_casts(normalized)?
3708                    } else {
3709                        normalized
3710                    };
3711
3712                let normalized = if matches!(self.dialect_type, DialectType::SQLite)
3713                    && !matches!(target, DialectType::SQLite)
3714                {
3715                    Self::normalize_sqlite_double_quoted_defaults(normalized)?
3716                } else {
3717                    normalized
3718                };
3719
3720                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3721                    && matches!(target, DialectType::SQLite)
3722                {
3723                    Self::normalize_postgres_to_sqlite_types(normalized)?
3724                } else {
3725                    normalized
3726                };
3727
3728                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3729                    && matches!(target, DialectType::Fabric)
3730                {
3731                    Self::normalize_postgres_to_fabric_types(normalized)?
3732                } else {
3733                    normalized
3734                };
3735
3736                // For DuckDB target from BigQuery source: wrap UNNEST of struct arrays in
3737                // (SELECT UNNEST(..., max_depth => 2)) subquery
3738                // Must run BEFORE unnest_alias_to_column_alias since it changes alias structure
3739                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3740                    && matches!(target, DialectType::DuckDB)
3741                {
3742                    crate::transforms::wrap_duckdb_unnest_struct(normalized)?
3743                } else {
3744                    normalized
3745                };
3746
3747                // Convert BigQuery UNNEST aliases to column-alias format for DuckDB/Presto/Spark
3748                // UNNEST(arr) AS x -> UNNEST(arr) AS _t0(x)
3749                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3750                    && matches!(
3751                        target,
3752                        DialectType::DuckDB
3753                            | DialectType::Presto
3754                            | DialectType::Trino
3755                            | DialectType::Athena
3756                            | DialectType::Spark
3757                            | DialectType::Databricks
3758                    ) {
3759                    crate::transforms::unnest_alias_to_column_alias(normalized)?
3760                } else if matches!(self.dialect_type, DialectType::BigQuery)
3761                    && matches!(target, DialectType::BigQuery | DialectType::Redshift)
3762                {
3763                    // For BigQuery/Redshift targets: move UNNEST FROM items to CROSS JOINs
3764                    // but don't convert alias format (no _t0 wrapper)
3765                    let result = crate::transforms::unnest_from_to_cross_join(normalized)?;
3766                    // For Redshift: strip UNNEST when arg is a column reference path
3767                    if matches!(target, DialectType::Redshift) {
3768                        crate::transforms::strip_unnest_column_refs(result)?
3769                    } else {
3770                        result
3771                    }
3772                } else {
3773                    normalized
3774                };
3775
3776                // For Presto/Trino targets from PostgreSQL/Redshift source:
3777                // Wrap UNNEST aliases from GENERATE_SERIES conversion: AS s -> AS _u(s)
3778                let normalized = if matches!(
3779                    self.dialect_type,
3780                    DialectType::PostgreSQL | DialectType::Redshift
3781                ) && matches!(
3782                    target,
3783                    DialectType::Presto | DialectType::Trino | DialectType::Athena
3784                ) {
3785                    crate::transforms::wrap_unnest_join_aliases(normalized)?
3786                } else {
3787                    normalized
3788                };
3789
3790                // Eliminate DISTINCT ON with target-dialect awareness
3791                // This must happen after source transform (which may produce DISTINCT ON)
3792                // and before target transform, with knowledge of the target dialect's NULL ordering behavior
3793                let normalized = crate::transforms::eliminate_distinct_on_for_dialect(
3794                    normalized,
3795                    Some(target),
3796                    Some(self.dialect_type),
3797                )?;
3798
3799                // GENERATE_DATE_ARRAY in UNNEST -> Snowflake ARRAY_GENERATE_RANGE + DATEADD
3800                let normalized = if matches!(target, DialectType::Snowflake) {
3801                    Self::transform_generate_date_array_snowflake(normalized)?
3802                } else {
3803                    normalized
3804                };
3805
3806                // CROSS JOIN UNNEST -> LATERAL VIEW EXPLODE/INLINE for Spark/Hive/Databricks
3807                let normalized = if matches!(
3808                    target,
3809                    DialectType::Spark | DialectType::Databricks | DialectType::Hive
3810                ) {
3811                    crate::transforms::unnest_to_explode_select(normalized)?
3812                } else {
3813                    normalized
3814                };
3815
3816                // Wrap UNION with ORDER BY/LIMIT in a subquery for dialects that require it
3817                let normalized = if matches!(target, DialectType::ClickHouse | DialectType::TSQL) {
3818                    crate::transforms::no_limit_order_by_union(normalized)?
3819                } else {
3820                    normalized
3821                };
3822
3823                // TSQL: Convert COUNT(*) -> COUNT_BIG(*) when source is not TSQL/Fabric
3824                // Python sqlglot does this in the TSQL generator, but we can't do it there
3825                // because it would break TSQL -> TSQL identity
3826                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3827                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3828                {
3829                    transform_recursive(normalized, &|e| {
3830                        if let Expression::Count(ref c) = e {
3831                            // Build COUNT_BIG(...) as an AggregateFunction
3832                            let args = if c.star {
3833                                vec![Expression::Star(crate::expressions::Star {
3834                                    table: None,
3835                                    except: None,
3836                                    replace: None,
3837                                    rename: None,
3838                                    trailing_comments: Vec::new(),
3839                                    span: None,
3840                                })]
3841                            } else if let Some(ref this) = c.this {
3842                                vec![this.clone()]
3843                            } else {
3844                                vec![]
3845                            };
3846                            Ok(Expression::AggregateFunction(Box::new(
3847                                crate::expressions::AggregateFunction {
3848                                    name: "COUNT_BIG".to_string(),
3849                                    args,
3850                                    distinct: c.distinct,
3851                                    filter: c.filter.clone(),
3852                                    order_by: Vec::new(),
3853                                    limit: None,
3854                                    ignore_nulls: None,
3855                                    inferred_type: None,
3856                                },
3857                            )))
3858                        } else {
3859                            Ok(e)
3860                        }
3861                    })?
3862                } else {
3863                    normalized
3864                };
3865
3866                // T-SQL/Fabric do not have a scalar boolean type. Keep predicate
3867                // contexts intact, but materialize boolean-valued expressions used
3868                // as values before target transforms add ORDER BY null sort keys.
3869                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3870                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3871                {
3872                    Self::rewrite_boolean_values_for_tsql(normalized)?
3873                } else {
3874                    normalized
3875                };
3876
3877                let normalized = if matches!(
3878                    self.dialect_type,
3879                    DialectType::PostgreSQL | DialectType::CockroachDB
3880                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3881                {
3882                    Self::rewrite_postgres_format_for_tsql(normalized, target)?
3883                } else {
3884                    normalized
3885                };
3886
3887                let normalized = if self.dialect_type == DialectType::PostgreSQL
3888                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
3889                {
3890                    Self::normalize_postgres_only_for_tsql(normalized)?
3891                } else {
3892                    normalized
3893                };
3894
3895                let transformed =
3896                    target_dialect.transform_with_guard(normalized, opts.complexity_guard)?;
3897
3898                // T-SQL and Fabric do not support aggregate FILTER clauses. Rewrite any
3899                // remaining filters after target transforms so special aggregate rewrites
3900                // (for example BOOL_OR/BOOL_AND) can consume their filters first.
3901                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3902                    Self::rewrite_aggregate_filters_for_tsql(transformed)?
3903                } else {
3904                    transformed
3905                };
3906
3907                let transformed = if matches!(
3908                    self.dialect_type,
3909                    DialectType::PostgreSQL | DialectType::CockroachDB
3910                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3911                {
3912                    crate::transforms::grouped_percentiles_to_tsql_windows(transformed)?
3913                } else {
3914                    transformed
3915                };
3916
3917                let transformed = if matches!(
3918                    self.dialect_type,
3919                    DialectType::PostgreSQL | DialectType::CockroachDB
3920                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3921                {
3922                    Self::normalize_postgres_trim_for_tsql(transformed)?
3923                } else {
3924                    transformed
3925                };
3926
3927                let transformed = if matches!(
3928                    self.dialect_type,
3929                    DialectType::PostgreSQL | DialectType::CockroachDB
3930                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3931                {
3932                    Self::rewrite_postgres_json_array_elements_select_for_tsql(transformed)?
3933                } else {
3934                    transformed
3935                };
3936
3937                // DuckDB target: when FROM is RANGE(n), replace SEQ's ROW_NUMBER pattern with `range`
3938                let transformed = if matches!(target, DialectType::DuckDB) {
3939                    Self::seq_rownum_to_range(transformed)?
3940                } else {
3941                    transformed
3942                };
3943
3944                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3945                    Self::reject_tsql_interval_casts(&transformed, target, opts)?;
3946                }
3947
3948                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3949                    Self::rewrite_tsql_interval_casts_to_varchar(transformed)?
3950                } else {
3951                    transformed
3952                };
3953
3954                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3955                    Self::legalize_tsql_nested_order_by(transformed)?
3956                } else {
3957                    transformed
3958                };
3959
3960                Self::reject_strict_unsupported(&transformed, self.dialect_type, target, opts)?;
3961
3962                let mut sql = target_dialect.generate_with_transpile_options(
3963                    &transformed,
3964                    self.dialect_type,
3965                    opts,
3966                )?;
3967
3968                // Align a known Snowflake pretty-print edge case with Python sqlglot output.
3969                if opts.pretty && target == DialectType::Snowflake {
3970                    sql = Self::normalize_snowflake_pretty(sql);
3971                }
3972
3973                Ok(sql)
3974            })
3975            .collect()
3976    }
3977}
3978
3979// Transpile-only methods: cross-dialect normalization and helpers
3980#[cfg(feature = "transpile")]
3981impl Dialect {
3982    fn legalize_tsql_nested_order_by(expr: Expression) -> Result<Expression> {
3983        let preserve_root_order = matches!(&expr, Expression::Select(select) if Self::tsql_select_needs_order_offset(select));
3984
3985        let mut transformed = transform_recursive(expr, &|node| match node {
3986            Expression::Select(mut select) => {
3987                Self::legalize_tsql_select_offset(&mut select);
3988                if Self::tsql_select_needs_order_offset(&select) {
3989                    select.offset = Some(Offset {
3990                        this: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
3991                        rows: Some(true),
3992                    });
3993                }
3994                Ok(Expression::Select(select))
3995            }
3996            Expression::Subquery(mut subquery) => {
3997                Self::legalize_tsql_offset(&mut subquery.order_by, &mut subquery.offset, false);
3998                Ok(Expression::Subquery(subquery))
3999            }
4000            Expression::Union(mut union) => {
4001                Self::legalize_tsql_set_offset(&mut union.order_by, &mut union.offset);
4002                Ok(Expression::Union(union))
4003            }
4004            Expression::Intersect(mut intersect) => {
4005                Self::legalize_tsql_set_offset(&mut intersect.order_by, &mut intersect.offset);
4006                Ok(Expression::Intersect(intersect))
4007            }
4008            Expression::Except(mut except) => {
4009                Self::legalize_tsql_set_offset(&mut except.order_by, &mut except.offset);
4010                Ok(Expression::Except(except))
4011            }
4012            other => Ok(other),
4013        })?;
4014
4015        if preserve_root_order {
4016            if let Expression::Select(select) = &mut transformed {
4017                select.offset = None;
4018            }
4019        }
4020
4021        Ok(transformed)
4022    }
4023
4024    fn legalize_tsql_select_offset(select: &mut crate::expressions::Select) {
4025        let has_fetch = select.fetch.is_some();
4026        Self::legalize_tsql_offset(&mut select.order_by, &mut select.offset, has_fetch);
4027    }
4028
4029    fn legalize_tsql_offset(
4030        order_by: &mut Option<OrderBy>,
4031        offset: &mut Option<Offset>,
4032        retain_inert_offset: bool,
4033    ) {
4034        if order_by.is_some() {
4035            return;
4036        }
4037
4038        if offset
4039            .as_ref()
4040            .is_some_and(|offset| Self::tsql_offset_is_inert(&offset.this))
4041            && !retain_inert_offset
4042        {
4043            *offset = None;
4044        } else if offset.is_some() {
4045            *order_by = Some(Generator::dummy_tsql_order_by());
4046        }
4047    }
4048
4049    fn legalize_tsql_set_offset(
4050        order_by: &mut Option<OrderBy>,
4051        offset: &mut Option<Box<Expression>>,
4052    ) {
4053        if order_by.is_some() {
4054            return;
4055        }
4056
4057        if offset.as_deref().is_some_and(Self::tsql_offset_is_inert) {
4058            *offset = None;
4059        } else if offset.is_some() {
4060            *order_by = Some(Generator::dummy_tsql_order_by());
4061        }
4062    }
4063
4064    fn tsql_offset_is_inert(expr: &Expression) -> bool {
4065        match expr {
4066            Expression::Null(_) => true,
4067            Expression::Literal(literal) => match literal.as_ref() {
4068                Literal::Number(value) => value.parse::<i128>().is_ok_and(|value| value == 0),
4069                _ => false,
4070            },
4071            _ => false,
4072        }
4073    }
4074
4075    fn tsql_select_needs_order_offset(select: &crate::expressions::Select) -> bool {
4076        select.order_by.is_some()
4077            && select.top.is_none()
4078            && select.limit.is_none()
4079            && select.offset.is_none()
4080            && select.fetch.is_none()
4081            && select.for_xml.is_empty()
4082            && select.for_json.is_empty()
4083    }
4084
4085    fn reject_strict_unsupported(
4086        expr: &Expression,
4087        source: DialectType,
4088        target: DialectType,
4089        opts: &TranspileOptions,
4090    ) -> Result<()> {
4091        if !matches!(
4092            opts.unsupported_level,
4093            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4094        ) {
4095            return Ok(());
4096        }
4097
4098        let mut diagnostics = Vec::new();
4099
4100        for node in expr.dfs() {
4101            if matches!(target, DialectType::Fabric | DialectType::Hive)
4102                && Self::node_has_recursive_with(node)
4103            {
4104                Self::push_unsupported_diagnostic(&mut diagnostics, "recursive CTEs");
4105            }
4106
4107            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4108                && Self::node_has_lateral(node)
4109            {
4110                Self::push_unsupported_diagnostic(&mut diagnostics, "LATERAL joins and subqueries");
4111            }
4112
4113            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4114                if Self::node_has_join_using(node) {
4115                    Self::push_unsupported_diagnostic(&mut diagnostics, "JOIN USING clauses");
4116                }
4117                if Self::node_has_natural_join(node) {
4118                    Self::push_unsupported_diagnostic(&mut diagnostics, "NATURAL JOIN");
4119                }
4120                if Self::node_has_unsupported_relation_column_aliases(node) {
4121                    Self::push_unsupported_diagnostic(
4122                        &mut diagnostics,
4123                        "column alias lists on base or joined table references",
4124                    );
4125                }
4126                if Self::node_has_qualified_whole_row_aggregate_argument(node) {
4127                    Self::push_unsupported_diagnostic(
4128                        &mut diagnostics,
4129                        "qualified whole-row aggregate arguments",
4130                    );
4131                }
4132            }
4133
4134            if !Self::target_supports_distinct_on(target) && Self::node_has_distinct_on(node) {
4135                Self::push_unsupported_diagnostic(&mut diagnostics, "DISTINCT ON");
4136            }
4137
4138            if !Self::target_supports_remaining_unnest(target) && Self::node_is_unnest(node) {
4139                Self::push_unsupported_diagnostic(&mut diagnostics, "UNNEST");
4140            }
4141
4142            if !Self::target_supports_remaining_explode(target) && Self::node_is_explode(node) {
4143                Self::push_unsupported_diagnostic(&mut diagnostics, "EXPLODE");
4144            }
4145
4146            if Self::target_lacks_array_agg(target) && Self::node_is_array_agg(node) {
4147                Self::push_unsupported_diagnostic(&mut diagnostics, "ARRAY_AGG");
4148            }
4149
4150            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4151                && Self::node_is_distinct_string_agg(node)
4152            {
4153                Self::push_unsupported_diagnostic(&mut diagnostics, "STRING_AGG with DISTINCT");
4154            }
4155
4156            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4157                && matches!(node, Expression::NthValue(_))
4158            {
4159                Self::push_unsupported_diagnostic(&mut diagnostics, "NTH_VALUE");
4160            }
4161
4162            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4163                if let Some(frame) = Self::node_window_frame(node) {
4164                    if matches!(frame.kind, WindowFrameKind::Groups) {
4165                        Self::push_unsupported_diagnostic(&mut diagnostics, "GROUPS window frames");
4166                    }
4167                    if matches!(frame.kind, WindowFrameKind::Range)
4168                        && (Self::window_frame_bound_has_value_offset(&frame.start)
4169                            || frame
4170                                .end
4171                                .as_ref()
4172                                .is_some_and(Self::window_frame_bound_has_value_offset))
4173                    {
4174                        Self::push_unsupported_diagnostic(
4175                            &mut diagnostics,
4176                            "value-offset RANGE window frames",
4177                        );
4178                    }
4179                    if frame.exclude.is_some() {
4180                        Self::push_unsupported_diagnostic(
4181                            &mut diagnostics,
4182                            "window frame EXCLUDE clauses",
4183                        );
4184                    }
4185                }
4186            }
4187
4188            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4189                && Self::node_is_regex_predicate(node)
4190            {
4191                Self::push_unsupported_diagnostic(
4192                    &mut diagnostics,
4193                    "regular expression predicates",
4194                );
4195            }
4196
4197            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4198                && Self::node_is_non_subquery_any(node)
4199            {
4200                Self::push_unsupported_diagnostic(
4201                    &mut diagnostics,
4202                    "ANY over non-subquery expressions",
4203                );
4204            }
4205
4206            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4207                && Self::node_is_row_value_subquery_comparison(node)
4208            {
4209                Self::push_unsupported_diagnostic(
4210                    &mut diagnostics,
4211                    "row-value subquery comparisons",
4212                );
4213            }
4214
4215            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4216                && Self::node_is_row_value_values_membership(node)
4217            {
4218                Self::push_unsupported_diagnostic(
4219                    &mut diagnostics,
4220                    "row-value VALUES membership comparisons",
4221                );
4222            }
4223
4224            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4225                && Self::node_has_fetch_with_ties(node)
4226            {
4227                Self::push_unsupported_diagnostic(&mut diagnostics, "FETCH WITH TIES without TOP");
4228            }
4229
4230            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4231                && Self::node_is_overlaps(node)
4232            {
4233                Self::push_unsupported_diagnostic(&mut diagnostics, "OVERLAPS");
4234            }
4235
4236            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4237                && Self::node_is_date_bin(node)
4238            {
4239                Self::push_unsupported_diagnostic(&mut diagnostics, "DATE_BIN");
4240            }
4241
4242            if source == DialectType::PostgreSQL
4243                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4244                && Self::node_is_unresolved_postgres_date_subtraction(node)
4245            {
4246                Self::push_unsupported_diagnostic(
4247                    &mut diagnostics,
4248                    "PostgreSQL date subtraction with an unresolved column type",
4249                );
4250            }
4251
4252            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4253                && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
4254            {
4255                if Self::node_is_postgres_json_build_object(node)
4256                    && !(matches!(target, DialectType::TSQL | DialectType::Fabric)
4257                        && Self::postgres_json_build_object_can_lower_to_json_object(node))
4258                {
4259                    Self::push_unsupported_diagnostic(
4260                        &mut diagnostics,
4261                        "PostgreSQL JSON_BUILD_OBJECT",
4262                    );
4263                }
4264                if Self::node_is_function_named(node, "TO_TSVECTOR") {
4265                    Self::push_unsupported_diagnostic(&mut diagnostics, "PostgreSQL TO_TSVECTOR");
4266                }
4267                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4268                    if let Some(collation_name) =
4269                        Self::postgres_tsql_unsupported_collation_name(node)
4270                    {
4271                        Self::push_unsupported_diagnostic(
4272                            &mut diagnostics,
4273                            &format!("PostgreSQL collation \"{collation_name}\""),
4274                        );
4275                    }
4276                    if let Some(array_semantics) =
4277                        Self::postgres_tsql_unsupported_array_semantics(node)
4278                    {
4279                        Self::push_unsupported_diagnostic(
4280                            &mut diagnostics,
4281                            &format!("PostgreSQL {array_semantics}"),
4282                        );
4283                    }
4284                    if let Some(function_name) = Self::postgres_tsql_unsupported_function_name(node)
4285                    {
4286                        Self::push_unsupported_diagnostic(
4287                            &mut diagnostics,
4288                            &format!("PostgreSQL {function_name}"),
4289                        );
4290                    }
4291                }
4292                if matches!(target, DialectType::TSQL | DialectType::Fabric)
4293                    && Self::node_is_postgres_type_function_cast(node)
4294                {
4295                    Self::push_unsupported_diagnostic(
4296                        &mut diagnostics,
4297                        "PostgreSQL type-name function casts",
4298                    );
4299                }
4300            }
4301
4302            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4303                break;
4304            }
4305        }
4306
4307        if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4308            Self::collect_tsql_unsupported_ordered_sets(expr, &mut diagnostics);
4309            Self::collect_tsql_windows_missing_order(expr, &HashMap::new(), &mut diagnostics);
4310        }
4311
4312        if diagnostics.is_empty() {
4313            return Ok(());
4314        }
4315
4316        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4317            1
4318        } else {
4319            opts.max_unsupported.max(1)
4320        };
4321        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4322        if diagnostics.len() > limit {
4323            messages.push(format!("... and {} more", diagnostics.len() - limit));
4324        }
4325
4326        Err(crate::error::Error::unsupported(
4327            messages.join("; "),
4328            target.to_string(),
4329        ))
4330    }
4331
4332    fn reject_postgres_tsql_strict_regex_predicates(
4333        expr: &Expression,
4334        source: DialectType,
4335        target: DialectType,
4336        opts: &TranspileOptions,
4337    ) -> Result<()> {
4338        if !matches!(
4339            opts.unsupported_level,
4340            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4341        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4342            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4343        {
4344            return Ok(());
4345        }
4346
4347        if expr.dfs().any(Self::node_is_regex_predicate) {
4348            return Err(crate::error::Error::unsupported(
4349                "regular expression predicates",
4350                target.to_string(),
4351            ));
4352        }
4353
4354        Ok(())
4355    }
4356
4357    fn push_unsupported_diagnostic(diagnostics: &mut Vec<String>, message: &str) {
4358        if !diagnostics.iter().any(|existing| existing == message) {
4359            diagnostics.push(message.to_string());
4360        }
4361    }
4362
4363    fn node_is_unresolved_postgres_date_subtraction(expr: &Expression) -> bool {
4364        let Expression::Sub(op) = expr else {
4365            return false;
4366        };
4367
4368        (Self::is_explicit_date_expr(&op.left) && Self::is_column_expr(&op.right))
4369            || (Self::is_column_expr(&op.left) && Self::is_explicit_date_expr(&op.right))
4370    }
4371
4372    fn is_column_expr(expr: &Expression) -> bool {
4373        match expr {
4374            Expression::Column(_) => true,
4375            Expression::Paren(paren) => Self::is_column_expr(&paren.this),
4376            _ => false,
4377        }
4378    }
4379
4380    fn node_window_frame(expr: &Expression) -> Option<&WindowFrame> {
4381        match expr {
4382            Expression::WindowFunction(window) => window.over.frame.as_ref(),
4383            Expression::Window(window) | Expression::WindowSpec(window) => window.frame.as_ref(),
4384            _ => None,
4385        }
4386    }
4387
4388    fn window_frame_bound_has_value_offset(bound: &WindowFrameBound) -> bool {
4389        matches!(
4390            bound,
4391            WindowFrameBound::Preceding(_)
4392                | WindowFrameBound::Following(_)
4393                | WindowFrameBound::Value(_)
4394                | WindowFrameBound::BarePreceding
4395                | WindowFrameBound::BareFollowing
4396        )
4397    }
4398
4399    fn collect_tsql_windows_missing_order(
4400        expr: &Expression,
4401        active_windows: &HashMap<String, Over>,
4402        diagnostics: &mut Vec<String>,
4403    ) {
4404        if let Expression::Select(select) = expr {
4405            let local_windows = select
4406                .windows
4407                .as_ref()
4408                .map(|windows| {
4409                    windows
4410                        .iter()
4411                        .map(|window| (window.name.name.to_ascii_lowercase(), window.spec.clone()))
4412                        .collect()
4413                })
4414                .unwrap_or_default();
4415
4416            for child in expr.children() {
4417                Self::collect_tsql_windows_missing_order(child, &local_windows, diagnostics);
4418            }
4419            return;
4420        }
4421
4422        if let Expression::WindowFunction(window) = expr {
4423            let (has_order, has_frame) = Self::effective_window_order_and_frame(
4424                &window.over,
4425                active_windows,
4426                &mut Vec::new(),
4427            );
4428
4429            if !has_order {
4430                if has_frame {
4431                    Self::push_unsupported_diagnostic(
4432                        diagnostics,
4433                        "window frames without ORDER BY",
4434                    );
4435                }
4436                if let Some(function_name) =
4437                    Self::tsql_window_function_requiring_order(&window.this)
4438                {
4439                    Self::push_unsupported_diagnostic(
4440                        diagnostics,
4441                        &format!("{function_name} without ORDER BY"),
4442                    );
4443                }
4444            }
4445        }
4446
4447        for child in expr.children() {
4448            Self::collect_tsql_windows_missing_order(child, active_windows, diagnostics);
4449        }
4450    }
4451
4452    fn effective_window_order_and_frame(
4453        over: &Over,
4454        active_windows: &HashMap<String, Over>,
4455        seen: &mut Vec<String>,
4456    ) -> (bool, bool) {
4457        let inherited = over
4458            .window_name
4459            .as_ref()
4460            .and_then(|name| {
4461                let key = name.name.to_ascii_lowercase();
4462                if seen.iter().any(|seen_name| seen_name == &key) {
4463                    return None;
4464                }
4465                let named = active_windows.get(&key)?;
4466                seen.push(key);
4467                let properties =
4468                    Self::effective_window_order_and_frame(named, active_windows, seen);
4469                seen.pop();
4470                Some(properties)
4471            })
4472            .unwrap_or((false, false));
4473
4474        (
4475            !over.order_by.is_empty() || inherited.0,
4476            over.frame.is_some() || inherited.1,
4477        )
4478    }
4479
4480    fn tsql_window_function_requiring_order(expr: &Expression) -> Option<&'static str> {
4481        match expr {
4482            Expression::FirstValue(_) => Some("FIRST_VALUE"),
4483            Expression::LastValue(_) => Some("LAST_VALUE"),
4484            Expression::Function(function) if function.name.eq_ignore_ascii_case("FIRST_VALUE") => {
4485                Some("FIRST_VALUE")
4486            }
4487            Expression::Function(function) if function.name.eq_ignore_ascii_case("LAST_VALUE") => {
4488                Some("LAST_VALUE")
4489            }
4490            _ => None,
4491        }
4492    }
4493
4494    fn collect_tsql_unsupported_ordered_sets(expr: &Expression, diagnostics: &mut Vec<String>) {
4495        match expr {
4496            Expression::WindowFunction(window) => {
4497                if let Expression::WithinGroup(within_group) = &window.this {
4498                    if Self::within_group_is_hypothetical_set(within_group) {
4499                        Self::push_unsupported_diagnostic(
4500                            diagnostics,
4501                            "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
4502                        );
4503                        return;
4504                    }
4505
4506                    if Self::within_group_is_mode(within_group) {
4507                        Self::push_unsupported_diagnostic(
4508                            diagnostics,
4509                            "MODE ordered-set aggregates",
4510                        );
4511                        return;
4512                    }
4513
4514                    if Self::within_group_is_percentile(within_group) {
4515                        if !window.over.order_by.is_empty() || window.over.frame.is_some() {
4516                            Self::push_unsupported_diagnostic(
4517                                diagnostics,
4518                                "PERCENTILE_CONT/PERCENTILE_DISC window ORDER BY or frame clauses",
4519                            );
4520                        }
4521                        return;
4522                    }
4523                }
4524            }
4525            Expression::WithinGroup(within_group) => {
4526                if Self::within_group_is_hypothetical_set(within_group) {
4527                    Self::push_unsupported_diagnostic(
4528                        diagnostics,
4529                        "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
4530                    );
4531                    return;
4532                }
4533
4534                if Self::within_group_is_mode(within_group) {
4535                    Self::push_unsupported_diagnostic(diagnostics, "MODE ordered-set aggregates");
4536                    return;
4537                }
4538
4539                if Self::within_group_is_percentile(within_group) {
4540                    Self::push_unsupported_diagnostic(
4541                        diagnostics,
4542                        "PERCENTILE_CONT/PERCENTILE_DISC ordered-set aggregates without OVER",
4543                    );
4544                    return;
4545                }
4546            }
4547            _ => {}
4548        }
4549
4550        for child in expr.children() {
4551            Self::collect_tsql_unsupported_ordered_sets(child, diagnostics);
4552        }
4553    }
4554
4555    fn within_group_is_hypothetical_set(within_group: &crate::expressions::WithinGroup) -> bool {
4556        match &within_group.this {
4557            Expression::Function(function) => Self::is_hypothetical_set_name(&function.name),
4558            Expression::AggregateFunction(function) => {
4559                Self::is_hypothetical_set_name(&function.name)
4560            }
4561            Expression::Rank(_)
4562            | Expression::DenseRank(_)
4563            | Expression::CumeDist(_)
4564            | Expression::PercentRank(_) => true,
4565            _ => false,
4566        }
4567    }
4568
4569    fn within_group_is_percentile(within_group: &crate::expressions::WithinGroup) -> bool {
4570        match &within_group.this {
4571            Expression::Function(function) => Self::is_percentile_ordered_set_name(&function.name),
4572            Expression::AggregateFunction(function) => {
4573                Self::is_percentile_ordered_set_name(&function.name)
4574            }
4575            Expression::PercentileCont(_) | Expression::PercentileDisc(_) => true,
4576            _ => false,
4577        }
4578    }
4579
4580    fn within_group_is_mode(within_group: &crate::expressions::WithinGroup) -> bool {
4581        match &within_group.this {
4582            Expression::Function(function) => function.name.eq_ignore_ascii_case("MODE"),
4583            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case("MODE"),
4584            Expression::Mode(_) => true,
4585            _ => false,
4586        }
4587    }
4588
4589    fn is_percentile_ordered_set_name(name: &str) -> bool {
4590        name.eq_ignore_ascii_case("PERCENTILE_CONT") || name.eq_ignore_ascii_case("PERCENTILE_DISC")
4591    }
4592
4593    fn is_hypothetical_set_name(name: &str) -> bool {
4594        name.eq_ignore_ascii_case("RANK")
4595            || name.eq_ignore_ascii_case("DENSE_RANK")
4596            || name.eq_ignore_ascii_case("CUME_DIST")
4597            || name.eq_ignore_ascii_case("PERCENT_RANK")
4598    }
4599
4600    fn target_supports_distinct_on(target: DialectType) -> bool {
4601        matches!(target, DialectType::PostgreSQL | DialectType::DuckDB)
4602    }
4603
4604    fn node_has_distinct_on(expr: &Expression) -> bool {
4605        matches!(
4606            expr,
4607            Expression::Select(select)
4608                if select
4609                    .distinct_on
4610                    .as_ref()
4611                    .is_some_and(|distinct_on| !distinct_on.is_empty())
4612        )
4613    }
4614
4615    fn node_has_recursive_with(expr: &Expression) -> bool {
4616        fn recursive(with: &Option<With>) -> bool {
4617            with.as_ref().is_some_and(|with| with.recursive)
4618        }
4619
4620        match expr {
4621            Expression::With(with) => with.recursive,
4622            Expression::Select(select) => recursive(&select.with),
4623            Expression::Union(union) => recursive(&union.with),
4624            Expression::Intersect(intersect) => recursive(&intersect.with),
4625            Expression::Except(except) => recursive(&except.with),
4626            Expression::Pivot(pivot) => recursive(&pivot.with),
4627            Expression::Insert(insert) => recursive(&insert.with),
4628            Expression::Update(update) => recursive(&update.with),
4629            Expression::Delete(delete) => recursive(&delete.with),
4630            _ => false,
4631        }
4632    }
4633
4634    fn node_has_lateral(expr: &Expression) -> bool {
4635        fn join_has_lateral(join: &Join) -> bool {
4636            matches!(
4637                join.kind,
4638                crate::expressions::JoinKind::Lateral | crate::expressions::JoinKind::LeftLateral
4639            ) || Dialect::node_has_lateral(&join.this)
4640                || join.on.as_ref().is_some_and(Dialect::node_has_lateral)
4641                || join
4642                    .match_condition
4643                    .as_ref()
4644                    .is_some_and(Dialect::node_has_lateral)
4645                || join.pivots.iter().any(Dialect::node_has_lateral)
4646        }
4647
4648        fn joins_have_lateral(joins: &[Join]) -> bool {
4649            joins.iter().any(join_has_lateral)
4650        }
4651
4652        match expr {
4653            Expression::Subquery(subquery) => {
4654                subquery.lateral || Dialect::node_has_lateral(&subquery.this)
4655            }
4656            Expression::Lateral(_) | Expression::LateralView(_) => true,
4657            Expression::Join(join) => join_has_lateral(join),
4658            Expression::Select(select) => {
4659                !select.lateral_views.is_empty()
4660                    || joins_have_lateral(&select.joins)
4661                    || select
4662                        .from
4663                        .as_ref()
4664                        .is_some_and(|from| from.expressions.iter().any(Dialect::node_has_lateral))
4665            }
4666            Expression::JoinedTable(joined) => {
4667                !joined.lateral_views.is_empty()
4668                    || Dialect::node_has_lateral(&joined.left)
4669                    || joins_have_lateral(&joined.joins)
4670            }
4671            Expression::Update(update) => {
4672                joins_have_lateral(&update.table_joins) || joins_have_lateral(&update.from_joins)
4673            }
4674            _ => false,
4675        }
4676    }
4677
4678    fn node_has_join_using(expr: &Expression) -> bool {
4679        fn has_using(joins: &[Join]) -> bool {
4680            joins.iter().any(|join| !join.using.is_empty())
4681        }
4682
4683        match expr {
4684            Expression::Join(join) => !join.using.is_empty(),
4685            Expression::Select(select) => has_using(&select.joins),
4686            Expression::JoinedTable(joined) => has_using(&joined.joins),
4687            Expression::Update(update) => {
4688                has_using(&update.table_joins) || has_using(&update.from_joins)
4689            }
4690            Expression::Delete(delete) => has_using(&delete.joins),
4691            _ => false,
4692        }
4693    }
4694
4695    fn node_has_natural_join(expr: &Expression) -> bool {
4696        fn is_natural(join: &Join) -> bool {
4697            matches!(
4698                join.kind,
4699                crate::expressions::JoinKind::Natural
4700                    | crate::expressions::JoinKind::NaturalLeft
4701                    | crate::expressions::JoinKind::NaturalRight
4702                    | crate::expressions::JoinKind::NaturalFull
4703            )
4704        }
4705
4706        fn has_natural(joins: &[Join]) -> bool {
4707            joins.iter().any(is_natural)
4708        }
4709
4710        match expr {
4711            Expression::Join(join) => is_natural(join),
4712            Expression::Select(select) => has_natural(&select.joins),
4713            Expression::JoinedTable(joined) => has_natural(&joined.joins),
4714            Expression::Update(update) => {
4715                has_natural(&update.table_joins) || has_natural(&update.from_joins)
4716            }
4717            Expression::Delete(delete) => has_natural(&delete.joins),
4718            _ => false,
4719        }
4720    }
4721
4722    fn node_has_unsupported_relation_column_aliases(expr: &Expression) -> bool {
4723        match expr {
4724            Expression::Table(table) => !table.column_aliases.is_empty(),
4725            Expression::Alias(alias) => {
4726                !alias.column_aliases.is_empty()
4727                    && matches!(
4728                        alias.this,
4729                        Expression::Table(_) | Expression::JoinedTable(_)
4730                    )
4731            }
4732            _ => false,
4733        }
4734    }
4735
4736    fn node_has_qualified_whole_row_aggregate_argument(expr: &Expression) -> bool {
4737        fn contains_qualified_star(expr: &Expression) -> bool {
4738            match expr {
4739                Expression::Star(star) => star.table.is_some(),
4740                // A star projected by an embedded query is not an argument of
4741                // the surrounding aggregate (for example, inside EXISTS).
4742                Expression::Select(_)
4743                | Expression::Subquery(_)
4744                | Expression::Union(_)
4745                | Expression::Intersect(_)
4746                | Expression::Except(_) => false,
4747                _ => expr.children().into_iter().any(contains_qualified_star),
4748            }
4749        }
4750
4751        let is_aggregate = matches!(
4752            expr,
4753            Expression::AggregateFunction(_)
4754                | Expression::Count(_)
4755                | Expression::Sum(_)
4756                | Expression::Avg(_)
4757                | Expression::Min(_)
4758                | Expression::Max(_)
4759                | Expression::GroupConcat(_)
4760                | Expression::StringAgg(_)
4761                | Expression::ListAgg(_)
4762                | Expression::ArrayAgg(_)
4763                | Expression::CountIf(_)
4764                | Expression::SumIf(_)
4765                | Expression::Stddev(_)
4766                | Expression::StddevPop(_)
4767                | Expression::StddevSamp(_)
4768                | Expression::Variance(_)
4769                | Expression::VarPop(_)
4770                | Expression::VarSamp(_)
4771                | Expression::Median(_)
4772                | Expression::Mode(_)
4773                | Expression::First(_)
4774                | Expression::Last(_)
4775                | Expression::AnyValue(_)
4776                | Expression::ApproxDistinct(_)
4777                | Expression::ApproxCountDistinct(_)
4778                | Expression::ApproxPercentile(_)
4779                | Expression::Percentile(_)
4780                | Expression::LogicalAnd(_)
4781                | Expression::LogicalOr(_)
4782                | Expression::Skewness(_)
4783                | Expression::BitwiseCount(_)
4784                | Expression::BitwiseAndAgg(_)
4785                | Expression::BitwiseOrAgg(_)
4786                | Expression::BitwiseXorAgg(_)
4787                | Expression::ArrayConcatAgg(_)
4788                | Expression::ArrayUniqueAgg(_)
4789                | Expression::BoolXorAgg(_)
4790                | Expression::JsonArrayAgg(_)
4791                | Expression::JsonObjectAgg(_)
4792                | Expression::ParameterizedAgg(_)
4793                | Expression::ArgMax(_)
4794                | Expression::ArgMin(_)
4795                | Expression::ApproxTopK(_)
4796                | Expression::ApproxTopKAccumulate(_)
4797                | Expression::ApproxTopKCombine(_)
4798                | Expression::ApproxTopKEstimate(_)
4799                | Expression::ApproxTopSum(_)
4800                | Expression::ApproxQuantiles(_)
4801                | Expression::AnonymousAggFunc(_)
4802                | Expression::CombinedAggFunc(_)
4803                | Expression::CombinedParameterizedAgg(_)
4804                | Expression::HashAgg(_)
4805                | Expression::ObjectAgg(_)
4806                | Expression::AIAgg(_)
4807        );
4808
4809        is_aggregate && expr.children().into_iter().any(contains_qualified_star)
4810    }
4811
4812    fn target_supports_remaining_unnest(target: DialectType) -> bool {
4813        matches!(
4814            target,
4815            DialectType::PostgreSQL
4816                | DialectType::BigQuery
4817                | DialectType::DuckDB
4818                | DialectType::Presto
4819                | DialectType::Trino
4820                | DialectType::Athena
4821        )
4822    }
4823
4824    fn target_supports_remaining_explode(target: DialectType) -> bool {
4825        matches!(
4826            target,
4827            DialectType::Spark | DialectType::Databricks | DialectType::Hive
4828        )
4829    }
4830
4831    fn target_lacks_array_agg(target: DialectType) -> bool {
4832        matches!(
4833            target,
4834            DialectType::Fabric
4835                | DialectType::TSQL
4836                | DialectType::MySQL
4837                | DialectType::SQLite
4838                | DialectType::Oracle
4839        )
4840    }
4841
4842    fn node_is_unnest(expr: &Expression) -> bool {
4843        matches!(expr, Expression::Unnest(_)) || Self::node_is_function_named(expr, "UNNEST")
4844    }
4845
4846    fn node_is_explode(expr: &Expression) -> bool {
4847        matches!(expr, Expression::Explode(_) | Expression::ExplodeOuter(_))
4848            || Self::node_is_function_named(expr, "EXPLODE")
4849            || Self::node_is_function_named(expr, "EXPLODE_OUTER")
4850    }
4851
4852    fn node_is_array_agg(expr: &Expression) -> bool {
4853        matches!(expr, Expression::ArrayAgg(_)) || Self::node_is_function_named(expr, "ARRAY_AGG")
4854    }
4855
4856    fn node_is_distinct_string_agg(expr: &Expression) -> bool {
4857        match expr {
4858            Expression::StringAgg(agg) => agg.distinct,
4859            Expression::Function(function) => {
4860                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
4861            }
4862            Expression::AggregateFunction(function) => {
4863                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
4864            }
4865            _ => false,
4866        }
4867    }
4868
4869    fn postgres_tsql_unsupported_collation_name(expr: &Expression) -> Option<&'static str> {
4870        let Expression::Collation(collation) = expr else {
4871            return None;
4872        };
4873
4874        if collation.collation.eq_ignore_ascii_case("C") {
4875            Some("C")
4876        } else if collation.collation.eq_ignore_ascii_case("POSIX") {
4877            Some("POSIX")
4878        } else {
4879            None
4880        }
4881    }
4882
4883    fn postgres_tsql_unsupported_array_semantics(expr: &Expression) -> Option<&'static str> {
4884        match expr {
4885            Expression::Array(_) | Expression::ArrayFunc(_) => Some("array literals"),
4886            Expression::Subscript(_) => Some("array subscripts"),
4887            Expression::ArraySlice(_) => Some("array slices"),
4888            Expression::DataType(DataType::Array { .. }) => Some("array data types"),
4889            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
4890                if matches!(&cast.to, DataType::Array { .. }) =>
4891            {
4892                Some("array data types")
4893            }
4894            Expression::ArrayLength(_) | Expression::ArraySize(_) => Some("ARRAY_LENGTH"),
4895            Expression::Cardinality(_) => Some("CARDINALITY"),
4896            Expression::ArrayToString(_) | Expression::ArrayJoin(_) => Some("ARRAY_TO_STRING"),
4897            Expression::StringToArray(_) => Some("STRING_TO_ARRAY"),
4898            Expression::ArrayContains(_)
4899            | Expression::ArrayPosition(_)
4900            | Expression::ArrayAppend(_)
4901            | Expression::ArrayPrepend(_)
4902            | Expression::ArrayConcat(_)
4903            | Expression::ArraySort(_)
4904            | Expression::ArrayReverse(_)
4905            | Expression::ArrayDistinct(_)
4906            | Expression::ArrayFilter(_)
4907            | Expression::ArrayTransform(_)
4908            | Expression::ArrayFlatten(_)
4909            | Expression::ArrayCompact(_)
4910            | Expression::ArrayIntersect(_)
4911            | Expression::ArrayUnion(_)
4912            | Expression::ArrayExcept(_)
4913            | Expression::ArrayRemove(_)
4914            | Expression::ArrayZip(_)
4915            | Expression::ArrayAll(_)
4916            | Expression::ArrayAny(_)
4917            | Expression::ArrayConstructCompact(_)
4918            | Expression::ArraySum(_) => Some("array functions"),
4919            Expression::ArrayContainsAll(_)
4920            | Expression::ArrayContainedBy(_)
4921            | Expression::ArrayOverlaps(_) => Some("array operators"),
4922            Expression::Function(function) => {
4923                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
4924            }
4925            Expression::AggregateFunction(function) => {
4926                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
4927            }
4928            _ => None,
4929        }
4930    }
4931
4932    fn postgres_tsql_unsupported_array_function_name_str(name: &str) -> Option<&'static str> {
4933        if name.eq_ignore_ascii_case("ARRAY") {
4934            Some("array literals")
4935        } else if name.eq_ignore_ascii_case("ARRAY_LENGTH")
4936            || name.eq_ignore_ascii_case("ARRAY_SIZE")
4937        {
4938            Some("ARRAY_LENGTH")
4939        } else if name.eq_ignore_ascii_case("CARDINALITY") {
4940            Some("CARDINALITY")
4941        } else if name.eq_ignore_ascii_case("ARRAY_TO_STRING")
4942            || name.eq_ignore_ascii_case("ARRAY_JOIN")
4943        {
4944            Some("ARRAY_TO_STRING")
4945        } else if name.eq_ignore_ascii_case("STRING_TO_ARRAY") {
4946            Some("STRING_TO_ARRAY")
4947        } else {
4948            None
4949        }
4950    }
4951
4952    fn node_is_regex_predicate(expr: &Expression) -> bool {
4953        matches!(
4954            expr,
4955            Expression::SimilarTo(_) | Expression::RegexpLike(_) | Expression::RegexpILike(_)
4956        ) || Self::node_is_function_named(expr, "REGEXP_LIKE")
4957            || Self::node_is_function_named(expr, "REGEXP_I_LIKE")
4958            || Self::node_is_function_named(expr, "REGEXP_ILIKE")
4959    }
4960
4961    fn node_is_non_subquery_any(expr: &Expression) -> bool {
4962        matches!(
4963            expr,
4964            Expression::Any(q) if !Self::quantified_rhs_is_subquery(&q.subquery)
4965        )
4966    }
4967
4968    fn quantified_rhs_is_subquery(expr: &Expression) -> bool {
4969        match expr {
4970            Expression::Select(_) | Expression::Subquery(_) => true,
4971            Expression::Paren(paren) => Self::quantified_rhs_is_subquery(&paren.this),
4972            _ => false,
4973        }
4974    }
4975
4976    fn node_is_row_value_subquery_comparison(expr: &Expression) -> bool {
4977        match expr {
4978            Expression::In(in_expr) => {
4979                Self::in_rhs_is_subquery_like(in_expr) && Self::expr_is_row_value(&in_expr.this)
4980            }
4981            Expression::Eq(op) | Expression::Neq(op) => {
4982                (Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right))
4983                    || (Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left))
4984            }
4985            _ => false,
4986        }
4987    }
4988
4989    fn node_is_row_value_values_membership(expr: &Expression) -> bool {
4990        matches!(
4991            expr,
4992            Expression::In(in_expr)
4993                if Self::expr_is_row_value(&in_expr.this)
4994                    && Self::in_rhs_is_values_like(in_expr)
4995        )
4996    }
4997
4998    fn expr_is_row_value(expr: &Expression) -> bool {
4999        match expr {
5000            Expression::Tuple(tuple) => tuple.expressions.len() > 1,
5001            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
5002                function.args.len() > 1
5003            }
5004            Expression::Paren(paren) => Self::expr_is_row_value(&paren.this),
5005            _ => false,
5006        }
5007    }
5008
5009    fn expr_is_subquery_like(expr: &Expression) -> bool {
5010        match expr {
5011            Expression::Select(_) | Expression::Subquery(_) => true,
5012            Expression::Paren(paren) => Self::expr_is_subquery_like(&paren.this),
5013            _ => false,
5014        }
5015    }
5016
5017    fn in_rhs_is_subquery_like(in_expr: &crate::expressions::In) -> bool {
5018        if in_expr
5019            .query
5020            .as_ref()
5021            .is_some_and(Self::expr_is_subquery_like)
5022        {
5023            return true;
5024        }
5025
5026        in_expr.expressions.len() == 1 && Self::expr_is_subquery_like(&in_expr.expressions[0])
5027    }
5028
5029    fn in_rhs_is_values_like(in_expr: &crate::expressions::In) -> bool {
5030        if in_expr
5031            .query
5032            .as_ref()
5033            .is_some_and(Self::expr_is_values_like)
5034        {
5035            return true;
5036        }
5037
5038        (in_expr.expressions.len() == 1
5039            && Self::expr_is_values_like(&in_expr.expressions[0]))
5040            || in_expr.expressions.first().is_some_and(|expr| {
5041                matches!(expr, Expression::Function(function) if function.name.eq_ignore_ascii_case("VALUES"))
5042            })
5043    }
5044
5045    fn expr_is_values_like(expr: &Expression) -> bool {
5046        match expr {
5047            Expression::Values(_) => true,
5048            Expression::Paren(paren) => Self::expr_is_values_like(&paren.this),
5049            Expression::Subquery(subquery) => Self::expr_is_values_like(&subquery.this),
5050            _ => false,
5051        }
5052    }
5053
5054    fn normalize_tsql_fetch_overlaps_date_bin(expr: Expression) -> Result<Expression> {
5055        transform_recursive(expr, &|e| match e {
5056            Expression::Select(mut select) => {
5057                if select.top.is_none() && select.offset.is_none() {
5058                    if let Some(fetch) = select.fetch.take() {
5059                        if let Some(top) = Self::fetch_with_ties_to_top(fetch.clone()) {
5060                            select.top = Some(top);
5061                        } else {
5062                            select.fetch = Some(fetch);
5063                        }
5064                    }
5065                }
5066                Self::rewrite_tsql_overlaps_in_select_predicates(&mut select)?;
5067                Ok(Expression::Select(select))
5068            }
5069            Expression::DateBin(date_bin) => {
5070                let date_bin = *date_bin;
5071                if let Some(rewritten) = Self::date_bin_to_date_bucket(date_bin.clone()) {
5072                    Ok(rewritten)
5073                } else {
5074                    Ok(Expression::DateBin(Box::new(date_bin)))
5075                }
5076            }
5077            Expression::Function(function) => {
5078                let function = *function;
5079                if function.name.eq_ignore_ascii_case("DATE_BIN") {
5080                    if let Some(rewritten) = Self::date_bin_function_to_date_bucket(&function) {
5081                        Ok(rewritten)
5082                    } else {
5083                        Ok(Expression::Function(Box::new(function)))
5084                    }
5085                } else {
5086                    Ok(Expression::Function(Box::new(function)))
5087                }
5088            }
5089            _ => Ok(e),
5090        })
5091    }
5092
5093    fn rewrite_tsql_overlaps_in_select_predicates(
5094        select: &mut crate::expressions::Select,
5095    ) -> Result<()> {
5096        if let Some(where_clause) = &mut select.where_clause {
5097            where_clause.this = Self::rewrite_tsql_overlaps_predicate(where_clause.this.clone())?;
5098        }
5099        if let Some(having) = &mut select.having {
5100            having.this = Self::rewrite_tsql_overlaps_predicate(having.this.clone())?;
5101        }
5102        if let Some(qualify) = &mut select.qualify {
5103            qualify.this = Self::rewrite_tsql_overlaps_predicate(qualify.this.clone())?;
5104        }
5105        for join in &mut select.joins {
5106            if let Some(on) = join.on.take() {
5107                join.on = Some(Self::rewrite_tsql_overlaps_predicate(on)?);
5108            }
5109            if let Some(match_condition) = join.match_condition.take() {
5110                join.match_condition =
5111                    Some(Self::rewrite_tsql_overlaps_predicate(match_condition)?);
5112            }
5113        }
5114        Ok(())
5115    }
5116
5117    fn rewrite_tsql_overlaps_predicate(expr: Expression) -> Result<Expression> {
5118        transform_recursive(expr, &|e| match e {
5119            Expression::Overlaps(overlaps) => {
5120                let overlaps = *overlaps;
5121                if let Some(rewritten) = Self::rewrite_full_overlaps_for_tsql(&overlaps) {
5122                    Ok(rewritten)
5123                } else {
5124                    Ok(Expression::Overlaps(Box::new(overlaps)))
5125                }
5126            }
5127            _ => Ok(e),
5128        })
5129    }
5130
5131    fn fetch_with_ties_to_top(fetch: Fetch) -> Option<Top> {
5132        if !fetch.with_ties {
5133            return None;
5134        }
5135
5136        fetch.count.map(|count| Top {
5137            this: count,
5138            percent: fetch.percent,
5139            with_ties: true,
5140            parenthesized: true,
5141        })
5142    }
5143
5144    fn rewrite_full_overlaps_for_tsql(
5145        overlaps: &crate::expressions::OverlapsExpr,
5146    ) -> Option<Expression> {
5147        let (left_start, left_end, right_start, right_end) =
5148            if let (Some(left_start), Some(left_end), Some(right_start), Some(right_end)) = (
5149                overlaps.left_start.as_ref(),
5150                overlaps.left_end.as_ref(),
5151                overlaps.right_start.as_ref(),
5152                overlaps.right_end.as_ref(),
5153            ) {
5154                (left_start, left_end, right_start, right_end)
5155            } else if let (
5156                Some(Expression::Tuple(left_tuple)),
5157                Some(Expression::Tuple(right_tuple)),
5158            ) = (&overlaps.this, &overlaps.expression)
5159            {
5160                if left_tuple.expressions.len() != 2 || right_tuple.expressions.len() != 2 {
5161                    return None;
5162                }
5163                (
5164                    &left_tuple.expressions[0],
5165                    &left_tuple.expressions[1],
5166                    &right_tuple.expressions[0],
5167                    &right_tuple.expressions[1],
5168                )
5169            } else {
5170                return None;
5171            };
5172
5173        let left_min = Self::case_min(left_start.clone(), left_end.clone());
5174        let left_max = Self::case_max(left_start.clone(), left_end.clone());
5175        let right_min = Self::case_min(right_start.clone(), right_end.clone());
5176        let right_max = Self::case_max(right_start.clone(), right_end.clone());
5177
5178        Some(Expression::And(Box::new(BinaryOp::new(
5179            Expression::Lte(Box::new(BinaryOp::new(left_min, right_max))),
5180            Expression::Lte(Box::new(BinaryOp::new(right_min, left_max))),
5181        ))))
5182    }
5183
5184    fn case_min(left: Expression, right: Expression) -> Expression {
5185        Expression::Case(Box::new(Case {
5186            operand: None,
5187            whens: vec![(
5188                Expression::Lte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
5189                left,
5190            )],
5191            else_: Some(right),
5192            comments: Vec::new(),
5193            inferred_type: None,
5194        }))
5195    }
5196
5197    fn case_max(left: Expression, right: Expression) -> Expression {
5198        Expression::Case(Box::new(Case {
5199            operand: None,
5200            whens: vec![(
5201                Expression::Gte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
5202                left,
5203            )],
5204            else_: Some(right),
5205            comments: Vec::new(),
5206            inferred_type: None,
5207        }))
5208    }
5209
5210    fn date_bin_to_date_bucket(date_bin: DateBin) -> Option<Expression> {
5211        if date_bin.unit.is_some() || date_bin.zone.is_some() {
5212            return None;
5213        }
5214
5215        let (datepart, number) = Self::date_bucket_parts(&date_bin.this)?;
5216        let mut args = vec![
5217            Self::date_bucket_datepart(datepart),
5218            number,
5219            *date_bin.expression,
5220        ];
5221        if let Some(origin) = date_bin.origin {
5222            args.push(*origin);
5223        }
5224
5225        Some(Expression::Function(Box::new(Function::new(
5226            "DATE_BUCKET".to_string(),
5227            args,
5228        ))))
5229    }
5230
5231    fn date_bin_function_to_date_bucket(function: &Function) -> Option<Expression> {
5232        if !(2..=3).contains(&function.args.len()) {
5233            return None;
5234        }
5235
5236        let (datepart, number) = Self::date_bucket_parts(&function.args[0])?;
5237        let mut args = vec![
5238            Self::date_bucket_datepart(datepart),
5239            number,
5240            function.args[1].clone(),
5241        ];
5242        if let Some(origin) = function.args.get(2) {
5243            args.push(origin.clone());
5244        }
5245
5246        Some(Expression::Function(Box::new(Function::new(
5247            "DATE_BUCKET".to_string(),
5248            args,
5249        ))))
5250    }
5251
5252    fn date_bucket_parts(stride: &Expression) -> Option<(&'static str, Expression)> {
5253        match stride {
5254            Expression::Literal(lit) => match lit.as_ref() {
5255                Literal::String(value) => Self::date_bucket_parts_from_string(value),
5256                _ => None,
5257            },
5258            Expression::Interval(interval) => Self::date_bucket_parts_from_interval(interval),
5259            _ => None,
5260        }
5261    }
5262
5263    fn date_bucket_parts_from_interval(interval: &Interval) -> Option<(&'static str, Expression)> {
5264        match &interval.unit {
5265            Some(IntervalUnitSpec::Simple { unit, .. }) => {
5266                let datepart = Self::date_bucket_datepart_from_unit(*unit)?;
5267                let amount = interval
5268                    .this
5269                    .as_ref()
5270                    .and_then(Self::date_bucket_amount_expr)?;
5271                Some((datepart, amount))
5272            }
5273            None => interval.this.as_ref().and_then(|expr| match expr {
5274                Expression::Literal(lit) => match lit.as_ref() {
5275                    Literal::String(value) => Self::date_bucket_parts_from_string(value),
5276                    _ => None,
5277                },
5278                _ => None,
5279            }),
5280            _ => None,
5281        }
5282    }
5283
5284    fn date_bucket_parts_from_string(value: &str) -> Option<(&'static str, Expression)> {
5285        let mut parts = value.split_whitespace();
5286        let amount = parts.next()?;
5287        let unit = parts.next()?;
5288        if parts.next().is_some() {
5289            return None;
5290        }
5291
5292        Some((
5293            Self::date_bucket_datepart_from_name(unit)?,
5294            Self::positive_integer_expr(amount)?,
5295        ))
5296    }
5297
5298    fn date_bucket_amount_expr(expr: &Expression) -> Option<Expression> {
5299        match expr {
5300            Expression::Literal(lit) => match lit.as_ref() {
5301                Literal::Number(value) => Self::positive_integer_expr(value),
5302                Literal::String(value) => Self::positive_integer_expr(value),
5303                _ => None,
5304            },
5305            _ => Some(expr.clone()),
5306        }
5307    }
5308
5309    fn positive_integer_expr(value: &str) -> Option<Expression> {
5310        let parsed = value.trim().parse::<i64>().ok()?;
5311        (parsed > 0).then(|| Expression::number(parsed))
5312    }
5313
5314    fn date_bucket_datepart(datepart: &str) -> Expression {
5315        Expression::Var(Box::new(Var {
5316            this: datepart.to_string(),
5317        }))
5318    }
5319
5320    fn date_bucket_datepart_from_unit(unit: IntervalUnit) -> Option<&'static str> {
5321        match unit {
5322            IntervalUnit::Week => Some("WEEK"),
5323            IntervalUnit::Day => Some("DAY"),
5324            IntervalUnit::Hour => Some("HOUR"),
5325            IntervalUnit::Minute => Some("MINUTE"),
5326            IntervalUnit::Second => Some("SECOND"),
5327            IntervalUnit::Millisecond => Some("MILLISECOND"),
5328            _ => None,
5329        }
5330    }
5331
5332    fn date_bucket_datepart_from_name(unit: &str) -> Option<&'static str> {
5333        match unit.trim().to_ascii_uppercase().as_str() {
5334            "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" => Some("WEEK"),
5335            "DAY" | "DAYS" | "D" | "DD" => Some("DAY"),
5336            "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some("HOUR"),
5337            "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some("MINUTE"),
5338            "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some("SECOND"),
5339            "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MILLISEC" | "MILLISECS" => {
5340                Some("MILLISECOND")
5341            }
5342            _ => None,
5343        }
5344    }
5345
5346    fn node_has_fetch_with_ties(expr: &Expression) -> bool {
5347        matches!(
5348            expr,
5349            Expression::Select(select)
5350                if select
5351                    .fetch
5352                    .as_ref()
5353                    .is_some_and(|fetch| fetch.with_ties)
5354        )
5355    }
5356
5357    fn node_is_overlaps(expr: &Expression) -> bool {
5358        matches!(expr, Expression::Overlaps(_))
5359    }
5360
5361    fn node_is_date_bin(expr: &Expression) -> bool {
5362        matches!(expr, Expression::DateBin(_)) || Self::node_is_function_named(expr, "DATE_BIN")
5363    }
5364
5365    fn node_is_function_named(expr: &Expression, name: &str) -> bool {
5366        match expr {
5367            Expression::Function(function) => function.name.eq_ignore_ascii_case(name),
5368            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case(name),
5369            _ => false,
5370        }
5371    }
5372
5373    fn node_is_postgres_json_build_object(expr: &Expression) -> bool {
5374        match expr {
5375            Expression::Function(function) => {
5376                function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
5377                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT")
5378            }
5379            _ => false,
5380        }
5381    }
5382
5383    fn postgres_json_build_object_can_lower_to_json_object(expr: &Expression) -> bool {
5384        matches!(
5385            expr,
5386            Expression::Function(function)
5387                if (function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
5388                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT"))
5389                    && !function.distinct
5390                    && function.args.len() % 2 == 0
5391        )
5392    }
5393
5394    fn node_is_postgres_json_array_elements(expr: &Expression) -> bool {
5395        matches!(
5396            expr,
5397            Expression::Function(function)
5398                if function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS")
5399                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS")
5400                    || function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT")
5401                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT")
5402        )
5403    }
5404
5405    fn postgres_tsql_unsupported_function_name(expr: &Expression) -> Option<&'static str> {
5406        match expr {
5407            Expression::Lpad(_) => Some("LPAD"),
5408            Expression::Rpad(_) => Some("RPAD"),
5409            Expression::SplitPart(_) => Some("SPLIT_PART"),
5410            Expression::Initcap(_) => Some("INITCAP"),
5411            Expression::ToJson(_) => Some("TO_JSON"),
5412            Expression::JSONBObjectAgg(_) => Some("JSONB_OBJECT_AGG"),
5413            Expression::ToNumber(_) => Some("TO_NUMBER"),
5414            Expression::WidthBucket(_) => Some("WIDTH_BUCKET"),
5415            Expression::BitwiseAndAgg(_) => Some("BIT_AND"),
5416            Expression::BitwiseOrAgg(_) => Some("BIT_OR"),
5417            Expression::BitwiseXorAgg(_) => Some("BIT_XOR"),
5418            Expression::Corr(_) => Some("CORR"),
5419            Expression::CovarPop(_) => Some("COVAR_POP"),
5420            Expression::CovarSamp(_) => Some("COVAR_SAMP"),
5421            Expression::RegrAvgx(_) => Some("REGR_AVGX"),
5422            Expression::RegrAvgy(_) => Some("REGR_AVGY"),
5423            Expression::RegrCount(_) => Some("REGR_COUNT"),
5424            Expression::RegrIntercept(_) => Some("REGR_INTERCEPT"),
5425            Expression::RegrR2(_) => Some("REGR_R2"),
5426            Expression::RegrSlope(_) => Some("REGR_SLOPE"),
5427            Expression::RegrSxx(_) => Some("REGR_SXX"),
5428            Expression::RegrSxy(_) => Some("REGR_SXY"),
5429            Expression::RegrSyy(_) => Some("REGR_SYY"),
5430            Expression::Function(function) => {
5431                Self::postgres_tsql_unsupported_function_name_str(&function.name)
5432            }
5433            Expression::AggregateFunction(function) => {
5434                Self::postgres_tsql_unsupported_function_name_str(&function.name)
5435            }
5436            _ => None,
5437        }
5438    }
5439
5440    fn postgres_tsql_unsupported_function_name_str(name: &str) -> Option<&'static str> {
5441        if name.eq_ignore_ascii_case("LPAD") {
5442            Some("LPAD")
5443        } else if name.eq_ignore_ascii_case("RPAD") {
5444            Some("RPAD")
5445        } else if name.eq_ignore_ascii_case("SPLIT_PART") {
5446            Some("SPLIT_PART")
5447        } else if name.eq_ignore_ascii_case("INITCAP") {
5448            Some("INITCAP")
5449        } else if name.eq_ignore_ascii_case("TO_JSON") {
5450            Some("TO_JSON")
5451        } else if name.eq_ignore_ascii_case("TO_JSONB") {
5452            Some("TO_JSONB")
5453        } else if name.eq_ignore_ascii_case("JSONB_OBJECT_AGG") {
5454            Some("JSONB_OBJECT_AGG")
5455        } else if name.eq_ignore_ascii_case("ROW_TO_JSON") {
5456            Some("ROW_TO_JSON")
5457        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS") {
5458            Some("JSON_ARRAY_ELEMENTS")
5459        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS") {
5460            Some("JSONB_ARRAY_ELEMENTS")
5461        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT") {
5462            Some("JSON_ARRAY_ELEMENTS_TEXT")
5463        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT") {
5464            Some("JSONB_ARRAY_ELEMENTS_TEXT")
5465        } else if name.eq_ignore_ascii_case("ENCODE") {
5466            Some("ENCODE")
5467        } else if name.eq_ignore_ascii_case("AGE") {
5468            Some("AGE")
5469        } else if name.eq_ignore_ascii_case("ERF") {
5470            Some("ERF")
5471        } else if name.eq_ignore_ascii_case("GCD") {
5472            Some("GCD")
5473        } else if name.eq_ignore_ascii_case("LCM") {
5474            Some("LCM")
5475        } else if name.eq_ignore_ascii_case("QUOTE_LITERAL") {
5476            Some("QUOTE_LITERAL")
5477        } else if name.eq_ignore_ascii_case("WIDTH_BUCKET") {
5478            Some("WIDTH_BUCKET")
5479        } else if name.eq_ignore_ascii_case("SCALE") {
5480            Some("SCALE")
5481        } else if name.eq_ignore_ascii_case("TRIM_SCALE") {
5482            Some("TRIM_SCALE")
5483        } else if name.eq_ignore_ascii_case("MIN_SCALE") {
5484            Some("MIN_SCALE")
5485        } else if name.eq_ignore_ascii_case("FACTORIAL") {
5486            Some("FACTORIAL")
5487        } else if name.eq_ignore_ascii_case("PG_LSN") {
5488            Some("PG_LSN")
5489        } else if name.eq_ignore_ascii_case("TO_CHAR") {
5490            Some("TO_CHAR")
5491        } else if name.eq_ignore_ascii_case("PG_TYPEOF") {
5492            Some("PG_TYPEOF")
5493        } else if name.eq_ignore_ascii_case("BIT_AND") {
5494            Some("BIT_AND")
5495        } else if name.eq_ignore_ascii_case("BIT_OR") {
5496            Some("BIT_OR")
5497        } else if name.eq_ignore_ascii_case("BIT_XOR") {
5498            Some("BIT_XOR")
5499        } else if name.eq_ignore_ascii_case("CORR") {
5500            Some("CORR")
5501        } else if name.eq_ignore_ascii_case("COVAR_POP") {
5502            Some("COVAR_POP")
5503        } else if name.eq_ignore_ascii_case("COVAR_SAMP") {
5504            Some("COVAR_SAMP")
5505        } else if name.eq_ignore_ascii_case("REGR_AVGX") {
5506            Some("REGR_AVGX")
5507        } else if name.eq_ignore_ascii_case("REGR_AVGY") {
5508            Some("REGR_AVGY")
5509        } else if name.eq_ignore_ascii_case("REGR_COUNT") {
5510            Some("REGR_COUNT")
5511        } else if name.eq_ignore_ascii_case("REGR_INTERCEPT") {
5512            Some("REGR_INTERCEPT")
5513        } else if name.eq_ignore_ascii_case("REGR_R2") {
5514            Some("REGR_R2")
5515        } else if name.eq_ignore_ascii_case("REGR_SLOPE") {
5516            Some("REGR_SLOPE")
5517        } else if name.eq_ignore_ascii_case("REGR_SXX") {
5518            Some("REGR_SXX")
5519        } else if name.eq_ignore_ascii_case("REGR_SXY") {
5520            Some("REGR_SXY")
5521        } else if name.eq_ignore_ascii_case("REGR_SYY") {
5522            Some("REGR_SYY")
5523        } else if name.eq_ignore_ascii_case("FLOAT8_ACCUM") {
5524            Some("FLOAT8_ACCUM")
5525        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_ACCUM") {
5526            Some("FLOAT8_REGR_ACCUM")
5527        } else if name.eq_ignore_ascii_case("FLOAT8_COMBINE") {
5528            Some("FLOAT8_COMBINE")
5529        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_COMBINE") {
5530            Some("FLOAT8_REGR_COMBINE")
5531        } else if name.eq_ignore_ascii_case("BOOLAND_STATEFUNC") {
5532            Some("BOOLAND_STATEFUNC")
5533        } else if name.eq_ignore_ascii_case("BOOLOR_STATEFUNC") {
5534            Some("BOOLOR_STATEFUNC")
5535        } else {
5536            None
5537        }
5538    }
5539
5540    fn normalize_postgres_trim_for_tsql(expr: Expression) -> Result<Expression> {
5541        transform_recursive(expr, &|e| match e {
5542            Expression::Trim(trim) => {
5543                let mut trim = *trim;
5544                match trim.position {
5545                    crate::expressions::TrimPosition::Both
5546                        if trim.position_explicit && trim.characters.is_some() =>
5547                    {
5548                        trim.position_explicit = false;
5549                        trim.sql_standard_syntax = true;
5550                        Ok(Expression::Trim(Box::new(trim)))
5551                    }
5552                    crate::expressions::TrimPosition::Leading if trim.characters.is_some() => {
5553                        let characters = trim.characters.take().expect("checked above");
5554                        Ok(Expression::Function(Box::new(Function::new(
5555                            "LTRIM",
5556                            vec![trim.this, characters],
5557                        ))))
5558                    }
5559                    crate::expressions::TrimPosition::Trailing if trim.characters.is_some() => {
5560                        let characters = trim.characters.take().expect("checked above");
5561                        Ok(Expression::Function(Box::new(Function::new(
5562                            "RTRIM",
5563                            vec![trim.this, characters],
5564                        ))))
5565                    }
5566                    _ => Ok(Expression::Trim(Box::new(trim))),
5567                }
5568            }
5569            other => Ok(other),
5570        })
5571    }
5572
5573    fn normalize_postgres_only_for_tsql(expr: Expression) -> Result<Expression> {
5574        transform_recursive(expr, &|e| match e {
5575            Expression::Table(mut table) if table.only => {
5576                table.only = false;
5577                Ok(Expression::Table(table))
5578            }
5579            other => Ok(other),
5580        })
5581    }
5582
5583    fn rewrite_postgres_json_array_elements_select_for_tsql(
5584        expr: Expression,
5585    ) -> Result<Expression> {
5586        let Expression::Select(select) = expr else {
5587            return Ok(expr);
5588        };
5589        let mut select = *select;
5590        if !Self::is_plain_single_projection_select(&select) {
5591            return Ok(Expression::Select(Box::new(select)));
5592        }
5593
5594        let Some(json_arg) =
5595            Self::postgres_json_array_elements_projection_arg(&select.expressions[0])
5596        else {
5597            return Ok(Expression::Select(Box::new(select)));
5598        };
5599
5600        select.expressions = vec![Expression::column("value")];
5601        select.from = Some(From {
5602            expressions: vec![Expression::OpenJSON(Box::new(
5603                crate::expressions::OpenJSON {
5604                    this: Box::new(json_arg),
5605                    path: None,
5606                    expressions: Vec::new(),
5607                },
5608            ))],
5609        });
5610
5611        Ok(Expression::Select(Box::new(select)))
5612    }
5613
5614    fn is_plain_single_projection_select(select: &crate::expressions::Select) -> bool {
5615        select.expressions.len() == 1
5616            && select.from.is_none()
5617            && select.joins.is_empty()
5618            && select.lateral_views.is_empty()
5619            && select.prewhere.is_none()
5620            && select.where_clause.is_none()
5621            && select.group_by.is_none()
5622            && select.having.is_none()
5623            && select.qualify.is_none()
5624            && select.order_by.is_none()
5625            && select.distribute_by.is_none()
5626            && select.cluster_by.is_none()
5627            && select.sort_by.is_none()
5628            && select.limit.is_none()
5629            && select.offset.is_none()
5630            && select.limit_by.is_none()
5631            && select.fetch.is_none()
5632            && !select.distinct
5633            && select.distinct_on.is_none()
5634            && select.top.is_none()
5635            && select.with.is_none()
5636            && select.sample.is_none()
5637            && select.into.is_none()
5638            && select.locks.is_empty()
5639            && select.for_xml.is_empty()
5640            && select.for_json.is_empty()
5641            && select.exclude.is_none()
5642    }
5643
5644    fn postgres_json_array_elements_projection_arg(expr: &Expression) -> Option<Expression> {
5645        match expr {
5646            Expression::Function(function)
5647                if Self::node_is_postgres_json_array_elements(expr) && function.args.len() == 1 =>
5648            {
5649                Some(function.args[0].clone())
5650            }
5651            Expression::Alias(alias) => {
5652                Self::postgres_json_array_elements_projection_arg(&alias.this)
5653            }
5654            _ => None,
5655        }
5656    }
5657
5658    fn normalize_postgres_type_function_casts(expr: Expression) -> Result<Expression> {
5659        transform_recursive(expr, &|e| match e {
5660            Expression::Function(function) => {
5661                let mut function = *function;
5662                if function.args.len() == 1
5663                    && !function.distinct
5664                    && !function.quoted
5665                    && !function.use_bracket_syntax
5666                    && !function.name.contains('.')
5667                {
5668                    if let Some(to) = Self::postgres_type_function_data_type(&function.name) {
5669                        let this = function.args.remove(0);
5670                        return Ok(Expression::Cast(Box::new(Cast {
5671                            this,
5672                            to,
5673                            trailing_comments: function.trailing_comments,
5674                            double_colon_syntax: false,
5675                            format: None,
5676                            default: None,
5677                            inferred_type: function.inferred_type,
5678                        })));
5679                    }
5680                }
5681                Ok(Expression::Function(Box::new(function)))
5682            }
5683            _ => Ok(e),
5684        })
5685    }
5686
5687    fn node_is_postgres_type_function_cast(expr: &Expression) -> bool {
5688        matches!(
5689            expr,
5690            Expression::Function(function)
5691                if !function.quoted
5692                    && !function.use_bracket_syntax
5693                    && !function.name.contains('.')
5694                    && Self::postgres_type_function_data_type(&function.name).is_some()
5695        )
5696    }
5697
5698    fn postgres_type_function_data_type(name: &str) -> Option<DataType> {
5699        match name.to_ascii_uppercase().as_str() {
5700            "NUMERIC" | "DECIMAL" | "DEC" => Some(DataType::Decimal {
5701                precision: None,
5702                scale: None,
5703            }),
5704            "INT2" | "SMALLINT" => Some(DataType::SmallInt { length: None }),
5705            "INT4" | "INT" => Some(DataType::Int {
5706                length: None,
5707                integer_spelling: false,
5708            }),
5709            "INTEGER" => Some(DataType::Int {
5710                length: None,
5711                integer_spelling: true,
5712            }),
5713            "INT8" | "BIGINT" => Some(DataType::BigInt { length: None }),
5714            "FLOAT4" | "REAL" => Some(DataType::Float {
5715                precision: None,
5716                scale: None,
5717                real_spelling: true,
5718            }),
5719            "FLOAT8" => Some(DataType::Double {
5720                precision: None,
5721                scale: None,
5722            }),
5723            "BOOL" | "BOOLEAN" => Some(DataType::Boolean),
5724            "TEXT" => Some(DataType::Text),
5725            "VARCHAR" => Some(DataType::VarChar {
5726                length: None,
5727                parenthesized_length: false,
5728            }),
5729            "UUID" => Some(DataType::Uuid),
5730            _ => None,
5731        }
5732    }
5733
5734    fn rewrite_boolean_values_for_tsql(expr: Expression) -> Result<Expression> {
5735        match expr {
5736            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
5737            Expression::Subquery(mut subquery) => {
5738                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
5739                Ok(Expression::Subquery(subquery))
5740            }
5741            Expression::Union(mut union) => {
5742                let left = std::mem::replace(&mut union.left, Expression::null());
5743                let right = std::mem::replace(&mut union.right, Expression::null());
5744                union.left = Self::rewrite_boolean_values_for_tsql(left)?;
5745                union.right = Self::rewrite_boolean_values_for_tsql(right)?;
5746                if let Some(mut with) = union.with.take() {
5747                    with.ctes = with
5748                        .ctes
5749                        .into_iter()
5750                        .map(|mut cte| {
5751                            cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
5752                            Ok(cte)
5753                        })
5754                        .collect::<Result<Vec<_>>>()?;
5755                    union.with = Some(with);
5756                }
5757                Ok(Expression::Union(union))
5758            }
5759            Expression::Intersect(mut intersect) => {
5760                let left = std::mem::replace(&mut intersect.left, Expression::null());
5761                let right = std::mem::replace(&mut intersect.right, Expression::null());
5762                intersect.left = Self::rewrite_boolean_values_for_tsql(left)?;
5763                intersect.right = Self::rewrite_boolean_values_for_tsql(right)?;
5764                Ok(Expression::Intersect(intersect))
5765            }
5766            Expression::Except(mut except) => {
5767                let left = std::mem::replace(&mut except.left, Expression::null());
5768                let right = std::mem::replace(&mut except.right, Expression::null());
5769                except.left = Self::rewrite_boolean_values_for_tsql(left)?;
5770                except.right = Self::rewrite_boolean_values_for_tsql(right)?;
5771                Ok(Expression::Except(except))
5772            }
5773            other => Self::rewrite_tsql_boolean_embedded_queries(other),
5774        }
5775    }
5776
5777    fn rewrite_postgres_format_for_tsql(
5778        expr: Expression,
5779        target: DialectType,
5780    ) -> Result<Expression> {
5781        transform_recursive(expr, &|e| match e {
5782            Expression::Function(f) if f.name.eq_ignore_ascii_case("FORMAT") => {
5783                Self::postgres_format_function_to_tsql(*f, target)
5784            }
5785            other => Ok(other),
5786        })
5787    }
5788
5789    fn postgres_format_function_to_tsql(f: Function, target: DialectType) -> Result<Expression> {
5790        let Some(format_expr) = f.args.first() else {
5791            return Err(Self::unsupported_postgres_format_for_tsql(
5792                target,
5793                "missing format string",
5794            ));
5795        };
5796
5797        let format = match format_expr {
5798            Expression::Literal(lit) if lit.is_string() => lit.value_str(),
5799            _ => {
5800                return Err(Self::unsupported_postgres_format_for_tsql(
5801                    target,
5802                    "dynamic format strings",
5803                ))
5804            }
5805        };
5806
5807        let value_args = &f.args[1..];
5808        let mut arg_index = 0usize;
5809        let mut literal = String::new();
5810        let mut segments = Vec::new();
5811        let mut chars = format.chars();
5812
5813        while let Some(ch) = chars.next() {
5814            if ch != '%' {
5815                literal.push(ch);
5816                continue;
5817            }
5818
5819            let Some(specifier) = chars.next() else {
5820                return Err(Self::unsupported_postgres_format_for_tsql(
5821                    target,
5822                    "unterminated format specifier",
5823                ));
5824            };
5825
5826            match specifier {
5827                '%' => literal.push('%'),
5828                's' => {
5829                    if !literal.is_empty() {
5830                        segments.push(Expression::string(std::mem::take(&mut literal)));
5831                    }
5832                    let Some(arg) = value_args.get(arg_index) else {
5833                        return Err(Self::unsupported_postgres_format_for_tsql(
5834                            target,
5835                            "not enough arguments",
5836                        ));
5837                    };
5838                    segments.push(arg.clone());
5839                    arg_index += 1;
5840                }
5841                other => {
5842                    return Err(Self::unsupported_postgres_format_for_tsql(
5843                        target,
5844                        format!("unsupported format specifier %{other}"),
5845                    ))
5846                }
5847            }
5848        }
5849
5850        if !literal.is_empty() {
5851            segments.push(Expression::string(literal));
5852        }
5853
5854        if arg_index != value_args.len() {
5855            return Err(Self::unsupported_postgres_format_for_tsql(
5856                target,
5857                "unused format arguments",
5858            ));
5859        }
5860
5861        Ok(Self::postgres_format_segments_to_tsql_concat(segments))
5862    }
5863
5864    fn postgres_format_segments_to_tsql_concat(mut segments: Vec<Expression>) -> Expression {
5865        if segments.is_empty() {
5866            return Expression::string("");
5867        }
5868
5869        if segments.len() == 1 {
5870            let only = segments.pop().expect("one segment");
5871            if matches!(&only, Expression::Literal(lit) if lit.is_string()) {
5872                return only;
5873            }
5874
5875            return Expression::Function(Box::new(Function::new(
5876                "CONCAT".to_string(),
5877                vec![only, Expression::string("")],
5878            )));
5879        }
5880
5881        Expression::Function(Box::new(Function::new("CONCAT".to_string(), segments)))
5882    }
5883
5884    fn unsupported_postgres_format_for_tsql(
5885        target: DialectType,
5886        reason: impl Into<String>,
5887    ) -> crate::error::Error {
5888        crate::error::Error::unsupported(
5889            format!("PostgreSQL format() ({})", reason.into()),
5890            target.to_string(),
5891        )
5892    }
5893
5894    fn rewrite_boolean_values_in_tsql_select(
5895        mut select: Box<crate::expressions::Select>,
5896    ) -> Result<Expression> {
5897        if let Some(mut with) = select.with.take() {
5898            with.ctes = with
5899                .ctes
5900                .into_iter()
5901                .map(|mut cte| {
5902                    cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
5903                    Ok(cte)
5904                })
5905                .collect::<Result<Vec<_>>>()?;
5906            select.with = Some(with);
5907        }
5908
5909        select.expressions = select
5910            .expressions
5911            .into_iter()
5912            .map(Self::rewrite_tsql_boolean_scalar_value)
5913            .collect::<Result<Vec<_>>>()?;
5914
5915        if let Some(mut from) = select.from.take() {
5916            from.expressions = from
5917                .expressions
5918                .into_iter()
5919                .map(Self::rewrite_tsql_boolean_embedded_queries)
5920                .collect::<Result<Vec<_>>>()?;
5921            select.from = Some(from);
5922        }
5923
5924        select.joins = select
5925            .joins
5926            .into_iter()
5927            .map(|mut join| {
5928                join.this = Self::rewrite_tsql_boolean_embedded_queries(join.this)?;
5929                if let Some(on) = join.on.take() {
5930                    join.on = Some(Self::rewrite_tsql_boolean_predicate_context(on)?);
5931                }
5932                if let Some(match_condition) = join.match_condition.take() {
5933                    join.match_condition = Some(Self::rewrite_tsql_boolean_predicate_context(
5934                        match_condition,
5935                    )?);
5936                }
5937                join.pivots = join
5938                    .pivots
5939                    .into_iter()
5940                    .map(Self::rewrite_tsql_boolean_embedded_queries)
5941                    .collect::<Result<Vec<_>>>()?;
5942                Ok(join)
5943            })
5944            .collect::<Result<Vec<_>>>()?;
5945
5946        select.lateral_views = select
5947            .lateral_views
5948            .into_iter()
5949            .map(|mut lateral_view| {
5950                lateral_view.this = Self::rewrite_tsql_boolean_embedded_queries(lateral_view.this)?;
5951                Ok(lateral_view)
5952            })
5953            .collect::<Result<Vec<_>>>()?;
5954
5955        if let Some(prewhere) = select.prewhere.take() {
5956            select.prewhere = Some(Self::rewrite_tsql_boolean_predicate_context(prewhere)?);
5957        }
5958
5959        if let Some(mut where_clause) = select.where_clause.take() {
5960            where_clause.this = Self::rewrite_tsql_boolean_predicate_context(where_clause.this)?;
5961            select.where_clause = Some(where_clause);
5962        }
5963
5964        if let Some(mut group_by) = select.group_by.take() {
5965            group_by.expressions = group_by
5966                .expressions
5967                .into_iter()
5968                .map(Self::rewrite_tsql_boolean_scalar_value)
5969                .collect::<Result<Vec<_>>>()?;
5970            select.group_by = Some(group_by);
5971        }
5972
5973        if let Some(mut having) = select.having.take() {
5974            having.this = Self::rewrite_tsql_boolean_predicate_context(having.this)?;
5975            select.having = Some(having);
5976        }
5977
5978        if let Some(mut qualify) = select.qualify.take() {
5979            qualify.this = Self::rewrite_tsql_boolean_predicate_context(qualify.this)?;
5980            select.qualify = Some(qualify);
5981        }
5982
5983        if let Some(mut order_by) = select.order_by.take() {
5984            order_by.expressions = Self::rewrite_tsql_boolean_ordered_values(order_by.expressions)?;
5985            select.order_by = Some(order_by);
5986        }
5987
5988        if let Some(mut distribute_by) = select.distribute_by.take() {
5989            distribute_by.expressions = distribute_by
5990                .expressions
5991                .into_iter()
5992                .map(Self::rewrite_tsql_boolean_scalar_value)
5993                .collect::<Result<Vec<_>>>()?;
5994            select.distribute_by = Some(distribute_by);
5995        }
5996
5997        if let Some(mut cluster_by) = select.cluster_by.take() {
5998            cluster_by.expressions =
5999                Self::rewrite_tsql_boolean_ordered_values(cluster_by.expressions)?;
6000            select.cluster_by = Some(cluster_by);
6001        }
6002
6003        if let Some(mut sort_by) = select.sort_by.take() {
6004            sort_by.expressions = Self::rewrite_tsql_boolean_ordered_values(sort_by.expressions)?;
6005            select.sort_by = Some(sort_by);
6006        }
6007
6008        if let Some(limit_by) = select.limit_by.take() {
6009            select.limit_by = Some(
6010                limit_by
6011                    .into_iter()
6012                    .map(Self::rewrite_tsql_boolean_scalar_value)
6013                    .collect::<Result<Vec<_>>>()?,
6014            );
6015        }
6016
6017        if let Some(distinct_on) = select.distinct_on.take() {
6018            select.distinct_on = Some(
6019                distinct_on
6020                    .into_iter()
6021                    .map(Self::rewrite_tsql_boolean_scalar_value)
6022                    .collect::<Result<Vec<_>>>()?,
6023            );
6024        }
6025
6026        if let Some(mut sample) = select.sample.take() {
6027            sample.size = Self::rewrite_tsql_boolean_embedded_queries(sample.size)?;
6028            if let Some(offset) = sample.offset.take() {
6029                sample.offset = Some(Self::rewrite_tsql_boolean_embedded_queries(offset)?);
6030            }
6031            if let Some(bucket_numerator) = sample.bucket_numerator.take() {
6032                sample.bucket_numerator = Some(Box::new(
6033                    Self::rewrite_tsql_boolean_embedded_queries(*bucket_numerator)?,
6034                ));
6035            }
6036            if let Some(bucket_denominator) = sample.bucket_denominator.take() {
6037                sample.bucket_denominator = Some(Box::new(
6038                    Self::rewrite_tsql_boolean_embedded_queries(*bucket_denominator)?,
6039                ));
6040            }
6041            if let Some(bucket_field) = sample.bucket_field.take() {
6042                sample.bucket_field = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6043                    *bucket_field,
6044                )?));
6045            }
6046            select.sample = Some(sample);
6047        }
6048
6049        if let Some(settings) = select.settings.take() {
6050            select.settings = Some(
6051                settings
6052                    .into_iter()
6053                    .map(Self::rewrite_tsql_boolean_embedded_queries)
6054                    .collect::<Result<Vec<_>>>()?,
6055            );
6056        }
6057
6058        if let Some(format) = select.format.take() {
6059            select.format = Some(Self::rewrite_tsql_boolean_embedded_queries(format)?);
6060        }
6061
6062        if let Some(mut windows) = select.windows.take() {
6063            for window in windows.iter_mut() {
6064                Self::rewrite_tsql_boolean_over_values(&mut window.spec)?;
6065            }
6066            select.windows = Some(windows);
6067        }
6068
6069        Ok(Expression::Select(select))
6070    }
6071
6072    fn rewrite_tsql_boolean_scalar_value(expr: Expression) -> Result<Expression> {
6073        if Self::is_tsql_boolean_value_expression(&expr) {
6074            let predicate = Self::rewrite_tsql_boolean_predicate_context(expr)?;
6075            return Ok(Self::tsql_boolean_value_case(predicate));
6076        }
6077
6078        match expr {
6079            Expression::Alias(mut alias) => {
6080                alias.this = Self::rewrite_tsql_boolean_scalar_value(alias.this)?;
6081                Ok(Expression::Alias(alias))
6082            }
6083            Expression::Paren(mut paren) => {
6084                paren.this = Self::rewrite_tsql_boolean_scalar_value(paren.this)?;
6085                Ok(Expression::Paren(paren))
6086            }
6087            Expression::Cast(mut cast) => {
6088                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6089                if let Some(format) = cast.format.take() {
6090                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6091                        *format,
6092                    )?));
6093                }
6094                if let Some(default) = cast.default.take() {
6095                    cast.default =
6096                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6097                }
6098                Ok(Expression::Cast(cast))
6099            }
6100            Expression::TryCast(mut cast) => {
6101                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6102                if let Some(format) = cast.format.take() {
6103                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6104                        *format,
6105                    )?));
6106                }
6107                if let Some(default) = cast.default.take() {
6108                    cast.default =
6109                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6110                }
6111                Ok(Expression::TryCast(cast))
6112            }
6113            Expression::SafeCast(mut cast) => {
6114                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6115                if let Some(format) = cast.format.take() {
6116                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6117                        *format,
6118                    )?));
6119                }
6120                if let Some(default) = cast.default.take() {
6121                    cast.default =
6122                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6123                }
6124                Ok(Expression::SafeCast(cast))
6125            }
6126            Expression::Case(mut case) => {
6127                if let Some(operand) = case.operand.take() {
6128                    case.operand = Some(Self::rewrite_tsql_boolean_scalar_value(operand)?);
6129                }
6130                case.whens = case
6131                    .whens
6132                    .into_iter()
6133                    .map(|(condition, result)| {
6134                        Ok((
6135                            Self::rewrite_tsql_boolean_predicate_context(condition)?,
6136                            Self::rewrite_tsql_boolean_scalar_value(result)?,
6137                        ))
6138                    })
6139                    .collect::<Result<Vec<_>>>()?;
6140                if let Some(else_) = case.else_.take() {
6141                    case.else_ = Some(Self::rewrite_tsql_boolean_scalar_value(else_)?);
6142                }
6143                Ok(Expression::Case(case))
6144            }
6145            Expression::IfFunc(mut if_func) => {
6146                if_func.condition =
6147                    Self::rewrite_tsql_boolean_predicate_context(if_func.condition)?;
6148                if_func.true_value = Self::rewrite_tsql_boolean_scalar_value(if_func.true_value)?;
6149                if let Some(false_value) = if_func.false_value.take() {
6150                    if_func.false_value =
6151                        Some(Self::rewrite_tsql_boolean_scalar_value(false_value)?);
6152                }
6153                Ok(Expression::IfFunc(if_func))
6154            }
6155            Expression::WindowFunction(mut window_function) => {
6156                window_function.this =
6157                    Self::rewrite_tsql_boolean_embedded_queries(window_function.this)?;
6158                Self::rewrite_tsql_boolean_over_values(&mut window_function.over)?;
6159                if let Some(mut keep) = window_function.keep.take() {
6160                    keep.order_by = Self::rewrite_tsql_boolean_ordered_values(keep.order_by)?;
6161                    window_function.keep = Some(keep);
6162                }
6163                Ok(Expression::WindowFunction(window_function))
6164            }
6165            Expression::WithinGroup(mut within_group) => {
6166                within_group.this = Self::rewrite_tsql_boolean_embedded_queries(within_group.this)?;
6167                within_group.order_by =
6168                    Self::rewrite_tsql_boolean_ordered_values(within_group.order_by)?;
6169                Ok(Expression::WithinGroup(within_group))
6170            }
6171            Expression::Subquery(mut subquery) => {
6172                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
6173                Ok(Expression::Subquery(subquery))
6174            }
6175            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
6176            other => Self::rewrite_tsql_boolean_embedded_queries(other),
6177        }
6178    }
6179
6180    fn rewrite_tsql_boolean_predicate_context(expr: Expression) -> Result<Expression> {
6181        let expr = Self::rewrite_tsql_boolean_embedded_queries(expr)?;
6182        Ok(crate::transforms::ensure_bool_condition(expr))
6183    }
6184
6185    fn rewrite_tsql_boolean_embedded_queries(expr: Expression) -> Result<Expression> {
6186        transform_recursive(expr, &|e| match e {
6187            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
6188            Expression::Subquery(mut subquery) => {
6189                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
6190                Ok(Expression::Subquery(subquery))
6191            }
6192            Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => {
6193                Self::rewrite_boolean_values_for_tsql(e)
6194            }
6195            other => Ok(other),
6196        })
6197    }
6198
6199    fn rewrite_tsql_boolean_ordered_values(
6200        ordered: Vec<crate::expressions::Ordered>,
6201    ) -> Result<Vec<crate::expressions::Ordered>> {
6202        ordered
6203            .into_iter()
6204            .map(|mut ordered| {
6205                ordered.this = Self::rewrite_tsql_boolean_scalar_value(ordered.this)?;
6206                if let Some(with_fill) = ordered.with_fill.take() {
6207                    ordered.with_fill = Some(Box::new(
6208                        Self::rewrite_tsql_boolean_with_fill_values(*with_fill)?,
6209                    ));
6210                }
6211                Ok(ordered)
6212            })
6213            .collect()
6214    }
6215
6216    fn rewrite_tsql_boolean_with_fill_values(
6217        mut with_fill: crate::expressions::WithFill,
6218    ) -> Result<crate::expressions::WithFill> {
6219        if let Some(from) = with_fill.from_.take() {
6220            with_fill.from_ = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*from)?));
6221        }
6222        if let Some(to) = with_fill.to.take() {
6223            with_fill.to = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*to)?));
6224        }
6225        if let Some(step) = with_fill.step.take() {
6226            with_fill.step = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*step)?));
6227        }
6228        if let Some(staleness) = with_fill.staleness.take() {
6229            with_fill.staleness = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
6230                *staleness,
6231            )?));
6232        }
6233        if let Some(interpolate) = with_fill.interpolate.take() {
6234            with_fill.interpolate = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
6235                *interpolate,
6236            )?));
6237        }
6238        Ok(with_fill)
6239    }
6240
6241    fn rewrite_tsql_boolean_over_values(over: &mut crate::expressions::Over) -> Result<()> {
6242        over.partition_by = std::mem::take(&mut over.partition_by)
6243            .into_iter()
6244            .map(Self::rewrite_tsql_boolean_scalar_value)
6245            .collect::<Result<Vec<_>>>()?;
6246        over.order_by =
6247            Self::rewrite_tsql_boolean_ordered_values(std::mem::take(&mut over.order_by))?;
6248        Ok(())
6249    }
6250
6251    fn is_tsql_boolean_value_expression(expr: &Expression) -> bool {
6252        match expr {
6253            Expression::Paren(paren) => Self::is_tsql_boolean_value_expression(&paren.this),
6254            Expression::Eq(_)
6255            | Expression::Neq(_)
6256            | Expression::Lt(_)
6257            | Expression::Lte(_)
6258            | Expression::Gt(_)
6259            | Expression::Gte(_)
6260            | Expression::Is(_)
6261            | Expression::IsNull(_)
6262            | Expression::IsTrue(_)
6263            | Expression::IsFalse(_)
6264            | Expression::Like(_)
6265            | Expression::ILike(_)
6266            | Expression::StartsWith(_)
6267            | Expression::SimilarTo(_)
6268            | Expression::Glob(_)
6269            | Expression::RegexpLike(_)
6270            | Expression::In(_)
6271            | Expression::Between(_)
6272            | Expression::Exists(_)
6273            | Expression::And(_)
6274            | Expression::Or(_)
6275            | Expression::Not(_)
6276            | Expression::Any(_)
6277            | Expression::All(_)
6278            | Expression::NullSafeEq(_)
6279            | Expression::NullSafeNeq(_)
6280            | Expression::EqualNull(_) => true,
6281            _ => false,
6282        }
6283    }
6284
6285    fn tsql_boolean_value_case(predicate: Expression) -> Expression {
6286        let case = Expression::Case(Box::new(crate::expressions::Case {
6287            operand: None,
6288            whens: vec![(predicate, Expression::number(1))],
6289            else_: Some(Expression::number(0)),
6290            comments: Vec::new(),
6291            inferred_type: None,
6292        }));
6293
6294        Expression::Cast(Box::new(Cast {
6295            this: case,
6296            to: DataType::Boolean,
6297            trailing_comments: Vec::new(),
6298            double_colon_syntax: false,
6299            format: None,
6300            default: None,
6301            inferred_type: None,
6302        }))
6303    }
6304
6305    fn rewrite_aggregate_filters_for_tsql(expr: Expression) -> Result<Expression> {
6306        transform_recursive(expr, &|e| Self::rewrite_aggregate_filter_for_tsql(e))
6307    }
6308
6309    fn rewrite_aggregate_filter_for_tsql(expr: Expression) -> Result<Expression> {
6310        macro_rules! rewrite_agg_filter {
6311            ($variant:ident, $agg:expr) => {{
6312                let mut agg = $agg;
6313                if let Some(filter) = agg.filter.take() {
6314                    let this = std::mem::replace(&mut agg.this, Expression::null());
6315                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6316                }
6317                Ok(Expression::$variant(agg))
6318            }};
6319        }
6320
6321        match expr {
6322            Expression::Filter(filter) => {
6323                let condition = match *filter.expression {
6324                    Expression::Where(where_) => where_.this,
6325                    other => other,
6326                };
6327                Ok(Self::push_filter_into_tsql_aggregate(
6328                    *filter.this,
6329                    condition,
6330                ))
6331            }
6332            Expression::AggregateFunction(mut agg) => {
6333                if let Some(filter) = agg.filter.take() {
6334                    Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
6335                }
6336                Ok(Expression::AggregateFunction(agg))
6337            }
6338            Expression::Count(mut count) => {
6339                if let Some(filter) = count.filter.take() {
6340                    let value = if count.star {
6341                        Expression::number(1)
6342                    } else {
6343                        count.this.take().unwrap_or_else(|| Expression::number(1))
6344                    };
6345                    count.star = false;
6346                    count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
6347                }
6348                Ok(Expression::Count(count))
6349            }
6350            Expression::Sum(agg) => rewrite_agg_filter!(Sum, agg),
6351            Expression::Avg(agg) => rewrite_agg_filter!(Avg, agg),
6352            Expression::Min(agg) => rewrite_agg_filter!(Min, agg),
6353            Expression::Max(agg) => rewrite_agg_filter!(Max, agg),
6354            Expression::ArrayAgg(agg) => rewrite_agg_filter!(ArrayAgg, agg),
6355            Expression::CountIf(agg) => Ok(Expression::CountIf(agg)),
6356            Expression::Stddev(agg) => rewrite_agg_filter!(Stddev, agg),
6357            Expression::StddevPop(agg) => rewrite_agg_filter!(StddevPop, agg),
6358            Expression::StddevSamp(agg) => rewrite_agg_filter!(StddevSamp, agg),
6359            Expression::Variance(agg) => rewrite_agg_filter!(Variance, agg),
6360            Expression::VarPop(agg) => rewrite_agg_filter!(VarPop, agg),
6361            Expression::VarSamp(agg) => rewrite_agg_filter!(VarSamp, agg),
6362            Expression::Median(agg) => rewrite_agg_filter!(Median, agg),
6363            Expression::Mode(agg) => rewrite_agg_filter!(Mode, agg),
6364            Expression::First(agg) => rewrite_agg_filter!(First, agg),
6365            Expression::Last(agg) => rewrite_agg_filter!(Last, agg),
6366            Expression::AnyValue(agg) => rewrite_agg_filter!(AnyValue, agg),
6367            Expression::ApproxDistinct(agg) => rewrite_agg_filter!(ApproxDistinct, agg),
6368            Expression::ApproxCountDistinct(agg) => {
6369                rewrite_agg_filter!(ApproxCountDistinct, agg)
6370            }
6371            Expression::LogicalAnd(agg) => rewrite_agg_filter!(LogicalAnd, agg),
6372            Expression::LogicalOr(agg) => rewrite_agg_filter!(LogicalOr, agg),
6373            Expression::Skewness(agg) => rewrite_agg_filter!(Skewness, agg),
6374            Expression::ArrayConcatAgg(agg) => rewrite_agg_filter!(ArrayConcatAgg, agg),
6375            Expression::ArrayUniqueAgg(agg) => rewrite_agg_filter!(ArrayUniqueAgg, agg),
6376            Expression::BoolXorAgg(agg) => rewrite_agg_filter!(BoolXorAgg, agg),
6377            Expression::BitwiseAndAgg(agg) => rewrite_agg_filter!(BitwiseAndAgg, agg),
6378            Expression::BitwiseOrAgg(agg) => rewrite_agg_filter!(BitwiseOrAgg, agg),
6379            Expression::BitwiseXorAgg(agg) => rewrite_agg_filter!(BitwiseXorAgg, agg),
6380            Expression::StringAgg(mut agg) => {
6381                if let Some(filter) = agg.filter.take() {
6382                    let this = std::mem::replace(&mut agg.this, Expression::null());
6383                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6384                }
6385                Ok(Expression::StringAgg(agg))
6386            }
6387            Expression::GroupConcat(mut agg) => {
6388                if let Some(filter) = agg.filter.take() {
6389                    let this = std::mem::replace(&mut agg.this, Expression::null());
6390                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6391                }
6392                Ok(Expression::GroupConcat(agg))
6393            }
6394            Expression::ListAgg(mut agg) => {
6395                if let Some(filter) = agg.filter.take() {
6396                    let this = std::mem::replace(&mut agg.this, Expression::null());
6397                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6398                }
6399                Ok(Expression::ListAgg(agg))
6400            }
6401            Expression::WithinGroup(mut within_group) => {
6402                within_group.this = Self::rewrite_aggregate_filters_for_tsql(within_group.this)?;
6403                Ok(Expression::WithinGroup(within_group))
6404            }
6405            other => Ok(other),
6406        }
6407    }
6408
6409    fn push_filter_into_tsql_aggregate(expr: Expression, filter: Expression) -> Expression {
6410        macro_rules! push_agg_filter {
6411            ($variant:ident, $agg:expr) => {{
6412                let mut agg = $agg;
6413                let this = std::mem::replace(&mut agg.this, Expression::null());
6414                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6415                agg.filter = None;
6416                Expression::$variant(agg)
6417            }};
6418        }
6419
6420        match expr {
6421            Expression::AggregateFunction(mut agg) => {
6422                Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
6423                Expression::AggregateFunction(agg)
6424            }
6425            Expression::Count(mut count) => {
6426                let value = if count.star {
6427                    Expression::number(1)
6428                } else {
6429                    count.this.take().unwrap_or_else(|| Expression::number(1))
6430                };
6431                count.star = false;
6432                count.filter = None;
6433                count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
6434                Expression::Count(count)
6435            }
6436            Expression::Sum(agg) => push_agg_filter!(Sum, agg),
6437            Expression::Avg(agg) => push_agg_filter!(Avg, agg),
6438            Expression::Min(agg) => push_agg_filter!(Min, agg),
6439            Expression::Max(agg) => push_agg_filter!(Max, agg),
6440            Expression::ArrayAgg(agg) => push_agg_filter!(ArrayAgg, agg),
6441            Expression::CountIf(mut agg) => {
6442                agg.filter = Some(filter);
6443                Expression::CountIf(agg)
6444            }
6445            Expression::Stddev(agg) => push_agg_filter!(Stddev, agg),
6446            Expression::StddevPop(agg) => push_agg_filter!(StddevPop, agg),
6447            Expression::StddevSamp(agg) => push_agg_filter!(StddevSamp, agg),
6448            Expression::Variance(agg) => push_agg_filter!(Variance, agg),
6449            Expression::VarPop(agg) => push_agg_filter!(VarPop, agg),
6450            Expression::VarSamp(agg) => push_agg_filter!(VarSamp, agg),
6451            Expression::Median(agg) => push_agg_filter!(Median, agg),
6452            Expression::Mode(agg) => push_agg_filter!(Mode, agg),
6453            Expression::First(agg) => push_agg_filter!(First, agg),
6454            Expression::Last(agg) => push_agg_filter!(Last, agg),
6455            Expression::AnyValue(agg) => push_agg_filter!(AnyValue, agg),
6456            Expression::ApproxDistinct(agg) => push_agg_filter!(ApproxDistinct, agg),
6457            Expression::ApproxCountDistinct(agg) => {
6458                push_agg_filter!(ApproxCountDistinct, agg)
6459            }
6460            Expression::LogicalAnd(agg) => push_agg_filter!(LogicalAnd, agg),
6461            Expression::LogicalOr(agg) => push_agg_filter!(LogicalOr, agg),
6462            Expression::Skewness(agg) => push_agg_filter!(Skewness, agg),
6463            Expression::ArrayConcatAgg(agg) => push_agg_filter!(ArrayConcatAgg, agg),
6464            Expression::ArrayUniqueAgg(agg) => push_agg_filter!(ArrayUniqueAgg, agg),
6465            Expression::BoolXorAgg(agg) => push_agg_filter!(BoolXorAgg, agg),
6466            Expression::BitwiseAndAgg(agg) => push_agg_filter!(BitwiseAndAgg, agg),
6467            Expression::BitwiseOrAgg(agg) => push_agg_filter!(BitwiseOrAgg, agg),
6468            Expression::BitwiseXorAgg(agg) => push_agg_filter!(BitwiseXorAgg, agg),
6469            Expression::StringAgg(mut agg) => {
6470                let this = std::mem::replace(&mut agg.this, Expression::null());
6471                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6472                agg.filter = None;
6473                Expression::StringAgg(agg)
6474            }
6475            Expression::GroupConcat(mut agg) => {
6476                let this = std::mem::replace(&mut agg.this, Expression::null());
6477                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6478                agg.filter = None;
6479                Expression::GroupConcat(agg)
6480            }
6481            Expression::ListAgg(mut agg) => {
6482                let this = std::mem::replace(&mut agg.this, Expression::null());
6483                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6484                agg.filter = None;
6485                Expression::ListAgg(agg)
6486            }
6487            Expression::WithinGroup(mut within_group) => {
6488                within_group.this =
6489                    Self::push_filter_into_tsql_aggregate(within_group.this, filter);
6490                Expression::WithinGroup(within_group)
6491            }
6492            other => Expression::Filter(Box::new(crate::expressions::Filter {
6493                this: Box::new(other),
6494                expression: Box::new(filter),
6495            })),
6496        }
6497    }
6498
6499    fn rewrite_generic_aggregate_filter_for_tsql(
6500        agg: &mut crate::expressions::AggregateFunction,
6501        filter: Expression,
6502    ) {
6503        let is_count =
6504            agg.name.eq_ignore_ascii_case("COUNT") || agg.name.eq_ignore_ascii_case("COUNT_BIG");
6505        let is_count_star = is_count
6506            && (agg.args.is_empty()
6507                || (agg.args.len() == 1 && matches!(agg.args[0], Expression::Star(_))));
6508
6509        if is_count_star {
6510            agg.args = vec![Self::conditional_aggregate_value_for_tsql(
6511                filter,
6512                Expression::number(1),
6513            )];
6514        } else if !agg.args.is_empty() {
6515            agg.args = agg
6516                .args
6517                .drain(..)
6518                .map(|arg| Self::conditional_aggregate_value_for_tsql(filter.clone(), arg))
6519                .collect();
6520        } else {
6521            agg.filter = Some(filter);
6522        }
6523    }
6524
6525    fn conditional_aggregate_value_for_tsql(filter: Expression, value: Expression) -> Expression {
6526        Expression::Case(Box::new(crate::expressions::Case {
6527            operand: None,
6528            whens: vec![(filter, value)],
6529            else_: None,
6530            comments: Vec::new(),
6531            inferred_type: None,
6532        }))
6533    }
6534
6535    fn reject_pgvector_distance_operators_for_sqlite(&self, sql: &str) -> Result<()> {
6536        let tokens = self.tokenize(sql)?;
6537        for (i, token) in tokens.iter().enumerate() {
6538            if token.token_type == TokenType::NullsafeEq {
6539                return Err(crate::error::Error::unsupported(
6540                    "PostgreSQL pgvector cosine distance operator <=>",
6541                    "SQLite",
6542                ));
6543            }
6544            if token.token_type == TokenType::Lt
6545                && tokens
6546                    .get(i + 1)
6547                    .is_some_and(|token| token.token_type == TokenType::Tilde)
6548                && tokens
6549                    .get(i + 2)
6550                    .is_some_and(|token| token.token_type == TokenType::Gt)
6551            {
6552                return Err(crate::error::Error::unsupported(
6553                    "PostgreSQL pgvector Hamming distance operator <~>",
6554                    "SQLite",
6555                ));
6556            }
6557        }
6558        Ok(())
6559    }
6560
6561    fn normalize_sqlite_double_quoted_defaults(expr: Expression) -> Result<Expression> {
6562        fn normalize_default_expr(expr: Expression) -> Result<Expression> {
6563            transform_recursive(expr, &|e| match e {
6564                Expression::Column(col)
6565                    if col.table.is_none() && col.name.quoted && !col.join_mark =>
6566                {
6567                    Ok(Expression::Literal(Box::new(Literal::String(
6568                        col.name.name,
6569                    ))))
6570                }
6571                Expression::Identifier(id) if id.quoted => {
6572                    Ok(Expression::Literal(Box::new(Literal::String(id.name))))
6573                }
6574                _ => Ok(e),
6575            })
6576        }
6577
6578        fn normalize_column_default(col: &mut crate::expressions::ColumnDef) -> Result<()> {
6579            if let Some(default) = col.default.take() {
6580                col.default = Some(normalize_default_expr(default)?);
6581            }
6582
6583            for constraint in &mut col.constraints {
6584                if let ColumnConstraint::Default(default) = constraint {
6585                    *default = normalize_default_expr(default.clone())?;
6586                }
6587            }
6588
6589            Ok(())
6590        }
6591
6592        transform_recursive(expr, &|e| match e {
6593            Expression::CreateTable(mut ct) => {
6594                for column in &mut ct.columns {
6595                    normalize_column_default(column)?;
6596                }
6597                Ok(Expression::CreateTable(ct))
6598            }
6599            Expression::ColumnDef(mut col) => {
6600                normalize_column_default(&mut col)?;
6601                Ok(Expression::ColumnDef(col))
6602            }
6603            _ => Ok(e),
6604        })
6605    }
6606
6607    fn normalize_postgres_to_sqlite_types(expr: Expression) -> Result<Expression> {
6608        fn sqlite_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
6609            use crate::expressions::DataType;
6610
6611            match dt {
6612                DataType::Bit { .. } => DataType::Int {
6613                    length: None,
6614                    integer_spelling: true,
6615                },
6616                DataType::TextWithLength { .. } => DataType::Text,
6617                DataType::VarChar { .. } => DataType::Text,
6618                DataType::Char { .. } => DataType::Text,
6619                DataType::Timestamp { timezone: true, .. } => DataType::Text,
6620                DataType::Custom { name } => {
6621                    let base = name
6622                        .split_once('(')
6623                        .map_or(name.as_str(), |(base, _)| base)
6624                        .trim();
6625                    if base.eq_ignore_ascii_case("TSVECTOR")
6626                        || base.eq_ignore_ascii_case("TIMESTAMPTZ")
6627                        || base.eq_ignore_ascii_case("TIMESTAMP WITH TIME ZONE")
6628                        || base.eq_ignore_ascii_case("NVARCHAR")
6629                        || base.eq_ignore_ascii_case("NCHAR")
6630                    {
6631                        DataType::Text
6632                    } else {
6633                        DataType::Custom { name }
6634                    }
6635                }
6636                _ => dt,
6637            }
6638        }
6639
6640        transform_recursive(expr, &|e| match e {
6641            Expression::DataType(dt) => Ok(Expression::DataType(sqlite_type(dt))),
6642            Expression::CreateTable(mut ct) => {
6643                for column in &mut ct.columns {
6644                    column.data_type = sqlite_type(column.data_type.clone());
6645                }
6646                Ok(Expression::CreateTable(ct))
6647            }
6648            _ => Ok(e),
6649        })
6650    }
6651
6652    fn normalize_postgres_to_fabric_types(expr: Expression) -> Result<Expression> {
6653        fn fabric_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
6654            use crate::expressions::DataType;
6655
6656            match dt {
6657                DataType::Decimal {
6658                    precision: None,
6659                    scale: None,
6660                } => DataType::Decimal {
6661                    precision: Some(38),
6662                    scale: Some(10),
6663                },
6664                DataType::Json | DataType::JsonB => DataType::Custom {
6665                    name: "VARCHAR(MAX)".to_string(),
6666                },
6667                _ => dt,
6668            }
6669        }
6670
6671        transform_recursive(expr, &|e| match e {
6672            Expression::DataType(dt) => Ok(Expression::DataType(fabric_type(dt))),
6673            Expression::CreateTable(mut ct) => {
6674                for column in &mut ct.columns {
6675                    column.data_type = fabric_type(column.data_type.clone());
6676                }
6677                Ok(Expression::CreateTable(ct))
6678            }
6679            Expression::ColumnDef(mut col) => {
6680                col.data_type = fabric_type(col.data_type);
6681                Ok(Expression::ColumnDef(col))
6682            }
6683            _ => Ok(e),
6684        })
6685    }
6686
6687    /// For DuckDB target: when FROM clause contains RANGE(n), replace
6688    /// `(ROW_NUMBER() OVER (ORDER BY 1 NULLS FIRST) - 1)` with `range` in select expressions.
6689    /// This handles SEQ1/2/4/8 → RANGE transpilation from Snowflake.
6690    fn seq_rownum_to_range(expr: Expression) -> Result<Expression> {
6691        if let Expression::Select(mut select) = expr {
6692            // Check if FROM contains a RANGE function
6693            let has_range_from = if let Some(ref from) = select.from {
6694                from.expressions.iter().any(|e| {
6695                    // Check for direct RANGE(...) or aliased RANGE(...)
6696                    match e {
6697                        Expression::Function(f) => f.name.eq_ignore_ascii_case("RANGE"),
6698                        Expression::Alias(a) => {
6699                            matches!(&a.this, Expression::Function(f) if f.name.eq_ignore_ascii_case("RANGE"))
6700                        }
6701                        _ => false,
6702                    }
6703                })
6704            } else {
6705                false
6706            };
6707
6708            if has_range_from {
6709                // Replace the ROW_NUMBER pattern in select expressions
6710                select.expressions = select
6711                    .expressions
6712                    .into_iter()
6713                    .map(|e| Self::replace_rownum_with_range(e))
6714                    .collect();
6715            }
6716
6717            Ok(Expression::Select(select))
6718        } else {
6719            Ok(expr)
6720        }
6721    }
6722
6723    /// Replace `(ROW_NUMBER() OVER (...) - 1)` with `range` column reference
6724    fn replace_rownum_with_range(expr: Expression) -> Expression {
6725        match expr {
6726            // Match: (ROW_NUMBER() OVER (...) - 1) % N → range % N
6727            Expression::Mod(op) => {
6728                let new_left = Self::try_replace_rownum_paren(&op.left);
6729                Expression::Mod(Box::new(crate::expressions::BinaryOp {
6730                    left: new_left,
6731                    right: op.right,
6732                    left_comments: op.left_comments,
6733                    operator_comments: op.operator_comments,
6734                    trailing_comments: op.trailing_comments,
6735                    inferred_type: op.inferred_type,
6736                }))
6737            }
6738            // Match: (CASE WHEN (ROW...) % N >= ... THEN ... ELSE ... END)
6739            Expression::Paren(p) => {
6740                let inner = Self::replace_rownum_with_range(p.this);
6741                Expression::Paren(Box::new(crate::expressions::Paren {
6742                    this: inner,
6743                    trailing_comments: p.trailing_comments,
6744                }))
6745            }
6746            Expression::Case(mut c) => {
6747                // Replace ROW_NUMBER in WHEN conditions and THEN expressions
6748                c.whens = c
6749                    .whens
6750                    .into_iter()
6751                    .map(|(cond, then)| {
6752                        (
6753                            Self::replace_rownum_with_range(cond),
6754                            Self::replace_rownum_with_range(then),
6755                        )
6756                    })
6757                    .collect();
6758                if let Some(else_) = c.else_ {
6759                    c.else_ = Some(Self::replace_rownum_with_range(else_));
6760                }
6761                Expression::Case(c)
6762            }
6763            Expression::Gte(op) => Expression::Gte(Box::new(crate::expressions::BinaryOp {
6764                left: Self::replace_rownum_with_range(op.left),
6765                right: op.right,
6766                left_comments: op.left_comments,
6767                operator_comments: op.operator_comments,
6768                trailing_comments: op.trailing_comments,
6769                inferred_type: op.inferred_type,
6770            })),
6771            Expression::Sub(op) => Expression::Sub(Box::new(crate::expressions::BinaryOp {
6772                left: Self::replace_rownum_with_range(op.left),
6773                right: op.right,
6774                left_comments: op.left_comments,
6775                operator_comments: op.operator_comments,
6776                trailing_comments: op.trailing_comments,
6777                inferred_type: op.inferred_type,
6778            })),
6779            Expression::Alias(mut a) => {
6780                a.this = Self::replace_rownum_with_range(a.this);
6781                Expression::Alias(a)
6782            }
6783            other => other,
6784        }
6785    }
6786
6787    /// Check if an expression is `(ROW_NUMBER() OVER (...) - 1)` and replace with `range`
6788    fn try_replace_rownum_paren(expr: &Expression) -> Expression {
6789        if let Expression::Paren(ref p) = expr {
6790            if let Expression::Sub(ref sub) = p.this {
6791                if let Expression::WindowFunction(ref wf) = sub.left {
6792                    if let Expression::Function(ref f) = wf.this {
6793                        if f.name.eq_ignore_ascii_case("ROW_NUMBER") {
6794                            if let Expression::Literal(ref lit) = sub.right {
6795                                if let crate::expressions::Literal::Number(ref n) = lit.as_ref() {
6796                                    if n == "1" {
6797                                        return Expression::column("range");
6798                                    }
6799                                }
6800                            }
6801                        }
6802                    }
6803                }
6804            }
6805        }
6806        expr.clone()
6807    }
6808
6809    /// Transform BigQuery GENERATE_DATE_ARRAY in UNNEST for Snowflake target.
6810    /// Converts:
6811    ///   SELECT ..., alias, ... FROM t CROSS JOIN UNNEST(GENERATE_DATE_ARRAY(start, end, INTERVAL '1' unit)) AS alias
6812    /// To:
6813    ///   SELECT ..., DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE)) AS alias, ...
6814    ///   FROM t, LATERAL FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)) AS _t0(seq, key, path, index, alias, this)
6815    fn transform_generate_date_array_snowflake(expr: Expression) -> Result<Expression> {
6816        use crate::expressions::*;
6817        transform_recursive(expr, &|e| {
6818            // Handle ARRAY_SIZE(GENERATE_DATE_ARRAY(...)) -> ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM subquery))
6819            if let Expression::ArraySize(ref af) = e {
6820                if let Expression::Function(ref f) = af.this {
6821                    if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
6822                        let result = Self::convert_array_size_gda_snowflake(f)?;
6823                        return Ok(result);
6824                    }
6825                }
6826            }
6827
6828            let Expression::Select(mut sel) = e else {
6829                return Ok(e);
6830            };
6831
6832            // Find joins with UNNEST containing GenerateSeries (from GENERATE_DATE_ARRAY conversion)
6833            let mut gda_info: Option<(String, Expression, Expression, String)> = None; // (alias_name, start_expr, end_expr, unit)
6834            let mut gda_join_idx: Option<usize> = None;
6835
6836            for (idx, join) in sel.joins.iter().enumerate() {
6837                // The join.this may be:
6838                // 1. Unnest(UnnestFunc { alias: Some("mnth"), ... })
6839                // 2. Alias(Alias { this: Unnest(UnnestFunc { alias: None, ... }), alias: "mnth", ... })
6840                let (unnest_ref, alias_name) = match &join.this {
6841                    Expression::Unnest(ref unnest) => {
6842                        let alias = unnest.alias.as_ref().map(|id| id.name.clone());
6843                        (Some(unnest.as_ref()), alias)
6844                    }
6845                    Expression::Alias(ref a) => {
6846                        if let Expression::Unnest(ref unnest) = a.this {
6847                            (Some(unnest.as_ref()), Some(a.alias.name.clone()))
6848                        } else {
6849                            (None, None)
6850                        }
6851                    }
6852                    _ => (None, None),
6853                };
6854
6855                if let (Some(unnest), Some(alias)) = (unnest_ref, alias_name) {
6856                    // Check the main expression (this) of the UNNEST for GENERATE_DATE_ARRAY function
6857                    if let Expression::Function(ref f) = unnest.this {
6858                        if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
6859                            let start_expr = f.args[0].clone();
6860                            let end_expr = f.args[1].clone();
6861                            let step = f.args.get(2).cloned();
6862
6863                            // Extract unit from step interval
6864                            let unit = if let Some(Expression::Interval(ref iv)) = step {
6865                                if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
6866                                    Some(format!("{:?}", unit).to_ascii_uppercase())
6867                                } else if let Some(ref this) = iv.this {
6868                                    // The interval may be stored as a string like "1 MONTH"
6869                                    if let Expression::Literal(lit) = this {
6870                                        if let Literal::String(ref s) = lit.as_ref() {
6871                                            let parts: Vec<&str> = s.split_whitespace().collect();
6872                                            if parts.len() == 2 {
6873                                                Some(parts[1].to_ascii_uppercase())
6874                                            } else if parts.len() == 1 {
6875                                                // Single word like "MONTH" or just "1"
6876                                                let upper = parts[0].to_ascii_uppercase();
6877                                                if matches!(
6878                                                    upper.as_str(),
6879                                                    "YEAR"
6880                                                        | "QUARTER"
6881                                                        | "MONTH"
6882                                                        | "WEEK"
6883                                                        | "DAY"
6884                                                        | "HOUR"
6885                                                        | "MINUTE"
6886                                                        | "SECOND"
6887                                                ) {
6888                                                    Some(upper)
6889                                                } else {
6890                                                    None
6891                                                }
6892                                            } else {
6893                                                None
6894                                            }
6895                                        } else {
6896                                            None
6897                                        }
6898                                    } else {
6899                                        None
6900                                    }
6901                                } else {
6902                                    None
6903                                }
6904                            } else {
6905                                None
6906                            };
6907
6908                            if let Some(unit_str) = unit {
6909                                gda_info = Some((alias, start_expr, end_expr, unit_str));
6910                                gda_join_idx = Some(idx);
6911                            }
6912                        }
6913                    }
6914                }
6915                if gda_info.is_some() {
6916                    break;
6917                }
6918            }
6919
6920            let Some((alias_name, start_expr, end_expr, unit_str)) = gda_info else {
6921                // Also check FROM clause for UNNEST(GENERATE_DATE_ARRAY(...)) patterns
6922                // This handles Generic->Snowflake where GENERATE_DATE_ARRAY is in FROM, not in JOIN
6923                let result = Self::try_transform_from_gda_snowflake(sel);
6924                return result;
6925            };
6926            let join_idx = gda_join_idx.unwrap();
6927
6928            // Build ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)
6929            // ARRAY_GENERATE_RANGE uses exclusive end, and we need DATEDIFF + 1 values
6930            // (inclusive date range), so the exclusive end is DATEDIFF + 1.
6931            let datediff = Expression::Function(Box::new(Function::new(
6932                "DATEDIFF".to_string(),
6933                vec![
6934                    Expression::boxed_column(Column {
6935                        name: Identifier::new(&unit_str),
6936                        table: None,
6937                        join_mark: false,
6938                        trailing_comments: vec![],
6939                        span: None,
6940                        inferred_type: None,
6941                    }),
6942                    start_expr.clone(),
6943                    end_expr.clone(),
6944                ],
6945            )));
6946            let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
6947                left: datediff,
6948                right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
6949                left_comments: vec![],
6950                operator_comments: vec![],
6951                trailing_comments: vec![],
6952                inferred_type: None,
6953            }));
6954
6955            let array_gen_range = Expression::Function(Box::new(Function::new(
6956                "ARRAY_GENERATE_RANGE".to_string(),
6957                vec![
6958                    Expression::Literal(Box::new(Literal::Number("0".to_string()))),
6959                    datediff_plus_one,
6960                ],
6961            )));
6962
6963            // Build FLATTEN(INPUT => ARRAY_GENERATE_RANGE(...))
6964            let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
6965                name: Identifier::new("INPUT"),
6966                value: array_gen_range,
6967                separator: crate::expressions::NamedArgSeparator::DArrow,
6968            }));
6969            let flatten = Expression::Function(Box::new(Function::new(
6970                "FLATTEN".to_string(),
6971                vec![flatten_input],
6972            )));
6973
6974            // Build LATERAL FLATTEN(...) AS _t0(seq, key, path, index, alias, this)
6975            let alias_table = Alias {
6976                this: flatten,
6977                alias: Identifier::new("_t0"),
6978                column_aliases: vec![
6979                    Identifier::new("seq"),
6980                    Identifier::new("key"),
6981                    Identifier::new("path"),
6982                    Identifier::new("index"),
6983                    Identifier::new(&alias_name),
6984                    Identifier::new("this"),
6985                ],
6986                alias_explicit_as: false,
6987                alias_keyword: None,
6988                pre_alias_comments: vec![],
6989                trailing_comments: vec![],
6990                inferred_type: None,
6991            };
6992            let lateral_expr = Expression::Lateral(Box::new(Lateral {
6993                this: Box::new(Expression::Alias(Box::new(alias_table))),
6994                view: None,
6995                outer: None,
6996                alias: None,
6997                alias_quoted: false,
6998                cross_apply: None,
6999                ordinality: None,
7000                column_aliases: vec![],
7001            }));
7002
7003            // Remove the original join and add to FROM expressions
7004            sel.joins.remove(join_idx);
7005            if let Some(ref mut from) = sel.from {
7006                from.expressions.push(lateral_expr);
7007            }
7008
7009            // Build DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE))
7010            let dateadd_expr = Expression::Function(Box::new(Function::new(
7011                "DATEADD".to_string(),
7012                vec![
7013                    Expression::boxed_column(Column {
7014                        name: Identifier::new(&unit_str),
7015                        table: None,
7016                        join_mark: false,
7017                        trailing_comments: vec![],
7018                        span: None,
7019                        inferred_type: None,
7020                    }),
7021                    Expression::Cast(Box::new(Cast {
7022                        this: Expression::boxed_column(Column {
7023                            name: Identifier::new(&alias_name),
7024                            table: None,
7025                            join_mark: false,
7026                            trailing_comments: vec![],
7027                            span: None,
7028                            inferred_type: None,
7029                        }),
7030                        to: DataType::Int {
7031                            length: None,
7032                            integer_spelling: false,
7033                        },
7034                        trailing_comments: vec![],
7035                        double_colon_syntax: false,
7036                        format: None,
7037                        default: None,
7038                        inferred_type: None,
7039                    })),
7040                    Expression::Cast(Box::new(Cast {
7041                        this: start_expr.clone(),
7042                        to: DataType::Date,
7043                        trailing_comments: vec![],
7044                        double_colon_syntax: false,
7045                        format: None,
7046                        default: None,
7047                        inferred_type: None,
7048                    })),
7049                ],
7050            )));
7051
7052            // Replace references to the alias in the SELECT list
7053            let new_exprs: Vec<Expression> = sel
7054                .expressions
7055                .iter()
7056                .map(|expr| Self::replace_column_ref_with_dateadd(expr, &alias_name, &dateadd_expr))
7057                .collect();
7058            sel.expressions = new_exprs;
7059
7060            Ok(Expression::Select(sel))
7061        })
7062    }
7063
7064    /// Helper: replace column references to `alias_name` with dateadd expression
7065    fn replace_column_ref_with_dateadd(
7066        expr: &Expression,
7067        alias_name: &str,
7068        dateadd: &Expression,
7069    ) -> Expression {
7070        use crate::expressions::*;
7071        match expr {
7072            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
7073                // Plain column reference -> DATEADD(...) AS alias_name
7074                Expression::Alias(Box::new(Alias {
7075                    this: dateadd.clone(),
7076                    alias: Identifier::new(alias_name),
7077                    column_aliases: vec![],
7078                    alias_explicit_as: false,
7079                    alias_keyword: None,
7080                    pre_alias_comments: vec![],
7081                    trailing_comments: vec![],
7082                    inferred_type: None,
7083                }))
7084            }
7085            Expression::Alias(a) => {
7086                // Check if the inner expression references the alias
7087                let new_this = Self::replace_column_ref_inner(&a.this, alias_name, dateadd);
7088                Expression::Alias(Box::new(Alias {
7089                    this: new_this,
7090                    alias: a.alias.clone(),
7091                    column_aliases: a.column_aliases.clone(),
7092                    alias_explicit_as: false,
7093                    alias_keyword: None,
7094                    pre_alias_comments: a.pre_alias_comments.clone(),
7095                    trailing_comments: a.trailing_comments.clone(),
7096                    inferred_type: None,
7097                }))
7098            }
7099            _ => expr.clone(),
7100        }
7101    }
7102
7103    /// Helper: replace column references in inner expression (not top-level)
7104    fn replace_column_ref_inner(
7105        expr: &Expression,
7106        alias_name: &str,
7107        dateadd: &Expression,
7108    ) -> Expression {
7109        use crate::expressions::*;
7110        match expr {
7111            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
7112                dateadd.clone()
7113            }
7114            Expression::Add(op) => {
7115                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7116                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7117                Expression::Add(Box::new(BinaryOp {
7118                    left,
7119                    right,
7120                    left_comments: op.left_comments.clone(),
7121                    operator_comments: op.operator_comments.clone(),
7122                    trailing_comments: op.trailing_comments.clone(),
7123                    inferred_type: None,
7124                }))
7125            }
7126            Expression::Sub(op) => {
7127                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7128                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7129                Expression::Sub(Box::new(BinaryOp {
7130                    left,
7131                    right,
7132                    left_comments: op.left_comments.clone(),
7133                    operator_comments: op.operator_comments.clone(),
7134                    trailing_comments: op.trailing_comments.clone(),
7135                    inferred_type: None,
7136                }))
7137            }
7138            Expression::Mul(op) => {
7139                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7140                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7141                Expression::Mul(Box::new(BinaryOp {
7142                    left,
7143                    right,
7144                    left_comments: op.left_comments.clone(),
7145                    operator_comments: op.operator_comments.clone(),
7146                    trailing_comments: op.trailing_comments.clone(),
7147                    inferred_type: None,
7148                }))
7149            }
7150            _ => expr.clone(),
7151        }
7152    }
7153
7154    /// Handle UNNEST(GENERATE_DATE_ARRAY(...)) in FROM clause for Snowflake target.
7155    /// Converts to a subquery with DATEADD + TABLE(FLATTEN(ARRAY_GENERATE_RANGE(...))).
7156    fn try_transform_from_gda_snowflake(
7157        mut sel: Box<crate::expressions::Select>,
7158    ) -> Result<Expression> {
7159        use crate::expressions::*;
7160
7161        // Extract GDA info from FROM clause
7162        let mut gda_info: Option<(
7163            usize,
7164            String,
7165            Expression,
7166            Expression,
7167            String,
7168            Option<(String, Vec<Identifier>)>,
7169        )> = None; // (from_idx, col_name, start, end, unit, outer_alias)
7170
7171        if let Some(ref from) = sel.from {
7172            for (idx, table_expr) in from.expressions.iter().enumerate() {
7173                // Pattern 1: UNNEST(GENERATE_DATE_ARRAY(...))
7174                // Pattern 2: Alias(UNNEST(GENERATE_DATE_ARRAY(...))) AS _q(date_week)
7175                let (unnest_opt, outer_alias_info) = match table_expr {
7176                    Expression::Unnest(ref unnest) => (Some(unnest.as_ref()), None),
7177                    Expression::Alias(ref a) => {
7178                        if let Expression::Unnest(ref unnest) = a.this {
7179                            let alias_info = (a.alias.name.clone(), a.column_aliases.clone());
7180                            (Some(unnest.as_ref()), Some(alias_info))
7181                        } else {
7182                            (None, None)
7183                        }
7184                    }
7185                    _ => (None, None),
7186                };
7187
7188                if let Some(unnest) = unnest_opt {
7189                    // Check for GENERATE_DATE_ARRAY function
7190                    let func_opt = match &unnest.this {
7191                        Expression::Function(ref f)
7192                            if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY")
7193                                && f.args.len() >= 2 =>
7194                        {
7195                            Some(f)
7196                        }
7197                        // Also check for GenerateSeries (from earlier normalization)
7198                        _ => None,
7199                    };
7200
7201                    if let Some(f) = func_opt {
7202                        let start_expr = f.args[0].clone();
7203                        let end_expr = f.args[1].clone();
7204                        let step = f.args.get(2).cloned();
7205
7206                        // Extract unit and column name
7207                        let unit = Self::extract_interval_unit_str(&step);
7208                        let col_name = outer_alias_info
7209                            .as_ref()
7210                            .and_then(|(_, cols)| cols.first().map(|id| id.name.clone()))
7211                            .unwrap_or_else(|| "value".to_string());
7212
7213                        if let Some(unit_str) = unit {
7214                            gda_info = Some((
7215                                idx,
7216                                col_name,
7217                                start_expr,
7218                                end_expr,
7219                                unit_str,
7220                                outer_alias_info,
7221                            ));
7222                            break;
7223                        }
7224                    }
7225                }
7226            }
7227        }
7228
7229        let Some((from_idx, col_name, start_expr, end_expr, unit_str, outer_alias_info)) = gda_info
7230        else {
7231            return Ok(Expression::Select(sel));
7232        };
7233
7234        // Build the Snowflake subquery:
7235        // (SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
7236        //  FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(seq, key, path, index, col_name, this))
7237
7238        // DATEDIFF(unit, start, end)
7239        let datediff = Expression::Function(Box::new(Function::new(
7240            "DATEDIFF".to_string(),
7241            vec![
7242                Expression::boxed_column(Column {
7243                    name: Identifier::new(&unit_str),
7244                    table: None,
7245                    join_mark: false,
7246                    trailing_comments: vec![],
7247                    span: None,
7248                    inferred_type: None,
7249                }),
7250                start_expr.clone(),
7251                end_expr.clone(),
7252            ],
7253        )));
7254        // DATEDIFF(...) + 1
7255        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
7256            left: datediff,
7257            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
7258            left_comments: vec![],
7259            operator_comments: vec![],
7260            trailing_comments: vec![],
7261            inferred_type: None,
7262        }));
7263
7264        let array_gen_range = Expression::Function(Box::new(Function::new(
7265            "ARRAY_GENERATE_RANGE".to_string(),
7266            vec![
7267                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
7268                datediff_plus_one,
7269            ],
7270        )));
7271
7272        // TABLE(FLATTEN(INPUT => ...))
7273        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
7274            name: Identifier::new("INPUT"),
7275            value: array_gen_range,
7276            separator: crate::expressions::NamedArgSeparator::DArrow,
7277        }));
7278        let flatten = Expression::Function(Box::new(Function::new(
7279            "FLATTEN".to_string(),
7280            vec![flatten_input],
7281        )));
7282
7283        // Determine alias name for the table: use outer alias or _t0
7284        let table_alias_name = outer_alias_info
7285            .as_ref()
7286            .map(|(name, _)| name.clone())
7287            .unwrap_or_else(|| "_t0".to_string());
7288
7289        // TABLE(FLATTEN(...)) AS _t0(seq, key, path, index, col_name, this)
7290        let table_func =
7291            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
7292        let flatten_aliased = Expression::Alias(Box::new(Alias {
7293            this: table_func,
7294            alias: Identifier::new(&table_alias_name),
7295            column_aliases: vec![
7296                Identifier::new("seq"),
7297                Identifier::new("key"),
7298                Identifier::new("path"),
7299                Identifier::new("index"),
7300                Identifier::new(&col_name),
7301                Identifier::new("this"),
7302            ],
7303            alias_explicit_as: false,
7304            alias_keyword: None,
7305            pre_alias_comments: vec![],
7306            trailing_comments: vec![],
7307            inferred_type: None,
7308        }));
7309
7310        // SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
7311        let dateadd_expr = Expression::Function(Box::new(Function::new(
7312            "DATEADD".to_string(),
7313            vec![
7314                Expression::boxed_column(Column {
7315                    name: Identifier::new(&unit_str),
7316                    table: None,
7317                    join_mark: false,
7318                    trailing_comments: vec![],
7319                    span: None,
7320                    inferred_type: None,
7321                }),
7322                Expression::Cast(Box::new(Cast {
7323                    this: Expression::boxed_column(Column {
7324                        name: Identifier::new(&col_name),
7325                        table: None,
7326                        join_mark: false,
7327                        trailing_comments: vec![],
7328                        span: None,
7329                        inferred_type: None,
7330                    }),
7331                    to: DataType::Int {
7332                        length: None,
7333                        integer_spelling: false,
7334                    },
7335                    trailing_comments: vec![],
7336                    double_colon_syntax: false,
7337                    format: None,
7338                    default: None,
7339                    inferred_type: None,
7340                })),
7341                // Use start_expr directly - it's already been normalized (DATE literal -> CAST)
7342                start_expr.clone(),
7343            ],
7344        )));
7345        let dateadd_aliased = Expression::Alias(Box::new(Alias {
7346            this: dateadd_expr,
7347            alias: Identifier::new(&col_name),
7348            column_aliases: vec![],
7349            alias_explicit_as: false,
7350            alias_keyword: None,
7351            pre_alias_comments: vec![],
7352            trailing_comments: vec![],
7353            inferred_type: None,
7354        }));
7355
7356        // Build inner SELECT
7357        let mut inner_select = Select::new();
7358        inner_select.expressions = vec![dateadd_aliased];
7359        inner_select.from = Some(From {
7360            expressions: vec![flatten_aliased],
7361        });
7362
7363        let inner_select_expr = Expression::Select(Box::new(inner_select));
7364        let subquery = Expression::Subquery(Box::new(Subquery {
7365            this: inner_select_expr,
7366            alias: None,
7367            column_aliases: vec![],
7368            alias_explicit_as: false,
7369            alias_keyword: None,
7370            order_by: None,
7371            limit: None,
7372            offset: None,
7373            distribute_by: None,
7374            sort_by: None,
7375            cluster_by: None,
7376            lateral: false,
7377            modifiers_inside: false,
7378            trailing_comments: vec![],
7379            inferred_type: None,
7380        }));
7381
7382        // If there was an outer alias (e.g., AS _q(date_week)), wrap with alias
7383        let replacement = if let Some((alias_name, col_aliases)) = outer_alias_info {
7384            Expression::Alias(Box::new(Alias {
7385                this: subquery,
7386                alias: Identifier::new(&alias_name),
7387                column_aliases: col_aliases,
7388                alias_explicit_as: false,
7389                alias_keyword: None,
7390                pre_alias_comments: vec![],
7391                trailing_comments: vec![],
7392                inferred_type: None,
7393            }))
7394        } else {
7395            subquery
7396        };
7397
7398        // Replace the FROM expression
7399        if let Some(ref mut from) = sel.from {
7400            from.expressions[from_idx] = replacement;
7401        }
7402
7403        Ok(Expression::Select(sel))
7404    }
7405
7406    /// Convert ARRAY_SIZE(GENERATE_DATE_ARRAY(start, end, step)) for Snowflake.
7407    /// Produces: ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM (SELECT DATEADD(unit, CAST(value AS INT), start) AS value
7408    ///   FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(...))))
7409    fn convert_array_size_gda_snowflake(f: &crate::expressions::Function) -> Result<Expression> {
7410        use crate::expressions::*;
7411
7412        let start_expr = f.args[0].clone();
7413        let end_expr = f.args[1].clone();
7414        let step = f.args.get(2).cloned();
7415        let unit_str = Self::extract_interval_unit_str(&step).unwrap_or_else(|| "DAY".to_string());
7416        let col_name = "value";
7417
7418        // Build the inner subquery: same as try_transform_from_gda_snowflake
7419        let datediff = Expression::Function(Box::new(Function::new(
7420            "DATEDIFF".to_string(),
7421            vec![
7422                Expression::boxed_column(Column {
7423                    name: Identifier::new(&unit_str),
7424                    table: None,
7425                    join_mark: false,
7426                    trailing_comments: vec![],
7427                    span: None,
7428                    inferred_type: None,
7429                }),
7430                start_expr.clone(),
7431                end_expr.clone(),
7432            ],
7433        )));
7434        // DATEDIFF(...) + 1
7435        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
7436            left: datediff,
7437            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
7438            left_comments: vec![],
7439            operator_comments: vec![],
7440            trailing_comments: vec![],
7441            inferred_type: None,
7442        }));
7443
7444        let array_gen_range = Expression::Function(Box::new(Function::new(
7445            "ARRAY_GENERATE_RANGE".to_string(),
7446            vec![
7447                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
7448                datediff_plus_one,
7449            ],
7450        )));
7451
7452        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
7453            name: Identifier::new("INPUT"),
7454            value: array_gen_range,
7455            separator: crate::expressions::NamedArgSeparator::DArrow,
7456        }));
7457        let flatten = Expression::Function(Box::new(Function::new(
7458            "FLATTEN".to_string(),
7459            vec![flatten_input],
7460        )));
7461
7462        let table_func =
7463            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
7464        let flatten_aliased = Expression::Alias(Box::new(Alias {
7465            this: table_func,
7466            alias: Identifier::new("_t0"),
7467            column_aliases: vec![
7468                Identifier::new("seq"),
7469                Identifier::new("key"),
7470                Identifier::new("path"),
7471                Identifier::new("index"),
7472                Identifier::new(col_name),
7473                Identifier::new("this"),
7474            ],
7475            alias_explicit_as: false,
7476            alias_keyword: None,
7477            pre_alias_comments: vec![],
7478            trailing_comments: vec![],
7479            inferred_type: None,
7480        }));
7481
7482        let dateadd_expr = Expression::Function(Box::new(Function::new(
7483            "DATEADD".to_string(),
7484            vec![
7485                Expression::boxed_column(Column {
7486                    name: Identifier::new(&unit_str),
7487                    table: None,
7488                    join_mark: false,
7489                    trailing_comments: vec![],
7490                    span: None,
7491                    inferred_type: None,
7492                }),
7493                Expression::Cast(Box::new(Cast {
7494                    this: Expression::boxed_column(Column {
7495                        name: Identifier::new(col_name),
7496                        table: None,
7497                        join_mark: false,
7498                        trailing_comments: vec![],
7499                        span: None,
7500                        inferred_type: None,
7501                    }),
7502                    to: DataType::Int {
7503                        length: None,
7504                        integer_spelling: false,
7505                    },
7506                    trailing_comments: vec![],
7507                    double_colon_syntax: false,
7508                    format: None,
7509                    default: None,
7510                    inferred_type: None,
7511                })),
7512                start_expr.clone(),
7513            ],
7514        )));
7515        let dateadd_aliased = Expression::Alias(Box::new(Alias {
7516            this: dateadd_expr,
7517            alias: Identifier::new(col_name),
7518            column_aliases: vec![],
7519            alias_explicit_as: false,
7520            alias_keyword: None,
7521            pre_alias_comments: vec![],
7522            trailing_comments: vec![],
7523            inferred_type: None,
7524        }));
7525
7526        // Inner SELECT: SELECT DATEADD(...) AS value FROM TABLE(FLATTEN(...)) AS _t0(...)
7527        let mut inner_select = Select::new();
7528        inner_select.expressions = vec![dateadd_aliased];
7529        inner_select.from = Some(From {
7530            expressions: vec![flatten_aliased],
7531        });
7532
7533        // Wrap in subquery for the inner part
7534        let inner_subquery = Expression::Subquery(Box::new(Subquery {
7535            this: Expression::Select(Box::new(inner_select)),
7536            alias: None,
7537            column_aliases: vec![],
7538            alias_explicit_as: false,
7539            alias_keyword: None,
7540            order_by: None,
7541            limit: None,
7542            offset: None,
7543            distribute_by: None,
7544            sort_by: None,
7545            cluster_by: None,
7546            lateral: false,
7547            modifiers_inside: false,
7548            trailing_comments: vec![],
7549            inferred_type: None,
7550        }));
7551
7552        // Outer: SELECT ARRAY_AGG(*) FROM (inner_subquery)
7553        let star = Expression::Star(Star {
7554            table: None,
7555            except: None,
7556            replace: None,
7557            rename: None,
7558            trailing_comments: vec![],
7559            span: None,
7560        });
7561        let array_agg = Expression::ArrayAgg(Box::new(AggFunc {
7562            this: star,
7563            distinct: false,
7564            filter: None,
7565            order_by: vec![],
7566            name: Some("ARRAY_AGG".to_string()),
7567            ignore_nulls: None,
7568            having_max: None,
7569            limit: None,
7570            inferred_type: None,
7571        }));
7572
7573        let mut outer_select = Select::new();
7574        outer_select.expressions = vec![array_agg];
7575        outer_select.from = Some(From {
7576            expressions: vec![inner_subquery],
7577        });
7578
7579        // Wrap in a subquery
7580        let outer_subquery = Expression::Subquery(Box::new(Subquery {
7581            this: Expression::Select(Box::new(outer_select)),
7582            alias: None,
7583            column_aliases: vec![],
7584            alias_explicit_as: false,
7585            alias_keyword: None,
7586            order_by: None,
7587            limit: None,
7588            offset: None,
7589            distribute_by: None,
7590            sort_by: None,
7591            cluster_by: None,
7592            lateral: false,
7593            modifiers_inside: false,
7594            trailing_comments: vec![],
7595            inferred_type: None,
7596        }));
7597
7598        // ARRAY_SIZE(subquery)
7599        Ok(Expression::ArraySize(Box::new(UnaryFunc::new(
7600            outer_subquery,
7601        ))))
7602    }
7603
7604    /// Extract interval unit string from an optional step expression.
7605    fn extract_interval_unit_str(step: &Option<Expression>) -> Option<String> {
7606        use crate::expressions::*;
7607        if let Some(Expression::Interval(ref iv)) = step {
7608            if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
7609                return Some(format!("{:?}", unit).to_ascii_uppercase());
7610            }
7611            if let Some(ref this) = iv.this {
7612                if let Expression::Literal(lit) = this {
7613                    if let Literal::String(ref s) = lit.as_ref() {
7614                        let parts: Vec<&str> = s.split_whitespace().collect();
7615                        if parts.len() == 2 {
7616                            return Some(parts[1].to_ascii_uppercase());
7617                        } else if parts.len() == 1 {
7618                            let upper = parts[0].to_ascii_uppercase();
7619                            if matches!(
7620                                upper.as_str(),
7621                                "YEAR"
7622                                    | "QUARTER"
7623                                    | "MONTH"
7624                                    | "WEEK"
7625                                    | "DAY"
7626                                    | "HOUR"
7627                                    | "MINUTE"
7628                                    | "SECOND"
7629                            ) {
7630                                return Some(upper);
7631                            }
7632                        }
7633                    }
7634                }
7635            }
7636        }
7637        // Default to DAY if no step or no interval
7638        if step.is_none() {
7639            return Some("DAY".to_string());
7640        }
7641        None
7642    }
7643
7644    fn normalize_snowflake_pretty(mut sql: String) -> String {
7645        if sql.contains("LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)")
7646            && sql.contains("ARRAY_GENERATE_RANGE(0, (GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1) + 1)")
7647        {
7648            sql = sql.replace(
7649                "AND uc.user_id <> ALL (SELECT DISTINCT\n      _id\n    FROM users, LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)\n    WHERE\n      GET_PATH(datasource.value, 'name') = 'something')",
7650                "AND uc.user_id <> ALL (\n      SELECT DISTINCT\n        _id\n      FROM users, LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)\n      WHERE\n        GET_PATH(datasource.value, 'name') = 'something'\n    )",
7651            );
7652
7653            sql = sql.replace(
7654                "CROSS JOIN TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, (GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1) + 1))) AS _u(seq, key, path, index, pos, this)",
7655                "CROSS JOIN TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, (\n  GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1\n) + 1))) AS _u(seq, key, path, index, pos, this)",
7656            );
7657
7658            sql = sql.replace(
7659                "OR (_u.pos > (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1)\n  AND _u_2.pos_2 = (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1))",
7660                "OR (\n    _u.pos > (\n      ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1\n    )\n    AND _u_2.pos_2 = (\n      ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1\n    )\n  )",
7661            );
7662        }
7663
7664        sql
7665    }
7666
7667    #[cfg(feature = "transpile")]
7668    fn wrap_tsql_top_level_values(expr: Expression) -> Expression {
7669        match expr {
7670            Expression::Values(values) => Self::tsql_values_as_select(*values),
7671            Expression::Union(mut union) => {
7672                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
7673                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
7674                union.left = Self::wrap_tsql_values_set_operand(left);
7675                union.right = Self::wrap_tsql_values_set_operand(right);
7676                Expression::Union(union)
7677            }
7678            Expression::Intersect(mut intersect) => {
7679                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
7680                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
7681                intersect.left = Self::wrap_tsql_values_set_operand(left);
7682                intersect.right = Self::wrap_tsql_values_set_operand(right);
7683                Expression::Intersect(intersect)
7684            }
7685            Expression::Except(mut except) => {
7686                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
7687                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
7688                except.left = Self::wrap_tsql_values_set_operand(left);
7689                except.right = Self::wrap_tsql_values_set_operand(right);
7690                Expression::Except(except)
7691            }
7692            other => other,
7693        }
7694    }
7695
7696    #[cfg(feature = "transpile")]
7697    fn wrap_tsql_values_set_operand(expr: Expression) -> Expression {
7698        match expr {
7699            Expression::Values(values) => Self::tsql_values_as_select(*values),
7700            Expression::Union(mut union) => {
7701                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
7702                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
7703                union.left = Self::wrap_tsql_values_set_operand(left);
7704                union.right = Self::wrap_tsql_values_set_operand(right);
7705                Expression::Union(union)
7706            }
7707            Expression::Intersect(mut intersect) => {
7708                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
7709                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
7710                intersect.left = Self::wrap_tsql_values_set_operand(left);
7711                intersect.right = Self::wrap_tsql_values_set_operand(right);
7712                Expression::Intersect(intersect)
7713            }
7714            Expression::Except(mut except) => {
7715                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
7716                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
7717                except.left = Self::wrap_tsql_values_set_operand(left);
7718                except.right = Self::wrap_tsql_values_set_operand(right);
7719                Expression::Except(except)
7720            }
7721            other => other,
7722        }
7723    }
7724
7725    #[cfg(feature = "transpile")]
7726    fn tsql_values_as_select(mut values: crate::expressions::Values) -> Expression {
7727        let column_aliases = if values.column_aliases.is_empty() {
7728            let column_count = values
7729                .expressions
7730                .first()
7731                .map(|row| row.expressions.len())
7732                .unwrap_or(0);
7733            (1..=column_count)
7734                .map(|index| Identifier::new(format!("column{index}")))
7735                .collect()
7736        } else {
7737            std::mem::take(&mut values.column_aliases)
7738        };
7739
7740        values.alias = None;
7741
7742        let values_subquery = Expression::Subquery(Box::new(crate::expressions::Subquery {
7743            this: Expression::Values(Box::new(values)),
7744            alias: Some(Identifier::new("_v")),
7745            column_aliases,
7746            alias_explicit_as: false,
7747            alias_keyword: None,
7748            order_by: None,
7749            limit: None,
7750            offset: None,
7751            distribute_by: None,
7752            sort_by: None,
7753            cluster_by: None,
7754            lateral: false,
7755            modifiers_inside: false,
7756            trailing_comments: Vec::new(),
7757            inferred_type: None,
7758        }));
7759
7760        let mut select = crate::expressions::Select::new();
7761        select.expressions = vec![Expression::star()];
7762        select.from = Some(From {
7763            expressions: vec![values_subquery],
7764        });
7765
7766        Expression::Select(Box::new(select))
7767    }
7768
7769    fn extract_interval_parts(
7770        interval_expr: &Expression,
7771    ) -> Option<(Expression, crate::expressions::IntervalUnit)> {
7772        use crate::expressions::{DataType, IntervalUnit, IntervalUnitSpec, Literal};
7773
7774        fn unit_from_str(unit: &str) -> Option<IntervalUnit> {
7775            match unit.trim().to_ascii_uppercase().as_str() {
7776                "YEAR" | "YEARS" | "Y" | "YR" | "YRS" | "YY" | "YYYY" => Some(IntervalUnit::Year),
7777                "QUARTER" | "QUARTERS" | "Q" | "QTR" | "QTRS" | "QQ" => Some(IntervalUnit::Quarter),
7778                "MONTH" | "MONTHS" | "MON" | "MONS" | "MM" => Some(IntervalUnit::Month),
7779                "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" | "ISOWEEK" => {
7780                    Some(IntervalUnit::Week)
7781                }
7782                "DAY" | "DAYS" | "D" | "DD" => Some(IntervalUnit::Day),
7783                "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some(IntervalUnit::Hour),
7784                "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some(IntervalUnit::Minute),
7785                "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some(IntervalUnit::Second),
7786                "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MSECOND"
7787                | "MSECONDS" | "MILLISEC" | "MILLISECS" | "MILLISECON" => {
7788                    Some(IntervalUnit::Millisecond)
7789                }
7790                "MICROSECOND" | "MICROSECONDS" | "US" | "USEC" | "USECS" | "USECOND"
7791                | "USECONDS" | "MICROSEC" | "MICROSECS" | "MCS" => Some(IntervalUnit::Microsecond),
7792                "NANOSECOND" | "NANOSECONDS" | "NS" | "NSEC" | "NSECS" | "NSECOND" | "NSECONDS"
7793                | "NANOSEC" | "NANOSECS" => Some(IntervalUnit::Nanosecond),
7794                _ => None,
7795            }
7796        }
7797
7798        fn parts_from_literal_string(s: &str) -> Option<(Expression, IntervalUnit)> {
7799            let mut parts = s.split_whitespace();
7800            let value = parts.next()?;
7801            let unit = unit_from_str(parts.next()?)?;
7802            Some((
7803                Expression::Literal(Box::new(Literal::String(value.to_string()))),
7804                unit,
7805            ))
7806        }
7807
7808        fn unit_from_spec(unit: &IntervalUnitSpec) -> Option<IntervalUnit> {
7809            match unit {
7810                IntervalUnitSpec::Simple { unit, .. } => Some(*unit),
7811                IntervalUnitSpec::Expr(expr) => match expr.as_ref() {
7812                    Expression::Day(_) => Some(IntervalUnit::Day),
7813                    Expression::Month(_) => Some(IntervalUnit::Month),
7814                    Expression::Year(_) => Some(IntervalUnit::Year),
7815                    Expression::Identifier(id) => unit_from_str(&id.name),
7816                    Expression::Var(v) => unit_from_str(&v.this),
7817                    Expression::Column(col) => unit_from_str(&col.name.name),
7818                    _ => None,
7819                },
7820                _ => None,
7821            }
7822        }
7823
7824        match interval_expr {
7825            Expression::Interval(iv) => {
7826                let val = iv.this.clone().unwrap_or(Expression::number(0));
7827                if let Expression::Literal(lit) = &val {
7828                    if let Literal::String(s) = lit.as_ref() {
7829                        if let Some(parts) = parts_from_literal_string(s) {
7830                            return Some(parts);
7831                        }
7832                    }
7833                }
7834                let unit = iv
7835                    .unit
7836                    .as_ref()
7837                    .and_then(unit_from_spec)
7838                    .unwrap_or(IntervalUnit::Day);
7839                Some((val, unit))
7840            }
7841            Expression::Cast(cast) if matches!(cast.to, DataType::Interval { .. }) => {
7842                if let Expression::Literal(lit) = &cast.this {
7843                    if let Literal::String(s) = lit.as_ref() {
7844                        if let Some(parts) = parts_from_literal_string(s) {
7845                            return Some(parts);
7846                        }
7847                    }
7848                }
7849                let unit = match &cast.to {
7850                    DataType::Interval {
7851                        unit: Some(unit), ..
7852                    } => unit_from_str(unit).unwrap_or(IntervalUnit::Day),
7853                    _ => IntervalUnit::Day,
7854                };
7855                Some((cast.this.clone(), unit))
7856            }
7857            _ => None,
7858        }
7859    }
7860
7861    fn data_type_is_interval(dt: &DataType) -> bool {
7862        match dt {
7863            DataType::Interval { .. } => true,
7864            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("INTERVAL"),
7865            _ => false,
7866        }
7867    }
7868
7869    fn node_is_interval_cast(node: &Expression) -> bool {
7870        match node {
7871            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
7872                Self::data_type_is_interval(&c.to)
7873            }
7874            _ => false,
7875        }
7876    }
7877
7878    fn reject_tsql_interval_casts(
7879        expr: &Expression,
7880        target: DialectType,
7881        opts: &TranspileOptions,
7882    ) -> Result<()> {
7883        if !matches!(
7884            opts.unsupported_level,
7885            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
7886        ) {
7887            return Ok(());
7888        }
7889
7890        if expr.dfs().any(Self::node_is_interval_cast) {
7891            return Err(crate::error::Error::unsupported(
7892                "INTERVAL casts",
7893                target.to_string(),
7894            ));
7895        }
7896
7897        Ok(())
7898    }
7899
7900    fn tsql_varchar_max_type() -> DataType {
7901        DataType::Custom {
7902            name: "VARCHAR(MAX)".to_string(),
7903        }
7904    }
7905
7906    fn rewrite_tsql_interval_casts_to_varchar(expr: Expression) -> Result<Expression> {
7907        transform_recursive(expr, &|e| match e {
7908            Expression::Cast(mut cast) if Self::data_type_is_interval(&cast.to) => {
7909                cast.to = Self::tsql_varchar_max_type();
7910                cast.double_colon_syntax = false;
7911                Ok(Expression::Cast(cast))
7912            }
7913            Expression::TryCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
7914                cast.to = Self::tsql_varchar_max_type();
7915                cast.double_colon_syntax = false;
7916                Ok(Expression::TryCast(cast))
7917            }
7918            Expression::SafeCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
7919                cast.to = Self::tsql_varchar_max_type();
7920                cast.double_colon_syntax = false;
7921                Ok(Expression::SafeCast(cast))
7922            }
7923            _ => Ok(e),
7924        })
7925    }
7926
7927    fn rewrite_tsql_interval_arithmetic(
7928        expr: &Expression,
7929        source: DialectType,
7930    ) -> Option<Expression> {
7931        match expr {
7932            Expression::Add(op) => {
7933                if Self::extract_interval_parts(&op.right).is_some() {
7934                    return Some(Self::build_tsql_dateadd_from_interval(
7935                        op.left.clone(),
7936                        &op.right,
7937                        false,
7938                    ));
7939                }
7940
7941                if Self::is_postgres_family_source(source) {
7942                    if Self::is_explicit_date_expr(&op.left)
7943                        && Self::is_integer_day_offset_expr(&op.right)
7944                    {
7945                        return Some(Self::build_tsql_dateadd_days(
7946                            op.left.clone(),
7947                            op.right.clone(),
7948                            false,
7949                        ));
7950                    }
7951
7952                    if Self::is_integer_day_offset_expr(&op.left)
7953                        && Self::is_explicit_date_expr(&op.right)
7954                    {
7955                        return Some(Self::build_tsql_dateadd_days(
7956                            op.right.clone(),
7957                            op.left.clone(),
7958                            false,
7959                        ));
7960                    }
7961                }
7962
7963                None
7964            }
7965            Expression::Sub(op) => {
7966                if Self::extract_interval_parts(&op.right).is_some() {
7967                    return Some(Self::build_tsql_dateadd_from_interval(
7968                        op.left.clone(),
7969                        &op.right,
7970                        true,
7971                    ));
7972                }
7973
7974                if Self::is_postgres_family_source(source) {
7975                    if Self::is_explicit_date_expr(&op.left)
7976                        && Self::is_explicit_date_expr(&op.right)
7977                    {
7978                        return Some(Self::build_tsql_datediff_days(
7979                            op.right.clone(),
7980                            op.left.clone(),
7981                        ));
7982                    }
7983
7984                    if Self::is_explicit_date_expr(&op.left)
7985                        && Self::is_integer_day_offset_expr(&op.right)
7986                    {
7987                        return Some(Self::build_tsql_dateadd_days(
7988                            op.left.clone(),
7989                            op.right.clone(),
7990                            true,
7991                        ));
7992                    }
7993                }
7994
7995                None
7996            }
7997            _ => None,
7998        }
7999    }
8000
8001    fn is_postgres_family_source(source: DialectType) -> bool {
8002        matches!(
8003            source,
8004            DialectType::PostgreSQL
8005                | DialectType::Redshift
8006                | DialectType::Materialize
8007                | DialectType::RisingWave
8008                | DialectType::CockroachDB
8009        )
8010    }
8011
8012    fn is_explicit_date_expr(expr: &Expression) -> bool {
8013        use crate::expressions::Literal;
8014
8015        match expr {
8016            Expression::Literal(lit) => matches!(lit.as_ref(), Literal::Date(_)),
8017            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
8018                matches!(c.to, crate::expressions::DataType::Date)
8019            }
8020            Expression::Paren(p) => Self::is_explicit_date_expr(&p.this),
8021            Expression::CurrentDate(_)
8022            | Expression::Date(_)
8023            | Expression::MakeDate(_)
8024            | Expression::ToDate(_)
8025            | Expression::DateStrToDate(_) => true,
8026            _ => false,
8027        }
8028    }
8029
8030    fn is_integer_day_offset_expr(expr: &Expression) -> bool {
8031        use crate::expressions::Literal;
8032
8033        match expr {
8034            Expression::Literal(lit) => match lit.as_ref() {
8035                Literal::Number(n) => n.parse::<i64>().is_ok(),
8036                _ => false,
8037            },
8038            Expression::Parameter(_) | Expression::Placeholder(_) => true,
8039            Expression::Neg(op) => Self::is_integer_day_offset_expr(&op.this),
8040            Expression::Paren(p) => Self::is_integer_day_offset_expr(&p.this),
8041            _ => false,
8042        }
8043    }
8044
8045    fn build_tsql_datediff_days(start: Expression, end: Expression) -> Expression {
8046        Expression::Function(Box::new(Function::new(
8047            "DATEDIFF".to_string(),
8048            vec![Expression::Identifier(Identifier::new("DAY")), start, end],
8049        )))
8050    }
8051
8052    fn build_tsql_dateadd_days(date: Expression, amount: Expression, subtract: bool) -> Expression {
8053        Expression::Function(Box::new(Function::new(
8054            "DATEADD".to_string(),
8055            vec![
8056                Expression::Identifier(Identifier::new("DAY")),
8057                Self::tsql_dateadd_amount(amount, subtract),
8058                date,
8059            ],
8060        )))
8061    }
8062
8063    fn build_tsql_dateadd_from_interval(
8064        date: Expression,
8065        interval: &Expression,
8066        subtract: bool,
8067    ) -> Expression {
8068        let (value, unit) = Self::extract_interval_parts(interval)
8069            .unwrap_or_else(|| (interval.clone(), crate::expressions::IntervalUnit::Day));
8070        let unit = normalization::temporal::interval_unit_to_string(&unit);
8071        let amount = Self::tsql_dateadd_amount(value, subtract);
8072
8073        Expression::Function(Box::new(Function::new(
8074            "DATEADD".to_string(),
8075            vec![Expression::Identifier(Identifier::new(unit)), amount, date],
8076        )))
8077    }
8078
8079    fn tsql_dateadd_amount(value: Expression, negate: bool) -> Expression {
8080        use crate::expressions::{Parameter, ParameterStyle, UnaryOp};
8081
8082        fn numeric_literal_value(value: &Expression) -> Option<&str> {
8083            match value {
8084                Expression::Literal(lit) => match lit.as_ref() {
8085                    crate::expressions::Literal::Number(n)
8086                    | crate::expressions::Literal::String(n) => Some(n.as_str()),
8087                    _ => None,
8088                },
8089                _ => None,
8090            }
8091        }
8092
8093        fn colon_parameter(value: &Expression) -> Option<Expression> {
8094            let Expression::Literal(lit) = value else {
8095                return None;
8096            };
8097            let crate::expressions::Literal::String(s) = lit.as_ref() else {
8098                return None;
8099            };
8100            let name = s.strip_prefix(':')?;
8101            if name.is_empty()
8102                || !name
8103                    .chars()
8104                    .all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
8105            {
8106                return None;
8107            }
8108
8109            Some(Expression::Parameter(Box::new(Parameter {
8110                name: if name.chars().all(|ch| ch.is_ascii_digit()) {
8111                    None
8112                } else {
8113                    Some(name.to_string())
8114                },
8115                index: name.parse::<u32>().ok(),
8116                style: ParameterStyle::Colon,
8117                quoted: false,
8118                string_quoted: false,
8119                expression: None,
8120            })))
8121        }
8122
8123        let value = colon_parameter(&value).unwrap_or(value);
8124
8125        if let Some(n) = numeric_literal_value(&value) {
8126            if let Ok(parsed) = n.parse::<f64>() {
8127                let normalized = if negate { -parsed } else { parsed };
8128                let rendered = if normalized.fract() == 0.0 {
8129                    format!("{}", normalized as i64)
8130                } else {
8131                    normalized.to_string()
8132                };
8133                return Expression::Literal(Box::new(crate::expressions::Literal::Number(
8134                    rendered,
8135                )));
8136            }
8137        }
8138
8139        if !negate {
8140            return value;
8141        }
8142
8143        match value {
8144            Expression::Neg(op) => op.this,
8145            other => Expression::Neg(Box::new(UnaryOp {
8146                this: other,
8147                inferred_type: None,
8148            })),
8149        }
8150    }
8151
8152    /// Internal TO_DATE function that won't be converted to CAST by the Snowflake handler.
8153    /// Uses the name `_POLYGLOT_TO_DATE` which is not recognized by the TO_DATE -> CAST logic.
8154    /// The Snowflake DATEDIFF handler converts these back to TO_DATE.
8155    const PRESERVED_TO_DATE: &'static str = "_POLYGLOT_TO_DATE";
8156}
8157
8158#[cfg(test)]
8159mod tests {
8160    use super::*;
8161
8162    #[test]
8163    fn built_in_dialect_instances_share_tokenizer_config() {
8164        let first = Dialect::get(DialectType::PostgreSQL);
8165        let second = Dialect::get(DialectType::PostgreSQL);
8166
8167        assert!(first.tokenizer.shares_config_with(&second.tokenizer));
8168    }
8169
8170    #[test]
8171    fn test_dialect_type_from_str() {
8172        assert_eq!(
8173            "postgres".parse::<DialectType>().unwrap(),
8174            DialectType::PostgreSQL
8175        );
8176        assert_eq!(
8177            "postgresql".parse::<DialectType>().unwrap(),
8178            DialectType::PostgreSQL
8179        );
8180        assert_eq!("mysql".parse::<DialectType>().unwrap(), DialectType::MySQL);
8181        assert_eq!(
8182            "bigquery".parse::<DialectType>().unwrap(),
8183            DialectType::BigQuery
8184        );
8185    }
8186
8187    #[test]
8188    fn test_basic_transpile() {
8189        let dialect = Dialect::get(DialectType::Generic);
8190        let result = dialect
8191            .transpile("SELECT 1", DialectType::PostgreSQL)
8192            .unwrap();
8193        assert_eq!(result.len(), 1);
8194        assert_eq!(result[0], "SELECT 1");
8195    }
8196
8197    #[test]
8198    fn test_sqlite_double_quoted_column_defaults_to_postgres_strings() {
8199        let sqlite = Dialect::get(DialectType::SQLite);
8200        let result = sqlite
8201            .transpile(
8202                r#"CREATE TABLE "_collections" (
8203                    "type" TEXT DEFAULT "base" NOT NULL,
8204                    "fields" JSON DEFAULT "[]" NOT NULL,
8205                    "options" JSON DEFAULT "{}" NOT NULL
8206                )"#,
8207                DialectType::PostgreSQL,
8208            )
8209            .unwrap();
8210
8211        assert!(result[0].contains(r#""type" TEXT DEFAULT 'base' NOT NULL"#));
8212        assert!(result[0].contains(r#""fields" JSON DEFAULT '[]' NOT NULL"#));
8213        assert!(result[0].contains(r#""options" JSON DEFAULT '{}' NOT NULL"#));
8214    }
8215
8216    #[test]
8217    fn test_sqlite_identity_preserves_double_quoted_column_defaults() {
8218        let sqlite = Dialect::get(DialectType::SQLite);
8219        let result = sqlite
8220            .transpile(
8221                r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#,
8222                DialectType::SQLite,
8223            )
8224            .unwrap();
8225
8226        assert_eq!(
8227            result[0],
8228            r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#
8229        );
8230    }
8231
8232    #[test]
8233    fn test_function_transformation_mysql() {
8234        // NVL should be transformed to IFNULL in MySQL
8235        let dialect = Dialect::get(DialectType::Generic);
8236        let result = dialect
8237            .transpile("SELECT NVL(a, b)", DialectType::MySQL)
8238            .unwrap();
8239        assert_eq!(result[0], "SELECT IFNULL(a, b)");
8240    }
8241
8242    #[test]
8243    fn test_get_path_duckdb() {
8244        // Test: step by step
8245        let snowflake = Dialect::get(DialectType::Snowflake);
8246
8247        // Step 1: Parse and check what Snowflake produces as intermediate
8248        let result_sf_sf = snowflake
8249            .transpile(
8250                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
8251                DialectType::Snowflake,
8252            )
8253            .unwrap();
8254        eprintln!("Snowflake->Snowflake colon: {}", result_sf_sf[0]);
8255
8256        // Step 2: DuckDB target
8257        let result_sf_dk = snowflake
8258            .transpile(
8259                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
8260                DialectType::DuckDB,
8261            )
8262            .unwrap();
8263        eprintln!("Snowflake->DuckDB colon: {}", result_sf_dk[0]);
8264
8265        // Step 3: GET_PATH directly
8266        let result_gp = snowflake
8267            .transpile(
8268                "SELECT GET_PATH(PARSE_JSON('{\"fruit\":\"banana\"}'), 'fruit')",
8269                DialectType::DuckDB,
8270            )
8271            .unwrap();
8272        eprintln!("Snowflake->DuckDB explicit GET_PATH: {}", result_gp[0]);
8273    }
8274
8275    #[test]
8276    fn test_function_transformation_postgres() {
8277        // IFNULL should be transformed to COALESCE in PostgreSQL
8278        let dialect = Dialect::get(DialectType::Generic);
8279        let result = dialect
8280            .transpile("SELECT IFNULL(a, b)", DialectType::PostgreSQL)
8281            .unwrap();
8282        assert_eq!(result[0], "SELECT COALESCE(a, b)");
8283
8284        // NVL should also be transformed to COALESCE
8285        let result = dialect
8286            .transpile("SELECT NVL(a, b)", DialectType::PostgreSQL)
8287            .unwrap();
8288        assert_eq!(result[0], "SELECT COALESCE(a, b)");
8289    }
8290
8291    #[test]
8292    fn test_hive_cast_to_trycast() {
8293        // Hive CAST should become TRY_CAST for targets that support it
8294        let hive = Dialect::get(DialectType::Hive);
8295        let result = hive
8296            .transpile("CAST(1 AS INT)", DialectType::DuckDB)
8297            .unwrap();
8298        assert_eq!(result[0], "TRY_CAST(1 AS INT)");
8299
8300        let result = hive
8301            .transpile("CAST(1 AS INT)", DialectType::Presto)
8302            .unwrap();
8303        assert_eq!(result[0], "TRY_CAST(1 AS INTEGER)");
8304    }
8305
8306    #[test]
8307    fn test_hive_array_identity() {
8308        // Hive ARRAY<DATE> should preserve angle bracket syntax
8309        let sql = "CREATE EXTERNAL TABLE `my_table` (`a7` ARRAY<DATE>) ROW FORMAT SERDE 'a' STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c' LOCATION 'd' TBLPROPERTIES ('e'='f')";
8310        let hive = Dialect::get(DialectType::Hive);
8311
8312        // Test via transpile (this works)
8313        let result = hive.transpile(sql, DialectType::Hive).unwrap();
8314        eprintln!("Hive ARRAY via transpile: {}", result[0]);
8315        assert!(
8316            result[0].contains("ARRAY<DATE>"),
8317            "transpile: Expected ARRAY<DATE>, got: {}",
8318            result[0]
8319        );
8320
8321        // Test via parse -> transform -> generate (identity test path)
8322        let ast = hive.parse(sql).unwrap();
8323        let transformed = hive.transform(ast[0].clone()).unwrap();
8324        let output = hive.generate(&transformed).unwrap();
8325        eprintln!("Hive ARRAY via identity path: {}", output);
8326        assert!(
8327            output.contains("ARRAY<DATE>"),
8328            "identity path: Expected ARRAY<DATE>, got: {}",
8329            output
8330        );
8331    }
8332
8333    #[test]
8334    fn test_starrocks_delete_between_expansion() {
8335        // StarRocks doesn't support BETWEEN in DELETE statements
8336        let dialect = Dialect::get(DialectType::Generic);
8337
8338        // BETWEEN should be expanded to >= AND <= in DELETE
8339        let result = dialect
8340            .transpile(
8341                "DELETE FROM t WHERE a BETWEEN b AND c",
8342                DialectType::StarRocks,
8343            )
8344            .unwrap();
8345        assert_eq!(result[0], "DELETE FROM t WHERE a >= b AND a <= c");
8346
8347        // NOT BETWEEN should be expanded to < OR > in DELETE
8348        let result = dialect
8349            .transpile(
8350                "DELETE FROM t WHERE a NOT BETWEEN b AND c",
8351                DialectType::StarRocks,
8352            )
8353            .unwrap();
8354        assert_eq!(result[0], "DELETE FROM t WHERE a < b OR a > c");
8355
8356        // BETWEEN in SELECT should NOT be expanded (StarRocks supports it there)
8357        let result = dialect
8358            .transpile(
8359                "SELECT * FROM t WHERE a BETWEEN b AND c",
8360                DialectType::StarRocks,
8361            )
8362            .unwrap();
8363        assert!(
8364            result[0].contains("BETWEEN"),
8365            "BETWEEN should be preserved in SELECT"
8366        );
8367    }
8368
8369    #[test]
8370    fn test_snowflake_ltrim_rtrim_parse() {
8371        let sf = Dialect::get(DialectType::Snowflake);
8372        let sql = "SELECT LTRIM(RTRIM(col)) FROM t1";
8373        let result = sf.transpile(sql, DialectType::DuckDB);
8374        match &result {
8375            Ok(r) => eprintln!("LTRIM/RTRIM result: {}", r[0]),
8376            Err(e) => eprintln!("LTRIM/RTRIM error: {}", e),
8377        }
8378        assert!(
8379            result.is_ok(),
8380            "Expected successful parse of LTRIM(RTRIM(col)), got error: {:?}",
8381            result.err()
8382        );
8383    }
8384
8385    #[test]
8386    fn test_duckdb_count_if_parse() {
8387        let duck = Dialect::get(DialectType::DuckDB);
8388        let sql = "COUNT_IF(x)";
8389        let result = duck.transpile(sql, DialectType::DuckDB);
8390        match &result {
8391            Ok(r) => eprintln!("COUNT_IF result: {}", r[0]),
8392            Err(e) => eprintln!("COUNT_IF error: {}", e),
8393        }
8394        assert!(
8395            result.is_ok(),
8396            "Expected successful parse of COUNT_IF(x), got error: {:?}",
8397            result.err()
8398        );
8399    }
8400
8401    #[test]
8402    fn test_tsql_cast_tinyint_parse() {
8403        let tsql = Dialect::get(DialectType::TSQL);
8404        let sql = "CAST(X AS TINYINT)";
8405        let result = tsql.transpile(sql, DialectType::DuckDB);
8406        match &result {
8407            Ok(r) => eprintln!("TSQL CAST TINYINT result: {}", r[0]),
8408            Err(e) => eprintln!("TSQL CAST TINYINT error: {}", e),
8409        }
8410        assert!(
8411            result.is_ok(),
8412            "Expected successful transpile, got error: {:?}",
8413            result.err()
8414        );
8415    }
8416
8417    #[test]
8418    fn test_pg_hash_bitwise_xor() {
8419        let dialect = Dialect::get(DialectType::PostgreSQL);
8420        let result = dialect.transpile("x # y", DialectType::PostgreSQL).unwrap();
8421        assert_eq!(result[0], "x # y");
8422    }
8423
8424    #[test]
8425    fn test_pg_array_to_duckdb() {
8426        let dialect = Dialect::get(DialectType::PostgreSQL);
8427        let result = dialect
8428            .transpile("SELECT ARRAY[1, 2, 3] @> ARRAY[1, 2]", DialectType::DuckDB)
8429            .unwrap();
8430        assert_eq!(result[0], "SELECT [1, 2, 3] @> [1, 2]");
8431    }
8432
8433    #[test]
8434    fn test_array_remove_bigquery() {
8435        let dialect = Dialect::get(DialectType::Generic);
8436        let result = dialect
8437            .transpile("ARRAY_REMOVE(the_array, target)", DialectType::BigQuery)
8438            .unwrap();
8439        assert_eq!(
8440            result[0],
8441            "ARRAY(SELECT _u FROM UNNEST(the_array) AS _u WHERE _u <> target)"
8442        );
8443    }
8444
8445    #[test]
8446    fn test_map_clickhouse_case() {
8447        let dialect = Dialect::get(DialectType::Generic);
8448        let parsed = dialect
8449            .parse("CAST(MAP('a', '1') AS MAP(TEXT, TEXT))")
8450            .unwrap();
8451        eprintln!("MAP parsed: {:?}", parsed);
8452        let result = dialect
8453            .transpile(
8454                "CAST(MAP('a', '1') AS MAP(TEXT, TEXT))",
8455                DialectType::ClickHouse,
8456            )
8457            .unwrap();
8458        eprintln!("MAP result: {}", result[0]);
8459    }
8460
8461    #[test]
8462    fn test_generate_date_array_presto() {
8463        let dialect = Dialect::get(DialectType::Generic);
8464        let result = dialect.transpile(
8465            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8466            DialectType::Presto,
8467        ).unwrap();
8468        eprintln!("GDA -> Presto: {}", result[0]);
8469        assert_eq!(result[0], "SELECT * FROM UNNEST(SEQUENCE(CAST('2020-01-01' AS DATE), CAST('2020-02-01' AS DATE), (1 * INTERVAL '7' DAY)))");
8470    }
8471
8472    #[test]
8473    fn test_generate_date_array_postgres() {
8474        let dialect = Dialect::get(DialectType::Generic);
8475        let result = dialect.transpile(
8476            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8477            DialectType::PostgreSQL,
8478        ).unwrap();
8479        eprintln!("GDA -> PostgreSQL: {}", result[0]);
8480    }
8481
8482    #[test]
8483    fn test_generate_date_array_snowflake() {
8484        let dialect = Dialect::get(DialectType::Generic);
8485        let result = dialect
8486            .transpile(
8487                "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8488                DialectType::Snowflake,
8489            )
8490            .unwrap();
8491        eprintln!("GDA -> Snowflake: {}", result[0]);
8492    }
8493
8494    #[test]
8495    fn test_array_length_generate_date_array_snowflake() {
8496        let dialect = Dialect::get(DialectType::Generic);
8497        let result = dialect.transpile(
8498            "SELECT ARRAY_LENGTH(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8499            DialectType::Snowflake,
8500        ).unwrap();
8501        eprintln!("ARRAY_LENGTH(GDA) -> Snowflake: {}", result[0]);
8502    }
8503
8504    #[test]
8505    fn test_generate_date_array_mysql() {
8506        let dialect = Dialect::get(DialectType::Generic);
8507        let result = dialect.transpile(
8508            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8509            DialectType::MySQL,
8510        ).unwrap();
8511        eprintln!("GDA -> MySQL: {}", result[0]);
8512    }
8513
8514    #[test]
8515    fn test_generate_date_array_redshift() {
8516        let dialect = Dialect::get(DialectType::Generic);
8517        let result = dialect.transpile(
8518            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8519            DialectType::Redshift,
8520        ).unwrap();
8521        eprintln!("GDA -> Redshift: {}", result[0]);
8522    }
8523
8524    #[test]
8525    fn test_generate_date_array_tsql() {
8526        let dialect = Dialect::get(DialectType::Generic);
8527        let result = dialect.transpile(
8528            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8529            DialectType::TSQL,
8530        ).unwrap();
8531        eprintln!("GDA -> TSQL: {}", result[0]);
8532    }
8533
8534    #[test]
8535    fn test_struct_colon_syntax() {
8536        let dialect = Dialect::get(DialectType::Generic);
8537        // Test without colon first
8538        let result = dialect.transpile(
8539            "CAST((1, 2, 3, 4) AS STRUCT<a TINYINT, b SMALLINT, c INT, d BIGINT>)",
8540            DialectType::ClickHouse,
8541        );
8542        match result {
8543            Ok(r) => eprintln!("STRUCT no colon -> ClickHouse: {}", r[0]),
8544            Err(e) => eprintln!("STRUCT no colon error: {}", e),
8545        }
8546        // Now test with colon
8547        let result = dialect.transpile(
8548            "CAST((1, 2, 3, 4) AS STRUCT<a: TINYINT, b: SMALLINT, c: INT, d: BIGINT>)",
8549            DialectType::ClickHouse,
8550        );
8551        match result {
8552            Ok(r) => eprintln!("STRUCT colon -> ClickHouse: {}", r[0]),
8553            Err(e) => eprintln!("STRUCT colon error: {}", e),
8554        }
8555    }
8556
8557    #[test]
8558    fn test_generate_date_array_cte_wrapped_mysql() {
8559        let dialect = Dialect::get(DialectType::Generic);
8560        let result = dialect.transpile(
8561            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
8562            DialectType::MySQL,
8563        ).unwrap();
8564        eprintln!("GDA CTE -> MySQL: {}", result[0]);
8565    }
8566
8567    #[test]
8568    fn test_generate_date_array_cte_wrapped_tsql() {
8569        let dialect = Dialect::get(DialectType::Generic);
8570        let result = dialect.transpile(
8571            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
8572            DialectType::TSQL,
8573        ).unwrap();
8574        eprintln!("GDA CTE -> TSQL: {}", result[0]);
8575    }
8576
8577    #[test]
8578    fn test_decode_literal_no_null_check() {
8579        // Oracle DECODE with all literals should produce simple equality, no IS NULL
8580        let dialect = Dialect::get(DialectType::Oracle);
8581        let result = dialect
8582            .transpile("SELECT decode(1,2,3,4)", DialectType::DuckDB)
8583            .unwrap();
8584        assert_eq!(
8585            result[0], "SELECT CASE WHEN 1 = 2 THEN 3 ELSE 4 END",
8586            "Literal DECODE should not have IS NULL checks"
8587        );
8588    }
8589
8590    #[test]
8591    fn test_decode_column_vs_literal_no_null_check() {
8592        // Oracle DECODE with column vs literal should use simple equality (like sqlglot)
8593        let dialect = Dialect::get(DialectType::Oracle);
8594        let result = dialect
8595            .transpile("SELECT decode(col, 2, 3, 4) FROM t", DialectType::DuckDB)
8596            .unwrap();
8597        assert_eq!(
8598            result[0], "SELECT CASE WHEN col = 2 THEN 3 ELSE 4 END FROM t",
8599            "Column vs literal DECODE should not have IS NULL checks"
8600        );
8601    }
8602
8603    #[test]
8604    fn test_decode_column_vs_column_keeps_null_check() {
8605        // Oracle DECODE with column vs column should keep null-safe comparison
8606        let dialect = Dialect::get(DialectType::Oracle);
8607        let result = dialect
8608            .transpile("SELECT decode(col, col2, 3, 4) FROM t", DialectType::DuckDB)
8609            .unwrap();
8610        assert!(
8611            result[0].contains("IS NULL"),
8612            "Column vs column DECODE should have IS NULL checks, got: {}",
8613            result[0]
8614        );
8615    }
8616
8617    #[test]
8618    fn test_decode_null_search() {
8619        // Oracle DECODE with NULL search should use IS NULL
8620        let dialect = Dialect::get(DialectType::Oracle);
8621        let result = dialect
8622            .transpile("SELECT decode(col, NULL, 3, 4) FROM t", DialectType::DuckDB)
8623            .unwrap();
8624        assert_eq!(
8625            result[0],
8626            "SELECT CASE WHEN col IS NULL THEN 3 ELSE 4 END FROM t",
8627        );
8628    }
8629
8630    // =========================================================================
8631    // REGEXP function transpilation tests
8632    // =========================================================================
8633
8634    #[test]
8635    fn test_regexp_substr_snowflake_to_duckdb_2arg() {
8636        let dialect = Dialect::get(DialectType::Snowflake);
8637        let result = dialect
8638            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern')", DialectType::DuckDB)
8639            .unwrap();
8640        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8641    }
8642
8643    #[test]
8644    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos1() {
8645        let dialect = Dialect::get(DialectType::Snowflake);
8646        let result = dialect
8647            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 1)", DialectType::DuckDB)
8648            .unwrap();
8649        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8650    }
8651
8652    #[test]
8653    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos_gt1() {
8654        let dialect = Dialect::get(DialectType::Snowflake);
8655        let result = dialect
8656            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 3)", DialectType::DuckDB)
8657            .unwrap();
8658        assert_eq!(
8659            result[0],
8660            "SELECT REGEXP_EXTRACT(NULLIF(SUBSTRING(s, 3), ''), 'pattern')"
8661        );
8662    }
8663
8664    #[test]
8665    fn test_regexp_substr_snowflake_to_duckdb_4arg_occ_gt1() {
8666        let dialect = Dialect::get(DialectType::Snowflake);
8667        let result = dialect
8668            .transpile(
8669                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 3)",
8670                DialectType::DuckDB,
8671            )
8672            .unwrap();
8673        assert_eq!(
8674            result[0],
8675            "SELECT ARRAY_EXTRACT(REGEXP_EXTRACT_ALL(s, 'pattern'), 3)"
8676        );
8677    }
8678
8679    #[test]
8680    fn test_regexp_substr_snowflake_to_duckdb_5arg_e_flag() {
8681        let dialect = Dialect::get(DialectType::Snowflake);
8682        let result = dialect
8683            .transpile(
8684                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')",
8685                DialectType::DuckDB,
8686            )
8687            .unwrap();
8688        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8689    }
8690
8691    #[test]
8692    fn test_regexp_substr_snowflake_to_duckdb_6arg_group0() {
8693        let dialect = Dialect::get(DialectType::Snowflake);
8694        let result = dialect
8695            .transpile(
8696                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
8697                DialectType::DuckDB,
8698            )
8699            .unwrap();
8700        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8701    }
8702
8703    #[test]
8704    fn test_regexp_substr_snowflake_identity_strip_group0() {
8705        let dialect = Dialect::get(DialectType::Snowflake);
8706        let result = dialect
8707            .transpile(
8708                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
8709                DialectType::Snowflake,
8710            )
8711            .unwrap();
8712        assert_eq!(result[0], "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')");
8713    }
8714
8715    #[test]
8716    fn test_regexp_substr_all_snowflake_to_duckdb_2arg() {
8717        let dialect = Dialect::get(DialectType::Snowflake);
8718        let result = dialect
8719            .transpile(
8720                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')",
8721                DialectType::DuckDB,
8722            )
8723            .unwrap();
8724        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8725    }
8726
8727    #[test]
8728    fn test_regexp_substr_all_snowflake_to_duckdb_3arg_pos_gt1() {
8729        let dialect = Dialect::get(DialectType::Snowflake);
8730        let result = dialect
8731            .transpile(
8732                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 3)",
8733                DialectType::DuckDB,
8734            )
8735            .unwrap();
8736        assert_eq!(
8737            result[0],
8738            "SELECT REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')"
8739        );
8740    }
8741
8742    #[test]
8743    fn test_regexp_substr_all_snowflake_to_duckdb_5arg_e_flag() {
8744        let dialect = Dialect::get(DialectType::Snowflake);
8745        let result = dialect
8746            .transpile(
8747                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')",
8748                DialectType::DuckDB,
8749            )
8750            .unwrap();
8751        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8752    }
8753
8754    #[test]
8755    fn test_regexp_substr_all_snowflake_to_duckdb_6arg_group0() {
8756        let dialect = Dialect::get(DialectType::Snowflake);
8757        let result = dialect
8758            .transpile(
8759                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
8760                DialectType::DuckDB,
8761            )
8762            .unwrap();
8763        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8764    }
8765
8766    #[test]
8767    fn test_regexp_substr_all_snowflake_identity_strip_group0() {
8768        let dialect = Dialect::get(DialectType::Snowflake);
8769        let result = dialect
8770            .transpile(
8771                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
8772                DialectType::Snowflake,
8773            )
8774            .unwrap();
8775        assert_eq!(
8776            result[0],
8777            "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')"
8778        );
8779    }
8780
8781    #[test]
8782    fn test_regexp_count_snowflake_to_duckdb_2arg() {
8783        let dialect = Dialect::get(DialectType::Snowflake);
8784        let result = dialect
8785            .transpile("SELECT REGEXP_COUNT(s, 'pattern')", DialectType::DuckDB)
8786            .unwrap();
8787        assert_eq!(
8788            result[0],
8789            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(s, 'pattern')) END"
8790        );
8791    }
8792
8793    #[test]
8794    fn test_regexp_count_snowflake_to_duckdb_3arg() {
8795        let dialect = Dialect::get(DialectType::Snowflake);
8796        let result = dialect
8797            .transpile("SELECT REGEXP_COUNT(s, 'pattern', 3)", DialectType::DuckDB)
8798            .unwrap();
8799        assert_eq!(
8800            result[0],
8801            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')) END"
8802        );
8803    }
8804
8805    #[test]
8806    fn test_regexp_count_snowflake_to_duckdb_4arg_flags() {
8807        let dialect = Dialect::get(DialectType::Snowflake);
8808        let result = dialect
8809            .transpile(
8810                "SELECT REGEXP_COUNT(s, 'pattern', 1, 'i')",
8811                DialectType::DuckDB,
8812            )
8813            .unwrap();
8814        assert_eq!(
8815            result[0],
8816            "SELECT CASE WHEN '(?i)' || 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 1), '(?i)' || 'pattern')) END"
8817        );
8818    }
8819
8820    #[test]
8821    fn test_regexp_count_snowflake_to_duckdb_4arg_flags_literal_string() {
8822        let dialect = Dialect::get(DialectType::Snowflake);
8823        let result = dialect
8824            .transpile(
8825                "SELECT REGEXP_COUNT('Hello World', 'L', 1, 'im')",
8826                DialectType::DuckDB,
8827            )
8828            .unwrap();
8829        assert_eq!(
8830            result[0],
8831            "SELECT CASE WHEN '(?im)' || 'L' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING('Hello World', 1), '(?im)' || 'L')) END"
8832        );
8833    }
8834
8835    #[test]
8836    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos1_occ1() {
8837        let dialect = Dialect::get(DialectType::Snowflake);
8838        let result = dialect
8839            .transpile(
8840                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 1, 1)",
8841                DialectType::DuckDB,
8842            )
8843            .unwrap();
8844        assert_eq!(result[0], "SELECT REGEXP_REPLACE(s, 'pattern', 'repl')");
8845    }
8846
8847    #[test]
8848    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ0() {
8849        let dialect = Dialect::get(DialectType::Snowflake);
8850        let result = dialect
8851            .transpile(
8852                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 0)",
8853                DialectType::DuckDB,
8854            )
8855            .unwrap();
8856        assert_eq!(
8857            result[0],
8858            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl', 'g')"
8859        );
8860    }
8861
8862    #[test]
8863    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ1() {
8864        let dialect = Dialect::get(DialectType::Snowflake);
8865        let result = dialect
8866            .transpile(
8867                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 1)",
8868                DialectType::DuckDB,
8869            )
8870            .unwrap();
8871        assert_eq!(
8872            result[0],
8873            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl')"
8874        );
8875    }
8876
8877    #[test]
8878    fn test_rlike_snowflake_to_duckdb_2arg() {
8879        let dialect = Dialect::get(DialectType::Snowflake);
8880        let result = dialect
8881            .transpile("SELECT RLIKE(a, b)", DialectType::DuckDB)
8882            .unwrap();
8883        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b)");
8884    }
8885
8886    #[test]
8887    fn test_rlike_snowflake_to_duckdb_3arg_flags() {
8888        let dialect = Dialect::get(DialectType::Snowflake);
8889        let result = dialect
8890            .transpile("SELECT RLIKE(a, b, 'i')", DialectType::DuckDB)
8891            .unwrap();
8892        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b, 'i')");
8893    }
8894
8895    #[test]
8896    fn test_regexp_extract_all_bigquery_to_snowflake_no_capture() {
8897        let dialect = Dialect::get(DialectType::BigQuery);
8898        let result = dialect
8899            .transpile(
8900                "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')",
8901                DialectType::Snowflake,
8902            )
8903            .unwrap();
8904        assert_eq!(result[0], "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')");
8905    }
8906
8907    #[test]
8908    fn test_regexp_extract_all_bigquery_to_snowflake_with_capture() {
8909        let dialect = Dialect::get(DialectType::BigQuery);
8910        let result = dialect
8911            .transpile(
8912                "SELECT REGEXP_EXTRACT_ALL(s, '(a)[0-9]')",
8913                DialectType::Snowflake,
8914            )
8915            .unwrap();
8916        assert_eq!(
8917            result[0],
8918            "SELECT REGEXP_SUBSTR_ALL(s, '(a)[0-9]', 1, 1, 'c', 1)"
8919        );
8920    }
8921
8922    #[test]
8923    fn test_regexp_instr_snowflake_to_duckdb_2arg() {
8924        let dialect = Dialect::get(DialectType::Snowflake);
8925        let result = dialect
8926            .transpile("SELECT REGEXP_INSTR(s, 'pattern')", DialectType::DuckDB)
8927            .unwrap();
8928        assert!(
8929            result[0].contains("CASE WHEN"),
8930            "Expected CASE WHEN in result: {}",
8931            result[0]
8932        );
8933        assert!(
8934            result[0].contains("LIST_SUM"),
8935            "Expected LIST_SUM in result: {}",
8936            result[0]
8937        );
8938    }
8939
8940    #[test]
8941    fn test_array_except_generic_to_duckdb() {
8942        let dialect = Dialect::get(DialectType::Generic);
8943        let result = dialect
8944            .transpile(
8945                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
8946                DialectType::DuckDB,
8947            )
8948            .unwrap();
8949        eprintln!("ARRAY_EXCEPT Generic->DuckDB: {}", result[0]);
8950        assert!(
8951            result[0].contains("CASE WHEN"),
8952            "Expected CASE WHEN: {}",
8953            result[0]
8954        );
8955        assert!(
8956            result[0].contains("LIST_FILTER"),
8957            "Expected LIST_FILTER: {}",
8958            result[0]
8959        );
8960        assert!(
8961            result[0].contains("LIST_DISTINCT"),
8962            "Expected LIST_DISTINCT: {}",
8963            result[0]
8964        );
8965        assert!(
8966            result[0].contains("IS NOT DISTINCT FROM"),
8967            "Expected IS NOT DISTINCT FROM: {}",
8968            result[0]
8969        );
8970        assert!(
8971            result[0].contains("= 0"),
8972            "Expected = 0 filter: {}",
8973            result[0]
8974        );
8975    }
8976
8977    #[test]
8978    fn test_array_except_generic_to_snowflake() {
8979        let dialect = Dialect::get(DialectType::Generic);
8980        let result = dialect
8981            .transpile(
8982                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
8983                DialectType::Snowflake,
8984            )
8985            .unwrap();
8986        eprintln!("ARRAY_EXCEPT Generic->Snowflake: {}", result[0]);
8987        assert_eq!(result[0], "SELECT ARRAY_EXCEPT([1, 2, 3], [2])");
8988    }
8989
8990    #[test]
8991    fn test_array_except_generic_to_presto() {
8992        let dialect = Dialect::get(DialectType::Generic);
8993        let result = dialect
8994            .transpile(
8995                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
8996                DialectType::Presto,
8997            )
8998            .unwrap();
8999        eprintln!("ARRAY_EXCEPT Generic->Presto: {}", result[0]);
9000        assert_eq!(result[0], "SELECT ARRAY_EXCEPT(ARRAY[1, 2, 3], ARRAY[2])");
9001    }
9002
9003    #[test]
9004    fn test_array_except_snowflake_to_duckdb() {
9005        let dialect = Dialect::get(DialectType::Snowflake);
9006        let result = dialect
9007            .transpile("SELECT ARRAY_EXCEPT([1, 2, 3], [2])", DialectType::DuckDB)
9008            .unwrap();
9009        eprintln!("ARRAY_EXCEPT Snowflake->DuckDB: {}", result[0]);
9010        assert!(
9011            result[0].contains("CASE WHEN"),
9012            "Expected CASE WHEN: {}",
9013            result[0]
9014        );
9015        assert!(
9016            result[0].contains("LIST_TRANSFORM"),
9017            "Expected LIST_TRANSFORM: {}",
9018            result[0]
9019        );
9020    }
9021
9022    #[test]
9023    fn test_array_contains_snowflake_to_snowflake() {
9024        let dialect = Dialect::get(DialectType::Snowflake);
9025        let result = dialect
9026            .transpile(
9027                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
9028                DialectType::Snowflake,
9029            )
9030            .unwrap();
9031        eprintln!("ARRAY_CONTAINS Snowflake->Snowflake: {}", result[0]);
9032        assert_eq!(result[0], "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])");
9033    }
9034
9035    #[test]
9036    fn test_array_contains_snowflake_to_duckdb() {
9037        let dialect = Dialect::get(DialectType::Snowflake);
9038        let result = dialect
9039            .transpile(
9040                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
9041                DialectType::DuckDB,
9042            )
9043            .unwrap();
9044        eprintln!("ARRAY_CONTAINS Snowflake->DuckDB: {}", result[0]);
9045        assert!(
9046            result[0].contains("CASE WHEN"),
9047            "Expected CASE WHEN: {}",
9048            result[0]
9049        );
9050        assert!(
9051            result[0].contains("NULLIF"),
9052            "Expected NULLIF: {}",
9053            result[0]
9054        );
9055        assert!(
9056            result[0].contains("ARRAY_CONTAINS"),
9057            "Expected ARRAY_CONTAINS: {}",
9058            result[0]
9059        );
9060    }
9061
9062    #[test]
9063    fn test_array_distinct_snowflake_to_duckdb() {
9064        let dialect = Dialect::get(DialectType::Snowflake);
9065        let result = dialect
9066            .transpile(
9067                "SELECT ARRAY_DISTINCT([1, 2, 2, 3, 1])",
9068                DialectType::DuckDB,
9069            )
9070            .unwrap();
9071        eprintln!("ARRAY_DISTINCT Snowflake->DuckDB: {}", result[0]);
9072        assert!(
9073            result[0].contains("CASE WHEN"),
9074            "Expected CASE WHEN: {}",
9075            result[0]
9076        );
9077        assert!(
9078            result[0].contains("LIST_DISTINCT"),
9079            "Expected LIST_DISTINCT: {}",
9080            result[0]
9081        );
9082        assert!(
9083            result[0].contains("LIST_APPEND"),
9084            "Expected LIST_APPEND: {}",
9085            result[0]
9086        );
9087        assert!(
9088            result[0].contains("LIST_FILTER"),
9089            "Expected LIST_FILTER: {}",
9090            result[0]
9091        );
9092    }
9093}