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