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                Self::reject_postgres_tsql_strict_unknown_text_casts(
3419                    &expr,
3420                    self.dialect_type,
3421                    target,
3422                    opts,
3423                )?;
3424
3425                // When source and target differ, first normalize the source dialect's
3426                // AST constructs to standard SQL, so that the target dialect can handle them.
3427                // This handles cases like Snowflake's SQUARE -> POWER, DIV0 -> CASE, etc.
3428                let normalized =
3429                    if self.dialect_type != target && self.dialect_type != DialectType::Generic {
3430                        self.transform_with_guard(expr, opts.complexity_guard)?
3431                    } else {
3432                        expr
3433                    };
3434
3435                // For TSQL source targeting non-TSQL: unwrap ISNULL(JSON_QUERY(...), JSON_VALUE(...))
3436                // to just JSON_QUERY(...) so cross_dialect_normalize can convert it cleanly.
3437                // The TSQL read transform wraps JsonQuery in ISNULL for identity, but for
3438                // cross-dialect transpilation we need the unwrapped JSON_QUERY.
3439                let normalized =
3440                    if matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3441                        && !matches!(target, DialectType::TSQL | DialectType::Fabric)
3442                    {
3443                        transform_recursive(normalized, &|e| {
3444                            if let Expression::Function(ref f) = e {
3445                                if f.name.eq_ignore_ascii_case("ISNULL") && f.args.len() == 2 {
3446                                    // Check if first arg is JSON_QUERY and second is JSON_VALUE
3447                                    if let (
3448                                        Expression::Function(ref jq),
3449                                        Expression::Function(ref jv),
3450                                    ) = (&f.args[0], &f.args[1])
3451                                    {
3452                                        if jq.name.eq_ignore_ascii_case("JSON_QUERY")
3453                                            && jv.name.eq_ignore_ascii_case("JSON_VALUE")
3454                                        {
3455                                            // Unwrap: return just JSON_QUERY(...)
3456                                            return Ok(f.args[0].clone());
3457                                        }
3458                                    }
3459                                }
3460                            }
3461                            Ok(e)
3462                        })?
3463                    } else {
3464                        normalized
3465                    };
3466
3467                // Snowflake source to non-Snowflake target: CURRENT_TIME -> LOCALTIME
3468                // Snowflake's CURRENT_TIME is equivalent to LOCALTIME in other dialects.
3469                // Python sqlglot parses Snowflake's CURRENT_TIME as Localtime expression.
3470                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3471                    && !matches!(target, DialectType::Snowflake)
3472                {
3473                    transform_recursive(normalized, &|e| {
3474                        if let Expression::Function(ref f) = e {
3475                            if f.name.eq_ignore_ascii_case("CURRENT_TIME") {
3476                                return Ok(Expression::Localtime(Box::new(
3477                                    crate::expressions::Localtime { this: None },
3478                                )));
3479                            }
3480                        }
3481                        Ok(e)
3482                    })?
3483                } else {
3484                    normalized
3485                };
3486
3487                // Snowflake source to DuckDB target: REPEAT(' ', n) -> REPEAT(' ', CAST(n AS BIGINT))
3488                // Snowflake's SPACE(n) is converted to REPEAT(' ', n) by the Snowflake source
3489                // transform. DuckDB requires the count argument to be BIGINT.
3490                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3491                    && matches!(target, DialectType::DuckDB)
3492                {
3493                    transform_recursive(normalized, &|e| {
3494                        if let Expression::Function(ref f) = e {
3495                            if f.name.eq_ignore_ascii_case("REPEAT") && f.args.len() == 2 {
3496                                // Check if first arg is space string literal
3497                                if let Expression::Literal(ref lit) = f.args[0] {
3498                                    if let crate::expressions::Literal::String(ref s) = lit.as_ref()
3499                                    {
3500                                        if s == " " {
3501                                            // Wrap second arg in CAST(... AS BIGINT) if not already
3502                                            if !matches!(f.args[1], Expression::Cast(_)) {
3503                                                let mut new_args = f.args.clone();
3504                                                new_args[1] = Expression::Cast(Box::new(
3505                                                    crate::expressions::Cast {
3506                                                        this: new_args[1].clone(),
3507                                                        to: crate::expressions::DataType::BigInt {
3508                                                            length: None,
3509                                                        },
3510                                                        trailing_comments: Vec::new(),
3511                                                        double_colon_syntax: false,
3512                                                        format: None,
3513                                                        default: None,
3514                                                        inferred_type: None,
3515                                                    },
3516                                                ));
3517                                                return Ok(Expression::Function(Box::new(
3518                                                    crate::expressions::Function {
3519                                                        name: f.name.clone(),
3520                                                        args: new_args,
3521                                                        distinct: f.distinct,
3522                                                        trailing_comments: f
3523                                                            .trailing_comments
3524                                                            .clone(),
3525                                                        use_bracket_syntax: f.use_bracket_syntax,
3526                                                        no_parens: f.no_parens,
3527                                                        quoted: f.quoted,
3528                                                        span: None,
3529                                                        inferred_type: None,
3530                                                    },
3531                                                )));
3532                                            }
3533                                        }
3534                                    }
3535                                }
3536                            }
3537                        }
3538                        Ok(e)
3539                    })?
3540                } else {
3541                    normalized
3542                };
3543
3544                // Propagate struct field names in arrays (for BigQuery source to non-BigQuery target)
3545                // BigQuery->BigQuery should NOT propagate names (BigQuery handles implicit inheritance)
3546                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3547                    && !matches!(target, DialectType::BigQuery)
3548                {
3549                    crate::transforms::propagate_struct_field_names(normalized)?
3550                } else {
3551                    normalized
3552                };
3553
3554                // Snowflake source to DuckDB target: RANDOM()/RANDOM(seed) -> scaled RANDOM()
3555                // Snowflake RANDOM() returns integer in [-2^63, 2^63-1], DuckDB RANDOM() returns float [0, 1)
3556                // Skip RANDOM inside UNIFORM/NORMAL/ZIPF/RANDSTR generator args since those
3557                // functions handle their generator args differently (as float seeds).
3558                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3559                    && matches!(target, DialectType::DuckDB)
3560                {
3561                    fn make_scaled_random() -> Expression {
3562                        let lower =
3563                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3564                                "-9.223372036854776E+18".to_string(),
3565                            )));
3566                        let upper =
3567                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3568                                "9.223372036854776e+18".to_string(),
3569                            )));
3570                        let random_call = Expression::Random(crate::expressions::Random);
3571                        let range_size = Expression::Paren(Box::new(crate::expressions::Paren {
3572                            this: Expression::Sub(Box::new(crate::expressions::BinaryOp {
3573                                left: upper,
3574                                right: lower.clone(),
3575                                left_comments: vec![],
3576                                operator_comments: vec![],
3577                                trailing_comments: vec![],
3578                                inferred_type: None,
3579                            })),
3580                            trailing_comments: vec![],
3581                        }));
3582                        let scaled = Expression::Mul(Box::new(crate::expressions::BinaryOp {
3583                            left: random_call,
3584                            right: range_size,
3585                            left_comments: vec![],
3586                            operator_comments: vec![],
3587                            trailing_comments: vec![],
3588                            inferred_type: None,
3589                        }));
3590                        let shifted = Expression::Add(Box::new(crate::expressions::BinaryOp {
3591                            left: lower,
3592                            right: scaled,
3593                            left_comments: vec![],
3594                            operator_comments: vec![],
3595                            trailing_comments: vec![],
3596                            inferred_type: None,
3597                        }));
3598                        Expression::Cast(Box::new(crate::expressions::Cast {
3599                            this: shifted,
3600                            to: crate::expressions::DataType::BigInt { length: None },
3601                            trailing_comments: vec![],
3602                            double_colon_syntax: false,
3603                            format: None,
3604                            default: None,
3605                            inferred_type: None,
3606                        }))
3607                    }
3608
3609                    // Pre-process: protect seeded RANDOM(seed) inside UNIFORM/NORMAL/ZIPF/RANDSTR
3610                    // by converting Rand{seed: Some(s)} to Function{name:"RANDOM", args:[s]}.
3611                    // This prevents transform_recursive (which is bottom-up) from expanding
3612                    // seeded RANDOM into make_scaled_random() and losing the seed value.
3613                    // Unseeded RANDOM()/Rand{seed:None} is left as-is so it gets expanded
3614                    // and then un-expanded back to Expression::Random by the code below.
3615                    let normalized = transform_recursive(normalized, &|e| {
3616                        if let Expression::Function(ref f) = e {
3617                            let n = f.name.to_ascii_uppercase();
3618                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" || n == "RANDSTR" {
3619                                if let Expression::Function(mut f) = e {
3620                                    for arg in f.args.iter_mut() {
3621                                        if let Expression::Rand(ref r) = arg {
3622                                            if r.lower.is_none() && r.upper.is_none() {
3623                                                if let Some(ref seed) = r.seed {
3624                                                    // Convert Rand{seed: Some(s)} to Function("RANDOM", [s])
3625                                                    // so it won't be expanded by the RANDOM expansion below
3626                                                    *arg = Expression::Function(Box::new(
3627                                                        crate::expressions::Function::new(
3628                                                            "RANDOM".to_string(),
3629                                                            vec![*seed.clone()],
3630                                                        ),
3631                                                    ));
3632                                                }
3633                                            }
3634                                        }
3635                                    }
3636                                    return Ok(Expression::Function(f));
3637                                }
3638                            }
3639                        }
3640                        Ok(e)
3641                    })?;
3642
3643                    // transform_recursive processes bottom-up, so RANDOM() (unseeded) inside
3644                    // generator functions (UNIFORM, NORMAL, ZIPF) gets expanded before
3645                    // we see the parent. We detect this and undo the expansion by replacing
3646                    // the expanded pattern back with Expression::Random.
3647                    // Seeded RANDOM(seed) was already protected above as Function("RANDOM", [seed]).
3648                    // Note: RANDSTR is NOT included here — it needs the expanded form for unseeded
3649                    // RANDOM() since the DuckDB handler uses the expanded SQL as-is in the hash.
3650                    transform_recursive(normalized, &|e| {
3651                        if let Expression::Function(ref f) = e {
3652                            let n = f.name.to_ascii_uppercase();
3653                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" {
3654                                if let Expression::Function(mut f) = e {
3655                                    for arg in f.args.iter_mut() {
3656                                        // Detect expanded RANDOM pattern: CAST(-9.22... + RANDOM() * (...) AS BIGINT)
3657                                        if let Expression::Cast(ref cast) = arg {
3658                                            if matches!(
3659                                                cast.to,
3660                                                crate::expressions::DataType::BigInt { .. }
3661                                            ) {
3662                                                if let Expression::Add(ref add) = cast.this {
3663                                                    if let Expression::Literal(ref lit) = add.left {
3664                                                        if let crate::expressions::Literal::Number(
3665                                                            ref num,
3666                                                        ) = lit.as_ref()
3667                                                        {
3668                                                            if num == "-9.223372036854776E+18" {
3669                                                                *arg = Expression::Random(
3670                                                                    crate::expressions::Random,
3671                                                                );
3672                                                            }
3673                                                        }
3674                                                    }
3675                                                }
3676                                            }
3677                                        }
3678                                    }
3679                                    return Ok(Expression::Function(f));
3680                                }
3681                                return Ok(e);
3682                            }
3683                        }
3684                        match e {
3685                            Expression::Random(_) => Ok(make_scaled_random()),
3686                            // Rand(seed) with no bounds: drop seed and expand
3687                            // (DuckDB RANDOM doesn't support seeds)
3688                            Expression::Rand(ref r) if r.lower.is_none() && r.upper.is_none() => {
3689                                Ok(make_scaled_random())
3690                            }
3691                            _ => Ok(e),
3692                        }
3693                    })?
3694                } else {
3695                    normalized
3696                };
3697
3698                // Apply cross-dialect semantic normalizations
3699                let normalized = normalization::normalize(normalized, self.dialect_type, target)?;
3700
3701                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3702                    Self::normalize_tsql_fetch_overlaps_date_bin(normalized)?
3703                } else {
3704                    normalized
3705                };
3706
3707                let normalized =
3708                    if matches!(
3709                        self.dialect_type,
3710                        DialectType::PostgreSQL | DialectType::CockroachDB
3711                    ) && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
3712                    {
3713                        Self::normalize_postgres_type_function_casts(normalized, target)?
3714                    } else {
3715                        normalized
3716                    };
3717
3718                let normalized = if matches!(self.dialect_type, DialectType::SQLite)
3719                    && !matches!(target, DialectType::SQLite)
3720                {
3721                    Self::normalize_sqlite_double_quoted_defaults(normalized)?
3722                } else {
3723                    normalized
3724                };
3725
3726                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3727                    && matches!(target, DialectType::SQLite)
3728                {
3729                    Self::normalize_postgres_to_sqlite_types(normalized)?
3730                } else {
3731                    normalized
3732                };
3733
3734                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3735                    && matches!(target, DialectType::Fabric)
3736                {
3737                    Self::normalize_postgres_to_fabric_types(normalized)?
3738                } else {
3739                    normalized
3740                };
3741
3742                // For DuckDB target from BigQuery source: wrap UNNEST of struct arrays in
3743                // (SELECT UNNEST(..., max_depth => 2)) subquery
3744                // Must run BEFORE unnest_alias_to_column_alias since it changes alias structure
3745                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3746                    && matches!(target, DialectType::DuckDB)
3747                {
3748                    crate::transforms::wrap_duckdb_unnest_struct(normalized)?
3749                } else {
3750                    normalized
3751                };
3752
3753                // Convert BigQuery UNNEST aliases to column-alias format for DuckDB/Presto/Spark
3754                // UNNEST(arr) AS x -> UNNEST(arr) AS _t0(x)
3755                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3756                    && matches!(
3757                        target,
3758                        DialectType::DuckDB
3759                            | DialectType::Presto
3760                            | DialectType::Trino
3761                            | DialectType::Athena
3762                            | DialectType::Spark
3763                            | DialectType::Databricks
3764                    ) {
3765                    crate::transforms::unnest_alias_to_column_alias(normalized)?
3766                } else if matches!(self.dialect_type, DialectType::BigQuery)
3767                    && matches!(target, DialectType::BigQuery | DialectType::Redshift)
3768                {
3769                    // For BigQuery/Redshift targets: move UNNEST FROM items to CROSS JOINs
3770                    // but don't convert alias format (no _t0 wrapper)
3771                    let result = crate::transforms::unnest_from_to_cross_join(normalized)?;
3772                    // For Redshift: strip UNNEST when arg is a column reference path
3773                    if matches!(target, DialectType::Redshift) {
3774                        crate::transforms::strip_unnest_column_refs(result)?
3775                    } else {
3776                        result
3777                    }
3778                } else {
3779                    normalized
3780                };
3781
3782                // For Presto/Trino targets from PostgreSQL/Redshift source:
3783                // Wrap UNNEST aliases from GENERATE_SERIES conversion: AS s -> AS _u(s)
3784                let normalized = if matches!(
3785                    self.dialect_type,
3786                    DialectType::PostgreSQL | DialectType::Redshift
3787                ) && matches!(
3788                    target,
3789                    DialectType::Presto | DialectType::Trino | DialectType::Athena
3790                ) {
3791                    crate::transforms::wrap_unnest_join_aliases(normalized)?
3792                } else {
3793                    normalized
3794                };
3795
3796                // Eliminate DISTINCT ON with target-dialect awareness
3797                // This must happen after source transform (which may produce DISTINCT ON)
3798                // and before target transform, with knowledge of the target dialect's NULL ordering behavior
3799                let normalized = crate::transforms::eliminate_distinct_on_for_dialect(
3800                    normalized,
3801                    Some(target),
3802                    Some(self.dialect_type),
3803                )?;
3804
3805                // GENERATE_DATE_ARRAY in UNNEST -> Snowflake ARRAY_GENERATE_RANGE + DATEADD
3806                let normalized = if matches!(target, DialectType::Snowflake) {
3807                    Self::transform_generate_date_array_snowflake(normalized)?
3808                } else {
3809                    normalized
3810                };
3811
3812                // CROSS JOIN UNNEST -> LATERAL VIEW EXPLODE/INLINE for Spark/Hive/Databricks
3813                let normalized = if matches!(
3814                    target,
3815                    DialectType::Spark | DialectType::Databricks | DialectType::Hive
3816                ) {
3817                    crate::transforms::unnest_to_explode_select(normalized)?
3818                } else {
3819                    normalized
3820                };
3821
3822                // Wrap UNION with ORDER BY/LIMIT in a subquery for dialects that require it
3823                let normalized = if matches!(target, DialectType::ClickHouse | DialectType::TSQL) {
3824                    crate::transforms::no_limit_order_by_union(normalized)?
3825                } else {
3826                    normalized
3827                };
3828
3829                let normalized = if matches!(
3830                    self.dialect_type,
3831                    DialectType::PostgreSQL | DialectType::CockroachDB
3832                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3833                {
3834                    Self::normalize_postgres_boolean_semantics_for_tsql(normalized)?
3835                } else {
3836                    normalized
3837                };
3838
3839                // TSQL: Convert COUNT(*) -> COUNT_BIG(*) when source is not TSQL/Fabric
3840                // Python sqlglot does this in the TSQL generator, but we can't do it there
3841                // because it would break TSQL -> TSQL identity
3842                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3843                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3844                {
3845                    transform_recursive(normalized, &|e| {
3846                        if let Expression::Count(ref c) = e {
3847                            // Build COUNT_BIG(...) as an AggregateFunction
3848                            let args = if c.star {
3849                                vec![Expression::Star(crate::expressions::Star {
3850                                    table: None,
3851                                    except: None,
3852                                    replace: None,
3853                                    rename: None,
3854                                    trailing_comments: Vec::new(),
3855                                    span: None,
3856                                })]
3857                            } else if let Some(ref this) = c.this {
3858                                vec![this.clone()]
3859                            } else {
3860                                vec![]
3861                            };
3862                            Ok(Expression::AggregateFunction(Box::new(
3863                                crate::expressions::AggregateFunction {
3864                                    name: "COUNT_BIG".to_string(),
3865                                    args,
3866                                    distinct: c.distinct,
3867                                    filter: c.filter.clone(),
3868                                    order_by: Vec::new(),
3869                                    limit: None,
3870                                    ignore_nulls: None,
3871                                    inferred_type: None,
3872                                },
3873                            )))
3874                        } else {
3875                            Ok(e)
3876                        }
3877                    })?
3878                } else {
3879                    normalized
3880                };
3881
3882                // T-SQL/Fabric do not have a scalar boolean type. Keep predicate
3883                // contexts intact, but materialize boolean-valued expressions used
3884                // as values before target transforms add ORDER BY null sort keys.
3885                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3886                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3887                {
3888                    Self::rewrite_boolean_values_for_tsql(normalized)?
3889                } else {
3890                    normalized
3891                };
3892
3893                let normalized = if matches!(
3894                    self.dialect_type,
3895                    DialectType::PostgreSQL | DialectType::CockroachDB
3896                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3897                {
3898                    Self::rewrite_postgres_format_for_tsql(normalized, target)?
3899                } else {
3900                    normalized
3901                };
3902
3903                let normalized = if self.dialect_type == DialectType::PostgreSQL
3904                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
3905                {
3906                    Self::normalize_postgres_only_for_tsql(normalized)?
3907                } else {
3908                    normalized
3909                };
3910
3911                let transformed =
3912                    target_dialect.transform_with_guard(normalized, opts.complexity_guard)?;
3913
3914                // T-SQL and Fabric do not support aggregate FILTER clauses. Rewrite any
3915                // remaining filters after target transforms so special aggregate rewrites
3916                // (for example BOOL_OR/BOOL_AND) can consume their filters first.
3917                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3918                    Self::rewrite_aggregate_filters_for_tsql(transformed)?
3919                } else {
3920                    transformed
3921                };
3922
3923                let transformed = if matches!(
3924                    self.dialect_type,
3925                    DialectType::PostgreSQL | DialectType::CockroachDB
3926                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3927                {
3928                    crate::transforms::grouped_percentiles_to_tsql_windows(transformed)?
3929                } else {
3930                    transformed
3931                };
3932
3933                let transformed = if matches!(
3934                    self.dialect_type,
3935                    DialectType::PostgreSQL | DialectType::CockroachDB
3936                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3937                {
3938                    Self::normalize_postgres_trim_for_tsql(transformed)?
3939                } else {
3940                    transformed
3941                };
3942
3943                let transformed = if matches!(
3944                    self.dialect_type,
3945                    DialectType::PostgreSQL | DialectType::CockroachDB
3946                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3947                {
3948                    Self::rewrite_postgres_json_array_elements_select_for_tsql(transformed)?
3949                } else {
3950                    transformed
3951                };
3952
3953                // DuckDB target: when FROM is RANGE(n), replace SEQ's ROW_NUMBER pattern with `range`
3954                let transformed = if matches!(target, DialectType::DuckDB) {
3955                    Self::seq_rownum_to_range(transformed)?
3956                } else {
3957                    transformed
3958                };
3959
3960                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3961                    Self::reject_tsql_interval_casts(&transformed, target, opts)?;
3962                }
3963
3964                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3965                    Self::rewrite_tsql_interval_casts_to_varchar(transformed)?
3966                } else {
3967                    transformed
3968                };
3969
3970                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3971                    Self::legalize_tsql_nested_order_by(transformed)?
3972                } else {
3973                    transformed
3974                };
3975
3976                Self::reject_strict_unsupported(&transformed, self.dialect_type, target, opts)?;
3977
3978                let mut sql = target_dialect.generate_with_transpile_options(
3979                    &transformed,
3980                    self.dialect_type,
3981                    opts,
3982                )?;
3983
3984                // Align a known Snowflake pretty-print edge case with Python sqlglot output.
3985                if opts.pretty && target == DialectType::Snowflake {
3986                    sql = Self::normalize_snowflake_pretty(sql);
3987                }
3988
3989                Ok(sql)
3990            })
3991            .collect()
3992    }
3993}
3994
3995// Transpile-only methods: cross-dialect normalization and helpers
3996#[cfg(feature = "transpile")]
3997impl Dialect {
3998    fn legalize_tsql_nested_order_by(expr: Expression) -> Result<Expression> {
3999        let preserve_root_order = matches!(&expr, Expression::Select(select) if Self::tsql_select_needs_order_offset(select));
4000
4001        let mut transformed = transform_recursive(expr, &|node| match node {
4002            Expression::Select(mut select) => {
4003                Self::legalize_tsql_select_offset(&mut select);
4004                if Self::tsql_select_needs_order_offset(&select) {
4005                    select.offset = Some(Offset {
4006                        this: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
4007                        rows: Some(true),
4008                    });
4009                }
4010                Ok(Expression::Select(select))
4011            }
4012            Expression::Subquery(mut subquery) => {
4013                Self::legalize_tsql_offset(&mut subquery.order_by, &mut subquery.offset, false);
4014                Ok(Expression::Subquery(subquery))
4015            }
4016            Expression::Union(mut union) => {
4017                Self::legalize_tsql_set_offset(&mut union.order_by, &mut union.offset);
4018                Ok(Expression::Union(union))
4019            }
4020            Expression::Intersect(mut intersect) => {
4021                Self::legalize_tsql_set_offset(&mut intersect.order_by, &mut intersect.offset);
4022                Ok(Expression::Intersect(intersect))
4023            }
4024            Expression::Except(mut except) => {
4025                Self::legalize_tsql_set_offset(&mut except.order_by, &mut except.offset);
4026                Ok(Expression::Except(except))
4027            }
4028            other => Ok(other),
4029        })?;
4030
4031        if preserve_root_order {
4032            if let Expression::Select(select) = &mut transformed {
4033                select.offset = None;
4034            }
4035        }
4036
4037        Ok(transformed)
4038    }
4039
4040    fn legalize_tsql_select_offset(select: &mut crate::expressions::Select) {
4041        let has_fetch = select.fetch.is_some();
4042        Self::legalize_tsql_offset(&mut select.order_by, &mut select.offset, has_fetch);
4043    }
4044
4045    fn legalize_tsql_offset(
4046        order_by: &mut Option<OrderBy>,
4047        offset: &mut Option<Offset>,
4048        retain_inert_offset: bool,
4049    ) {
4050        if order_by.is_some() {
4051            return;
4052        }
4053
4054        if offset
4055            .as_ref()
4056            .is_some_and(|offset| Self::tsql_offset_is_inert(&offset.this))
4057            && !retain_inert_offset
4058        {
4059            *offset = None;
4060        } else if offset.is_some() {
4061            *order_by = Some(Generator::dummy_tsql_order_by());
4062        }
4063    }
4064
4065    fn legalize_tsql_set_offset(
4066        order_by: &mut Option<OrderBy>,
4067        offset: &mut Option<Box<Expression>>,
4068    ) {
4069        if order_by.is_some() {
4070            return;
4071        }
4072
4073        if offset.as_deref().is_some_and(Self::tsql_offset_is_inert) {
4074            *offset = None;
4075        } else if offset.is_some() {
4076            *order_by = Some(Generator::dummy_tsql_order_by());
4077        }
4078    }
4079
4080    fn tsql_offset_is_inert(expr: &Expression) -> bool {
4081        match expr {
4082            Expression::Null(_) => true,
4083            Expression::Literal(literal) => match literal.as_ref() {
4084                Literal::Number(value) => value.parse::<i128>().is_ok_and(|value| value == 0),
4085                _ => false,
4086            },
4087            _ => false,
4088        }
4089    }
4090
4091    fn tsql_select_needs_order_offset(select: &crate::expressions::Select) -> bool {
4092        select.order_by.is_some()
4093            && select.top.is_none()
4094            && select.limit.is_none()
4095            && select.offset.is_none()
4096            && select.fetch.is_none()
4097            && select.for_xml.is_empty()
4098            && select.for_json.is_empty()
4099    }
4100
4101    fn reject_strict_unsupported(
4102        expr: &Expression,
4103        source: DialectType,
4104        target: DialectType,
4105        opts: &TranspileOptions,
4106    ) -> Result<()> {
4107        if !matches!(
4108            opts.unsupported_level,
4109            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4110        ) {
4111            return Ok(());
4112        }
4113
4114        let mut diagnostics = Vec::new();
4115
4116        for node in expr.dfs() {
4117            if matches!(target, DialectType::Fabric | DialectType::Hive)
4118                && Self::node_has_recursive_with(node)
4119            {
4120                Self::push_unsupported_diagnostic(&mut diagnostics, "recursive CTEs");
4121            }
4122
4123            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4124                && Self::node_has_lateral(node)
4125            {
4126                Self::push_unsupported_diagnostic(&mut diagnostics, "LATERAL joins and subqueries");
4127            }
4128
4129            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4130                if Self::node_has_join_using(node) {
4131                    Self::push_unsupported_diagnostic(&mut diagnostics, "JOIN USING clauses");
4132                }
4133                if Self::node_has_natural_join(node) {
4134                    Self::push_unsupported_diagnostic(&mut diagnostics, "NATURAL JOIN");
4135                }
4136                if Self::node_has_unsupported_relation_column_aliases(node) {
4137                    Self::push_unsupported_diagnostic(
4138                        &mut diagnostics,
4139                        "column alias lists on base or joined table references",
4140                    );
4141                }
4142                if Self::node_has_qualified_whole_row_aggregate_argument(node) {
4143                    Self::push_unsupported_diagnostic(
4144                        &mut diagnostics,
4145                        "qualified whole-row aggregate arguments",
4146                    );
4147                }
4148            }
4149
4150            if !Self::target_supports_distinct_on(target) && Self::node_has_distinct_on(node) {
4151                Self::push_unsupported_diagnostic(&mut diagnostics, "DISTINCT ON");
4152            }
4153
4154            if !Self::target_supports_remaining_unnest(target) && Self::node_is_unnest(node) {
4155                Self::push_unsupported_diagnostic(&mut diagnostics, "UNNEST");
4156            }
4157
4158            if !Self::target_supports_remaining_explode(target) && Self::node_is_explode(node) {
4159                Self::push_unsupported_diagnostic(&mut diagnostics, "EXPLODE");
4160            }
4161
4162            if Self::target_lacks_array_agg(target) && Self::node_is_array_agg(node) {
4163                Self::push_unsupported_diagnostic(&mut diagnostics, "ARRAY_AGG");
4164            }
4165
4166            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4167                && Self::node_is_distinct_string_agg(node)
4168            {
4169                Self::push_unsupported_diagnostic(&mut diagnostics, "STRING_AGG with DISTINCT");
4170            }
4171
4172            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4173                && matches!(node, Expression::NthValue(_))
4174            {
4175                Self::push_unsupported_diagnostic(&mut diagnostics, "NTH_VALUE");
4176            }
4177
4178            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4179                if let Some(frame) = Self::node_window_frame(node) {
4180                    if matches!(frame.kind, WindowFrameKind::Groups) {
4181                        Self::push_unsupported_diagnostic(&mut diagnostics, "GROUPS window frames");
4182                    }
4183                    if matches!(frame.kind, WindowFrameKind::Range)
4184                        && (Self::window_frame_bound_has_value_offset(&frame.start)
4185                            || frame
4186                                .end
4187                                .as_ref()
4188                                .is_some_and(Self::window_frame_bound_has_value_offset))
4189                    {
4190                        Self::push_unsupported_diagnostic(
4191                            &mut diagnostics,
4192                            "value-offset RANGE window frames",
4193                        );
4194                    }
4195                    if frame.exclude.is_some() {
4196                        Self::push_unsupported_diagnostic(
4197                            &mut diagnostics,
4198                            "window frame EXCLUDE clauses",
4199                        );
4200                    }
4201                }
4202            }
4203
4204            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4205                && Self::node_is_regex_predicate(node)
4206            {
4207                Self::push_unsupported_diagnostic(
4208                    &mut diagnostics,
4209                    "regular expression predicates",
4210                );
4211            }
4212
4213            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4214                && Self::node_is_non_subquery_any(node)
4215            {
4216                Self::push_unsupported_diagnostic(
4217                    &mut diagnostics,
4218                    "ANY over non-subquery expressions",
4219                );
4220            }
4221
4222            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4223                && Self::node_is_row_value_subquery_comparison(node)
4224            {
4225                Self::push_unsupported_diagnostic(
4226                    &mut diagnostics,
4227                    "row-value subquery comparisons",
4228                );
4229            }
4230
4231            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4232                && Self::node_is_row_value_values_membership(node)
4233            {
4234                Self::push_unsupported_diagnostic(
4235                    &mut diagnostics,
4236                    "row-value VALUES membership comparisons",
4237                );
4238            }
4239
4240            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4241                && Self::node_has_fetch_with_ties(node)
4242            {
4243                Self::push_unsupported_diagnostic(&mut diagnostics, "FETCH WITH TIES without TOP");
4244            }
4245
4246            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4247                && Self::node_is_overlaps(node)
4248            {
4249                Self::push_unsupported_diagnostic(&mut diagnostics, "OVERLAPS");
4250            }
4251
4252            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4253                && Self::node_is_date_bin(node)
4254            {
4255                Self::push_unsupported_diagnostic(&mut diagnostics, "DATE_BIN");
4256            }
4257
4258            if source == DialectType::PostgreSQL
4259                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4260                && Self::node_is_unresolved_postgres_date_subtraction(node)
4261            {
4262                Self::push_unsupported_diagnostic(
4263                    &mut diagnostics,
4264                    "PostgreSQL date subtraction with an unresolved column type",
4265                );
4266            }
4267
4268            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4269                && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
4270            {
4271                if Self::node_is_postgres_json_build_object(node)
4272                    && !(matches!(target, DialectType::TSQL | DialectType::Fabric)
4273                        && Self::postgres_json_build_object_can_lower_to_json_object(node))
4274                {
4275                    Self::push_unsupported_diagnostic(
4276                        &mut diagnostics,
4277                        "PostgreSQL JSON_BUILD_OBJECT",
4278                    );
4279                }
4280                if Self::node_is_function_named(node, "TO_TSVECTOR") {
4281                    Self::push_unsupported_diagnostic(&mut diagnostics, "PostgreSQL TO_TSVECTOR");
4282                }
4283                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4284                    if let Some(composite_semantics) =
4285                        Self::postgres_tsql_unsupported_composite_semantics(node)
4286                    {
4287                        Self::push_unsupported_diagnostic(
4288                            &mut diagnostics,
4289                            &format!("PostgreSQL {composite_semantics}"),
4290                        );
4291                    }
4292                    if Self::node_is_postgres_unknown_cast(node) {
4293                        Self::push_unsupported_diagnostic(
4294                            &mut diagnostics,
4295                            "PostgreSQL unresolved UNKNOWN casts",
4296                        );
4297                    }
4298                    if let Some(collation_name) =
4299                        Self::postgres_tsql_unsupported_collation_name(node)
4300                    {
4301                        Self::push_unsupported_diagnostic(
4302                            &mut diagnostics,
4303                            &format!("PostgreSQL collation \"{collation_name}\""),
4304                        );
4305                    }
4306                    if let Some(array_semantics) =
4307                        Self::postgres_tsql_unsupported_array_semantics(node)
4308                    {
4309                        Self::push_unsupported_diagnostic(
4310                            &mut diagnostics,
4311                            &format!("PostgreSQL {array_semantics}"),
4312                        );
4313                    }
4314                    if let Some(function_name) = Self::postgres_tsql_unsupported_function_name(node)
4315                    {
4316                        Self::push_unsupported_diagnostic(
4317                            &mut diagnostics,
4318                            &format!("PostgreSQL {function_name}"),
4319                        );
4320                    }
4321                }
4322                if matches!(target, DialectType::TSQL | DialectType::Fabric)
4323                    && Self::node_is_postgres_type_function_cast(node)
4324                {
4325                    Self::push_unsupported_diagnostic(
4326                        &mut diagnostics,
4327                        "PostgreSQL type-name function casts",
4328                    );
4329                }
4330            }
4331
4332            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4333                break;
4334            }
4335        }
4336
4337        if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4338            Self::collect_tsql_unsupported_ordered_sets(expr, &mut diagnostics);
4339            Self::collect_tsql_windows_missing_order(expr, &HashMap::new(), &mut diagnostics);
4340        }
4341
4342        if diagnostics.is_empty() {
4343            return Ok(());
4344        }
4345
4346        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4347            1
4348        } else {
4349            opts.max_unsupported.max(1)
4350        };
4351        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4352        if diagnostics.len() > limit {
4353            messages.push(format!("... and {} more", diagnostics.len() - limit));
4354        }
4355
4356        Err(crate::error::Error::unsupported(
4357            messages.join("; "),
4358            target.to_string(),
4359        ))
4360    }
4361
4362    fn reject_postgres_tsql_strict_regex_predicates(
4363        expr: &Expression,
4364        source: DialectType,
4365        target: DialectType,
4366        opts: &TranspileOptions,
4367    ) -> Result<()> {
4368        if !matches!(
4369            opts.unsupported_level,
4370            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4371        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4372            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4373        {
4374            return Ok(());
4375        }
4376
4377        if expr.dfs().any(Self::node_is_regex_predicate) {
4378            return Err(crate::error::Error::unsupported(
4379                "regular expression predicates",
4380                target.to_string(),
4381            ));
4382        }
4383
4384        Ok(())
4385    }
4386
4387    fn reject_postgres_tsql_strict_unknown_text_casts(
4388        expr: &Expression,
4389        source: DialectType,
4390        target: DialectType,
4391        opts: &TranspileOptions,
4392    ) -> Result<()> {
4393        if !matches!(
4394            opts.unsupported_level,
4395            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4396        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4397            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4398        {
4399            return Ok(());
4400        }
4401
4402        if expr.dfs().any(|node| {
4403            matches!(
4404                node,
4405                Expression::Cast(cast)
4406                    if matches!(cast.to, DataType::Text)
4407                        && Self::is_column_expr(&cast.this)
4408            )
4409        }) {
4410            return Err(crate::error::Error::unsupported(
4411                "PostgreSQL cast of a column with an unknown source type to text",
4412                target.to_string(),
4413            ));
4414        }
4415
4416        Ok(())
4417    }
4418
4419    fn push_unsupported_diagnostic(diagnostics: &mut Vec<String>, message: &str) {
4420        if !diagnostics.iter().any(|existing| existing == message) {
4421            diagnostics.push(message.to_string());
4422        }
4423    }
4424
4425    fn node_is_unresolved_postgres_date_subtraction(expr: &Expression) -> bool {
4426        let Expression::Sub(op) = expr else {
4427            return false;
4428        };
4429
4430        (Self::is_explicit_date_expr(&op.left) && Self::is_column_expr(&op.right))
4431            || (Self::is_column_expr(&op.left) && Self::is_explicit_date_expr(&op.right))
4432    }
4433
4434    fn is_column_expr(expr: &Expression) -> bool {
4435        match expr {
4436            Expression::Column(_) => true,
4437            Expression::Paren(paren) => Self::is_column_expr(&paren.this),
4438            _ => false,
4439        }
4440    }
4441
4442    fn node_window_frame(expr: &Expression) -> Option<&WindowFrame> {
4443        match expr {
4444            Expression::WindowFunction(window) => window.over.frame.as_ref(),
4445            Expression::Window(window) | Expression::WindowSpec(window) => window.frame.as_ref(),
4446            _ => None,
4447        }
4448    }
4449
4450    fn window_frame_bound_has_value_offset(bound: &WindowFrameBound) -> bool {
4451        matches!(
4452            bound,
4453            WindowFrameBound::Preceding(_)
4454                | WindowFrameBound::Following(_)
4455                | WindowFrameBound::Value(_)
4456                | WindowFrameBound::BarePreceding
4457                | WindowFrameBound::BareFollowing
4458        )
4459    }
4460
4461    fn collect_tsql_windows_missing_order(
4462        expr: &Expression,
4463        active_windows: &HashMap<String, Over>,
4464        diagnostics: &mut Vec<String>,
4465    ) {
4466        if let Expression::Select(select) = expr {
4467            let local_windows = select
4468                .windows
4469                .as_ref()
4470                .map(|windows| {
4471                    windows
4472                        .iter()
4473                        .map(|window| (window.name.name.to_ascii_lowercase(), window.spec.clone()))
4474                        .collect()
4475                })
4476                .unwrap_or_default();
4477
4478            for child in expr.children() {
4479                Self::collect_tsql_windows_missing_order(child, &local_windows, diagnostics);
4480            }
4481            return;
4482        }
4483
4484        if let Expression::WindowFunction(window) = expr {
4485            let (has_order, has_frame) = Self::effective_window_order_and_frame(
4486                &window.over,
4487                active_windows,
4488                &mut Vec::new(),
4489            );
4490
4491            if !has_order {
4492                if has_frame {
4493                    Self::push_unsupported_diagnostic(
4494                        diagnostics,
4495                        "window frames without ORDER BY",
4496                    );
4497                }
4498                if let Some(function_name) =
4499                    Self::tsql_window_function_requiring_order(&window.this)
4500                {
4501                    Self::push_unsupported_diagnostic(
4502                        diagnostics,
4503                        &format!("{function_name} without ORDER BY"),
4504                    );
4505                }
4506            }
4507        }
4508
4509        for child in expr.children() {
4510            Self::collect_tsql_windows_missing_order(child, active_windows, diagnostics);
4511        }
4512    }
4513
4514    fn effective_window_order_and_frame(
4515        over: &Over,
4516        active_windows: &HashMap<String, Over>,
4517        seen: &mut Vec<String>,
4518    ) -> (bool, bool) {
4519        let inherited = over
4520            .window_name
4521            .as_ref()
4522            .and_then(|name| {
4523                let key = name.name.to_ascii_lowercase();
4524                if seen.iter().any(|seen_name| seen_name == &key) {
4525                    return None;
4526                }
4527                let named = active_windows.get(&key)?;
4528                seen.push(key);
4529                let properties =
4530                    Self::effective_window_order_and_frame(named, active_windows, seen);
4531                seen.pop();
4532                Some(properties)
4533            })
4534            .unwrap_or((false, false));
4535
4536        (
4537            !over.order_by.is_empty() || inherited.0,
4538            over.frame.is_some() || inherited.1,
4539        )
4540    }
4541
4542    fn tsql_window_function_requiring_order(expr: &Expression) -> Option<&'static str> {
4543        match expr {
4544            Expression::FirstValue(_) => Some("FIRST_VALUE"),
4545            Expression::LastValue(_) => Some("LAST_VALUE"),
4546            Expression::Function(function) if function.name.eq_ignore_ascii_case("FIRST_VALUE") => {
4547                Some("FIRST_VALUE")
4548            }
4549            Expression::Function(function) if function.name.eq_ignore_ascii_case("LAST_VALUE") => {
4550                Some("LAST_VALUE")
4551            }
4552            _ => None,
4553        }
4554    }
4555
4556    fn collect_tsql_unsupported_ordered_sets(expr: &Expression, diagnostics: &mut Vec<String>) {
4557        match expr {
4558            Expression::WindowFunction(window) => {
4559                if let Expression::WithinGroup(within_group) = &window.this {
4560                    if Self::within_group_is_hypothetical_set(within_group) {
4561                        Self::push_unsupported_diagnostic(
4562                            diagnostics,
4563                            "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
4564                        );
4565                        return;
4566                    }
4567
4568                    if Self::within_group_is_mode(within_group) {
4569                        Self::push_unsupported_diagnostic(
4570                            diagnostics,
4571                            "MODE ordered-set aggregates",
4572                        );
4573                        return;
4574                    }
4575
4576                    if Self::within_group_is_percentile(within_group) {
4577                        if !window.over.order_by.is_empty() || window.over.frame.is_some() {
4578                            Self::push_unsupported_diagnostic(
4579                                diagnostics,
4580                                "PERCENTILE_CONT/PERCENTILE_DISC window ORDER BY or frame clauses",
4581                            );
4582                        }
4583                        return;
4584                    }
4585                }
4586            }
4587            Expression::WithinGroup(within_group) => {
4588                if Self::within_group_is_hypothetical_set(within_group) {
4589                    Self::push_unsupported_diagnostic(
4590                        diagnostics,
4591                        "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
4592                    );
4593                    return;
4594                }
4595
4596                if Self::within_group_is_mode(within_group) {
4597                    Self::push_unsupported_diagnostic(diagnostics, "MODE ordered-set aggregates");
4598                    return;
4599                }
4600
4601                if Self::within_group_is_percentile(within_group) {
4602                    Self::push_unsupported_diagnostic(
4603                        diagnostics,
4604                        "PERCENTILE_CONT/PERCENTILE_DISC ordered-set aggregates without OVER",
4605                    );
4606                    return;
4607                }
4608            }
4609            _ => {}
4610        }
4611
4612        for child in expr.children() {
4613            Self::collect_tsql_unsupported_ordered_sets(child, diagnostics);
4614        }
4615    }
4616
4617    fn within_group_is_hypothetical_set(within_group: &crate::expressions::WithinGroup) -> bool {
4618        match &within_group.this {
4619            Expression::Function(function) => Self::is_hypothetical_set_name(&function.name),
4620            Expression::AggregateFunction(function) => {
4621                Self::is_hypothetical_set_name(&function.name)
4622            }
4623            Expression::Rank(_)
4624            | Expression::DenseRank(_)
4625            | Expression::CumeDist(_)
4626            | Expression::PercentRank(_) => true,
4627            _ => false,
4628        }
4629    }
4630
4631    fn within_group_is_percentile(within_group: &crate::expressions::WithinGroup) -> bool {
4632        match &within_group.this {
4633            Expression::Function(function) => Self::is_percentile_ordered_set_name(&function.name),
4634            Expression::AggregateFunction(function) => {
4635                Self::is_percentile_ordered_set_name(&function.name)
4636            }
4637            Expression::PercentileCont(_) | Expression::PercentileDisc(_) => true,
4638            _ => false,
4639        }
4640    }
4641
4642    fn within_group_is_mode(within_group: &crate::expressions::WithinGroup) -> bool {
4643        match &within_group.this {
4644            Expression::Function(function) => function.name.eq_ignore_ascii_case("MODE"),
4645            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case("MODE"),
4646            Expression::Mode(_) => true,
4647            _ => false,
4648        }
4649    }
4650
4651    fn is_percentile_ordered_set_name(name: &str) -> bool {
4652        name.eq_ignore_ascii_case("PERCENTILE_CONT") || name.eq_ignore_ascii_case("PERCENTILE_DISC")
4653    }
4654
4655    fn is_hypothetical_set_name(name: &str) -> bool {
4656        name.eq_ignore_ascii_case("RANK")
4657            || name.eq_ignore_ascii_case("DENSE_RANK")
4658            || name.eq_ignore_ascii_case("CUME_DIST")
4659            || name.eq_ignore_ascii_case("PERCENT_RANK")
4660    }
4661
4662    fn target_supports_distinct_on(target: DialectType) -> bool {
4663        matches!(target, DialectType::PostgreSQL | DialectType::DuckDB)
4664    }
4665
4666    fn node_has_distinct_on(expr: &Expression) -> bool {
4667        matches!(
4668            expr,
4669            Expression::Select(select)
4670                if select
4671                    .distinct_on
4672                    .as_ref()
4673                    .is_some_and(|distinct_on| !distinct_on.is_empty())
4674        )
4675    }
4676
4677    fn node_has_recursive_with(expr: &Expression) -> bool {
4678        fn recursive(with: &Option<With>) -> bool {
4679            with.as_ref().is_some_and(|with| with.recursive)
4680        }
4681
4682        match expr {
4683            Expression::With(with) => with.recursive,
4684            Expression::Select(select) => recursive(&select.with),
4685            Expression::Union(union) => recursive(&union.with),
4686            Expression::Intersect(intersect) => recursive(&intersect.with),
4687            Expression::Except(except) => recursive(&except.with),
4688            Expression::Pivot(pivot) => recursive(&pivot.with),
4689            Expression::Insert(insert) => recursive(&insert.with),
4690            Expression::Update(update) => recursive(&update.with),
4691            Expression::Delete(delete) => recursive(&delete.with),
4692            _ => false,
4693        }
4694    }
4695
4696    fn node_has_lateral(expr: &Expression) -> bool {
4697        fn join_has_lateral(join: &Join) -> bool {
4698            matches!(
4699                join.kind,
4700                crate::expressions::JoinKind::Lateral | crate::expressions::JoinKind::LeftLateral
4701            ) || Dialect::node_has_lateral(&join.this)
4702                || join.on.as_ref().is_some_and(Dialect::node_has_lateral)
4703                || join
4704                    .match_condition
4705                    .as_ref()
4706                    .is_some_and(Dialect::node_has_lateral)
4707                || join.pivots.iter().any(Dialect::node_has_lateral)
4708        }
4709
4710        fn joins_have_lateral(joins: &[Join]) -> bool {
4711            joins.iter().any(join_has_lateral)
4712        }
4713
4714        match expr {
4715            Expression::Subquery(subquery) => {
4716                subquery.lateral || Dialect::node_has_lateral(&subquery.this)
4717            }
4718            Expression::Lateral(_) | Expression::LateralView(_) => true,
4719            Expression::Join(join) => join_has_lateral(join),
4720            Expression::Select(select) => {
4721                !select.lateral_views.is_empty()
4722                    || joins_have_lateral(&select.joins)
4723                    || select
4724                        .from
4725                        .as_ref()
4726                        .is_some_and(|from| from.expressions.iter().any(Dialect::node_has_lateral))
4727            }
4728            Expression::JoinedTable(joined) => {
4729                !joined.lateral_views.is_empty()
4730                    || Dialect::node_has_lateral(&joined.left)
4731                    || joins_have_lateral(&joined.joins)
4732            }
4733            Expression::Update(update) => {
4734                joins_have_lateral(&update.table_joins) || joins_have_lateral(&update.from_joins)
4735            }
4736            _ => false,
4737        }
4738    }
4739
4740    fn node_has_join_using(expr: &Expression) -> bool {
4741        fn has_using(joins: &[Join]) -> bool {
4742            joins.iter().any(|join| !join.using.is_empty())
4743        }
4744
4745        match expr {
4746            Expression::Join(join) => !join.using.is_empty(),
4747            Expression::Select(select) => has_using(&select.joins),
4748            Expression::JoinedTable(joined) => has_using(&joined.joins),
4749            Expression::Update(update) => {
4750                has_using(&update.table_joins) || has_using(&update.from_joins)
4751            }
4752            Expression::Delete(delete) => has_using(&delete.joins),
4753            _ => false,
4754        }
4755    }
4756
4757    fn node_has_natural_join(expr: &Expression) -> bool {
4758        fn is_natural(join: &Join) -> bool {
4759            matches!(
4760                join.kind,
4761                crate::expressions::JoinKind::Natural
4762                    | crate::expressions::JoinKind::NaturalLeft
4763                    | crate::expressions::JoinKind::NaturalRight
4764                    | crate::expressions::JoinKind::NaturalFull
4765            )
4766        }
4767
4768        fn has_natural(joins: &[Join]) -> bool {
4769            joins.iter().any(is_natural)
4770        }
4771
4772        match expr {
4773            Expression::Join(join) => is_natural(join),
4774            Expression::Select(select) => has_natural(&select.joins),
4775            Expression::JoinedTable(joined) => has_natural(&joined.joins),
4776            Expression::Update(update) => {
4777                has_natural(&update.table_joins) || has_natural(&update.from_joins)
4778            }
4779            Expression::Delete(delete) => has_natural(&delete.joins),
4780            _ => false,
4781        }
4782    }
4783
4784    fn node_has_unsupported_relation_column_aliases(expr: &Expression) -> bool {
4785        match expr {
4786            Expression::Table(table) => !table.column_aliases.is_empty(),
4787            Expression::Alias(alias) => {
4788                !alias.column_aliases.is_empty()
4789                    && matches!(
4790                        alias.this,
4791                        Expression::Table(_) | Expression::JoinedTable(_)
4792                    )
4793            }
4794            _ => false,
4795        }
4796    }
4797
4798    fn node_has_qualified_whole_row_aggregate_argument(expr: &Expression) -> bool {
4799        fn contains_qualified_star(expr: &Expression) -> bool {
4800            match expr {
4801                Expression::Star(star) => star.table.is_some(),
4802                // A star projected by an embedded query is not an argument of
4803                // the surrounding aggregate (for example, inside EXISTS).
4804                Expression::Select(_)
4805                | Expression::Subquery(_)
4806                | Expression::Union(_)
4807                | Expression::Intersect(_)
4808                | Expression::Except(_) => false,
4809                _ => expr.children().into_iter().any(contains_qualified_star),
4810            }
4811        }
4812
4813        let is_aggregate = matches!(
4814            expr,
4815            Expression::AggregateFunction(_)
4816                | Expression::Count(_)
4817                | Expression::Sum(_)
4818                | Expression::Avg(_)
4819                | Expression::Min(_)
4820                | Expression::Max(_)
4821                | Expression::GroupConcat(_)
4822                | Expression::StringAgg(_)
4823                | Expression::ListAgg(_)
4824                | Expression::ArrayAgg(_)
4825                | Expression::CountIf(_)
4826                | Expression::SumIf(_)
4827                | Expression::Stddev(_)
4828                | Expression::StddevPop(_)
4829                | Expression::StddevSamp(_)
4830                | Expression::Variance(_)
4831                | Expression::VarPop(_)
4832                | Expression::VarSamp(_)
4833                | Expression::Median(_)
4834                | Expression::Mode(_)
4835                | Expression::First(_)
4836                | Expression::Last(_)
4837                | Expression::AnyValue(_)
4838                | Expression::ApproxDistinct(_)
4839                | Expression::ApproxCountDistinct(_)
4840                | Expression::ApproxPercentile(_)
4841                | Expression::Percentile(_)
4842                | Expression::LogicalAnd(_)
4843                | Expression::LogicalOr(_)
4844                | Expression::Skewness(_)
4845                | Expression::BitwiseCount(_)
4846                | Expression::BitwiseAndAgg(_)
4847                | Expression::BitwiseOrAgg(_)
4848                | Expression::BitwiseXorAgg(_)
4849                | Expression::ArrayConcatAgg(_)
4850                | Expression::ArrayUniqueAgg(_)
4851                | Expression::BoolXorAgg(_)
4852                | Expression::JsonArrayAgg(_)
4853                | Expression::JsonObjectAgg(_)
4854                | Expression::ParameterizedAgg(_)
4855                | Expression::ArgMax(_)
4856                | Expression::ArgMin(_)
4857                | Expression::ApproxTopK(_)
4858                | Expression::ApproxTopKAccumulate(_)
4859                | Expression::ApproxTopKCombine(_)
4860                | Expression::ApproxTopKEstimate(_)
4861                | Expression::ApproxTopSum(_)
4862                | Expression::ApproxQuantiles(_)
4863                | Expression::AnonymousAggFunc(_)
4864                | Expression::CombinedAggFunc(_)
4865                | Expression::CombinedParameterizedAgg(_)
4866                | Expression::HashAgg(_)
4867                | Expression::ObjectAgg(_)
4868                | Expression::AIAgg(_)
4869        );
4870
4871        is_aggregate && expr.children().into_iter().any(contains_qualified_star)
4872    }
4873
4874    fn target_supports_remaining_unnest(target: DialectType) -> bool {
4875        matches!(
4876            target,
4877            DialectType::PostgreSQL
4878                | DialectType::BigQuery
4879                | DialectType::DuckDB
4880                | DialectType::Presto
4881                | DialectType::Trino
4882                | DialectType::Athena
4883        )
4884    }
4885
4886    fn target_supports_remaining_explode(target: DialectType) -> bool {
4887        matches!(
4888            target,
4889            DialectType::Spark | DialectType::Databricks | DialectType::Hive
4890        )
4891    }
4892
4893    fn target_lacks_array_agg(target: DialectType) -> bool {
4894        matches!(
4895            target,
4896            DialectType::Fabric
4897                | DialectType::TSQL
4898                | DialectType::MySQL
4899                | DialectType::SQLite
4900                | DialectType::Oracle
4901        )
4902    }
4903
4904    fn node_is_unnest(expr: &Expression) -> bool {
4905        matches!(expr, Expression::Unnest(_)) || Self::node_is_function_named(expr, "UNNEST")
4906    }
4907
4908    fn node_is_explode(expr: &Expression) -> bool {
4909        matches!(expr, Expression::Explode(_) | Expression::ExplodeOuter(_))
4910            || Self::node_is_function_named(expr, "EXPLODE")
4911            || Self::node_is_function_named(expr, "EXPLODE_OUTER")
4912    }
4913
4914    fn node_is_array_agg(expr: &Expression) -> bool {
4915        matches!(expr, Expression::ArrayAgg(_)) || Self::node_is_function_named(expr, "ARRAY_AGG")
4916    }
4917
4918    fn node_is_distinct_string_agg(expr: &Expression) -> bool {
4919        match expr {
4920            Expression::StringAgg(agg) => agg.distinct,
4921            Expression::Function(function) => {
4922                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
4923            }
4924            Expression::AggregateFunction(function) => {
4925                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
4926            }
4927            _ => false,
4928        }
4929    }
4930
4931    fn postgres_tsql_unsupported_collation_name(expr: &Expression) -> Option<&'static str> {
4932        let Expression::Collation(collation) = expr else {
4933            return None;
4934        };
4935
4936        if collation.collation.eq_ignore_ascii_case("C") {
4937            Some("C")
4938        } else if collation.collation.eq_ignore_ascii_case("POSIX") {
4939            Some("POSIX")
4940        } else {
4941            None
4942        }
4943    }
4944
4945    fn postgres_tsql_unsupported_composite_semantics(expr: &Expression) -> Option<&'static str> {
4946        match expr {
4947            Expression::Tuple(_) | Expression::Struct(_) | Expression::StructFunc(_) => {
4948                Some("row/composite values")
4949            }
4950            Expression::Function(function)
4951                if !function.quoted && function.name.eq_ignore_ascii_case("ROW") =>
4952            {
4953                Some("row/composite values")
4954            }
4955            Expression::StructExtract(_) => Some("row/composite field access"),
4956            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) if matches!(&cast.this, Expression::Star(star) if star.table.is_some()) => {
4957                Some("qualified whole-row casts")
4958            }
4959            _ => None,
4960        }
4961    }
4962
4963    fn node_is_postgres_unknown_cast(expr: &Expression) -> bool {
4964        match expr {
4965            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
4966                normalization::is_postgres_unknown_type(&cast.to)
4967            }
4968            _ => false,
4969        }
4970    }
4971
4972    fn postgres_tsql_unsupported_array_semantics(expr: &Expression) -> Option<&'static str> {
4973        match expr {
4974            Expression::Array(_) | Expression::ArrayFunc(_) => Some("array literals"),
4975            Expression::Subscript(_) => Some("array subscripts"),
4976            Expression::ArraySlice(_) => Some("array slices"),
4977            Expression::DataType(DataType::Array { .. }) => Some("array data types"),
4978            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
4979                if matches!(&cast.to, DataType::Array { .. }) =>
4980            {
4981                Some("array data types")
4982            }
4983            Expression::ArrayLength(_) | Expression::ArraySize(_) => Some("ARRAY_LENGTH"),
4984            Expression::Cardinality(_) => Some("CARDINALITY"),
4985            Expression::ArrayToString(_) | Expression::ArrayJoin(_) => Some("ARRAY_TO_STRING"),
4986            Expression::StringToArray(_) => Some("STRING_TO_ARRAY"),
4987            Expression::ArrayContains(_)
4988            | Expression::ArrayPosition(_)
4989            | Expression::ArrayAppend(_)
4990            | Expression::ArrayPrepend(_)
4991            | Expression::ArrayConcat(_)
4992            | Expression::ArraySort(_)
4993            | Expression::ArrayReverse(_)
4994            | Expression::ArrayDistinct(_)
4995            | Expression::ArrayFilter(_)
4996            | Expression::ArrayTransform(_)
4997            | Expression::ArrayFlatten(_)
4998            | Expression::ArrayCompact(_)
4999            | Expression::ArrayIntersect(_)
5000            | Expression::ArrayUnion(_)
5001            | Expression::ArrayExcept(_)
5002            | Expression::ArrayRemove(_)
5003            | Expression::ArrayZip(_)
5004            | Expression::ArrayAll(_)
5005            | Expression::ArrayAny(_)
5006            | Expression::ArrayConstructCompact(_)
5007            | Expression::ArraySum(_) => Some("array functions"),
5008            Expression::ArrayContainsAll(_)
5009            | Expression::ArrayContainedBy(_)
5010            | Expression::ArrayOverlaps(_) => Some("array operators"),
5011            Expression::Function(function) => {
5012                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5013            }
5014            Expression::AggregateFunction(function) => {
5015                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5016            }
5017            _ => None,
5018        }
5019    }
5020
5021    fn postgres_tsql_unsupported_array_function_name_str(name: &str) -> Option<&'static str> {
5022        if name.eq_ignore_ascii_case("ARRAY") {
5023            Some("array literals")
5024        } else if name.eq_ignore_ascii_case("ARRAY_LENGTH")
5025            || name.eq_ignore_ascii_case("ARRAY_SIZE")
5026        {
5027            Some("ARRAY_LENGTH")
5028        } else if name.eq_ignore_ascii_case("CARDINALITY") {
5029            Some("CARDINALITY")
5030        } else if name.eq_ignore_ascii_case("ARRAY_TO_STRING")
5031            || name.eq_ignore_ascii_case("ARRAY_JOIN")
5032        {
5033            Some("ARRAY_TO_STRING")
5034        } else if name.eq_ignore_ascii_case("STRING_TO_ARRAY") {
5035            Some("STRING_TO_ARRAY")
5036        } else {
5037            None
5038        }
5039    }
5040
5041    fn node_is_regex_predicate(expr: &Expression) -> bool {
5042        matches!(
5043            expr,
5044            Expression::SimilarTo(_) | Expression::RegexpLike(_) | Expression::RegexpILike(_)
5045        ) || Self::node_is_function_named(expr, "REGEXP_LIKE")
5046            || Self::node_is_function_named(expr, "REGEXP_I_LIKE")
5047            || Self::node_is_function_named(expr, "REGEXP_ILIKE")
5048    }
5049
5050    fn node_is_non_subquery_any(expr: &Expression) -> bool {
5051        matches!(
5052            expr,
5053            Expression::Any(q) if !Self::quantified_rhs_is_subquery(&q.subquery)
5054        )
5055    }
5056
5057    fn quantified_rhs_is_subquery(expr: &Expression) -> bool {
5058        match expr {
5059            Expression::Select(_) | Expression::Subquery(_) => true,
5060            Expression::Paren(paren) => Self::quantified_rhs_is_subquery(&paren.this),
5061            _ => false,
5062        }
5063    }
5064
5065    fn node_is_row_value_subquery_comparison(expr: &Expression) -> bool {
5066        match expr {
5067            Expression::In(in_expr) => {
5068                Self::in_rhs_is_subquery_like(in_expr) && Self::expr_is_row_value(&in_expr.this)
5069            }
5070            Expression::Eq(op) | Expression::Neq(op) => {
5071                (Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right))
5072                    || (Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left))
5073            }
5074            _ => false,
5075        }
5076    }
5077
5078    fn node_is_row_value_values_membership(expr: &Expression) -> bool {
5079        matches!(
5080            expr,
5081            Expression::In(in_expr)
5082                if Self::expr_is_row_value(&in_expr.this)
5083                    && Self::in_rhs_is_values_like(in_expr)
5084        )
5085    }
5086
5087    fn expr_is_row_value(expr: &Expression) -> bool {
5088        match expr {
5089            Expression::Tuple(tuple) => tuple.expressions.len() > 1,
5090            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
5091                function.args.len() > 1
5092            }
5093            Expression::Paren(paren) => Self::expr_is_row_value(&paren.this),
5094            _ => false,
5095        }
5096    }
5097
5098    fn expr_is_subquery_like(expr: &Expression) -> bool {
5099        match expr {
5100            Expression::Select(_) | Expression::Subquery(_) => true,
5101            Expression::Paren(paren) => Self::expr_is_subquery_like(&paren.this),
5102            _ => false,
5103        }
5104    }
5105
5106    fn in_rhs_is_subquery_like(in_expr: &crate::expressions::In) -> bool {
5107        if in_expr
5108            .query
5109            .as_ref()
5110            .is_some_and(Self::expr_is_subquery_like)
5111        {
5112            return true;
5113        }
5114
5115        in_expr.expressions.len() == 1 && Self::expr_is_subquery_like(&in_expr.expressions[0])
5116    }
5117
5118    fn in_rhs_is_values_like(in_expr: &crate::expressions::In) -> bool {
5119        if in_expr
5120            .query
5121            .as_ref()
5122            .is_some_and(Self::expr_is_values_like)
5123        {
5124            return true;
5125        }
5126
5127        (in_expr.expressions.len() == 1
5128            && Self::expr_is_values_like(&in_expr.expressions[0]))
5129            || in_expr.expressions.first().is_some_and(|expr| {
5130                matches!(expr, Expression::Function(function) if function.name.eq_ignore_ascii_case("VALUES"))
5131            })
5132    }
5133
5134    fn expr_is_values_like(expr: &Expression) -> bool {
5135        match expr {
5136            Expression::Values(_) => true,
5137            Expression::Paren(paren) => Self::expr_is_values_like(&paren.this),
5138            Expression::Subquery(subquery) => Self::expr_is_values_like(&subquery.this),
5139            _ => false,
5140        }
5141    }
5142
5143    fn normalize_tsql_fetch_overlaps_date_bin(expr: Expression) -> Result<Expression> {
5144        transform_recursive(expr, &|e| match e {
5145            Expression::Select(mut select) => {
5146                if select.top.is_none() && select.offset.is_none() {
5147                    if let Some(fetch) = select.fetch.take() {
5148                        if let Some(top) = Self::fetch_with_ties_to_top(fetch.clone()) {
5149                            select.top = Some(top);
5150                        } else {
5151                            select.fetch = Some(fetch);
5152                        }
5153                    }
5154                }
5155                Self::rewrite_tsql_overlaps_in_select_predicates(&mut select)?;
5156                Ok(Expression::Select(select))
5157            }
5158            Expression::DateBin(date_bin) => {
5159                let date_bin = *date_bin;
5160                if let Some(rewritten) = Self::date_bin_to_date_bucket(date_bin.clone()) {
5161                    Ok(rewritten)
5162                } else {
5163                    Ok(Expression::DateBin(Box::new(date_bin)))
5164                }
5165            }
5166            Expression::Function(function) => {
5167                let function = *function;
5168                if function.name.eq_ignore_ascii_case("DATE_BIN") {
5169                    if let Some(rewritten) = Self::date_bin_function_to_date_bucket(&function) {
5170                        Ok(rewritten)
5171                    } else {
5172                        Ok(Expression::Function(Box::new(function)))
5173                    }
5174                } else {
5175                    Ok(Expression::Function(Box::new(function)))
5176                }
5177            }
5178            _ => Ok(e),
5179        })
5180    }
5181
5182    fn rewrite_tsql_overlaps_in_select_predicates(
5183        select: &mut crate::expressions::Select,
5184    ) -> Result<()> {
5185        if let Some(where_clause) = &mut select.where_clause {
5186            where_clause.this = Self::rewrite_tsql_overlaps_predicate(where_clause.this.clone())?;
5187        }
5188        if let Some(having) = &mut select.having {
5189            having.this = Self::rewrite_tsql_overlaps_predicate(having.this.clone())?;
5190        }
5191        if let Some(qualify) = &mut select.qualify {
5192            qualify.this = Self::rewrite_tsql_overlaps_predicate(qualify.this.clone())?;
5193        }
5194        for join in &mut select.joins {
5195            if let Some(on) = join.on.take() {
5196                join.on = Some(Self::rewrite_tsql_overlaps_predicate(on)?);
5197            }
5198            if let Some(match_condition) = join.match_condition.take() {
5199                join.match_condition =
5200                    Some(Self::rewrite_tsql_overlaps_predicate(match_condition)?);
5201            }
5202        }
5203        Ok(())
5204    }
5205
5206    fn rewrite_tsql_overlaps_predicate(expr: Expression) -> Result<Expression> {
5207        transform_recursive(expr, &|e| match e {
5208            Expression::Overlaps(overlaps) => {
5209                let overlaps = *overlaps;
5210                if let Some(rewritten) = Self::rewrite_full_overlaps_for_tsql(&overlaps) {
5211                    Ok(rewritten)
5212                } else {
5213                    Ok(Expression::Overlaps(Box::new(overlaps)))
5214                }
5215            }
5216            _ => Ok(e),
5217        })
5218    }
5219
5220    fn fetch_with_ties_to_top(fetch: Fetch) -> Option<Top> {
5221        if !fetch.with_ties {
5222            return None;
5223        }
5224
5225        fetch.count.map(|count| Top {
5226            this: count,
5227            percent: fetch.percent,
5228            with_ties: true,
5229            parenthesized: true,
5230        })
5231    }
5232
5233    fn rewrite_full_overlaps_for_tsql(
5234        overlaps: &crate::expressions::OverlapsExpr,
5235    ) -> Option<Expression> {
5236        let (left_start, left_end, right_start, right_end) =
5237            if let (Some(left_start), Some(left_end), Some(right_start), Some(right_end)) = (
5238                overlaps.left_start.as_ref(),
5239                overlaps.left_end.as_ref(),
5240                overlaps.right_start.as_ref(),
5241                overlaps.right_end.as_ref(),
5242            ) {
5243                (left_start, left_end, right_start, right_end)
5244            } else if let (
5245                Some(Expression::Tuple(left_tuple)),
5246                Some(Expression::Tuple(right_tuple)),
5247            ) = (&overlaps.this, &overlaps.expression)
5248            {
5249                if left_tuple.expressions.len() != 2 || right_tuple.expressions.len() != 2 {
5250                    return None;
5251                }
5252                (
5253                    &left_tuple.expressions[0],
5254                    &left_tuple.expressions[1],
5255                    &right_tuple.expressions[0],
5256                    &right_tuple.expressions[1],
5257                )
5258            } else {
5259                return None;
5260            };
5261
5262        let left_min = Self::case_min(left_start.clone(), left_end.clone());
5263        let left_max = Self::case_max(left_start.clone(), left_end.clone());
5264        let right_min = Self::case_min(right_start.clone(), right_end.clone());
5265        let right_max = Self::case_max(right_start.clone(), right_end.clone());
5266
5267        Some(Expression::And(Box::new(BinaryOp::new(
5268            Expression::Lte(Box::new(BinaryOp::new(left_min, right_max))),
5269            Expression::Lte(Box::new(BinaryOp::new(right_min, left_max))),
5270        ))))
5271    }
5272
5273    fn case_min(left: Expression, right: Expression) -> Expression {
5274        Expression::Case(Box::new(Case {
5275            operand: None,
5276            whens: vec![(
5277                Expression::Lte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
5278                left,
5279            )],
5280            else_: Some(right),
5281            comments: Vec::new(),
5282            inferred_type: None,
5283        }))
5284    }
5285
5286    fn case_max(left: Expression, right: Expression) -> Expression {
5287        Expression::Case(Box::new(Case {
5288            operand: None,
5289            whens: vec![(
5290                Expression::Gte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
5291                left,
5292            )],
5293            else_: Some(right),
5294            comments: Vec::new(),
5295            inferred_type: None,
5296        }))
5297    }
5298
5299    fn date_bin_to_date_bucket(date_bin: DateBin) -> Option<Expression> {
5300        if date_bin.unit.is_some() || date_bin.zone.is_some() {
5301            return None;
5302        }
5303
5304        let (datepart, number) = Self::date_bucket_parts(&date_bin.this)?;
5305        let mut args = vec![
5306            Self::date_bucket_datepart(datepart),
5307            number,
5308            *date_bin.expression,
5309        ];
5310        if let Some(origin) = date_bin.origin {
5311            args.push(*origin);
5312        }
5313
5314        Some(Expression::Function(Box::new(Function::new(
5315            "DATE_BUCKET".to_string(),
5316            args,
5317        ))))
5318    }
5319
5320    fn date_bin_function_to_date_bucket(function: &Function) -> Option<Expression> {
5321        if !(2..=3).contains(&function.args.len()) {
5322            return None;
5323        }
5324
5325        let (datepart, number) = Self::date_bucket_parts(&function.args[0])?;
5326        let mut args = vec![
5327            Self::date_bucket_datepart(datepart),
5328            number,
5329            function.args[1].clone(),
5330        ];
5331        if let Some(origin) = function.args.get(2) {
5332            args.push(origin.clone());
5333        }
5334
5335        Some(Expression::Function(Box::new(Function::new(
5336            "DATE_BUCKET".to_string(),
5337            args,
5338        ))))
5339    }
5340
5341    fn date_bucket_parts(stride: &Expression) -> Option<(&'static str, Expression)> {
5342        match stride {
5343            Expression::Literal(lit) => match lit.as_ref() {
5344                Literal::String(value) => Self::date_bucket_parts_from_string(value),
5345                _ => None,
5346            },
5347            Expression::Interval(interval) => Self::date_bucket_parts_from_interval(interval),
5348            _ => None,
5349        }
5350    }
5351
5352    fn date_bucket_parts_from_interval(interval: &Interval) -> Option<(&'static str, Expression)> {
5353        match &interval.unit {
5354            Some(IntervalUnitSpec::Simple { unit, .. }) => {
5355                let datepart = Self::date_bucket_datepart_from_unit(*unit)?;
5356                let amount = interval
5357                    .this
5358                    .as_ref()
5359                    .and_then(Self::date_bucket_amount_expr)?;
5360                Some((datepart, amount))
5361            }
5362            None => interval.this.as_ref().and_then(|expr| match expr {
5363                Expression::Literal(lit) => match lit.as_ref() {
5364                    Literal::String(value) => Self::date_bucket_parts_from_string(value),
5365                    _ => None,
5366                },
5367                _ => None,
5368            }),
5369            _ => None,
5370        }
5371    }
5372
5373    fn date_bucket_parts_from_string(value: &str) -> Option<(&'static str, Expression)> {
5374        let mut parts = value.split_whitespace();
5375        let amount = parts.next()?;
5376        let unit = parts.next()?;
5377        if parts.next().is_some() {
5378            return None;
5379        }
5380
5381        Some((
5382            Self::date_bucket_datepart_from_name(unit)?,
5383            Self::positive_integer_expr(amount)?,
5384        ))
5385    }
5386
5387    fn date_bucket_amount_expr(expr: &Expression) -> Option<Expression> {
5388        match expr {
5389            Expression::Literal(lit) => match lit.as_ref() {
5390                Literal::Number(value) => Self::positive_integer_expr(value),
5391                Literal::String(value) => Self::positive_integer_expr(value),
5392                _ => None,
5393            },
5394            _ => Some(expr.clone()),
5395        }
5396    }
5397
5398    fn positive_integer_expr(value: &str) -> Option<Expression> {
5399        let parsed = value.trim().parse::<i64>().ok()?;
5400        (parsed > 0).then(|| Expression::number(parsed))
5401    }
5402
5403    fn date_bucket_datepart(datepart: &str) -> Expression {
5404        Expression::Var(Box::new(Var {
5405            this: datepart.to_string(),
5406        }))
5407    }
5408
5409    fn date_bucket_datepart_from_unit(unit: IntervalUnit) -> Option<&'static str> {
5410        match unit {
5411            IntervalUnit::Week => Some("WEEK"),
5412            IntervalUnit::Day => Some("DAY"),
5413            IntervalUnit::Hour => Some("HOUR"),
5414            IntervalUnit::Minute => Some("MINUTE"),
5415            IntervalUnit::Second => Some("SECOND"),
5416            IntervalUnit::Millisecond => Some("MILLISECOND"),
5417            _ => None,
5418        }
5419    }
5420
5421    fn date_bucket_datepart_from_name(unit: &str) -> Option<&'static str> {
5422        match unit.trim().to_ascii_uppercase().as_str() {
5423            "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" => Some("WEEK"),
5424            "DAY" | "DAYS" | "D" | "DD" => Some("DAY"),
5425            "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some("HOUR"),
5426            "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some("MINUTE"),
5427            "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some("SECOND"),
5428            "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MILLISEC" | "MILLISECS" => {
5429                Some("MILLISECOND")
5430            }
5431            _ => None,
5432        }
5433    }
5434
5435    fn node_has_fetch_with_ties(expr: &Expression) -> bool {
5436        matches!(
5437            expr,
5438            Expression::Select(select)
5439                if select
5440                    .fetch
5441                    .as_ref()
5442                    .is_some_and(|fetch| fetch.with_ties)
5443        )
5444    }
5445
5446    fn node_is_overlaps(expr: &Expression) -> bool {
5447        matches!(expr, Expression::Overlaps(_))
5448    }
5449
5450    fn node_is_date_bin(expr: &Expression) -> bool {
5451        matches!(expr, Expression::DateBin(_)) || Self::node_is_function_named(expr, "DATE_BIN")
5452    }
5453
5454    fn node_is_function_named(expr: &Expression, name: &str) -> bool {
5455        match expr {
5456            Expression::Function(function) => function.name.eq_ignore_ascii_case(name),
5457            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case(name),
5458            _ => false,
5459        }
5460    }
5461
5462    fn node_is_postgres_json_build_object(expr: &Expression) -> bool {
5463        match expr {
5464            Expression::Function(function) => {
5465                function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
5466                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT")
5467            }
5468            _ => false,
5469        }
5470    }
5471
5472    fn postgres_json_build_object_can_lower_to_json_object(expr: &Expression) -> bool {
5473        matches!(
5474            expr,
5475            Expression::Function(function)
5476                if (function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
5477                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT"))
5478                    && !function.distinct
5479                    && function.args.len() % 2 == 0
5480        )
5481    }
5482
5483    fn node_is_postgres_json_array_elements(expr: &Expression) -> bool {
5484        matches!(
5485            expr,
5486            Expression::Function(function)
5487                if function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS")
5488                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS")
5489                    || function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT")
5490                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT")
5491        )
5492    }
5493
5494    fn postgres_tsql_unsupported_function_name(expr: &Expression) -> Option<&'static str> {
5495        match expr {
5496            Expression::Lpad(_) => Some("LPAD"),
5497            Expression::Rpad(_) => Some("RPAD"),
5498            Expression::SplitPart(_) => Some("SPLIT_PART"),
5499            Expression::Initcap(_) => Some("INITCAP"),
5500            Expression::ToJson(_) => Some("TO_JSON"),
5501            Expression::JSONBObjectAgg(_) => Some("JSONB_OBJECT_AGG"),
5502            Expression::ToNumber(_) => Some("TO_NUMBER"),
5503            Expression::WidthBucket(_) => Some("WIDTH_BUCKET"),
5504            Expression::BitwiseAndAgg(_) => Some("BIT_AND"),
5505            Expression::BitwiseOrAgg(_) => Some("BIT_OR"),
5506            Expression::BitwiseXorAgg(_) => Some("BIT_XOR"),
5507            Expression::Corr(_) => Some("CORR"),
5508            Expression::CovarPop(_) => Some("COVAR_POP"),
5509            Expression::CovarSamp(_) => Some("COVAR_SAMP"),
5510            Expression::RegrAvgx(_) => Some("REGR_AVGX"),
5511            Expression::RegrAvgy(_) => Some("REGR_AVGY"),
5512            Expression::RegrCount(_) => Some("REGR_COUNT"),
5513            Expression::RegrIntercept(_) => Some("REGR_INTERCEPT"),
5514            Expression::RegrR2(_) => Some("REGR_R2"),
5515            Expression::RegrSlope(_) => Some("REGR_SLOPE"),
5516            Expression::RegrSxx(_) => Some("REGR_SXX"),
5517            Expression::RegrSxy(_) => Some("REGR_SXY"),
5518            Expression::RegrSyy(_) => Some("REGR_SYY"),
5519            Expression::Function(function) => {
5520                Self::postgres_tsql_unsupported_function_name_str(&function.name)
5521            }
5522            Expression::AggregateFunction(function) => {
5523                Self::postgres_tsql_unsupported_function_name_str(&function.name)
5524            }
5525            _ => None,
5526        }
5527    }
5528
5529    fn postgres_tsql_unsupported_function_name_str(name: &str) -> Option<&'static str> {
5530        if name.eq_ignore_ascii_case("LPAD") {
5531            Some("LPAD")
5532        } else if name.eq_ignore_ascii_case("RPAD") {
5533            Some("RPAD")
5534        } else if name.eq_ignore_ascii_case("SPLIT_PART") {
5535            Some("SPLIT_PART")
5536        } else if name.eq_ignore_ascii_case("INITCAP") {
5537            Some("INITCAP")
5538        } else if name.eq_ignore_ascii_case("TO_JSON") {
5539            Some("TO_JSON")
5540        } else if name.eq_ignore_ascii_case("TO_JSONB") {
5541            Some("TO_JSONB")
5542        } else if name.eq_ignore_ascii_case("JSONB_OBJECT_AGG") {
5543            Some("JSONB_OBJECT_AGG")
5544        } else if name.eq_ignore_ascii_case("ROW_TO_JSON") {
5545            Some("ROW_TO_JSON")
5546        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS") {
5547            Some("JSON_ARRAY_ELEMENTS")
5548        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS") {
5549            Some("JSONB_ARRAY_ELEMENTS")
5550        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT") {
5551            Some("JSON_ARRAY_ELEMENTS_TEXT")
5552        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT") {
5553            Some("JSONB_ARRAY_ELEMENTS_TEXT")
5554        } else if name.eq_ignore_ascii_case("ENCODE") {
5555            Some("ENCODE")
5556        } else if name.eq_ignore_ascii_case("AGE") {
5557            Some("AGE")
5558        } else if name.eq_ignore_ascii_case("ERF") {
5559            Some("ERF")
5560        } else if name.eq_ignore_ascii_case("GCD") {
5561            Some("GCD")
5562        } else if name.eq_ignore_ascii_case("LCM") {
5563            Some("LCM")
5564        } else if name.eq_ignore_ascii_case("QUOTE_LITERAL") {
5565            Some("QUOTE_LITERAL")
5566        } else if name.eq_ignore_ascii_case("WIDTH_BUCKET") {
5567            Some("WIDTH_BUCKET")
5568        } else if name.eq_ignore_ascii_case("SCALE") {
5569            Some("SCALE")
5570        } else if name.eq_ignore_ascii_case("TRIM_SCALE") {
5571            Some("TRIM_SCALE")
5572        } else if name.eq_ignore_ascii_case("MIN_SCALE") {
5573            Some("MIN_SCALE")
5574        } else if name.eq_ignore_ascii_case("FACTORIAL") {
5575            Some("FACTORIAL")
5576        } else if name.eq_ignore_ascii_case("PG_LSN") {
5577            Some("PG_LSN")
5578        } else if name.eq_ignore_ascii_case("TO_CHAR") {
5579            Some("TO_CHAR")
5580        } else if name.eq_ignore_ascii_case("PG_TYPEOF") {
5581            Some("PG_TYPEOF")
5582        } else if name.eq_ignore_ascii_case("BIT_AND") {
5583            Some("BIT_AND")
5584        } else if name.eq_ignore_ascii_case("BIT_OR") {
5585            Some("BIT_OR")
5586        } else if name.eq_ignore_ascii_case("BIT_XOR") {
5587            Some("BIT_XOR")
5588        } else if name.eq_ignore_ascii_case("CORR") {
5589            Some("CORR")
5590        } else if name.eq_ignore_ascii_case("COVAR_POP") {
5591            Some("COVAR_POP")
5592        } else if name.eq_ignore_ascii_case("COVAR_SAMP") {
5593            Some("COVAR_SAMP")
5594        } else if name.eq_ignore_ascii_case("REGR_AVGX") {
5595            Some("REGR_AVGX")
5596        } else if name.eq_ignore_ascii_case("REGR_AVGY") {
5597            Some("REGR_AVGY")
5598        } else if name.eq_ignore_ascii_case("REGR_COUNT") {
5599            Some("REGR_COUNT")
5600        } else if name.eq_ignore_ascii_case("REGR_INTERCEPT") {
5601            Some("REGR_INTERCEPT")
5602        } else if name.eq_ignore_ascii_case("REGR_R2") {
5603            Some("REGR_R2")
5604        } else if name.eq_ignore_ascii_case("REGR_SLOPE") {
5605            Some("REGR_SLOPE")
5606        } else if name.eq_ignore_ascii_case("REGR_SXX") {
5607            Some("REGR_SXX")
5608        } else if name.eq_ignore_ascii_case("REGR_SXY") {
5609            Some("REGR_SXY")
5610        } else if name.eq_ignore_ascii_case("REGR_SYY") {
5611            Some("REGR_SYY")
5612        } else if name.eq_ignore_ascii_case("FLOAT8_ACCUM") {
5613            Some("FLOAT8_ACCUM")
5614        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_ACCUM") {
5615            Some("FLOAT8_REGR_ACCUM")
5616        } else if name.eq_ignore_ascii_case("FLOAT8_COMBINE") {
5617            Some("FLOAT8_COMBINE")
5618        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_COMBINE") {
5619            Some("FLOAT8_REGR_COMBINE")
5620        } else if name.eq_ignore_ascii_case("BOOLAND_STATEFUNC") {
5621            Some("BOOLAND_STATEFUNC")
5622        } else if name.eq_ignore_ascii_case("BOOLOR_STATEFUNC") {
5623            Some("BOOLOR_STATEFUNC")
5624        } else {
5625            None
5626        }
5627    }
5628
5629    fn normalize_postgres_trim_for_tsql(expr: Expression) -> Result<Expression> {
5630        transform_recursive(expr, &|e| match e {
5631            Expression::Trim(trim) => {
5632                let mut trim = *trim;
5633                match trim.position {
5634                    crate::expressions::TrimPosition::Both
5635                        if trim.position_explicit && trim.characters.is_some() =>
5636                    {
5637                        trim.position_explicit = false;
5638                        trim.sql_standard_syntax = true;
5639                        Ok(Expression::Trim(Box::new(trim)))
5640                    }
5641                    crate::expressions::TrimPosition::Leading if trim.characters.is_some() => {
5642                        let characters = trim.characters.take().expect("checked above");
5643                        Ok(Expression::Function(Box::new(Function::new(
5644                            "LTRIM",
5645                            vec![trim.this, characters],
5646                        ))))
5647                    }
5648                    crate::expressions::TrimPosition::Trailing if trim.characters.is_some() => {
5649                        let characters = trim.characters.take().expect("checked above");
5650                        Ok(Expression::Function(Box::new(Function::new(
5651                            "RTRIM",
5652                            vec![trim.this, characters],
5653                        ))))
5654                    }
5655                    _ => Ok(Expression::Trim(Box::new(trim))),
5656                }
5657            }
5658            other => Ok(other),
5659        })
5660    }
5661
5662    fn normalize_postgres_only_for_tsql(expr: Expression) -> Result<Expression> {
5663        transform_recursive(expr, &|e| match e {
5664            Expression::Table(mut table) if table.only => {
5665                table.only = false;
5666                Ok(Expression::Table(table))
5667            }
5668            other => Ok(other),
5669        })
5670    }
5671
5672    fn rewrite_postgres_json_array_elements_select_for_tsql(
5673        expr: Expression,
5674    ) -> Result<Expression> {
5675        let Expression::Select(select) = expr else {
5676            return Ok(expr);
5677        };
5678        let mut select = *select;
5679        if !Self::is_plain_single_projection_select(&select) {
5680            return Ok(Expression::Select(Box::new(select)));
5681        }
5682
5683        let Some(json_arg) =
5684            Self::postgres_json_array_elements_projection_arg(&select.expressions[0])
5685        else {
5686            return Ok(Expression::Select(Box::new(select)));
5687        };
5688
5689        select.expressions = vec![Expression::column("value")];
5690        select.from = Some(From {
5691            expressions: vec![Expression::OpenJSON(Box::new(
5692                crate::expressions::OpenJSON {
5693                    this: Box::new(json_arg),
5694                    path: None,
5695                    expressions: Vec::new(),
5696                },
5697            ))],
5698        });
5699
5700        Ok(Expression::Select(Box::new(select)))
5701    }
5702
5703    fn is_plain_single_projection_select(select: &crate::expressions::Select) -> bool {
5704        select.expressions.len() == 1
5705            && select.from.is_none()
5706            && select.joins.is_empty()
5707            && select.lateral_views.is_empty()
5708            && select.prewhere.is_none()
5709            && select.where_clause.is_none()
5710            && select.group_by.is_none()
5711            && select.having.is_none()
5712            && select.qualify.is_none()
5713            && select.order_by.is_none()
5714            && select.distribute_by.is_none()
5715            && select.cluster_by.is_none()
5716            && select.sort_by.is_none()
5717            && select.limit.is_none()
5718            && select.offset.is_none()
5719            && select.limit_by.is_none()
5720            && select.fetch.is_none()
5721            && !select.distinct
5722            && select.distinct_on.is_none()
5723            && select.top.is_none()
5724            && select.with.is_none()
5725            && select.sample.is_none()
5726            && select.into.is_none()
5727            && select.locks.is_empty()
5728            && select.for_xml.is_empty()
5729            && select.for_json.is_empty()
5730            && select.exclude.is_none()
5731    }
5732
5733    fn postgres_json_array_elements_projection_arg(expr: &Expression) -> Option<Expression> {
5734        match expr {
5735            Expression::Function(function)
5736                if Self::node_is_postgres_json_array_elements(expr) && function.args.len() == 1 =>
5737            {
5738                Some(function.args[0].clone())
5739            }
5740            Expression::Alias(alias) => {
5741                Self::postgres_json_array_elements_projection_arg(&alias.this)
5742            }
5743            _ => None,
5744        }
5745    }
5746
5747    fn normalize_postgres_type_function_casts(
5748        expr: Expression,
5749        target: DialectType,
5750    ) -> Result<Expression> {
5751        transform_recursive(expr, &|e| match e {
5752            Expression::Function(function) => {
5753                let mut function = *function;
5754                if function.args.len() == 1
5755                    && !function.distinct
5756                    && !function.quoted
5757                    && !function.use_bracket_syntax
5758                    && !function.name.contains('.')
5759                {
5760                    if let Some(to) = Self::postgres_type_function_data_type(&function.name) {
5761                        let this = function.args.remove(0);
5762                        let cast = Cast {
5763                            this,
5764                            to,
5765                            trailing_comments: function.trailing_comments,
5766                            double_colon_syntax: false,
5767                            format: None,
5768                            default: None,
5769                            inferred_type: function.inferred_type,
5770                        };
5771                        return Ok(
5772                            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
5773                                normalization::rewrite_postgres_float_to_integer_cast(cast)
5774                            } else {
5775                                Expression::Cast(Box::new(cast))
5776                            },
5777                        );
5778                    }
5779                }
5780                Ok(Expression::Function(Box::new(function)))
5781            }
5782            _ => Ok(e),
5783        })
5784    }
5785
5786    fn node_is_postgres_type_function_cast(expr: &Expression) -> bool {
5787        matches!(
5788            expr,
5789            Expression::Function(function)
5790                if !function.quoted
5791                    && !function.use_bracket_syntax
5792                    && !function.name.contains('.')
5793                    && Self::postgres_type_function_data_type(&function.name).is_some()
5794        )
5795    }
5796
5797    fn postgres_type_function_data_type(name: &str) -> Option<DataType> {
5798        match name.to_ascii_uppercase().as_str() {
5799            "NUMERIC" | "DECIMAL" | "DEC" => Some(DataType::Decimal {
5800                precision: None,
5801                scale: None,
5802            }),
5803            "INT2" | "SMALLINT" => Some(DataType::SmallInt { length: None }),
5804            "INT4" | "INT" => Some(DataType::Int {
5805                length: None,
5806                integer_spelling: false,
5807            }),
5808            "INTEGER" => Some(DataType::Int {
5809                length: None,
5810                integer_spelling: true,
5811            }),
5812            "INT8" | "BIGINT" => Some(DataType::BigInt { length: None }),
5813            "FLOAT4" | "REAL" => Some(DataType::Float {
5814                precision: None,
5815                scale: None,
5816                real_spelling: true,
5817            }),
5818            "FLOAT8" => Some(DataType::Double {
5819                precision: None,
5820                scale: None,
5821            }),
5822            "BOOL" | "BOOLEAN" => Some(DataType::Boolean),
5823            "TEXT" => Some(DataType::Text),
5824            "VARCHAR" => Some(DataType::VarChar {
5825                length: None,
5826                parenthesized_length: false,
5827            }),
5828            "UUID" => Some(DataType::Uuid),
5829            _ => None,
5830        }
5831    }
5832
5833    fn rewrite_boolean_values_for_tsql(expr: Expression) -> Result<Expression> {
5834        match expr {
5835            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
5836            Expression::Subquery(mut subquery) => {
5837                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
5838                Ok(Expression::Subquery(subquery))
5839            }
5840            Expression::Union(mut union) => {
5841                let left = std::mem::replace(&mut union.left, Expression::null());
5842                let right = std::mem::replace(&mut union.right, Expression::null());
5843                union.left = Self::rewrite_boolean_values_for_tsql(left)?;
5844                union.right = Self::rewrite_boolean_values_for_tsql(right)?;
5845                if let Some(mut with) = union.with.take() {
5846                    with.ctes = with
5847                        .ctes
5848                        .into_iter()
5849                        .map(|mut cte| {
5850                            cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
5851                            Ok(cte)
5852                        })
5853                        .collect::<Result<Vec<_>>>()?;
5854                    union.with = Some(with);
5855                }
5856                Ok(Expression::Union(union))
5857            }
5858            Expression::Intersect(mut intersect) => {
5859                let left = std::mem::replace(&mut intersect.left, Expression::null());
5860                let right = std::mem::replace(&mut intersect.right, Expression::null());
5861                intersect.left = Self::rewrite_boolean_values_for_tsql(left)?;
5862                intersect.right = Self::rewrite_boolean_values_for_tsql(right)?;
5863                Ok(Expression::Intersect(intersect))
5864            }
5865            Expression::Except(mut except) => {
5866                let left = std::mem::replace(&mut except.left, Expression::null());
5867                let right = std::mem::replace(&mut except.right, Expression::null());
5868                except.left = Self::rewrite_boolean_values_for_tsql(left)?;
5869                except.right = Self::rewrite_boolean_values_for_tsql(right)?;
5870                Ok(Expression::Except(except))
5871            }
5872            other => Self::rewrite_tsql_boolean_embedded_queries(other),
5873        }
5874    }
5875
5876    fn rewrite_postgres_format_for_tsql(
5877        expr: Expression,
5878        target: DialectType,
5879    ) -> Result<Expression> {
5880        transform_recursive(expr, &|e| match e {
5881            Expression::Function(f) if f.name.eq_ignore_ascii_case("FORMAT") => {
5882                Self::postgres_format_function_to_tsql(*f, target)
5883            }
5884            other => Ok(other),
5885        })
5886    }
5887
5888    fn postgres_format_function_to_tsql(f: Function, target: DialectType) -> Result<Expression> {
5889        let Some(format_expr) = f.args.first() else {
5890            return Err(Self::unsupported_postgres_format_for_tsql(
5891                target,
5892                "missing format string",
5893            ));
5894        };
5895
5896        let format = match format_expr {
5897            Expression::Literal(lit) if lit.is_string() => lit.value_str(),
5898            _ => {
5899                return Err(Self::unsupported_postgres_format_for_tsql(
5900                    target,
5901                    "dynamic format strings",
5902                ))
5903            }
5904        };
5905
5906        let value_args = &f.args[1..];
5907        let mut arg_index = 0usize;
5908        let mut literal = String::new();
5909        let mut segments = Vec::new();
5910        let mut chars = format.chars();
5911
5912        while let Some(ch) = chars.next() {
5913            if ch != '%' {
5914                literal.push(ch);
5915                continue;
5916            }
5917
5918            let Some(specifier) = chars.next() else {
5919                return Err(Self::unsupported_postgres_format_for_tsql(
5920                    target,
5921                    "unterminated format specifier",
5922                ));
5923            };
5924
5925            match specifier {
5926                '%' => literal.push('%'),
5927                's' => {
5928                    if !literal.is_empty() {
5929                        segments.push(Expression::string(std::mem::take(&mut literal)));
5930                    }
5931                    let Some(arg) = value_args.get(arg_index) else {
5932                        return Err(Self::unsupported_postgres_format_for_tsql(
5933                            target,
5934                            "not enough arguments",
5935                        ));
5936                    };
5937                    segments.push(arg.clone());
5938                    arg_index += 1;
5939                }
5940                other => {
5941                    return Err(Self::unsupported_postgres_format_for_tsql(
5942                        target,
5943                        format!("unsupported format specifier %{other}"),
5944                    ))
5945                }
5946            }
5947        }
5948
5949        if !literal.is_empty() {
5950            segments.push(Expression::string(literal));
5951        }
5952
5953        if arg_index != value_args.len() {
5954            return Err(Self::unsupported_postgres_format_for_tsql(
5955                target,
5956                "unused format arguments",
5957            ));
5958        }
5959
5960        Ok(Self::postgres_format_segments_to_tsql_concat(segments))
5961    }
5962
5963    fn postgres_format_segments_to_tsql_concat(mut segments: Vec<Expression>) -> Expression {
5964        if segments.is_empty() {
5965            return Expression::string("");
5966        }
5967
5968        if segments.len() == 1 {
5969            let only = segments.pop().expect("one segment");
5970            if matches!(&only, Expression::Literal(lit) if lit.is_string()) {
5971                return only;
5972            }
5973
5974            return Expression::Function(Box::new(Function::new(
5975                "CONCAT".to_string(),
5976                vec![only, Expression::string("")],
5977            )));
5978        }
5979
5980        Expression::Function(Box::new(Function::new("CONCAT".to_string(), segments)))
5981    }
5982
5983    fn unsupported_postgres_format_for_tsql(
5984        target: DialectType,
5985        reason: impl Into<String>,
5986    ) -> crate::error::Error {
5987        crate::error::Error::unsupported(
5988            format!("PostgreSQL format() ({})", reason.into()),
5989            target.to_string(),
5990        )
5991    }
5992
5993    fn rewrite_boolean_values_in_tsql_select(
5994        mut select: Box<crate::expressions::Select>,
5995    ) -> Result<Expression> {
5996        if let Some(mut with) = select.with.take() {
5997            with.ctes = with
5998                .ctes
5999                .into_iter()
6000                .map(|mut cte| {
6001                    cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
6002                    Ok(cte)
6003                })
6004                .collect::<Result<Vec<_>>>()?;
6005            select.with = Some(with);
6006        }
6007
6008        select.expressions = select
6009            .expressions
6010            .into_iter()
6011            .map(Self::rewrite_tsql_boolean_scalar_value)
6012            .collect::<Result<Vec<_>>>()?;
6013
6014        if let Some(mut from) = select.from.take() {
6015            from.expressions = from
6016                .expressions
6017                .into_iter()
6018                .map(Self::rewrite_tsql_boolean_embedded_queries)
6019                .collect::<Result<Vec<_>>>()?;
6020            select.from = Some(from);
6021        }
6022
6023        select.joins = select
6024            .joins
6025            .into_iter()
6026            .map(|mut join| {
6027                join.this = Self::rewrite_tsql_boolean_embedded_queries(join.this)?;
6028                if let Some(on) = join.on.take() {
6029                    join.on = Some(Self::rewrite_tsql_boolean_predicate_context(on)?);
6030                }
6031                if let Some(match_condition) = join.match_condition.take() {
6032                    join.match_condition = Some(Self::rewrite_tsql_boolean_predicate_context(
6033                        match_condition,
6034                    )?);
6035                }
6036                join.pivots = join
6037                    .pivots
6038                    .into_iter()
6039                    .map(Self::rewrite_tsql_boolean_embedded_queries)
6040                    .collect::<Result<Vec<_>>>()?;
6041                Ok(join)
6042            })
6043            .collect::<Result<Vec<_>>>()?;
6044
6045        select.lateral_views = select
6046            .lateral_views
6047            .into_iter()
6048            .map(|mut lateral_view| {
6049                lateral_view.this = Self::rewrite_tsql_boolean_embedded_queries(lateral_view.this)?;
6050                Ok(lateral_view)
6051            })
6052            .collect::<Result<Vec<_>>>()?;
6053
6054        if let Some(prewhere) = select.prewhere.take() {
6055            select.prewhere = Some(Self::rewrite_tsql_boolean_predicate_context(prewhere)?);
6056        }
6057
6058        if let Some(mut where_clause) = select.where_clause.take() {
6059            where_clause.this = Self::rewrite_tsql_boolean_predicate_context(where_clause.this)?;
6060            select.where_clause = Some(where_clause);
6061        }
6062
6063        if let Some(mut group_by) = select.group_by.take() {
6064            group_by.expressions = group_by
6065                .expressions
6066                .into_iter()
6067                .map(Self::rewrite_tsql_boolean_scalar_value)
6068                .collect::<Result<Vec<_>>>()?;
6069            select.group_by = Some(group_by);
6070        }
6071
6072        if let Some(mut having) = select.having.take() {
6073            having.this = Self::rewrite_tsql_boolean_predicate_context(having.this)?;
6074            select.having = Some(having);
6075        }
6076
6077        if let Some(mut qualify) = select.qualify.take() {
6078            qualify.this = Self::rewrite_tsql_boolean_predicate_context(qualify.this)?;
6079            select.qualify = Some(qualify);
6080        }
6081
6082        if let Some(mut order_by) = select.order_by.take() {
6083            order_by.expressions = Self::rewrite_tsql_boolean_ordered_values(order_by.expressions)?;
6084            select.order_by = Some(order_by);
6085        }
6086
6087        if let Some(mut distribute_by) = select.distribute_by.take() {
6088            distribute_by.expressions = distribute_by
6089                .expressions
6090                .into_iter()
6091                .map(Self::rewrite_tsql_boolean_scalar_value)
6092                .collect::<Result<Vec<_>>>()?;
6093            select.distribute_by = Some(distribute_by);
6094        }
6095
6096        if let Some(mut cluster_by) = select.cluster_by.take() {
6097            cluster_by.expressions =
6098                Self::rewrite_tsql_boolean_ordered_values(cluster_by.expressions)?;
6099            select.cluster_by = Some(cluster_by);
6100        }
6101
6102        if let Some(mut sort_by) = select.sort_by.take() {
6103            sort_by.expressions = Self::rewrite_tsql_boolean_ordered_values(sort_by.expressions)?;
6104            select.sort_by = Some(sort_by);
6105        }
6106
6107        if let Some(limit_by) = select.limit_by.take() {
6108            select.limit_by = Some(
6109                limit_by
6110                    .into_iter()
6111                    .map(Self::rewrite_tsql_boolean_scalar_value)
6112                    .collect::<Result<Vec<_>>>()?,
6113            );
6114        }
6115
6116        if let Some(distinct_on) = select.distinct_on.take() {
6117            select.distinct_on = Some(
6118                distinct_on
6119                    .into_iter()
6120                    .map(Self::rewrite_tsql_boolean_scalar_value)
6121                    .collect::<Result<Vec<_>>>()?,
6122            );
6123        }
6124
6125        if let Some(mut sample) = select.sample.take() {
6126            sample.size = Self::rewrite_tsql_boolean_embedded_queries(sample.size)?;
6127            if let Some(offset) = sample.offset.take() {
6128                sample.offset = Some(Self::rewrite_tsql_boolean_embedded_queries(offset)?);
6129            }
6130            if let Some(bucket_numerator) = sample.bucket_numerator.take() {
6131                sample.bucket_numerator = Some(Box::new(
6132                    Self::rewrite_tsql_boolean_embedded_queries(*bucket_numerator)?,
6133                ));
6134            }
6135            if let Some(bucket_denominator) = sample.bucket_denominator.take() {
6136                sample.bucket_denominator = Some(Box::new(
6137                    Self::rewrite_tsql_boolean_embedded_queries(*bucket_denominator)?,
6138                ));
6139            }
6140            if let Some(bucket_field) = sample.bucket_field.take() {
6141                sample.bucket_field = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6142                    *bucket_field,
6143                )?));
6144            }
6145            select.sample = Some(sample);
6146        }
6147
6148        if let Some(settings) = select.settings.take() {
6149            select.settings = Some(
6150                settings
6151                    .into_iter()
6152                    .map(Self::rewrite_tsql_boolean_embedded_queries)
6153                    .collect::<Result<Vec<_>>>()?,
6154            );
6155        }
6156
6157        if let Some(format) = select.format.take() {
6158            select.format = Some(Self::rewrite_tsql_boolean_embedded_queries(format)?);
6159        }
6160
6161        if let Some(mut windows) = select.windows.take() {
6162            for window in windows.iter_mut() {
6163                Self::rewrite_tsql_boolean_over_values(&mut window.spec)?;
6164            }
6165            select.windows = Some(windows);
6166        }
6167
6168        Ok(Expression::Select(select))
6169    }
6170
6171    fn normalize_postgres_boolean_semantics_for_tsql(expr: Expression) -> Result<Expression> {
6172        transform_recursive(expr, &|e| match e {
6173            Expression::Function(function)
6174                if function.args.len() == 2
6175                    && (function.name.eq_ignore_ascii_case("BOOLEQ")
6176                        || function.name.eq_ignore_ascii_case("BOOLNE")) =>
6177            {
6178                let is_equal = function.name.eq_ignore_ascii_case("BOOLEQ");
6179                let mut args = function.args.into_iter();
6180                let op = BinaryOp {
6181                    left: args.next().expect("checked boolean operator arity"),
6182                    right: args.next().expect("checked boolean operator arity"),
6183                    left_comments: Vec::new(),
6184                    operator_comments: Vec::new(),
6185                    trailing_comments: function.trailing_comments,
6186                    inferred_type: None,
6187                };
6188                if is_equal {
6189                    Ok(Expression::Eq(Box::new(op)))
6190                } else {
6191                    Ok(Expression::Neq(Box::new(op)))
6192                }
6193            }
6194            Expression::Cast(cast)
6195                if matches!(cast.to, DataType::Text)
6196                    && Self::is_known_postgres_boolean_expression(&cast.this) =>
6197            {
6198                Ok(Self::postgres_boolean_text_value(cast.this))
6199            }
6200            other => Ok(other),
6201        })
6202    }
6203
6204    fn is_known_postgres_boolean_expression(expr: &Expression) -> bool {
6205        match expr {
6206            Expression::Boolean(_) => true,
6207            Expression::Cast(cast) => matches!(cast.to, DataType::Boolean),
6208            Expression::Paren(paren) => Self::is_known_postgres_boolean_expression(&paren.this),
6209            other => Self::is_tsql_boolean_value_expression(other),
6210        }
6211    }
6212
6213    fn postgres_boolean_text_value(predicate: Expression) -> Expression {
6214        if let Expression::Boolean(boolean) = predicate {
6215            return Expression::string(if boolean.value { "true" } else { "false" });
6216        }
6217
6218        let false_predicate = Expression::Not(Box::new(crate::expressions::UnaryOp {
6219            this: predicate.clone(),
6220            inferred_type: None,
6221        }));
6222        Expression::Case(Box::new(crate::expressions::Case {
6223            operand: None,
6224            whens: vec![
6225                (predicate, Expression::string("true")),
6226                (false_predicate, Expression::string("false")),
6227            ],
6228            else_: Some(Expression::null()),
6229            comments: Vec::new(),
6230            inferred_type: None,
6231        }))
6232    }
6233
6234    fn rewrite_tsql_boolean_scalar_value(expr: Expression) -> Result<Expression> {
6235        if let Expression::Boolean(boolean) = expr {
6236            return Ok(Expression::Cast(Box::new(Cast {
6237                this: Expression::Boolean(boolean),
6238                to: DataType::Boolean,
6239                trailing_comments: Vec::new(),
6240                double_colon_syntax: false,
6241                format: None,
6242                default: None,
6243                inferred_type: None,
6244            })));
6245        }
6246
6247        if Self::is_tsql_boolean_value_expression(&expr) {
6248            let predicate = Self::rewrite_tsql_boolean_predicate_context(expr)?;
6249            return Ok(Self::tsql_boolean_value_case(predicate));
6250        }
6251
6252        match expr {
6253            Expression::Alias(mut alias) => {
6254                alias.this = Self::rewrite_tsql_boolean_scalar_value(alias.this)?;
6255                Ok(Expression::Alias(alias))
6256            }
6257            Expression::Paren(mut paren) => {
6258                paren.this = Self::rewrite_tsql_boolean_scalar_value(paren.this)?;
6259                Ok(Expression::Paren(paren))
6260            }
6261            Expression::Cast(mut cast) => {
6262                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6263                if let Some(format) = cast.format.take() {
6264                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6265                        *format,
6266                    )?));
6267                }
6268                if let Some(default) = cast.default.take() {
6269                    cast.default =
6270                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6271                }
6272                Ok(Expression::Cast(cast))
6273            }
6274            Expression::TryCast(mut cast) => {
6275                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6276                if let Some(format) = cast.format.take() {
6277                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6278                        *format,
6279                    )?));
6280                }
6281                if let Some(default) = cast.default.take() {
6282                    cast.default =
6283                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6284                }
6285                Ok(Expression::TryCast(cast))
6286            }
6287            Expression::SafeCast(mut cast) => {
6288                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
6289                if let Some(format) = cast.format.take() {
6290                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_embedded_queries(
6291                        *format,
6292                    )?));
6293                }
6294                if let Some(default) = cast.default.take() {
6295                    cast.default =
6296                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
6297                }
6298                Ok(Expression::SafeCast(cast))
6299            }
6300            Expression::Case(mut case) => {
6301                let is_simple_case = case.operand.is_some();
6302                if let Some(operand) = case.operand.take() {
6303                    case.operand = Some(Self::rewrite_tsql_boolean_scalar_value(operand)?);
6304                }
6305                case.whens = case
6306                    .whens
6307                    .into_iter()
6308                    .map(|(condition, result)| {
6309                        let condition = if is_simple_case {
6310                            Self::rewrite_tsql_boolean_scalar_value(condition)?
6311                        } else {
6312                            Self::rewrite_tsql_boolean_predicate_context(condition)?
6313                        };
6314                        Ok((condition, Self::rewrite_tsql_boolean_scalar_value(result)?))
6315                    })
6316                    .collect::<Result<Vec<_>>>()?;
6317                if let Some(else_) = case.else_.take() {
6318                    case.else_ = Some(Self::rewrite_tsql_boolean_scalar_value(else_)?);
6319                }
6320                Ok(Expression::Case(case))
6321            }
6322            Expression::IfFunc(mut if_func) => {
6323                if_func.condition =
6324                    Self::rewrite_tsql_boolean_predicate_context(if_func.condition)?;
6325                if_func.true_value = Self::rewrite_tsql_boolean_scalar_value(if_func.true_value)?;
6326                if let Some(false_value) = if_func.false_value.take() {
6327                    if_func.false_value =
6328                        Some(Self::rewrite_tsql_boolean_scalar_value(false_value)?);
6329                }
6330                Ok(Expression::IfFunc(if_func))
6331            }
6332            Expression::WindowFunction(mut window_function) => {
6333                window_function.this =
6334                    Self::rewrite_tsql_boolean_embedded_queries(window_function.this)?;
6335                Self::rewrite_tsql_boolean_over_values(&mut window_function.over)?;
6336                if let Some(mut keep) = window_function.keep.take() {
6337                    keep.order_by = Self::rewrite_tsql_boolean_ordered_values(keep.order_by)?;
6338                    window_function.keep = Some(keep);
6339                }
6340                Ok(Expression::WindowFunction(window_function))
6341            }
6342            Expression::WithinGroup(mut within_group) => {
6343                within_group.this = Self::rewrite_tsql_boolean_embedded_queries(within_group.this)?;
6344                within_group.order_by =
6345                    Self::rewrite_tsql_boolean_ordered_values(within_group.order_by)?;
6346                Ok(Expression::WithinGroup(within_group))
6347            }
6348            Expression::Subquery(mut subquery) => {
6349                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
6350                Ok(Expression::Subquery(subquery))
6351            }
6352            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
6353            other => Self::rewrite_tsql_boolean_embedded_queries(other),
6354        }
6355    }
6356
6357    fn rewrite_tsql_boolean_predicate_context(expr: Expression) -> Result<Expression> {
6358        let expr = Self::rewrite_tsql_boolean_embedded_queries(expr)?;
6359        Ok(crate::transforms::ensure_bool_condition(expr))
6360    }
6361
6362    fn rewrite_tsql_boolean_embedded_queries(expr: Expression) -> Result<Expression> {
6363        transform_recursive(expr, &|e| match e {
6364            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
6365            Expression::Subquery(mut subquery) => {
6366                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
6367                Ok(Expression::Subquery(subquery))
6368            }
6369            Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => {
6370                Self::rewrite_boolean_values_for_tsql(e)
6371            }
6372            other => Ok(other),
6373        })
6374    }
6375
6376    fn rewrite_tsql_boolean_ordered_values(
6377        ordered: Vec<crate::expressions::Ordered>,
6378    ) -> Result<Vec<crate::expressions::Ordered>> {
6379        ordered
6380            .into_iter()
6381            .map(|mut ordered| {
6382                ordered.this = Self::rewrite_tsql_boolean_scalar_value(ordered.this)?;
6383                if let Some(with_fill) = ordered.with_fill.take() {
6384                    ordered.with_fill = Some(Box::new(
6385                        Self::rewrite_tsql_boolean_with_fill_values(*with_fill)?,
6386                    ));
6387                }
6388                Ok(ordered)
6389            })
6390            .collect()
6391    }
6392
6393    fn rewrite_tsql_boolean_with_fill_values(
6394        mut with_fill: crate::expressions::WithFill,
6395    ) -> Result<crate::expressions::WithFill> {
6396        if let Some(from) = with_fill.from_.take() {
6397            with_fill.from_ = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*from)?));
6398        }
6399        if let Some(to) = with_fill.to.take() {
6400            with_fill.to = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*to)?));
6401        }
6402        if let Some(step) = with_fill.step.take() {
6403            with_fill.step = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*step)?));
6404        }
6405        if let Some(staleness) = with_fill.staleness.take() {
6406            with_fill.staleness = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
6407                *staleness,
6408            )?));
6409        }
6410        if let Some(interpolate) = with_fill.interpolate.take() {
6411            with_fill.interpolate = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
6412                *interpolate,
6413            )?));
6414        }
6415        Ok(with_fill)
6416    }
6417
6418    fn rewrite_tsql_boolean_over_values(over: &mut crate::expressions::Over) -> Result<()> {
6419        over.partition_by = std::mem::take(&mut over.partition_by)
6420            .into_iter()
6421            .map(Self::rewrite_tsql_boolean_scalar_value)
6422            .collect::<Result<Vec<_>>>()?;
6423        over.order_by =
6424            Self::rewrite_tsql_boolean_ordered_values(std::mem::take(&mut over.order_by))?;
6425        Ok(())
6426    }
6427
6428    fn is_tsql_boolean_value_expression(expr: &Expression) -> bool {
6429        match expr {
6430            Expression::Paren(paren) => Self::is_tsql_boolean_value_expression(&paren.this),
6431            Expression::Eq(_)
6432            | Expression::Neq(_)
6433            | Expression::Lt(_)
6434            | Expression::Lte(_)
6435            | Expression::Gt(_)
6436            | Expression::Gte(_)
6437            | Expression::Is(_)
6438            | Expression::IsNull(_)
6439            | Expression::IsTrue(_)
6440            | Expression::IsFalse(_)
6441            | Expression::Like(_)
6442            | Expression::ILike(_)
6443            | Expression::StartsWith(_)
6444            | Expression::SimilarTo(_)
6445            | Expression::Glob(_)
6446            | Expression::RegexpLike(_)
6447            | Expression::In(_)
6448            | Expression::Between(_)
6449            | Expression::Exists(_)
6450            | Expression::And(_)
6451            | Expression::Or(_)
6452            | Expression::Not(_)
6453            | Expression::Any(_)
6454            | Expression::All(_)
6455            | Expression::NullSafeEq(_)
6456            | Expression::NullSafeNeq(_)
6457            | Expression::EqualNull(_) => true,
6458            _ => false,
6459        }
6460    }
6461
6462    fn tsql_boolean_value_case(predicate: Expression) -> Expression {
6463        let case = Expression::Case(Box::new(crate::expressions::Case {
6464            operand: None,
6465            whens: vec![(predicate, Expression::number(1))],
6466            else_: Some(Expression::number(0)),
6467            comments: Vec::new(),
6468            inferred_type: None,
6469        }));
6470
6471        Expression::Cast(Box::new(Cast {
6472            this: case,
6473            to: DataType::Boolean,
6474            trailing_comments: Vec::new(),
6475            double_colon_syntax: false,
6476            format: None,
6477            default: None,
6478            inferred_type: None,
6479        }))
6480    }
6481
6482    fn rewrite_aggregate_filters_for_tsql(expr: Expression) -> Result<Expression> {
6483        transform_recursive(expr, &|e| Self::rewrite_aggregate_filter_for_tsql(e))
6484    }
6485
6486    fn rewrite_aggregate_filter_for_tsql(expr: Expression) -> Result<Expression> {
6487        macro_rules! rewrite_agg_filter {
6488            ($variant:ident, $agg:expr) => {{
6489                let mut agg = $agg;
6490                if let Some(filter) = agg.filter.take() {
6491                    let this = std::mem::replace(&mut agg.this, Expression::null());
6492                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6493                }
6494                Ok(Expression::$variant(agg))
6495            }};
6496        }
6497
6498        match expr {
6499            Expression::Filter(filter) => {
6500                let condition = match *filter.expression {
6501                    Expression::Where(where_) => where_.this,
6502                    other => other,
6503                };
6504                Ok(Self::push_filter_into_tsql_aggregate(
6505                    *filter.this,
6506                    condition,
6507                ))
6508            }
6509            Expression::AggregateFunction(mut agg) => {
6510                if let Some(filter) = agg.filter.take() {
6511                    Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
6512                }
6513                Ok(Expression::AggregateFunction(agg))
6514            }
6515            Expression::Count(mut count) => {
6516                if let Some(filter) = count.filter.take() {
6517                    let value = if count.star {
6518                        Expression::number(1)
6519                    } else {
6520                        count.this.take().unwrap_or_else(|| Expression::number(1))
6521                    };
6522                    count.star = false;
6523                    count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
6524                }
6525                Ok(Expression::Count(count))
6526            }
6527            Expression::Sum(agg) => rewrite_agg_filter!(Sum, agg),
6528            Expression::Avg(agg) => rewrite_agg_filter!(Avg, agg),
6529            Expression::Min(agg) => rewrite_agg_filter!(Min, agg),
6530            Expression::Max(agg) => rewrite_agg_filter!(Max, agg),
6531            Expression::ArrayAgg(agg) => rewrite_agg_filter!(ArrayAgg, agg),
6532            Expression::CountIf(agg) => Ok(Expression::CountIf(agg)),
6533            Expression::Stddev(agg) => rewrite_agg_filter!(Stddev, agg),
6534            Expression::StddevPop(agg) => rewrite_agg_filter!(StddevPop, agg),
6535            Expression::StddevSamp(agg) => rewrite_agg_filter!(StddevSamp, agg),
6536            Expression::Variance(agg) => rewrite_agg_filter!(Variance, agg),
6537            Expression::VarPop(agg) => rewrite_agg_filter!(VarPop, agg),
6538            Expression::VarSamp(agg) => rewrite_agg_filter!(VarSamp, agg),
6539            Expression::Median(agg) => rewrite_agg_filter!(Median, agg),
6540            Expression::Mode(agg) => rewrite_agg_filter!(Mode, agg),
6541            Expression::First(agg) => rewrite_agg_filter!(First, agg),
6542            Expression::Last(agg) => rewrite_agg_filter!(Last, agg),
6543            Expression::AnyValue(agg) => rewrite_agg_filter!(AnyValue, agg),
6544            Expression::ApproxDistinct(agg) => rewrite_agg_filter!(ApproxDistinct, agg),
6545            Expression::ApproxCountDistinct(agg) => {
6546                rewrite_agg_filter!(ApproxCountDistinct, agg)
6547            }
6548            Expression::LogicalAnd(agg) => rewrite_agg_filter!(LogicalAnd, agg),
6549            Expression::LogicalOr(agg) => rewrite_agg_filter!(LogicalOr, agg),
6550            Expression::Skewness(agg) => rewrite_agg_filter!(Skewness, agg),
6551            Expression::ArrayConcatAgg(agg) => rewrite_agg_filter!(ArrayConcatAgg, agg),
6552            Expression::ArrayUniqueAgg(agg) => rewrite_agg_filter!(ArrayUniqueAgg, agg),
6553            Expression::BoolXorAgg(agg) => rewrite_agg_filter!(BoolXorAgg, agg),
6554            Expression::BitwiseAndAgg(agg) => rewrite_agg_filter!(BitwiseAndAgg, agg),
6555            Expression::BitwiseOrAgg(agg) => rewrite_agg_filter!(BitwiseOrAgg, agg),
6556            Expression::BitwiseXorAgg(agg) => rewrite_agg_filter!(BitwiseXorAgg, agg),
6557            Expression::StringAgg(mut agg) => {
6558                if let Some(filter) = agg.filter.take() {
6559                    let this = std::mem::replace(&mut agg.this, Expression::null());
6560                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6561                }
6562                Ok(Expression::StringAgg(agg))
6563            }
6564            Expression::GroupConcat(mut agg) => {
6565                if let Some(filter) = agg.filter.take() {
6566                    let this = std::mem::replace(&mut agg.this, Expression::null());
6567                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6568                }
6569                Ok(Expression::GroupConcat(agg))
6570            }
6571            Expression::ListAgg(mut agg) => {
6572                if let Some(filter) = agg.filter.take() {
6573                    let this = std::mem::replace(&mut agg.this, Expression::null());
6574                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6575                }
6576                Ok(Expression::ListAgg(agg))
6577            }
6578            Expression::WithinGroup(mut within_group) => {
6579                within_group.this = Self::rewrite_aggregate_filters_for_tsql(within_group.this)?;
6580                Ok(Expression::WithinGroup(within_group))
6581            }
6582            other => Ok(other),
6583        }
6584    }
6585
6586    fn push_filter_into_tsql_aggregate(expr: Expression, filter: Expression) -> Expression {
6587        macro_rules! push_agg_filter {
6588            ($variant:ident, $agg:expr) => {{
6589                let mut agg = $agg;
6590                let this = std::mem::replace(&mut agg.this, Expression::null());
6591                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6592                agg.filter = None;
6593                Expression::$variant(agg)
6594            }};
6595        }
6596
6597        match expr {
6598            Expression::AggregateFunction(mut agg) => {
6599                Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
6600                Expression::AggregateFunction(agg)
6601            }
6602            Expression::Count(mut count) => {
6603                let value = if count.star {
6604                    Expression::number(1)
6605                } else {
6606                    count.this.take().unwrap_or_else(|| Expression::number(1))
6607                };
6608                count.star = false;
6609                count.filter = None;
6610                count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
6611                Expression::Count(count)
6612            }
6613            Expression::Sum(agg) => push_agg_filter!(Sum, agg),
6614            Expression::Avg(agg) => push_agg_filter!(Avg, agg),
6615            Expression::Min(agg) => push_agg_filter!(Min, agg),
6616            Expression::Max(agg) => push_agg_filter!(Max, agg),
6617            Expression::ArrayAgg(agg) => push_agg_filter!(ArrayAgg, agg),
6618            Expression::CountIf(mut agg) => {
6619                agg.filter = Some(filter);
6620                Expression::CountIf(agg)
6621            }
6622            Expression::Stddev(agg) => push_agg_filter!(Stddev, agg),
6623            Expression::StddevPop(agg) => push_agg_filter!(StddevPop, agg),
6624            Expression::StddevSamp(agg) => push_agg_filter!(StddevSamp, agg),
6625            Expression::Variance(agg) => push_agg_filter!(Variance, agg),
6626            Expression::VarPop(agg) => push_agg_filter!(VarPop, agg),
6627            Expression::VarSamp(agg) => push_agg_filter!(VarSamp, agg),
6628            Expression::Median(agg) => push_agg_filter!(Median, agg),
6629            Expression::Mode(agg) => push_agg_filter!(Mode, agg),
6630            Expression::First(agg) => push_agg_filter!(First, agg),
6631            Expression::Last(agg) => push_agg_filter!(Last, agg),
6632            Expression::AnyValue(agg) => push_agg_filter!(AnyValue, agg),
6633            Expression::ApproxDistinct(agg) => push_agg_filter!(ApproxDistinct, agg),
6634            Expression::ApproxCountDistinct(agg) => {
6635                push_agg_filter!(ApproxCountDistinct, agg)
6636            }
6637            Expression::LogicalAnd(agg) => push_agg_filter!(LogicalAnd, agg),
6638            Expression::LogicalOr(agg) => push_agg_filter!(LogicalOr, agg),
6639            Expression::Skewness(agg) => push_agg_filter!(Skewness, agg),
6640            Expression::ArrayConcatAgg(agg) => push_agg_filter!(ArrayConcatAgg, agg),
6641            Expression::ArrayUniqueAgg(agg) => push_agg_filter!(ArrayUniqueAgg, agg),
6642            Expression::BoolXorAgg(agg) => push_agg_filter!(BoolXorAgg, agg),
6643            Expression::BitwiseAndAgg(agg) => push_agg_filter!(BitwiseAndAgg, agg),
6644            Expression::BitwiseOrAgg(agg) => push_agg_filter!(BitwiseOrAgg, agg),
6645            Expression::BitwiseXorAgg(agg) => push_agg_filter!(BitwiseXorAgg, agg),
6646            Expression::StringAgg(mut agg) => {
6647                let this = std::mem::replace(&mut agg.this, Expression::null());
6648                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6649                agg.filter = None;
6650                Expression::StringAgg(agg)
6651            }
6652            Expression::GroupConcat(mut agg) => {
6653                let this = std::mem::replace(&mut agg.this, Expression::null());
6654                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6655                agg.filter = None;
6656                Expression::GroupConcat(agg)
6657            }
6658            Expression::ListAgg(mut agg) => {
6659                let this = std::mem::replace(&mut agg.this, Expression::null());
6660                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
6661                agg.filter = None;
6662                Expression::ListAgg(agg)
6663            }
6664            Expression::WithinGroup(mut within_group) => {
6665                within_group.this =
6666                    Self::push_filter_into_tsql_aggregate(within_group.this, filter);
6667                Expression::WithinGroup(within_group)
6668            }
6669            other => Expression::Filter(Box::new(crate::expressions::Filter {
6670                this: Box::new(other),
6671                expression: Box::new(filter),
6672            })),
6673        }
6674    }
6675
6676    fn rewrite_generic_aggregate_filter_for_tsql(
6677        agg: &mut crate::expressions::AggregateFunction,
6678        filter: Expression,
6679    ) {
6680        let is_count =
6681            agg.name.eq_ignore_ascii_case("COUNT") || agg.name.eq_ignore_ascii_case("COUNT_BIG");
6682        let is_count_star = is_count
6683            && (agg.args.is_empty()
6684                || (agg.args.len() == 1 && matches!(agg.args[0], Expression::Star(_))));
6685
6686        if is_count_star {
6687            agg.args = vec![Self::conditional_aggregate_value_for_tsql(
6688                filter,
6689                Expression::number(1),
6690            )];
6691        } else if !agg.args.is_empty() {
6692            agg.args = agg
6693                .args
6694                .drain(..)
6695                .map(|arg| Self::conditional_aggregate_value_for_tsql(filter.clone(), arg))
6696                .collect();
6697        } else {
6698            agg.filter = Some(filter);
6699        }
6700    }
6701
6702    fn conditional_aggregate_value_for_tsql(filter: Expression, value: Expression) -> Expression {
6703        Expression::Case(Box::new(crate::expressions::Case {
6704            operand: None,
6705            whens: vec![(filter, value)],
6706            else_: None,
6707            comments: Vec::new(),
6708            inferred_type: None,
6709        }))
6710    }
6711
6712    fn reject_pgvector_distance_operators_for_sqlite(&self, sql: &str) -> Result<()> {
6713        let tokens = self.tokenize(sql)?;
6714        for (i, token) in tokens.iter().enumerate() {
6715            if token.token_type == TokenType::NullsafeEq {
6716                return Err(crate::error::Error::unsupported(
6717                    "PostgreSQL pgvector cosine distance operator <=>",
6718                    "SQLite",
6719                ));
6720            }
6721            if token.token_type == TokenType::Lt
6722                && tokens
6723                    .get(i + 1)
6724                    .is_some_and(|token| token.token_type == TokenType::Tilde)
6725                && tokens
6726                    .get(i + 2)
6727                    .is_some_and(|token| token.token_type == TokenType::Gt)
6728            {
6729                return Err(crate::error::Error::unsupported(
6730                    "PostgreSQL pgvector Hamming distance operator <~>",
6731                    "SQLite",
6732                ));
6733            }
6734        }
6735        Ok(())
6736    }
6737
6738    fn normalize_sqlite_double_quoted_defaults(expr: Expression) -> Result<Expression> {
6739        fn normalize_default_expr(expr: Expression) -> Result<Expression> {
6740            transform_recursive(expr, &|e| match e {
6741                Expression::Column(col)
6742                    if col.table.is_none() && col.name.quoted && !col.join_mark =>
6743                {
6744                    Ok(Expression::Literal(Box::new(Literal::String(
6745                        col.name.name,
6746                    ))))
6747                }
6748                Expression::Identifier(id) if id.quoted => {
6749                    Ok(Expression::Literal(Box::new(Literal::String(id.name))))
6750                }
6751                _ => Ok(e),
6752            })
6753        }
6754
6755        fn normalize_column_default(col: &mut crate::expressions::ColumnDef) -> Result<()> {
6756            if let Some(default) = col.default.take() {
6757                col.default = Some(normalize_default_expr(default)?);
6758            }
6759
6760            for constraint in &mut col.constraints {
6761                if let ColumnConstraint::Default(default) = constraint {
6762                    *default = normalize_default_expr(default.clone())?;
6763                }
6764            }
6765
6766            Ok(())
6767        }
6768
6769        transform_recursive(expr, &|e| match e {
6770            Expression::CreateTable(mut ct) => {
6771                for column in &mut ct.columns {
6772                    normalize_column_default(column)?;
6773                }
6774                Ok(Expression::CreateTable(ct))
6775            }
6776            Expression::ColumnDef(mut col) => {
6777                normalize_column_default(&mut col)?;
6778                Ok(Expression::ColumnDef(col))
6779            }
6780            _ => Ok(e),
6781        })
6782    }
6783
6784    fn normalize_postgres_to_sqlite_types(expr: Expression) -> Result<Expression> {
6785        fn sqlite_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
6786            use crate::expressions::DataType;
6787
6788            match dt {
6789                DataType::Bit { .. } => DataType::Int {
6790                    length: None,
6791                    integer_spelling: true,
6792                },
6793                DataType::TextWithLength { .. } => DataType::Text,
6794                DataType::VarChar { .. } => DataType::Text,
6795                DataType::Char { .. } => DataType::Text,
6796                DataType::Timestamp { timezone: true, .. } => DataType::Text,
6797                DataType::Custom { name } => {
6798                    let base = name
6799                        .split_once('(')
6800                        .map_or(name.as_str(), |(base, _)| base)
6801                        .trim();
6802                    if base.eq_ignore_ascii_case("TSVECTOR")
6803                        || base.eq_ignore_ascii_case("TIMESTAMPTZ")
6804                        || base.eq_ignore_ascii_case("TIMESTAMP WITH TIME ZONE")
6805                        || base.eq_ignore_ascii_case("NVARCHAR")
6806                        || base.eq_ignore_ascii_case("NCHAR")
6807                    {
6808                        DataType::Text
6809                    } else {
6810                        DataType::Custom { name }
6811                    }
6812                }
6813                _ => dt,
6814            }
6815        }
6816
6817        transform_recursive(expr, &|e| match e {
6818            Expression::DataType(dt) => Ok(Expression::DataType(sqlite_type(dt))),
6819            Expression::CreateTable(mut ct) => {
6820                for column in &mut ct.columns {
6821                    column.data_type = sqlite_type(column.data_type.clone());
6822                }
6823                Ok(Expression::CreateTable(ct))
6824            }
6825            _ => Ok(e),
6826        })
6827    }
6828
6829    fn normalize_postgres_to_fabric_types(expr: Expression) -> Result<Expression> {
6830        fn fabric_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
6831            use crate::expressions::DataType;
6832
6833            match dt {
6834                DataType::Decimal {
6835                    precision: None,
6836                    scale: None,
6837                } => DataType::Decimal {
6838                    precision: Some(38),
6839                    scale: Some(10),
6840                },
6841                DataType::Json | DataType::JsonB => DataType::Custom {
6842                    name: "VARCHAR(MAX)".to_string(),
6843                },
6844                _ => dt,
6845            }
6846        }
6847
6848        transform_recursive(expr, &|e| match e {
6849            Expression::DataType(dt) => Ok(Expression::DataType(fabric_type(dt))),
6850            Expression::CreateTable(mut ct) => {
6851                for column in &mut ct.columns {
6852                    column.data_type = fabric_type(column.data_type.clone());
6853                }
6854                Ok(Expression::CreateTable(ct))
6855            }
6856            Expression::ColumnDef(mut col) => {
6857                col.data_type = fabric_type(col.data_type);
6858                Ok(Expression::ColumnDef(col))
6859            }
6860            _ => Ok(e),
6861        })
6862    }
6863
6864    /// For DuckDB target: when FROM clause contains RANGE(n), replace
6865    /// `(ROW_NUMBER() OVER (ORDER BY 1 NULLS FIRST) - 1)` with `range` in select expressions.
6866    /// This handles SEQ1/2/4/8 → RANGE transpilation from Snowflake.
6867    fn seq_rownum_to_range(expr: Expression) -> Result<Expression> {
6868        if let Expression::Select(mut select) = expr {
6869            // Check if FROM contains a RANGE function
6870            let has_range_from = if let Some(ref from) = select.from {
6871                from.expressions.iter().any(|e| {
6872                    // Check for direct RANGE(...) or aliased RANGE(...)
6873                    match e {
6874                        Expression::Function(f) => f.name.eq_ignore_ascii_case("RANGE"),
6875                        Expression::Alias(a) => {
6876                            matches!(&a.this, Expression::Function(f) if f.name.eq_ignore_ascii_case("RANGE"))
6877                        }
6878                        _ => false,
6879                    }
6880                })
6881            } else {
6882                false
6883            };
6884
6885            if has_range_from {
6886                // Replace the ROW_NUMBER pattern in select expressions
6887                select.expressions = select
6888                    .expressions
6889                    .into_iter()
6890                    .map(|e| Self::replace_rownum_with_range(e))
6891                    .collect();
6892            }
6893
6894            Ok(Expression::Select(select))
6895        } else {
6896            Ok(expr)
6897        }
6898    }
6899
6900    /// Replace `(ROW_NUMBER() OVER (...) - 1)` with `range` column reference
6901    fn replace_rownum_with_range(expr: Expression) -> Expression {
6902        match expr {
6903            // Match: (ROW_NUMBER() OVER (...) - 1) % N → range % N
6904            Expression::Mod(op) => {
6905                let new_left = Self::try_replace_rownum_paren(&op.left);
6906                Expression::Mod(Box::new(crate::expressions::BinaryOp {
6907                    left: new_left,
6908                    right: op.right,
6909                    left_comments: op.left_comments,
6910                    operator_comments: op.operator_comments,
6911                    trailing_comments: op.trailing_comments,
6912                    inferred_type: op.inferred_type,
6913                }))
6914            }
6915            // Match: (CASE WHEN (ROW...) % N >= ... THEN ... ELSE ... END)
6916            Expression::Paren(p) => {
6917                let inner = Self::replace_rownum_with_range(p.this);
6918                Expression::Paren(Box::new(crate::expressions::Paren {
6919                    this: inner,
6920                    trailing_comments: p.trailing_comments,
6921                }))
6922            }
6923            Expression::Case(mut c) => {
6924                // Replace ROW_NUMBER in WHEN conditions and THEN expressions
6925                c.whens = c
6926                    .whens
6927                    .into_iter()
6928                    .map(|(cond, then)| {
6929                        (
6930                            Self::replace_rownum_with_range(cond),
6931                            Self::replace_rownum_with_range(then),
6932                        )
6933                    })
6934                    .collect();
6935                if let Some(else_) = c.else_ {
6936                    c.else_ = Some(Self::replace_rownum_with_range(else_));
6937                }
6938                Expression::Case(c)
6939            }
6940            Expression::Gte(op) => Expression::Gte(Box::new(crate::expressions::BinaryOp {
6941                left: Self::replace_rownum_with_range(op.left),
6942                right: op.right,
6943                left_comments: op.left_comments,
6944                operator_comments: op.operator_comments,
6945                trailing_comments: op.trailing_comments,
6946                inferred_type: op.inferred_type,
6947            })),
6948            Expression::Sub(op) => Expression::Sub(Box::new(crate::expressions::BinaryOp {
6949                left: Self::replace_rownum_with_range(op.left),
6950                right: op.right,
6951                left_comments: op.left_comments,
6952                operator_comments: op.operator_comments,
6953                trailing_comments: op.trailing_comments,
6954                inferred_type: op.inferred_type,
6955            })),
6956            Expression::Alias(mut a) => {
6957                a.this = Self::replace_rownum_with_range(a.this);
6958                Expression::Alias(a)
6959            }
6960            other => other,
6961        }
6962    }
6963
6964    /// Check if an expression is `(ROW_NUMBER() OVER (...) - 1)` and replace with `range`
6965    fn try_replace_rownum_paren(expr: &Expression) -> Expression {
6966        if let Expression::Paren(ref p) = expr {
6967            if let Expression::Sub(ref sub) = p.this {
6968                if let Expression::WindowFunction(ref wf) = sub.left {
6969                    if let Expression::Function(ref f) = wf.this {
6970                        if f.name.eq_ignore_ascii_case("ROW_NUMBER") {
6971                            if let Expression::Literal(ref lit) = sub.right {
6972                                if let crate::expressions::Literal::Number(ref n) = lit.as_ref() {
6973                                    if n == "1" {
6974                                        return Expression::column("range");
6975                                    }
6976                                }
6977                            }
6978                        }
6979                    }
6980                }
6981            }
6982        }
6983        expr.clone()
6984    }
6985
6986    /// Transform BigQuery GENERATE_DATE_ARRAY in UNNEST for Snowflake target.
6987    /// Converts:
6988    ///   SELECT ..., alias, ... FROM t CROSS JOIN UNNEST(GENERATE_DATE_ARRAY(start, end, INTERVAL '1' unit)) AS alias
6989    /// To:
6990    ///   SELECT ..., DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE)) AS alias, ...
6991    ///   FROM t, LATERAL FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)) AS _t0(seq, key, path, index, alias, this)
6992    fn transform_generate_date_array_snowflake(expr: Expression) -> Result<Expression> {
6993        use crate::expressions::*;
6994        transform_recursive(expr, &|e| {
6995            // Handle ARRAY_SIZE(GENERATE_DATE_ARRAY(...)) -> ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM subquery))
6996            if let Expression::ArraySize(ref af) = e {
6997                if let Expression::Function(ref f) = af.this {
6998                    if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
6999                        let result = Self::convert_array_size_gda_snowflake(f)?;
7000                        return Ok(result);
7001                    }
7002                }
7003            }
7004
7005            let Expression::Select(mut sel) = e else {
7006                return Ok(e);
7007            };
7008
7009            // Find joins with UNNEST containing GenerateSeries (from GENERATE_DATE_ARRAY conversion)
7010            let mut gda_info: Option<(String, Expression, Expression, String)> = None; // (alias_name, start_expr, end_expr, unit)
7011            let mut gda_join_idx: Option<usize> = None;
7012
7013            for (idx, join) in sel.joins.iter().enumerate() {
7014                // The join.this may be:
7015                // 1. Unnest(UnnestFunc { alias: Some("mnth"), ... })
7016                // 2. Alias(Alias { this: Unnest(UnnestFunc { alias: None, ... }), alias: "mnth", ... })
7017                let (unnest_ref, alias_name) = match &join.this {
7018                    Expression::Unnest(ref unnest) => {
7019                        let alias = unnest.alias.as_ref().map(|id| id.name.clone());
7020                        (Some(unnest.as_ref()), alias)
7021                    }
7022                    Expression::Alias(ref a) => {
7023                        if let Expression::Unnest(ref unnest) = a.this {
7024                            (Some(unnest.as_ref()), Some(a.alias.name.clone()))
7025                        } else {
7026                            (None, None)
7027                        }
7028                    }
7029                    _ => (None, None),
7030                };
7031
7032                if let (Some(unnest), Some(alias)) = (unnest_ref, alias_name) {
7033                    // Check the main expression (this) of the UNNEST for GENERATE_DATE_ARRAY function
7034                    if let Expression::Function(ref f) = unnest.this {
7035                        if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
7036                            let start_expr = f.args[0].clone();
7037                            let end_expr = f.args[1].clone();
7038                            let step = f.args.get(2).cloned();
7039
7040                            // Extract unit from step interval
7041                            let unit = if let Some(Expression::Interval(ref iv)) = step {
7042                                if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
7043                                    Some(format!("{:?}", unit).to_ascii_uppercase())
7044                                } else if let Some(ref this) = iv.this {
7045                                    // The interval may be stored as a string like "1 MONTH"
7046                                    if let Expression::Literal(lit) = this {
7047                                        if let Literal::String(ref s) = lit.as_ref() {
7048                                            let parts: Vec<&str> = s.split_whitespace().collect();
7049                                            if parts.len() == 2 {
7050                                                Some(parts[1].to_ascii_uppercase())
7051                                            } else if parts.len() == 1 {
7052                                                // Single word like "MONTH" or just "1"
7053                                                let upper = parts[0].to_ascii_uppercase();
7054                                                if matches!(
7055                                                    upper.as_str(),
7056                                                    "YEAR"
7057                                                        | "QUARTER"
7058                                                        | "MONTH"
7059                                                        | "WEEK"
7060                                                        | "DAY"
7061                                                        | "HOUR"
7062                                                        | "MINUTE"
7063                                                        | "SECOND"
7064                                                ) {
7065                                                    Some(upper)
7066                                                } else {
7067                                                    None
7068                                                }
7069                                            } else {
7070                                                None
7071                                            }
7072                                        } else {
7073                                            None
7074                                        }
7075                                    } else {
7076                                        None
7077                                    }
7078                                } else {
7079                                    None
7080                                }
7081                            } else {
7082                                None
7083                            };
7084
7085                            if let Some(unit_str) = unit {
7086                                gda_info = Some((alias, start_expr, end_expr, unit_str));
7087                                gda_join_idx = Some(idx);
7088                            }
7089                        }
7090                    }
7091                }
7092                if gda_info.is_some() {
7093                    break;
7094                }
7095            }
7096
7097            let Some((alias_name, start_expr, end_expr, unit_str)) = gda_info else {
7098                // Also check FROM clause for UNNEST(GENERATE_DATE_ARRAY(...)) patterns
7099                // This handles Generic->Snowflake where GENERATE_DATE_ARRAY is in FROM, not in JOIN
7100                let result = Self::try_transform_from_gda_snowflake(sel);
7101                return result;
7102            };
7103            let join_idx = gda_join_idx.unwrap();
7104
7105            // Build ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)
7106            // ARRAY_GENERATE_RANGE uses exclusive end, and we need DATEDIFF + 1 values
7107            // (inclusive date range), so the exclusive end is DATEDIFF + 1.
7108            let datediff = Expression::Function(Box::new(Function::new(
7109                "DATEDIFF".to_string(),
7110                vec![
7111                    Expression::boxed_column(Column {
7112                        name: Identifier::new(&unit_str),
7113                        table: None,
7114                        join_mark: false,
7115                        trailing_comments: vec![],
7116                        span: None,
7117                        inferred_type: None,
7118                    }),
7119                    start_expr.clone(),
7120                    end_expr.clone(),
7121                ],
7122            )));
7123            let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
7124                left: datediff,
7125                right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
7126                left_comments: vec![],
7127                operator_comments: vec![],
7128                trailing_comments: vec![],
7129                inferred_type: None,
7130            }));
7131
7132            let array_gen_range = Expression::Function(Box::new(Function::new(
7133                "ARRAY_GENERATE_RANGE".to_string(),
7134                vec![
7135                    Expression::Literal(Box::new(Literal::Number("0".to_string()))),
7136                    datediff_plus_one,
7137                ],
7138            )));
7139
7140            // Build FLATTEN(INPUT => ARRAY_GENERATE_RANGE(...))
7141            let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
7142                name: Identifier::new("INPUT"),
7143                value: array_gen_range,
7144                separator: crate::expressions::NamedArgSeparator::DArrow,
7145            }));
7146            let flatten = Expression::Function(Box::new(Function::new(
7147                "FLATTEN".to_string(),
7148                vec![flatten_input],
7149            )));
7150
7151            // Build LATERAL FLATTEN(...) AS _t0(seq, key, path, index, alias, this)
7152            let alias_table = Alias {
7153                this: flatten,
7154                alias: Identifier::new("_t0"),
7155                column_aliases: vec![
7156                    Identifier::new("seq"),
7157                    Identifier::new("key"),
7158                    Identifier::new("path"),
7159                    Identifier::new("index"),
7160                    Identifier::new(&alias_name),
7161                    Identifier::new("this"),
7162                ],
7163                alias_explicit_as: false,
7164                alias_keyword: None,
7165                pre_alias_comments: vec![],
7166                trailing_comments: vec![],
7167                inferred_type: None,
7168            };
7169            let lateral_expr = Expression::Lateral(Box::new(Lateral {
7170                this: Box::new(Expression::Alias(Box::new(alias_table))),
7171                view: None,
7172                outer: None,
7173                alias: None,
7174                alias_quoted: false,
7175                cross_apply: None,
7176                ordinality: None,
7177                column_aliases: vec![],
7178            }));
7179
7180            // Remove the original join and add to FROM expressions
7181            sel.joins.remove(join_idx);
7182            if let Some(ref mut from) = sel.from {
7183                from.expressions.push(lateral_expr);
7184            }
7185
7186            // Build DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE))
7187            let dateadd_expr = Expression::Function(Box::new(Function::new(
7188                "DATEADD".to_string(),
7189                vec![
7190                    Expression::boxed_column(Column {
7191                        name: Identifier::new(&unit_str),
7192                        table: None,
7193                        join_mark: false,
7194                        trailing_comments: vec![],
7195                        span: None,
7196                        inferred_type: None,
7197                    }),
7198                    Expression::Cast(Box::new(Cast {
7199                        this: Expression::boxed_column(Column {
7200                            name: Identifier::new(&alias_name),
7201                            table: None,
7202                            join_mark: false,
7203                            trailing_comments: vec![],
7204                            span: None,
7205                            inferred_type: None,
7206                        }),
7207                        to: DataType::Int {
7208                            length: None,
7209                            integer_spelling: false,
7210                        },
7211                        trailing_comments: vec![],
7212                        double_colon_syntax: false,
7213                        format: None,
7214                        default: None,
7215                        inferred_type: None,
7216                    })),
7217                    Expression::Cast(Box::new(Cast {
7218                        this: start_expr.clone(),
7219                        to: DataType::Date,
7220                        trailing_comments: vec![],
7221                        double_colon_syntax: false,
7222                        format: None,
7223                        default: None,
7224                        inferred_type: None,
7225                    })),
7226                ],
7227            )));
7228
7229            // Replace references to the alias in the SELECT list
7230            let new_exprs: Vec<Expression> = sel
7231                .expressions
7232                .iter()
7233                .map(|expr| Self::replace_column_ref_with_dateadd(expr, &alias_name, &dateadd_expr))
7234                .collect();
7235            sel.expressions = new_exprs;
7236
7237            Ok(Expression::Select(sel))
7238        })
7239    }
7240
7241    /// Helper: replace column references to `alias_name` with dateadd expression
7242    fn replace_column_ref_with_dateadd(
7243        expr: &Expression,
7244        alias_name: &str,
7245        dateadd: &Expression,
7246    ) -> Expression {
7247        use crate::expressions::*;
7248        match expr {
7249            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
7250                // Plain column reference -> DATEADD(...) AS alias_name
7251                Expression::Alias(Box::new(Alias {
7252                    this: dateadd.clone(),
7253                    alias: Identifier::new(alias_name),
7254                    column_aliases: vec![],
7255                    alias_explicit_as: false,
7256                    alias_keyword: None,
7257                    pre_alias_comments: vec![],
7258                    trailing_comments: vec![],
7259                    inferred_type: None,
7260                }))
7261            }
7262            Expression::Alias(a) => {
7263                // Check if the inner expression references the alias
7264                let new_this = Self::replace_column_ref_inner(&a.this, alias_name, dateadd);
7265                Expression::Alias(Box::new(Alias {
7266                    this: new_this,
7267                    alias: a.alias.clone(),
7268                    column_aliases: a.column_aliases.clone(),
7269                    alias_explicit_as: false,
7270                    alias_keyword: None,
7271                    pre_alias_comments: a.pre_alias_comments.clone(),
7272                    trailing_comments: a.trailing_comments.clone(),
7273                    inferred_type: None,
7274                }))
7275            }
7276            _ => expr.clone(),
7277        }
7278    }
7279
7280    /// Helper: replace column references in inner expression (not top-level)
7281    fn replace_column_ref_inner(
7282        expr: &Expression,
7283        alias_name: &str,
7284        dateadd: &Expression,
7285    ) -> Expression {
7286        use crate::expressions::*;
7287        match expr {
7288            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
7289                dateadd.clone()
7290            }
7291            Expression::Add(op) => {
7292                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7293                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7294                Expression::Add(Box::new(BinaryOp {
7295                    left,
7296                    right,
7297                    left_comments: op.left_comments.clone(),
7298                    operator_comments: op.operator_comments.clone(),
7299                    trailing_comments: op.trailing_comments.clone(),
7300                    inferred_type: None,
7301                }))
7302            }
7303            Expression::Sub(op) => {
7304                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7305                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7306                Expression::Sub(Box::new(BinaryOp {
7307                    left,
7308                    right,
7309                    left_comments: op.left_comments.clone(),
7310                    operator_comments: op.operator_comments.clone(),
7311                    trailing_comments: op.trailing_comments.clone(),
7312                    inferred_type: None,
7313                }))
7314            }
7315            Expression::Mul(op) => {
7316                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
7317                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
7318                Expression::Mul(Box::new(BinaryOp {
7319                    left,
7320                    right,
7321                    left_comments: op.left_comments.clone(),
7322                    operator_comments: op.operator_comments.clone(),
7323                    trailing_comments: op.trailing_comments.clone(),
7324                    inferred_type: None,
7325                }))
7326            }
7327            _ => expr.clone(),
7328        }
7329    }
7330
7331    /// Handle UNNEST(GENERATE_DATE_ARRAY(...)) in FROM clause for Snowflake target.
7332    /// Converts to a subquery with DATEADD + TABLE(FLATTEN(ARRAY_GENERATE_RANGE(...))).
7333    fn try_transform_from_gda_snowflake(
7334        mut sel: Box<crate::expressions::Select>,
7335    ) -> Result<Expression> {
7336        use crate::expressions::*;
7337
7338        // Extract GDA info from FROM clause
7339        let mut gda_info: Option<(
7340            usize,
7341            String,
7342            Expression,
7343            Expression,
7344            String,
7345            Option<(String, Vec<Identifier>)>,
7346        )> = None; // (from_idx, col_name, start, end, unit, outer_alias)
7347
7348        if let Some(ref from) = sel.from {
7349            for (idx, table_expr) in from.expressions.iter().enumerate() {
7350                // Pattern 1: UNNEST(GENERATE_DATE_ARRAY(...))
7351                // Pattern 2: Alias(UNNEST(GENERATE_DATE_ARRAY(...))) AS _q(date_week)
7352                let (unnest_opt, outer_alias_info) = match table_expr {
7353                    Expression::Unnest(ref unnest) => (Some(unnest.as_ref()), None),
7354                    Expression::Alias(ref a) => {
7355                        if let Expression::Unnest(ref unnest) = a.this {
7356                            let alias_info = (a.alias.name.clone(), a.column_aliases.clone());
7357                            (Some(unnest.as_ref()), Some(alias_info))
7358                        } else {
7359                            (None, None)
7360                        }
7361                    }
7362                    _ => (None, None),
7363                };
7364
7365                if let Some(unnest) = unnest_opt {
7366                    // Check for GENERATE_DATE_ARRAY function
7367                    let func_opt = match &unnest.this {
7368                        Expression::Function(ref f)
7369                            if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY")
7370                                && f.args.len() >= 2 =>
7371                        {
7372                            Some(f)
7373                        }
7374                        // Also check for GenerateSeries (from earlier normalization)
7375                        _ => None,
7376                    };
7377
7378                    if let Some(f) = func_opt {
7379                        let start_expr = f.args[0].clone();
7380                        let end_expr = f.args[1].clone();
7381                        let step = f.args.get(2).cloned();
7382
7383                        // Extract unit and column name
7384                        let unit = Self::extract_interval_unit_str(&step);
7385                        let col_name = outer_alias_info
7386                            .as_ref()
7387                            .and_then(|(_, cols)| cols.first().map(|id| id.name.clone()))
7388                            .unwrap_or_else(|| "value".to_string());
7389
7390                        if let Some(unit_str) = unit {
7391                            gda_info = Some((
7392                                idx,
7393                                col_name,
7394                                start_expr,
7395                                end_expr,
7396                                unit_str,
7397                                outer_alias_info,
7398                            ));
7399                            break;
7400                        }
7401                    }
7402                }
7403            }
7404        }
7405
7406        let Some((from_idx, col_name, start_expr, end_expr, unit_str, outer_alias_info)) = gda_info
7407        else {
7408            return Ok(Expression::Select(sel));
7409        };
7410
7411        // Build the Snowflake subquery:
7412        // (SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
7413        //  FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(seq, key, path, index, col_name, this))
7414
7415        // DATEDIFF(unit, start, end)
7416        let datediff = Expression::Function(Box::new(Function::new(
7417            "DATEDIFF".to_string(),
7418            vec![
7419                Expression::boxed_column(Column {
7420                    name: Identifier::new(&unit_str),
7421                    table: None,
7422                    join_mark: false,
7423                    trailing_comments: vec![],
7424                    span: None,
7425                    inferred_type: None,
7426                }),
7427                start_expr.clone(),
7428                end_expr.clone(),
7429            ],
7430        )));
7431        // DATEDIFF(...) + 1
7432        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
7433            left: datediff,
7434            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
7435            left_comments: vec![],
7436            operator_comments: vec![],
7437            trailing_comments: vec![],
7438            inferred_type: None,
7439        }));
7440
7441        let array_gen_range = Expression::Function(Box::new(Function::new(
7442            "ARRAY_GENERATE_RANGE".to_string(),
7443            vec![
7444                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
7445                datediff_plus_one,
7446            ],
7447        )));
7448
7449        // TABLE(FLATTEN(INPUT => ...))
7450        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
7451            name: Identifier::new("INPUT"),
7452            value: array_gen_range,
7453            separator: crate::expressions::NamedArgSeparator::DArrow,
7454        }));
7455        let flatten = Expression::Function(Box::new(Function::new(
7456            "FLATTEN".to_string(),
7457            vec![flatten_input],
7458        )));
7459
7460        // Determine alias name for the table: use outer alias or _t0
7461        let table_alias_name = outer_alias_info
7462            .as_ref()
7463            .map(|(name, _)| name.clone())
7464            .unwrap_or_else(|| "_t0".to_string());
7465
7466        // TABLE(FLATTEN(...)) AS _t0(seq, key, path, index, col_name, this)
7467        let table_func =
7468            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
7469        let flatten_aliased = Expression::Alias(Box::new(Alias {
7470            this: table_func,
7471            alias: Identifier::new(&table_alias_name),
7472            column_aliases: vec![
7473                Identifier::new("seq"),
7474                Identifier::new("key"),
7475                Identifier::new("path"),
7476                Identifier::new("index"),
7477                Identifier::new(&col_name),
7478                Identifier::new("this"),
7479            ],
7480            alias_explicit_as: false,
7481            alias_keyword: None,
7482            pre_alias_comments: vec![],
7483            trailing_comments: vec![],
7484            inferred_type: None,
7485        }));
7486
7487        // SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
7488        let dateadd_expr = Expression::Function(Box::new(Function::new(
7489            "DATEADD".to_string(),
7490            vec![
7491                Expression::boxed_column(Column {
7492                    name: Identifier::new(&unit_str),
7493                    table: None,
7494                    join_mark: false,
7495                    trailing_comments: vec![],
7496                    span: None,
7497                    inferred_type: None,
7498                }),
7499                Expression::Cast(Box::new(Cast {
7500                    this: Expression::boxed_column(Column {
7501                        name: Identifier::new(&col_name),
7502                        table: None,
7503                        join_mark: false,
7504                        trailing_comments: vec![],
7505                        span: None,
7506                        inferred_type: None,
7507                    }),
7508                    to: DataType::Int {
7509                        length: None,
7510                        integer_spelling: false,
7511                    },
7512                    trailing_comments: vec![],
7513                    double_colon_syntax: false,
7514                    format: None,
7515                    default: None,
7516                    inferred_type: None,
7517                })),
7518                // Use start_expr directly - it's already been normalized (DATE literal -> CAST)
7519                start_expr.clone(),
7520            ],
7521        )));
7522        let dateadd_aliased = Expression::Alias(Box::new(Alias {
7523            this: dateadd_expr,
7524            alias: Identifier::new(&col_name),
7525            column_aliases: vec![],
7526            alias_explicit_as: false,
7527            alias_keyword: None,
7528            pre_alias_comments: vec![],
7529            trailing_comments: vec![],
7530            inferred_type: None,
7531        }));
7532
7533        // Build inner SELECT
7534        let mut inner_select = Select::new();
7535        inner_select.expressions = vec![dateadd_aliased];
7536        inner_select.from = Some(From {
7537            expressions: vec![flatten_aliased],
7538        });
7539
7540        let inner_select_expr = Expression::Select(Box::new(inner_select));
7541        let subquery = Expression::Subquery(Box::new(Subquery {
7542            this: inner_select_expr,
7543            alias: None,
7544            column_aliases: vec![],
7545            alias_explicit_as: false,
7546            alias_keyword: None,
7547            order_by: None,
7548            limit: None,
7549            offset: None,
7550            distribute_by: None,
7551            sort_by: None,
7552            cluster_by: None,
7553            lateral: false,
7554            modifiers_inside: false,
7555            trailing_comments: vec![],
7556            inferred_type: None,
7557        }));
7558
7559        // If there was an outer alias (e.g., AS _q(date_week)), wrap with alias
7560        let replacement = if let Some((alias_name, col_aliases)) = outer_alias_info {
7561            Expression::Alias(Box::new(Alias {
7562                this: subquery,
7563                alias: Identifier::new(&alias_name),
7564                column_aliases: col_aliases,
7565                alias_explicit_as: false,
7566                alias_keyword: None,
7567                pre_alias_comments: vec![],
7568                trailing_comments: vec![],
7569                inferred_type: None,
7570            }))
7571        } else {
7572            subquery
7573        };
7574
7575        // Replace the FROM expression
7576        if let Some(ref mut from) = sel.from {
7577            from.expressions[from_idx] = replacement;
7578        }
7579
7580        Ok(Expression::Select(sel))
7581    }
7582
7583    /// Convert ARRAY_SIZE(GENERATE_DATE_ARRAY(start, end, step)) for Snowflake.
7584    /// Produces: ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM (SELECT DATEADD(unit, CAST(value AS INT), start) AS value
7585    ///   FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(...))))
7586    fn convert_array_size_gda_snowflake(f: &crate::expressions::Function) -> Result<Expression> {
7587        use crate::expressions::*;
7588
7589        let start_expr = f.args[0].clone();
7590        let end_expr = f.args[1].clone();
7591        let step = f.args.get(2).cloned();
7592        let unit_str = Self::extract_interval_unit_str(&step).unwrap_or_else(|| "DAY".to_string());
7593        let col_name = "value";
7594
7595        // Build the inner subquery: same as try_transform_from_gda_snowflake
7596        let datediff = Expression::Function(Box::new(Function::new(
7597            "DATEDIFF".to_string(),
7598            vec![
7599                Expression::boxed_column(Column {
7600                    name: Identifier::new(&unit_str),
7601                    table: None,
7602                    join_mark: false,
7603                    trailing_comments: vec![],
7604                    span: None,
7605                    inferred_type: None,
7606                }),
7607                start_expr.clone(),
7608                end_expr.clone(),
7609            ],
7610        )));
7611        // DATEDIFF(...) + 1
7612        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
7613            left: datediff,
7614            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
7615            left_comments: vec![],
7616            operator_comments: vec![],
7617            trailing_comments: vec![],
7618            inferred_type: None,
7619        }));
7620
7621        let array_gen_range = Expression::Function(Box::new(Function::new(
7622            "ARRAY_GENERATE_RANGE".to_string(),
7623            vec![
7624                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
7625                datediff_plus_one,
7626            ],
7627        )));
7628
7629        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
7630            name: Identifier::new("INPUT"),
7631            value: array_gen_range,
7632            separator: crate::expressions::NamedArgSeparator::DArrow,
7633        }));
7634        let flatten = Expression::Function(Box::new(Function::new(
7635            "FLATTEN".to_string(),
7636            vec![flatten_input],
7637        )));
7638
7639        let table_func =
7640            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
7641        let flatten_aliased = Expression::Alias(Box::new(Alias {
7642            this: table_func,
7643            alias: Identifier::new("_t0"),
7644            column_aliases: vec![
7645                Identifier::new("seq"),
7646                Identifier::new("key"),
7647                Identifier::new("path"),
7648                Identifier::new("index"),
7649                Identifier::new(col_name),
7650                Identifier::new("this"),
7651            ],
7652            alias_explicit_as: false,
7653            alias_keyword: None,
7654            pre_alias_comments: vec![],
7655            trailing_comments: vec![],
7656            inferred_type: None,
7657        }));
7658
7659        let dateadd_expr = Expression::Function(Box::new(Function::new(
7660            "DATEADD".to_string(),
7661            vec![
7662                Expression::boxed_column(Column {
7663                    name: Identifier::new(&unit_str),
7664                    table: None,
7665                    join_mark: false,
7666                    trailing_comments: vec![],
7667                    span: None,
7668                    inferred_type: None,
7669                }),
7670                Expression::Cast(Box::new(Cast {
7671                    this: Expression::boxed_column(Column {
7672                        name: Identifier::new(col_name),
7673                        table: None,
7674                        join_mark: false,
7675                        trailing_comments: vec![],
7676                        span: None,
7677                        inferred_type: None,
7678                    }),
7679                    to: DataType::Int {
7680                        length: None,
7681                        integer_spelling: false,
7682                    },
7683                    trailing_comments: vec![],
7684                    double_colon_syntax: false,
7685                    format: None,
7686                    default: None,
7687                    inferred_type: None,
7688                })),
7689                start_expr.clone(),
7690            ],
7691        )));
7692        let dateadd_aliased = Expression::Alias(Box::new(Alias {
7693            this: dateadd_expr,
7694            alias: Identifier::new(col_name),
7695            column_aliases: vec![],
7696            alias_explicit_as: false,
7697            alias_keyword: None,
7698            pre_alias_comments: vec![],
7699            trailing_comments: vec![],
7700            inferred_type: None,
7701        }));
7702
7703        // Inner SELECT: SELECT DATEADD(...) AS value FROM TABLE(FLATTEN(...)) AS _t0(...)
7704        let mut inner_select = Select::new();
7705        inner_select.expressions = vec![dateadd_aliased];
7706        inner_select.from = Some(From {
7707            expressions: vec![flatten_aliased],
7708        });
7709
7710        // Wrap in subquery for the inner part
7711        let inner_subquery = Expression::Subquery(Box::new(Subquery {
7712            this: Expression::Select(Box::new(inner_select)),
7713            alias: None,
7714            column_aliases: vec![],
7715            alias_explicit_as: false,
7716            alias_keyword: None,
7717            order_by: None,
7718            limit: None,
7719            offset: None,
7720            distribute_by: None,
7721            sort_by: None,
7722            cluster_by: None,
7723            lateral: false,
7724            modifiers_inside: false,
7725            trailing_comments: vec![],
7726            inferred_type: None,
7727        }));
7728
7729        // Outer: SELECT ARRAY_AGG(*) FROM (inner_subquery)
7730        let star = Expression::Star(Star {
7731            table: None,
7732            except: None,
7733            replace: None,
7734            rename: None,
7735            trailing_comments: vec![],
7736            span: None,
7737        });
7738        let array_agg = Expression::ArrayAgg(Box::new(AggFunc {
7739            this: star,
7740            distinct: false,
7741            filter: None,
7742            order_by: vec![],
7743            name: Some("ARRAY_AGG".to_string()),
7744            ignore_nulls: None,
7745            having_max: None,
7746            limit: None,
7747            inferred_type: None,
7748        }));
7749
7750        let mut outer_select = Select::new();
7751        outer_select.expressions = vec![array_agg];
7752        outer_select.from = Some(From {
7753            expressions: vec![inner_subquery],
7754        });
7755
7756        // Wrap in a subquery
7757        let outer_subquery = Expression::Subquery(Box::new(Subquery {
7758            this: Expression::Select(Box::new(outer_select)),
7759            alias: None,
7760            column_aliases: vec![],
7761            alias_explicit_as: false,
7762            alias_keyword: None,
7763            order_by: None,
7764            limit: None,
7765            offset: None,
7766            distribute_by: None,
7767            sort_by: None,
7768            cluster_by: None,
7769            lateral: false,
7770            modifiers_inside: false,
7771            trailing_comments: vec![],
7772            inferred_type: None,
7773        }));
7774
7775        // ARRAY_SIZE(subquery)
7776        Ok(Expression::ArraySize(Box::new(UnaryFunc::new(
7777            outer_subquery,
7778        ))))
7779    }
7780
7781    /// Extract interval unit string from an optional step expression.
7782    fn extract_interval_unit_str(step: &Option<Expression>) -> Option<String> {
7783        use crate::expressions::*;
7784        if let Some(Expression::Interval(ref iv)) = step {
7785            if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
7786                return Some(format!("{:?}", unit).to_ascii_uppercase());
7787            }
7788            if let Some(ref this) = iv.this {
7789                if let Expression::Literal(lit) = this {
7790                    if let Literal::String(ref s) = lit.as_ref() {
7791                        let parts: Vec<&str> = s.split_whitespace().collect();
7792                        if parts.len() == 2 {
7793                            return Some(parts[1].to_ascii_uppercase());
7794                        } else if parts.len() == 1 {
7795                            let upper = parts[0].to_ascii_uppercase();
7796                            if matches!(
7797                                upper.as_str(),
7798                                "YEAR"
7799                                    | "QUARTER"
7800                                    | "MONTH"
7801                                    | "WEEK"
7802                                    | "DAY"
7803                                    | "HOUR"
7804                                    | "MINUTE"
7805                                    | "SECOND"
7806                            ) {
7807                                return Some(upper);
7808                            }
7809                        }
7810                    }
7811                }
7812            }
7813        }
7814        // Default to DAY if no step or no interval
7815        if step.is_none() {
7816            return Some("DAY".to_string());
7817        }
7818        None
7819    }
7820
7821    fn normalize_snowflake_pretty(mut sql: String) -> String {
7822        if sql.contains("LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)")
7823            && sql.contains("ARRAY_GENERATE_RANGE(0, (GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1) + 1)")
7824        {
7825            sql = sql.replace(
7826                "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')",
7827                "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    )",
7828            );
7829
7830            sql = sql.replace(
7831                "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)",
7832                "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)",
7833            );
7834
7835            sql = sql.replace(
7836                "OR (_u.pos > (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1)\n  AND _u_2.pos_2 = (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1))",
7837                "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  )",
7838            );
7839        }
7840
7841        sql
7842    }
7843
7844    #[cfg(feature = "transpile")]
7845    fn wrap_tsql_top_level_values(expr: Expression) -> Expression {
7846        match expr {
7847            Expression::Values(values) => Self::tsql_values_as_select(*values),
7848            Expression::Union(mut union) => {
7849                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
7850                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
7851                union.left = Self::wrap_tsql_values_set_operand(left);
7852                union.right = Self::wrap_tsql_values_set_operand(right);
7853                Expression::Union(union)
7854            }
7855            Expression::Intersect(mut intersect) => {
7856                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
7857                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
7858                intersect.left = Self::wrap_tsql_values_set_operand(left);
7859                intersect.right = Self::wrap_tsql_values_set_operand(right);
7860                Expression::Intersect(intersect)
7861            }
7862            Expression::Except(mut except) => {
7863                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
7864                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
7865                except.left = Self::wrap_tsql_values_set_operand(left);
7866                except.right = Self::wrap_tsql_values_set_operand(right);
7867                Expression::Except(except)
7868            }
7869            other => other,
7870        }
7871    }
7872
7873    #[cfg(feature = "transpile")]
7874    fn wrap_tsql_values_set_operand(expr: Expression) -> Expression {
7875        match expr {
7876            Expression::Values(values) => Self::tsql_values_as_select(*values),
7877            Expression::Union(mut union) => {
7878                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
7879                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
7880                union.left = Self::wrap_tsql_values_set_operand(left);
7881                union.right = Self::wrap_tsql_values_set_operand(right);
7882                Expression::Union(union)
7883            }
7884            Expression::Intersect(mut intersect) => {
7885                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
7886                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
7887                intersect.left = Self::wrap_tsql_values_set_operand(left);
7888                intersect.right = Self::wrap_tsql_values_set_operand(right);
7889                Expression::Intersect(intersect)
7890            }
7891            Expression::Except(mut except) => {
7892                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
7893                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
7894                except.left = Self::wrap_tsql_values_set_operand(left);
7895                except.right = Self::wrap_tsql_values_set_operand(right);
7896                Expression::Except(except)
7897            }
7898            other => other,
7899        }
7900    }
7901
7902    #[cfg(feature = "transpile")]
7903    fn tsql_values_as_select(mut values: crate::expressions::Values) -> Expression {
7904        let column_aliases = if values.column_aliases.is_empty() {
7905            let column_count = values
7906                .expressions
7907                .first()
7908                .map(|row| row.expressions.len())
7909                .unwrap_or(0);
7910            (1..=column_count)
7911                .map(|index| Identifier::new(format!("column{index}")))
7912                .collect()
7913        } else {
7914            std::mem::take(&mut values.column_aliases)
7915        };
7916
7917        values.alias = None;
7918
7919        let values_subquery = Expression::Subquery(Box::new(crate::expressions::Subquery {
7920            this: Expression::Values(Box::new(values)),
7921            alias: Some(Identifier::new("_v")),
7922            column_aliases,
7923            alias_explicit_as: false,
7924            alias_keyword: None,
7925            order_by: None,
7926            limit: None,
7927            offset: None,
7928            distribute_by: None,
7929            sort_by: None,
7930            cluster_by: None,
7931            lateral: false,
7932            modifiers_inside: false,
7933            trailing_comments: Vec::new(),
7934            inferred_type: None,
7935        }));
7936
7937        let mut select = crate::expressions::Select::new();
7938        select.expressions = vec![Expression::star()];
7939        select.from = Some(From {
7940            expressions: vec![values_subquery],
7941        });
7942
7943        Expression::Select(Box::new(select))
7944    }
7945
7946    fn extract_interval_parts(
7947        interval_expr: &Expression,
7948    ) -> Option<(Expression, crate::expressions::IntervalUnit)> {
7949        use crate::expressions::{DataType, IntervalUnit, IntervalUnitSpec, Literal};
7950
7951        fn unit_from_str(unit: &str) -> Option<IntervalUnit> {
7952            match unit.trim().to_ascii_uppercase().as_str() {
7953                "YEAR" | "YEARS" | "Y" | "YR" | "YRS" | "YY" | "YYYY" => Some(IntervalUnit::Year),
7954                "QUARTER" | "QUARTERS" | "Q" | "QTR" | "QTRS" | "QQ" => Some(IntervalUnit::Quarter),
7955                "MONTH" | "MONTHS" | "MON" | "MONS" | "MM" => Some(IntervalUnit::Month),
7956                "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" | "ISOWEEK" => {
7957                    Some(IntervalUnit::Week)
7958                }
7959                "DAY" | "DAYS" | "D" | "DD" => Some(IntervalUnit::Day),
7960                "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some(IntervalUnit::Hour),
7961                "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some(IntervalUnit::Minute),
7962                "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some(IntervalUnit::Second),
7963                "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MSECOND"
7964                | "MSECONDS" | "MILLISEC" | "MILLISECS" | "MILLISECON" => {
7965                    Some(IntervalUnit::Millisecond)
7966                }
7967                "MICROSECOND" | "MICROSECONDS" | "US" | "USEC" | "USECS" | "USECOND"
7968                | "USECONDS" | "MICROSEC" | "MICROSECS" | "MCS" => Some(IntervalUnit::Microsecond),
7969                "NANOSECOND" | "NANOSECONDS" | "NS" | "NSEC" | "NSECS" | "NSECOND" | "NSECONDS"
7970                | "NANOSEC" | "NANOSECS" => Some(IntervalUnit::Nanosecond),
7971                _ => None,
7972            }
7973        }
7974
7975        fn parts_from_literal_string(s: &str) -> Option<(Expression, IntervalUnit)> {
7976            let mut parts = s.split_whitespace();
7977            let value = parts.next()?;
7978            let unit = unit_from_str(parts.next()?)?;
7979            Some((
7980                Expression::Literal(Box::new(Literal::String(value.to_string()))),
7981                unit,
7982            ))
7983        }
7984
7985        fn unit_from_spec(unit: &IntervalUnitSpec) -> Option<IntervalUnit> {
7986            match unit {
7987                IntervalUnitSpec::Simple { unit, .. } => Some(*unit),
7988                IntervalUnitSpec::Expr(expr) => match expr.as_ref() {
7989                    Expression::Day(_) => Some(IntervalUnit::Day),
7990                    Expression::Month(_) => Some(IntervalUnit::Month),
7991                    Expression::Year(_) => Some(IntervalUnit::Year),
7992                    Expression::Identifier(id) => unit_from_str(&id.name),
7993                    Expression::Var(v) => unit_from_str(&v.this),
7994                    Expression::Column(col) => unit_from_str(&col.name.name),
7995                    _ => None,
7996                },
7997                _ => None,
7998            }
7999        }
8000
8001        match interval_expr {
8002            Expression::Interval(iv) => {
8003                let val = iv.this.clone().unwrap_or(Expression::number(0));
8004                if let Expression::Literal(lit) = &val {
8005                    if let Literal::String(s) = lit.as_ref() {
8006                        if let Some(parts) = parts_from_literal_string(s) {
8007                            return Some(parts);
8008                        }
8009                    }
8010                }
8011                let unit = iv
8012                    .unit
8013                    .as_ref()
8014                    .and_then(unit_from_spec)
8015                    .unwrap_or(IntervalUnit::Day);
8016                Some((val, unit))
8017            }
8018            Expression::Cast(cast) if matches!(cast.to, DataType::Interval { .. }) => {
8019                if let Expression::Literal(lit) = &cast.this {
8020                    if let Literal::String(s) = lit.as_ref() {
8021                        if let Some(parts) = parts_from_literal_string(s) {
8022                            return Some(parts);
8023                        }
8024                    }
8025                }
8026                let unit = match &cast.to {
8027                    DataType::Interval {
8028                        unit: Some(unit), ..
8029                    } => unit_from_str(unit).unwrap_or(IntervalUnit::Day),
8030                    _ => IntervalUnit::Day,
8031                };
8032                Some((cast.this.clone(), unit))
8033            }
8034            _ => None,
8035        }
8036    }
8037
8038    fn data_type_is_interval(dt: &DataType) -> bool {
8039        match dt {
8040            DataType::Interval { .. } => true,
8041            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("INTERVAL"),
8042            _ => false,
8043        }
8044    }
8045
8046    fn node_is_interval_cast(node: &Expression) -> bool {
8047        match node {
8048            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
8049                Self::data_type_is_interval(&c.to)
8050            }
8051            _ => false,
8052        }
8053    }
8054
8055    fn reject_tsql_interval_casts(
8056        expr: &Expression,
8057        target: DialectType,
8058        opts: &TranspileOptions,
8059    ) -> Result<()> {
8060        if !matches!(
8061            opts.unsupported_level,
8062            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
8063        ) {
8064            return Ok(());
8065        }
8066
8067        if expr.dfs().any(Self::node_is_interval_cast) {
8068            return Err(crate::error::Error::unsupported(
8069                "INTERVAL casts",
8070                target.to_string(),
8071            ));
8072        }
8073
8074        Ok(())
8075    }
8076
8077    fn tsql_varchar_max_type() -> DataType {
8078        DataType::Custom {
8079            name: "VARCHAR(MAX)".to_string(),
8080        }
8081    }
8082
8083    fn rewrite_tsql_interval_casts_to_varchar(expr: Expression) -> Result<Expression> {
8084        transform_recursive(expr, &|e| match e {
8085            Expression::Cast(mut cast) if Self::data_type_is_interval(&cast.to) => {
8086                cast.to = Self::tsql_varchar_max_type();
8087                cast.double_colon_syntax = false;
8088                Ok(Expression::Cast(cast))
8089            }
8090            Expression::TryCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
8091                cast.to = Self::tsql_varchar_max_type();
8092                cast.double_colon_syntax = false;
8093                Ok(Expression::TryCast(cast))
8094            }
8095            Expression::SafeCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
8096                cast.to = Self::tsql_varchar_max_type();
8097                cast.double_colon_syntax = false;
8098                Ok(Expression::SafeCast(cast))
8099            }
8100            _ => Ok(e),
8101        })
8102    }
8103
8104    fn rewrite_tsql_interval_arithmetic(
8105        expr: &Expression,
8106        source: DialectType,
8107    ) -> Option<Expression> {
8108        match expr {
8109            Expression::Add(op) => {
8110                if Self::extract_interval_parts(&op.right).is_some() {
8111                    return Some(Self::build_tsql_dateadd_from_interval(
8112                        op.left.clone(),
8113                        &op.right,
8114                        false,
8115                    ));
8116                }
8117
8118                if Self::is_postgres_family_source(source) {
8119                    if Self::is_explicit_date_expr(&op.left)
8120                        && Self::is_integer_day_offset_expr(&op.right)
8121                    {
8122                        return Some(Self::build_tsql_dateadd_days(
8123                            op.left.clone(),
8124                            op.right.clone(),
8125                            false,
8126                        ));
8127                    }
8128
8129                    if Self::is_integer_day_offset_expr(&op.left)
8130                        && Self::is_explicit_date_expr(&op.right)
8131                    {
8132                        return Some(Self::build_tsql_dateadd_days(
8133                            op.right.clone(),
8134                            op.left.clone(),
8135                            false,
8136                        ));
8137                    }
8138                }
8139
8140                None
8141            }
8142            Expression::Sub(op) => {
8143                if Self::extract_interval_parts(&op.right).is_some() {
8144                    return Some(Self::build_tsql_dateadd_from_interval(
8145                        op.left.clone(),
8146                        &op.right,
8147                        true,
8148                    ));
8149                }
8150
8151                if Self::is_postgres_family_source(source) {
8152                    if Self::is_explicit_date_expr(&op.left)
8153                        && Self::is_explicit_date_expr(&op.right)
8154                    {
8155                        return Some(Self::build_tsql_datediff_days(
8156                            op.right.clone(),
8157                            op.left.clone(),
8158                        ));
8159                    }
8160
8161                    if Self::is_explicit_date_expr(&op.left)
8162                        && Self::is_integer_day_offset_expr(&op.right)
8163                    {
8164                        return Some(Self::build_tsql_dateadd_days(
8165                            op.left.clone(),
8166                            op.right.clone(),
8167                            true,
8168                        ));
8169                    }
8170                }
8171
8172                None
8173            }
8174            _ => None,
8175        }
8176    }
8177
8178    fn is_postgres_family_source(source: DialectType) -> bool {
8179        matches!(
8180            source,
8181            DialectType::PostgreSQL
8182                | DialectType::Redshift
8183                | DialectType::Materialize
8184                | DialectType::RisingWave
8185                | DialectType::CockroachDB
8186        )
8187    }
8188
8189    fn is_explicit_date_expr(expr: &Expression) -> bool {
8190        use crate::expressions::Literal;
8191
8192        match expr {
8193            Expression::Literal(lit) => matches!(lit.as_ref(), Literal::Date(_)),
8194            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
8195                matches!(c.to, crate::expressions::DataType::Date)
8196            }
8197            Expression::Paren(p) => Self::is_explicit_date_expr(&p.this),
8198            Expression::CurrentDate(_)
8199            | Expression::Date(_)
8200            | Expression::MakeDate(_)
8201            | Expression::ToDate(_)
8202            | Expression::DateStrToDate(_) => true,
8203            _ => false,
8204        }
8205    }
8206
8207    fn is_integer_day_offset_expr(expr: &Expression) -> bool {
8208        use crate::expressions::Literal;
8209
8210        match expr {
8211            Expression::Literal(lit) => match lit.as_ref() {
8212                Literal::Number(n) => n.parse::<i64>().is_ok(),
8213                _ => false,
8214            },
8215            Expression::Parameter(_) | Expression::Placeholder(_) => true,
8216            Expression::Neg(op) => Self::is_integer_day_offset_expr(&op.this),
8217            Expression::Paren(p) => Self::is_integer_day_offset_expr(&p.this),
8218            _ => false,
8219        }
8220    }
8221
8222    fn build_tsql_datediff_days(start: Expression, end: Expression) -> Expression {
8223        Expression::Function(Box::new(Function::new(
8224            "DATEDIFF".to_string(),
8225            vec![Expression::Identifier(Identifier::new("DAY")), start, end],
8226        )))
8227    }
8228
8229    fn build_tsql_dateadd_days(date: Expression, amount: Expression, subtract: bool) -> Expression {
8230        Expression::Function(Box::new(Function::new(
8231            "DATEADD".to_string(),
8232            vec![
8233                Expression::Identifier(Identifier::new("DAY")),
8234                Self::tsql_dateadd_amount(amount, subtract),
8235                date,
8236            ],
8237        )))
8238    }
8239
8240    fn build_tsql_dateadd_from_interval(
8241        date: Expression,
8242        interval: &Expression,
8243        subtract: bool,
8244    ) -> Expression {
8245        let (value, unit) = Self::extract_interval_parts(interval)
8246            .unwrap_or_else(|| (interval.clone(), crate::expressions::IntervalUnit::Day));
8247        let unit = normalization::temporal::interval_unit_to_string(&unit);
8248        let amount = Self::tsql_dateadd_amount(value, subtract);
8249
8250        Expression::Function(Box::new(Function::new(
8251            "DATEADD".to_string(),
8252            vec![Expression::Identifier(Identifier::new(unit)), amount, date],
8253        )))
8254    }
8255
8256    fn tsql_dateadd_amount(value: Expression, negate: bool) -> Expression {
8257        use crate::expressions::{Parameter, ParameterStyle, UnaryOp};
8258
8259        fn numeric_literal_value(value: &Expression) -> Option<&str> {
8260            match value {
8261                Expression::Literal(lit) => match lit.as_ref() {
8262                    crate::expressions::Literal::Number(n)
8263                    | crate::expressions::Literal::String(n) => Some(n.as_str()),
8264                    _ => None,
8265                },
8266                _ => None,
8267            }
8268        }
8269
8270        fn colon_parameter(value: &Expression) -> Option<Expression> {
8271            let Expression::Literal(lit) = value else {
8272                return None;
8273            };
8274            let crate::expressions::Literal::String(s) = lit.as_ref() else {
8275                return None;
8276            };
8277            let name = s.strip_prefix(':')?;
8278            if name.is_empty()
8279                || !name
8280                    .chars()
8281                    .all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
8282            {
8283                return None;
8284            }
8285
8286            Some(Expression::Parameter(Box::new(Parameter {
8287                name: if name.chars().all(|ch| ch.is_ascii_digit()) {
8288                    None
8289                } else {
8290                    Some(name.to_string())
8291                },
8292                index: name.parse::<u32>().ok(),
8293                style: ParameterStyle::Colon,
8294                quoted: false,
8295                string_quoted: false,
8296                expression: None,
8297            })))
8298        }
8299
8300        let value = colon_parameter(&value).unwrap_or(value);
8301
8302        if let Some(n) = numeric_literal_value(&value) {
8303            if let Ok(parsed) = n.parse::<f64>() {
8304                let normalized = if negate { -parsed } else { parsed };
8305                let rendered = if normalized.fract() == 0.0 {
8306                    format!("{}", normalized as i64)
8307                } else {
8308                    normalized.to_string()
8309                };
8310                return Expression::Literal(Box::new(crate::expressions::Literal::Number(
8311                    rendered,
8312                )));
8313            }
8314        }
8315
8316        if !negate {
8317            return value;
8318        }
8319
8320        match value {
8321            Expression::Neg(op) => op.this,
8322            other => Expression::Neg(Box::new(UnaryOp {
8323                this: other,
8324                inferred_type: None,
8325            })),
8326        }
8327    }
8328
8329    /// Internal TO_DATE function that won't be converted to CAST by the Snowflake handler.
8330    /// Uses the name `_POLYGLOT_TO_DATE` which is not recognized by the TO_DATE -> CAST logic.
8331    /// The Snowflake DATEDIFF handler converts these back to TO_DATE.
8332    const PRESERVED_TO_DATE: &'static str = "_POLYGLOT_TO_DATE";
8333}
8334
8335#[cfg(test)]
8336mod tests {
8337    use super::*;
8338
8339    #[test]
8340    fn built_in_dialect_instances_share_tokenizer_config() {
8341        let first = Dialect::get(DialectType::PostgreSQL);
8342        let second = Dialect::get(DialectType::PostgreSQL);
8343
8344        assert!(first.tokenizer.shares_config_with(&second.tokenizer));
8345    }
8346
8347    #[test]
8348    fn test_dialect_type_from_str() {
8349        assert_eq!(
8350            "postgres".parse::<DialectType>().unwrap(),
8351            DialectType::PostgreSQL
8352        );
8353        assert_eq!(
8354            "postgresql".parse::<DialectType>().unwrap(),
8355            DialectType::PostgreSQL
8356        );
8357        assert_eq!("mysql".parse::<DialectType>().unwrap(), DialectType::MySQL);
8358        assert_eq!(
8359            "bigquery".parse::<DialectType>().unwrap(),
8360            DialectType::BigQuery
8361        );
8362    }
8363
8364    #[test]
8365    fn test_basic_transpile() {
8366        let dialect = Dialect::get(DialectType::Generic);
8367        let result = dialect
8368            .transpile("SELECT 1", DialectType::PostgreSQL)
8369            .unwrap();
8370        assert_eq!(result.len(), 1);
8371        assert_eq!(result[0], "SELECT 1");
8372    }
8373
8374    #[test]
8375    fn test_sqlite_double_quoted_column_defaults_to_postgres_strings() {
8376        let sqlite = Dialect::get(DialectType::SQLite);
8377        let result = sqlite
8378            .transpile(
8379                r#"CREATE TABLE "_collections" (
8380                    "type" TEXT DEFAULT "base" NOT NULL,
8381                    "fields" JSON DEFAULT "[]" NOT NULL,
8382                    "options" JSON DEFAULT "{}" NOT NULL
8383                )"#,
8384                DialectType::PostgreSQL,
8385            )
8386            .unwrap();
8387
8388        assert!(result[0].contains(r#""type" TEXT DEFAULT 'base' NOT NULL"#));
8389        assert!(result[0].contains(r#""fields" JSON DEFAULT '[]' NOT NULL"#));
8390        assert!(result[0].contains(r#""options" JSON DEFAULT '{}' NOT NULL"#));
8391    }
8392
8393    #[test]
8394    fn test_sqlite_identity_preserves_double_quoted_column_defaults() {
8395        let sqlite = Dialect::get(DialectType::SQLite);
8396        let result = sqlite
8397            .transpile(
8398                r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#,
8399                DialectType::SQLite,
8400            )
8401            .unwrap();
8402
8403        assert_eq!(
8404            result[0],
8405            r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#
8406        );
8407    }
8408
8409    #[test]
8410    fn test_function_transformation_mysql() {
8411        // NVL should be transformed to IFNULL in MySQL
8412        let dialect = Dialect::get(DialectType::Generic);
8413        let result = dialect
8414            .transpile("SELECT NVL(a, b)", DialectType::MySQL)
8415            .unwrap();
8416        assert_eq!(result[0], "SELECT IFNULL(a, b)");
8417    }
8418
8419    #[test]
8420    fn test_get_path_duckdb() {
8421        // Test: step by step
8422        let snowflake = Dialect::get(DialectType::Snowflake);
8423
8424        // Step 1: Parse and check what Snowflake produces as intermediate
8425        let result_sf_sf = snowflake
8426            .transpile(
8427                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
8428                DialectType::Snowflake,
8429            )
8430            .unwrap();
8431        eprintln!("Snowflake->Snowflake colon: {}", result_sf_sf[0]);
8432
8433        // Step 2: DuckDB target
8434        let result_sf_dk = snowflake
8435            .transpile(
8436                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
8437                DialectType::DuckDB,
8438            )
8439            .unwrap();
8440        eprintln!("Snowflake->DuckDB colon: {}", result_sf_dk[0]);
8441
8442        // Step 3: GET_PATH directly
8443        let result_gp = snowflake
8444            .transpile(
8445                "SELECT GET_PATH(PARSE_JSON('{\"fruit\":\"banana\"}'), 'fruit')",
8446                DialectType::DuckDB,
8447            )
8448            .unwrap();
8449        eprintln!("Snowflake->DuckDB explicit GET_PATH: {}", result_gp[0]);
8450    }
8451
8452    #[test]
8453    fn test_function_transformation_postgres() {
8454        // IFNULL should be transformed to COALESCE in PostgreSQL
8455        let dialect = Dialect::get(DialectType::Generic);
8456        let result = dialect
8457            .transpile("SELECT IFNULL(a, b)", DialectType::PostgreSQL)
8458            .unwrap();
8459        assert_eq!(result[0], "SELECT COALESCE(a, b)");
8460
8461        // NVL should also be transformed to COALESCE
8462        let result = dialect
8463            .transpile("SELECT NVL(a, b)", DialectType::PostgreSQL)
8464            .unwrap();
8465        assert_eq!(result[0], "SELECT COALESCE(a, b)");
8466    }
8467
8468    #[test]
8469    fn test_hive_cast_to_trycast() {
8470        // Hive CAST should become TRY_CAST for targets that support it
8471        let hive = Dialect::get(DialectType::Hive);
8472        let result = hive
8473            .transpile("CAST(1 AS INT)", DialectType::DuckDB)
8474            .unwrap();
8475        assert_eq!(result[0], "TRY_CAST(1 AS INT)");
8476
8477        let result = hive
8478            .transpile("CAST(1 AS INT)", DialectType::Presto)
8479            .unwrap();
8480        assert_eq!(result[0], "TRY_CAST(1 AS INTEGER)");
8481    }
8482
8483    #[test]
8484    fn test_hive_array_identity() {
8485        // Hive ARRAY<DATE> should preserve angle bracket syntax
8486        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')";
8487        let hive = Dialect::get(DialectType::Hive);
8488
8489        // Test via transpile (this works)
8490        let result = hive.transpile(sql, DialectType::Hive).unwrap();
8491        eprintln!("Hive ARRAY via transpile: {}", result[0]);
8492        assert!(
8493            result[0].contains("ARRAY<DATE>"),
8494            "transpile: Expected ARRAY<DATE>, got: {}",
8495            result[0]
8496        );
8497
8498        // Test via parse -> transform -> generate (identity test path)
8499        let ast = hive.parse(sql).unwrap();
8500        let transformed = hive.transform(ast[0].clone()).unwrap();
8501        let output = hive.generate(&transformed).unwrap();
8502        eprintln!("Hive ARRAY via identity path: {}", output);
8503        assert!(
8504            output.contains("ARRAY<DATE>"),
8505            "identity path: Expected ARRAY<DATE>, got: {}",
8506            output
8507        );
8508    }
8509
8510    #[test]
8511    fn test_starrocks_delete_between_expansion() {
8512        // StarRocks doesn't support BETWEEN in DELETE statements
8513        let dialect = Dialect::get(DialectType::Generic);
8514
8515        // BETWEEN should be expanded to >= AND <= in DELETE
8516        let result = dialect
8517            .transpile(
8518                "DELETE FROM t WHERE a BETWEEN b AND c",
8519                DialectType::StarRocks,
8520            )
8521            .unwrap();
8522        assert_eq!(result[0], "DELETE FROM t WHERE a >= b AND a <= c");
8523
8524        // NOT BETWEEN should be expanded to < OR > in DELETE
8525        let result = dialect
8526            .transpile(
8527                "DELETE FROM t WHERE a NOT BETWEEN b AND c",
8528                DialectType::StarRocks,
8529            )
8530            .unwrap();
8531        assert_eq!(result[0], "DELETE FROM t WHERE a < b OR a > c");
8532
8533        // BETWEEN in SELECT should NOT be expanded (StarRocks supports it there)
8534        let result = dialect
8535            .transpile(
8536                "SELECT * FROM t WHERE a BETWEEN b AND c",
8537                DialectType::StarRocks,
8538            )
8539            .unwrap();
8540        assert!(
8541            result[0].contains("BETWEEN"),
8542            "BETWEEN should be preserved in SELECT"
8543        );
8544    }
8545
8546    #[test]
8547    fn test_snowflake_ltrim_rtrim_parse() {
8548        let sf = Dialect::get(DialectType::Snowflake);
8549        let sql = "SELECT LTRIM(RTRIM(col)) FROM t1";
8550        let result = sf.transpile(sql, DialectType::DuckDB);
8551        match &result {
8552            Ok(r) => eprintln!("LTRIM/RTRIM result: {}", r[0]),
8553            Err(e) => eprintln!("LTRIM/RTRIM error: {}", e),
8554        }
8555        assert!(
8556            result.is_ok(),
8557            "Expected successful parse of LTRIM(RTRIM(col)), got error: {:?}",
8558            result.err()
8559        );
8560    }
8561
8562    #[test]
8563    fn test_duckdb_count_if_parse() {
8564        let duck = Dialect::get(DialectType::DuckDB);
8565        let sql = "COUNT_IF(x)";
8566        let result = duck.transpile(sql, DialectType::DuckDB);
8567        match &result {
8568            Ok(r) => eprintln!("COUNT_IF result: {}", r[0]),
8569            Err(e) => eprintln!("COUNT_IF error: {}", e),
8570        }
8571        assert!(
8572            result.is_ok(),
8573            "Expected successful parse of COUNT_IF(x), got error: {:?}",
8574            result.err()
8575        );
8576    }
8577
8578    #[test]
8579    fn test_tsql_cast_tinyint_parse() {
8580        let tsql = Dialect::get(DialectType::TSQL);
8581        let sql = "CAST(X AS TINYINT)";
8582        let result = tsql.transpile(sql, DialectType::DuckDB);
8583        match &result {
8584            Ok(r) => eprintln!("TSQL CAST TINYINT result: {}", r[0]),
8585            Err(e) => eprintln!("TSQL CAST TINYINT error: {}", e),
8586        }
8587        assert!(
8588            result.is_ok(),
8589            "Expected successful transpile, got error: {:?}",
8590            result.err()
8591        );
8592    }
8593
8594    #[test]
8595    fn test_pg_hash_bitwise_xor() {
8596        let dialect = Dialect::get(DialectType::PostgreSQL);
8597        let result = dialect.transpile("x # y", DialectType::PostgreSQL).unwrap();
8598        assert_eq!(result[0], "x # y");
8599    }
8600
8601    #[test]
8602    fn test_pg_array_to_duckdb() {
8603        let dialect = Dialect::get(DialectType::PostgreSQL);
8604        let result = dialect
8605            .transpile("SELECT ARRAY[1, 2, 3] @> ARRAY[1, 2]", DialectType::DuckDB)
8606            .unwrap();
8607        assert_eq!(result[0], "SELECT [1, 2, 3] @> [1, 2]");
8608    }
8609
8610    #[test]
8611    fn test_array_remove_bigquery() {
8612        let dialect = Dialect::get(DialectType::Generic);
8613        let result = dialect
8614            .transpile("ARRAY_REMOVE(the_array, target)", DialectType::BigQuery)
8615            .unwrap();
8616        assert_eq!(
8617            result[0],
8618            "ARRAY(SELECT _u FROM UNNEST(the_array) AS _u WHERE _u <> target)"
8619        );
8620    }
8621
8622    #[test]
8623    fn test_map_clickhouse_case() {
8624        let dialect = Dialect::get(DialectType::Generic);
8625        let parsed = dialect
8626            .parse("CAST(MAP('a', '1') AS MAP(TEXT, TEXT))")
8627            .unwrap();
8628        eprintln!("MAP parsed: {:?}", parsed);
8629        let result = dialect
8630            .transpile(
8631                "CAST(MAP('a', '1') AS MAP(TEXT, TEXT))",
8632                DialectType::ClickHouse,
8633            )
8634            .unwrap();
8635        eprintln!("MAP result: {}", result[0]);
8636    }
8637
8638    #[test]
8639    fn test_generate_date_array_presto() {
8640        let dialect = Dialect::get(DialectType::Generic);
8641        let result = dialect.transpile(
8642            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8643            DialectType::Presto,
8644        ).unwrap();
8645        eprintln!("GDA -> Presto: {}", result[0]);
8646        assert_eq!(result[0], "SELECT * FROM UNNEST(SEQUENCE(CAST('2020-01-01' AS DATE), CAST('2020-02-01' AS DATE), (1 * INTERVAL '7' DAY)))");
8647    }
8648
8649    #[test]
8650    fn test_generate_date_array_postgres() {
8651        let dialect = Dialect::get(DialectType::Generic);
8652        let result = dialect.transpile(
8653            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8654            DialectType::PostgreSQL,
8655        ).unwrap();
8656        eprintln!("GDA -> PostgreSQL: {}", result[0]);
8657    }
8658
8659    #[test]
8660    fn test_generate_date_array_snowflake() {
8661        let dialect = Dialect::get(DialectType::Generic);
8662        let result = dialect
8663            .transpile(
8664                "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8665                DialectType::Snowflake,
8666            )
8667            .unwrap();
8668        eprintln!("GDA -> Snowflake: {}", result[0]);
8669    }
8670
8671    #[test]
8672    fn test_array_length_generate_date_array_snowflake() {
8673        let dialect = Dialect::get(DialectType::Generic);
8674        let result = dialect.transpile(
8675            "SELECT ARRAY_LENGTH(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8676            DialectType::Snowflake,
8677        ).unwrap();
8678        eprintln!("ARRAY_LENGTH(GDA) -> Snowflake: {}", result[0]);
8679    }
8680
8681    #[test]
8682    fn test_generate_date_array_mysql() {
8683        let dialect = Dialect::get(DialectType::Generic);
8684        let result = dialect.transpile(
8685            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8686            DialectType::MySQL,
8687        ).unwrap();
8688        eprintln!("GDA -> MySQL: {}", result[0]);
8689    }
8690
8691    #[test]
8692    fn test_generate_date_array_redshift() {
8693        let dialect = Dialect::get(DialectType::Generic);
8694        let result = dialect.transpile(
8695            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8696            DialectType::Redshift,
8697        ).unwrap();
8698        eprintln!("GDA -> Redshift: {}", result[0]);
8699    }
8700
8701    #[test]
8702    fn test_generate_date_array_tsql() {
8703        let dialect = Dialect::get(DialectType::Generic);
8704        let result = dialect.transpile(
8705            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
8706            DialectType::TSQL,
8707        ).unwrap();
8708        eprintln!("GDA -> TSQL: {}", result[0]);
8709    }
8710
8711    #[test]
8712    fn test_struct_colon_syntax() {
8713        let dialect = Dialect::get(DialectType::Generic);
8714        // Test without colon first
8715        let result = dialect.transpile(
8716            "CAST((1, 2, 3, 4) AS STRUCT<a TINYINT, b SMALLINT, c INT, d BIGINT>)",
8717            DialectType::ClickHouse,
8718        );
8719        match result {
8720            Ok(r) => eprintln!("STRUCT no colon -> ClickHouse: {}", r[0]),
8721            Err(e) => eprintln!("STRUCT no colon error: {}", e),
8722        }
8723        // Now test with colon
8724        let result = dialect.transpile(
8725            "CAST((1, 2, 3, 4) AS STRUCT<a: TINYINT, b: SMALLINT, c: INT, d: BIGINT>)",
8726            DialectType::ClickHouse,
8727        );
8728        match result {
8729            Ok(r) => eprintln!("STRUCT colon -> ClickHouse: {}", r[0]),
8730            Err(e) => eprintln!("STRUCT colon error: {}", e),
8731        }
8732    }
8733
8734    #[test]
8735    fn test_generate_date_array_cte_wrapped_mysql() {
8736        let dialect = Dialect::get(DialectType::Generic);
8737        let result = dialect.transpile(
8738            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
8739            DialectType::MySQL,
8740        ).unwrap();
8741        eprintln!("GDA CTE -> MySQL: {}", result[0]);
8742    }
8743
8744    #[test]
8745    fn test_generate_date_array_cte_wrapped_tsql() {
8746        let dialect = Dialect::get(DialectType::Generic);
8747        let result = dialect.transpile(
8748            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
8749            DialectType::TSQL,
8750        ).unwrap();
8751        eprintln!("GDA CTE -> TSQL: {}", result[0]);
8752    }
8753
8754    #[test]
8755    fn test_decode_literal_no_null_check() {
8756        // Oracle DECODE with all literals should produce simple equality, no IS NULL
8757        let dialect = Dialect::get(DialectType::Oracle);
8758        let result = dialect
8759            .transpile("SELECT decode(1,2,3,4)", DialectType::DuckDB)
8760            .unwrap();
8761        assert_eq!(
8762            result[0], "SELECT CASE WHEN 1 = 2 THEN 3 ELSE 4 END",
8763            "Literal DECODE should not have IS NULL checks"
8764        );
8765    }
8766
8767    #[test]
8768    fn test_decode_column_vs_literal_no_null_check() {
8769        // Oracle DECODE with column vs literal should use simple equality (like sqlglot)
8770        let dialect = Dialect::get(DialectType::Oracle);
8771        let result = dialect
8772            .transpile("SELECT decode(col, 2, 3, 4) FROM t", DialectType::DuckDB)
8773            .unwrap();
8774        assert_eq!(
8775            result[0], "SELECT CASE WHEN col = 2 THEN 3 ELSE 4 END FROM t",
8776            "Column vs literal DECODE should not have IS NULL checks"
8777        );
8778    }
8779
8780    #[test]
8781    fn test_decode_column_vs_column_keeps_null_check() {
8782        // Oracle DECODE with column vs column should keep null-safe comparison
8783        let dialect = Dialect::get(DialectType::Oracle);
8784        let result = dialect
8785            .transpile("SELECT decode(col, col2, 3, 4) FROM t", DialectType::DuckDB)
8786            .unwrap();
8787        assert!(
8788            result[0].contains("IS NULL"),
8789            "Column vs column DECODE should have IS NULL checks, got: {}",
8790            result[0]
8791        );
8792    }
8793
8794    #[test]
8795    fn test_decode_null_search() {
8796        // Oracle DECODE with NULL search should use IS NULL
8797        let dialect = Dialect::get(DialectType::Oracle);
8798        let result = dialect
8799            .transpile("SELECT decode(col, NULL, 3, 4) FROM t", DialectType::DuckDB)
8800            .unwrap();
8801        assert_eq!(
8802            result[0],
8803            "SELECT CASE WHEN col IS NULL THEN 3 ELSE 4 END FROM t",
8804        );
8805    }
8806
8807    // =========================================================================
8808    // REGEXP function transpilation tests
8809    // =========================================================================
8810
8811    #[test]
8812    fn test_regexp_substr_snowflake_to_duckdb_2arg() {
8813        let dialect = Dialect::get(DialectType::Snowflake);
8814        let result = dialect
8815            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern')", DialectType::DuckDB)
8816            .unwrap();
8817        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8818    }
8819
8820    #[test]
8821    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos1() {
8822        let dialect = Dialect::get(DialectType::Snowflake);
8823        let result = dialect
8824            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 1)", DialectType::DuckDB)
8825            .unwrap();
8826        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8827    }
8828
8829    #[test]
8830    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos_gt1() {
8831        let dialect = Dialect::get(DialectType::Snowflake);
8832        let result = dialect
8833            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 3)", DialectType::DuckDB)
8834            .unwrap();
8835        assert_eq!(
8836            result[0],
8837            "SELECT REGEXP_EXTRACT(NULLIF(SUBSTRING(s, 3), ''), 'pattern')"
8838        );
8839    }
8840
8841    #[test]
8842    fn test_regexp_substr_snowflake_to_duckdb_4arg_occ_gt1() {
8843        let dialect = Dialect::get(DialectType::Snowflake);
8844        let result = dialect
8845            .transpile(
8846                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 3)",
8847                DialectType::DuckDB,
8848            )
8849            .unwrap();
8850        assert_eq!(
8851            result[0],
8852            "SELECT ARRAY_EXTRACT(REGEXP_EXTRACT_ALL(s, 'pattern'), 3)"
8853        );
8854    }
8855
8856    #[test]
8857    fn test_regexp_substr_snowflake_to_duckdb_5arg_e_flag() {
8858        let dialect = Dialect::get(DialectType::Snowflake);
8859        let result = dialect
8860            .transpile(
8861                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')",
8862                DialectType::DuckDB,
8863            )
8864            .unwrap();
8865        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8866    }
8867
8868    #[test]
8869    fn test_regexp_substr_snowflake_to_duckdb_6arg_group0() {
8870        let dialect = Dialect::get(DialectType::Snowflake);
8871        let result = dialect
8872            .transpile(
8873                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
8874                DialectType::DuckDB,
8875            )
8876            .unwrap();
8877        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
8878    }
8879
8880    #[test]
8881    fn test_regexp_substr_snowflake_identity_strip_group0() {
8882        let dialect = Dialect::get(DialectType::Snowflake);
8883        let result = dialect
8884            .transpile(
8885                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
8886                DialectType::Snowflake,
8887            )
8888            .unwrap();
8889        assert_eq!(result[0], "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')");
8890    }
8891
8892    #[test]
8893    fn test_regexp_substr_all_snowflake_to_duckdb_2arg() {
8894        let dialect = Dialect::get(DialectType::Snowflake);
8895        let result = dialect
8896            .transpile(
8897                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')",
8898                DialectType::DuckDB,
8899            )
8900            .unwrap();
8901        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8902    }
8903
8904    #[test]
8905    fn test_regexp_substr_all_snowflake_to_duckdb_3arg_pos_gt1() {
8906        let dialect = Dialect::get(DialectType::Snowflake);
8907        let result = dialect
8908            .transpile(
8909                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 3)",
8910                DialectType::DuckDB,
8911            )
8912            .unwrap();
8913        assert_eq!(
8914            result[0],
8915            "SELECT REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')"
8916        );
8917    }
8918
8919    #[test]
8920    fn test_regexp_substr_all_snowflake_to_duckdb_5arg_e_flag() {
8921        let dialect = Dialect::get(DialectType::Snowflake);
8922        let result = dialect
8923            .transpile(
8924                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')",
8925                DialectType::DuckDB,
8926            )
8927            .unwrap();
8928        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8929    }
8930
8931    #[test]
8932    fn test_regexp_substr_all_snowflake_to_duckdb_6arg_group0() {
8933        let dialect = Dialect::get(DialectType::Snowflake);
8934        let result = dialect
8935            .transpile(
8936                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
8937                DialectType::DuckDB,
8938            )
8939            .unwrap();
8940        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
8941    }
8942
8943    #[test]
8944    fn test_regexp_substr_all_snowflake_identity_strip_group0() {
8945        let dialect = Dialect::get(DialectType::Snowflake);
8946        let result = dialect
8947            .transpile(
8948                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
8949                DialectType::Snowflake,
8950            )
8951            .unwrap();
8952        assert_eq!(
8953            result[0],
8954            "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')"
8955        );
8956    }
8957
8958    #[test]
8959    fn test_regexp_count_snowflake_to_duckdb_2arg() {
8960        let dialect = Dialect::get(DialectType::Snowflake);
8961        let result = dialect
8962            .transpile("SELECT REGEXP_COUNT(s, 'pattern')", DialectType::DuckDB)
8963            .unwrap();
8964        assert_eq!(
8965            result[0],
8966            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(s, 'pattern')) END"
8967        );
8968    }
8969
8970    #[test]
8971    fn test_regexp_count_snowflake_to_duckdb_3arg() {
8972        let dialect = Dialect::get(DialectType::Snowflake);
8973        let result = dialect
8974            .transpile("SELECT REGEXP_COUNT(s, 'pattern', 3)", DialectType::DuckDB)
8975            .unwrap();
8976        assert_eq!(
8977            result[0],
8978            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')) END"
8979        );
8980    }
8981
8982    #[test]
8983    fn test_regexp_count_snowflake_to_duckdb_4arg_flags() {
8984        let dialect = Dialect::get(DialectType::Snowflake);
8985        let result = dialect
8986            .transpile(
8987                "SELECT REGEXP_COUNT(s, 'pattern', 1, 'i')",
8988                DialectType::DuckDB,
8989            )
8990            .unwrap();
8991        assert_eq!(
8992            result[0],
8993            "SELECT CASE WHEN '(?i)' || 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 1), '(?i)' || 'pattern')) END"
8994        );
8995    }
8996
8997    #[test]
8998    fn test_regexp_count_snowflake_to_duckdb_4arg_flags_literal_string() {
8999        let dialect = Dialect::get(DialectType::Snowflake);
9000        let result = dialect
9001            .transpile(
9002                "SELECT REGEXP_COUNT('Hello World', 'L', 1, 'im')",
9003                DialectType::DuckDB,
9004            )
9005            .unwrap();
9006        assert_eq!(
9007            result[0],
9008            "SELECT CASE WHEN '(?im)' || 'L' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING('Hello World', 1), '(?im)' || 'L')) END"
9009        );
9010    }
9011
9012    #[test]
9013    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos1_occ1() {
9014        let dialect = Dialect::get(DialectType::Snowflake);
9015        let result = dialect
9016            .transpile(
9017                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 1, 1)",
9018                DialectType::DuckDB,
9019            )
9020            .unwrap();
9021        assert_eq!(result[0], "SELECT REGEXP_REPLACE(s, 'pattern', 'repl')");
9022    }
9023
9024    #[test]
9025    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ0() {
9026        let dialect = Dialect::get(DialectType::Snowflake);
9027        let result = dialect
9028            .transpile(
9029                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 0)",
9030                DialectType::DuckDB,
9031            )
9032            .unwrap();
9033        assert_eq!(
9034            result[0],
9035            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl', 'g')"
9036        );
9037    }
9038
9039    #[test]
9040    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ1() {
9041        let dialect = Dialect::get(DialectType::Snowflake);
9042        let result = dialect
9043            .transpile(
9044                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 1)",
9045                DialectType::DuckDB,
9046            )
9047            .unwrap();
9048        assert_eq!(
9049            result[0],
9050            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl')"
9051        );
9052    }
9053
9054    #[test]
9055    fn test_rlike_snowflake_to_duckdb_2arg() {
9056        let dialect = Dialect::get(DialectType::Snowflake);
9057        let result = dialect
9058            .transpile("SELECT RLIKE(a, b)", DialectType::DuckDB)
9059            .unwrap();
9060        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b)");
9061    }
9062
9063    #[test]
9064    fn test_rlike_snowflake_to_duckdb_3arg_flags() {
9065        let dialect = Dialect::get(DialectType::Snowflake);
9066        let result = dialect
9067            .transpile("SELECT RLIKE(a, b, 'i')", DialectType::DuckDB)
9068            .unwrap();
9069        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b, 'i')");
9070    }
9071
9072    #[test]
9073    fn test_regexp_extract_all_bigquery_to_snowflake_no_capture() {
9074        let dialect = Dialect::get(DialectType::BigQuery);
9075        let result = dialect
9076            .transpile(
9077                "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')",
9078                DialectType::Snowflake,
9079            )
9080            .unwrap();
9081        assert_eq!(result[0], "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')");
9082    }
9083
9084    #[test]
9085    fn test_regexp_extract_all_bigquery_to_snowflake_with_capture() {
9086        let dialect = Dialect::get(DialectType::BigQuery);
9087        let result = dialect
9088            .transpile(
9089                "SELECT REGEXP_EXTRACT_ALL(s, '(a)[0-9]')",
9090                DialectType::Snowflake,
9091            )
9092            .unwrap();
9093        assert_eq!(
9094            result[0],
9095            "SELECT REGEXP_SUBSTR_ALL(s, '(a)[0-9]', 1, 1, 'c', 1)"
9096        );
9097    }
9098
9099    #[test]
9100    fn test_regexp_instr_snowflake_to_duckdb_2arg() {
9101        let dialect = Dialect::get(DialectType::Snowflake);
9102        let result = dialect
9103            .transpile("SELECT REGEXP_INSTR(s, 'pattern')", DialectType::DuckDB)
9104            .unwrap();
9105        assert!(
9106            result[0].contains("CASE WHEN"),
9107            "Expected CASE WHEN in result: {}",
9108            result[0]
9109        );
9110        assert!(
9111            result[0].contains("LIST_SUM"),
9112            "Expected LIST_SUM in result: {}",
9113            result[0]
9114        );
9115    }
9116
9117    #[test]
9118    fn test_array_except_generic_to_duckdb() {
9119        let dialect = Dialect::get(DialectType::Generic);
9120        let result = dialect
9121            .transpile(
9122                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
9123                DialectType::DuckDB,
9124            )
9125            .unwrap();
9126        eprintln!("ARRAY_EXCEPT Generic->DuckDB: {}", result[0]);
9127        assert!(
9128            result[0].contains("CASE WHEN"),
9129            "Expected CASE WHEN: {}",
9130            result[0]
9131        );
9132        assert!(
9133            result[0].contains("LIST_FILTER"),
9134            "Expected LIST_FILTER: {}",
9135            result[0]
9136        );
9137        assert!(
9138            result[0].contains("LIST_DISTINCT"),
9139            "Expected LIST_DISTINCT: {}",
9140            result[0]
9141        );
9142        assert!(
9143            result[0].contains("IS NOT DISTINCT FROM"),
9144            "Expected IS NOT DISTINCT FROM: {}",
9145            result[0]
9146        );
9147        assert!(
9148            result[0].contains("= 0"),
9149            "Expected = 0 filter: {}",
9150            result[0]
9151        );
9152    }
9153
9154    #[test]
9155    fn test_array_except_generic_to_snowflake() {
9156        let dialect = Dialect::get(DialectType::Generic);
9157        let result = dialect
9158            .transpile(
9159                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
9160                DialectType::Snowflake,
9161            )
9162            .unwrap();
9163        eprintln!("ARRAY_EXCEPT Generic->Snowflake: {}", result[0]);
9164        assert_eq!(result[0], "SELECT ARRAY_EXCEPT([1, 2, 3], [2])");
9165    }
9166
9167    #[test]
9168    fn test_array_except_generic_to_presto() {
9169        let dialect = Dialect::get(DialectType::Generic);
9170        let result = dialect
9171            .transpile(
9172                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
9173                DialectType::Presto,
9174            )
9175            .unwrap();
9176        eprintln!("ARRAY_EXCEPT Generic->Presto: {}", result[0]);
9177        assert_eq!(result[0], "SELECT ARRAY_EXCEPT(ARRAY[1, 2, 3], ARRAY[2])");
9178    }
9179
9180    #[test]
9181    fn test_array_except_snowflake_to_duckdb() {
9182        let dialect = Dialect::get(DialectType::Snowflake);
9183        let result = dialect
9184            .transpile("SELECT ARRAY_EXCEPT([1, 2, 3], [2])", DialectType::DuckDB)
9185            .unwrap();
9186        eprintln!("ARRAY_EXCEPT Snowflake->DuckDB: {}", result[0]);
9187        assert!(
9188            result[0].contains("CASE WHEN"),
9189            "Expected CASE WHEN: {}",
9190            result[0]
9191        );
9192        assert!(
9193            result[0].contains("LIST_TRANSFORM"),
9194            "Expected LIST_TRANSFORM: {}",
9195            result[0]
9196        );
9197    }
9198
9199    #[test]
9200    fn test_array_contains_snowflake_to_snowflake() {
9201        let dialect = Dialect::get(DialectType::Snowflake);
9202        let result = dialect
9203            .transpile(
9204                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
9205                DialectType::Snowflake,
9206            )
9207            .unwrap();
9208        eprintln!("ARRAY_CONTAINS Snowflake->Snowflake: {}", result[0]);
9209        assert_eq!(result[0], "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])");
9210    }
9211
9212    #[test]
9213    fn test_array_contains_snowflake_to_duckdb() {
9214        let dialect = Dialect::get(DialectType::Snowflake);
9215        let result = dialect
9216            .transpile(
9217                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
9218                DialectType::DuckDB,
9219            )
9220            .unwrap();
9221        eprintln!("ARRAY_CONTAINS Snowflake->DuckDB: {}", result[0]);
9222        assert!(
9223            result[0].contains("CASE WHEN"),
9224            "Expected CASE WHEN: {}",
9225            result[0]
9226        );
9227        assert!(
9228            result[0].contains("NULLIF"),
9229            "Expected NULLIF: {}",
9230            result[0]
9231        );
9232        assert!(
9233            result[0].contains("ARRAY_CONTAINS"),
9234            "Expected ARRAY_CONTAINS: {}",
9235            result[0]
9236        );
9237    }
9238
9239    #[test]
9240    fn test_array_distinct_snowflake_to_duckdb() {
9241        let dialect = Dialect::get(DialectType::Snowflake);
9242        let result = dialect
9243            .transpile(
9244                "SELECT ARRAY_DISTINCT([1, 2, 2, 3, 1])",
9245                DialectType::DuckDB,
9246            )
9247            .unwrap();
9248        eprintln!("ARRAY_DISTINCT Snowflake->DuckDB: {}", result[0]);
9249        assert!(
9250            result[0].contains("CASE WHEN"),
9251            "Expected CASE WHEN: {}",
9252            result[0]
9253        );
9254        assert!(
9255            result[0].contains("LIST_DISTINCT"),
9256            "Expected LIST_DISTINCT: {}",
9257            result[0]
9258        );
9259        assert!(
9260            result[0].contains("LIST_APPEND"),
9261            "Expected LIST_APPEND: {}",
9262            result[0]
9263        );
9264        assert!(
9265            result[0].contains("LIST_FILTER"),
9266            "Expected LIST_FILTER: {}",
9267            result[0]
9268        );
9269    }
9270}