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