Skip to main content

polyglot_sql/dialects/
mod.rs

1//! SQL Dialect System
2//!
3//! This module implements the dialect abstraction layer that enables SQL transpilation
4//! between more than 30 SQL dialects. Each dialect encapsulates three concerns:
5//!
6//! - **Tokenization**: Dialect-specific lexing rules (e.g., BigQuery uses backtick quoting,
7//!   MySQL uses backtick for identifiers, TSQL uses square brackets).
8//! - **Generation**: How AST nodes are rendered back to SQL text, including identifier quoting
9//!   style, function name casing, and syntax variations.
10//! - **Transformation**: AST-level rewrites that convert dialect-specific constructs to/from
11//!   a normalized form (e.g., Snowflake `SQUARE(x)` becomes `POWER(x, 2)`).
12//!
13//! The primary entry point is [`Dialect::get`], which returns a configured [`Dialect`] instance
14//! for a given [`DialectType`]. From there, callers can [`parse`](Dialect::parse),
15//! [`generate`](Dialect::generate), [`transform`](Dialect::transform), or
16//! [`transpile`](Dialect::transpile) to another dialect in a single call.
17//!
18//! Each concrete dialect (e.g., `PostgresDialect`, `BigQueryDialect`) implements the
19//! [`DialectImpl`] trait, which provides configuration hooks and expression-level transforms.
20//! Dialect modules live in submodules of this module and are re-exported here.
21
22mod generic; // Always compiled
23#[cfg(feature = "transpile")]
24mod normalization;
25
26#[cfg(feature = "dialect-athena")]
27mod athena;
28#[cfg(feature = "dialect-bigquery")]
29mod bigquery;
30#[cfg(feature = "dialect-clickhouse")]
31mod clickhouse;
32#[cfg(feature = "dialect-cockroachdb")]
33mod cockroachdb;
34#[cfg(feature = "dialect-databricks")]
35mod databricks;
36#[cfg(feature = "dialect-datafusion")]
37mod datafusion;
38#[cfg(feature = "dialect-doris")]
39mod doris;
40#[cfg(feature = "dialect-dremio")]
41mod dremio;
42#[cfg(feature = "dialect-drill")]
43mod drill;
44#[cfg(feature = "dialect-druid")]
45mod druid;
46#[cfg(feature = "dialect-duckdb")]
47mod duckdb;
48#[cfg(feature = "dialect-dune")]
49mod dune;
50#[cfg(feature = "dialect-exasol")]
51mod exasol;
52#[cfg(feature = "dialect-fabric")]
53mod fabric;
54#[cfg(feature = "dialect-hive")]
55mod hive;
56#[cfg(feature = "dialect-materialize")]
57mod materialize;
58#[cfg(feature = "dialect-mysql")]
59mod mysql;
60#[cfg(feature = "dialect-oracle")]
61mod oracle;
62#[cfg(feature = "dialect-postgresql")]
63mod postgres;
64#[cfg(feature = "dialect-presto")]
65mod presto;
66#[cfg(feature = "dialect-redshift")]
67mod redshift;
68#[cfg(feature = "dialect-risingwave")]
69mod risingwave;
70#[cfg(feature = "dialect-singlestore")]
71mod singlestore;
72#[cfg(feature = "dialect-snowflake")]
73mod snowflake;
74#[cfg(feature = "dialect-solr")]
75mod solr;
76#[cfg(feature = "dialect-spark")]
77mod spark;
78#[cfg(feature = "dialect-sqlite")]
79mod sqlite;
80#[cfg(feature = "dialect-starrocks")]
81mod starrocks;
82#[cfg(feature = "dialect-tableau")]
83mod tableau;
84#[cfg(feature = "dialect-teradata")]
85mod teradata;
86#[cfg(feature = "dialect-tidb")]
87mod tidb;
88#[cfg(feature = "dialect-trino")]
89mod trino;
90#[cfg(feature = "dialect-tsql")]
91mod tsql;
92
93pub use generic::GenericDialect; // Always available
94
95#[cfg(feature = "dialect-athena")]
96pub use athena::AthenaDialect;
97#[cfg(feature = "dialect-bigquery")]
98pub use bigquery::BigQueryDialect;
99#[cfg(feature = "dialect-clickhouse")]
100pub use clickhouse::ClickHouseDialect;
101#[cfg(feature = "dialect-cockroachdb")]
102pub use cockroachdb::CockroachDBDialect;
103#[cfg(feature = "dialect-databricks")]
104pub use databricks::DatabricksDialect;
105#[cfg(feature = "dialect-datafusion")]
106pub use datafusion::DataFusionDialect;
107#[cfg(feature = "dialect-doris")]
108pub use doris::DorisDialect;
109#[cfg(feature = "dialect-dremio")]
110pub use dremio::DremioDialect;
111#[cfg(feature = "dialect-drill")]
112pub use drill::DrillDialect;
113#[cfg(feature = "dialect-druid")]
114pub use druid::DruidDialect;
115#[cfg(feature = "dialect-duckdb")]
116pub use duckdb::DuckDBDialect;
117#[cfg(feature = "dialect-dune")]
118pub use dune::DuneDialect;
119#[cfg(feature = "dialect-exasol")]
120pub use exasol::ExasolDialect;
121#[cfg(feature = "dialect-fabric")]
122pub use fabric::FabricDialect;
123#[cfg(feature = "dialect-hive")]
124pub use hive::HiveDialect;
125#[cfg(feature = "dialect-materialize")]
126pub use materialize::MaterializeDialect;
127#[cfg(feature = "dialect-mysql")]
128pub use mysql::MySQLDialect;
129#[cfg(feature = "dialect-oracle")]
130pub use oracle::OracleDialect;
131#[cfg(feature = "dialect-postgresql")]
132pub use postgres::PostgresDialect;
133#[cfg(feature = "dialect-presto")]
134pub use presto::PrestoDialect;
135#[cfg(feature = "dialect-redshift")]
136pub use redshift::RedshiftDialect;
137#[cfg(feature = "dialect-risingwave")]
138pub use risingwave::RisingWaveDialect;
139#[cfg(feature = "dialect-singlestore")]
140pub use singlestore::SingleStoreDialect;
141#[cfg(feature = "dialect-snowflake")]
142pub use snowflake::SnowflakeDialect;
143#[cfg(feature = "dialect-solr")]
144pub use solr::SolrDialect;
145#[cfg(feature = "dialect-spark")]
146pub use spark::SparkDialect;
147#[cfg(feature = "dialect-sqlite")]
148pub use sqlite::SQLiteDialect;
149#[cfg(feature = "dialect-starrocks")]
150pub use starrocks::StarRocksDialect;
151#[cfg(feature = "dialect-tableau")]
152pub use tableau::TableauDialect;
153#[cfg(feature = "dialect-teradata")]
154pub use teradata::TeradataDialect;
155#[cfg(feature = "dialect-tidb")]
156pub use tidb::TiDBDialect;
157#[cfg(feature = "dialect-trino")]
158pub use trino::TrinoDialect;
159#[cfg(feature = "dialect-tsql")]
160pub use tsql::TSQLDialect;
161
162use crate::error::Result;
163#[cfg(feature = "transpile")]
164use crate::expressions::{
165    BinaryOp, Case, Cast, ColumnConstraint, DateBin, 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};
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    fn parse_with_guard(
2944        &self,
2945        sql: &str,
2946        complexity_guard: ComplexityGuardOptions,
2947    ) -> Result<Vec<Expression>> {
2948        enforce_input(sql, &complexity_guard)?;
2949        let source: Arc<str> = Arc::from(sql);
2950        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
2951        let config = crate::parser::ParserConfig {
2952            dialect: Some(self.dialect_type),
2953            complexity_guard,
2954            ..Default::default()
2955        };
2956        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
2957        parser.parse()
2958    }
2959
2960    fn default_complexity_guard(&self) -> ComplexityGuardOptions {
2961        let mut guard = ComplexityGuardOptions::default();
2962        if matches!(self.dialect_type, DialectType::ClickHouse) {
2963            guard.max_ast_depth = Some(4_096);
2964            guard.max_function_call_depth = Some(512);
2965        }
2966        guard
2967    }
2968
2969    #[cfg(feature = "transpile")]
2970    fn default_transpile_complexity_guard(
2971        &self,
2972        target_dialect: &Dialect,
2973        guard: ComplexityGuardOptions,
2974    ) -> ComplexityGuardOptions {
2975        if guard != ComplexityGuardOptions::default() {
2976            return guard;
2977        }
2978
2979        if matches!(self.dialect_type, DialectType::ClickHouse)
2980            || matches!(target_dialect.dialect_type, DialectType::ClickHouse)
2981        {
2982            let mut guard = guard;
2983            guard.max_ast_depth = Some(4_096);
2984            guard.max_function_call_depth = Some(512);
2985            guard
2986        } else {
2987            guard
2988        }
2989    }
2990
2991    /// Parse a standalone SQL data type using this dialect's tokenizer and parser.
2992    ///
2993    /// This accepts type strings such as `DECIMAL(10, 2)`, `INT[]`, or
2994    /// `STRUCT(a INT, b VARCHAR)` without requiring a surrounding statement.
2995    pub fn parse_data_type(&self, sql: &str) -> Result<DataType> {
2996        let complexity_guard = self.default_complexity_guard();
2997        enforce_input(sql, &complexity_guard)?;
2998        let source: Arc<str> = Arc::from(sql);
2999        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
3000        let config = crate::parser::ParserConfig {
3001            dialect: Some(self.dialect_type),
3002            complexity_guard,
3003            ..Default::default()
3004        };
3005        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
3006        parser.parse_standalone_data_type()
3007    }
3008
3009    /// Tokenize SQL using this dialect's tokenizer configuration.
3010    pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
3011        self.tokenizer.tokenize(sql)
3012    }
3013
3014    /// Get the generator config for a specific expression (supports hybrid dialects).
3015    /// Returns an owned `GeneratorConfig` suitable for mutation before generation.
3016    #[cfg(feature = "generate")]
3017    fn get_config_for_expr(&self, expr: &Expression) -> GeneratorConfig {
3018        if let Some(ref config_fn) = self.generator_config_for_expr {
3019            config_fn(expr)
3020        } else {
3021            (*self.generator_config).clone()
3022        }
3023    }
3024
3025    /// Generates a SQL string from an [`Expression`] AST node.
3026    ///
3027    /// The output uses this dialect's generator configuration for identifier quoting,
3028    /// keyword casing, function name normalization, and syntax style. The result is
3029    /// a single-line (non-pretty) SQL string.
3030    #[cfg(feature = "generate")]
3031    pub fn generate(&self, expr: &Expression) -> Result<String> {
3032        // Fast path: when no per-expression config override, share the Arc cheaply.
3033        if self.generator_config_for_expr.is_none() {
3034            let mut generator = Generator::with_arc_config(self.generator_config.clone());
3035            return generator.generate(expr);
3036        }
3037        let config = self.get_config_for_expr(expr);
3038        let mut generator = Generator::with_config(config);
3039        generator.generate(expr)
3040    }
3041
3042    /// Generate SQL from an expression with pretty printing enabled
3043    #[cfg(feature = "generate")]
3044    pub fn generate_pretty(&self, expr: &Expression) -> Result<String> {
3045        let mut config = self.get_config_for_expr(expr);
3046        config.pretty = true;
3047        let mut generator = Generator::with_config(config);
3048        generator.generate(expr)
3049    }
3050
3051    /// Generate SQL from an expression with source dialect info (for transpilation)
3052    #[cfg(feature = "generate")]
3053    pub fn generate_with_source(&self, expr: &Expression, source: DialectType) -> Result<String> {
3054        let mut config = self.get_config_for_expr(expr);
3055        config.source_dialect = Some(source);
3056        let mut generator = Generator::with_config(config);
3057        generator.generate(expr)
3058    }
3059
3060    /// Generate SQL from an expression with pretty printing and source dialect info
3061    #[cfg(feature = "generate")]
3062    pub fn generate_pretty_with_source(
3063        &self,
3064        expr: &Expression,
3065        source: DialectType,
3066    ) -> Result<String> {
3067        let mut config = self.get_config_for_expr(expr);
3068        config.pretty = true;
3069        config.source_dialect = Some(source);
3070        let mut generator = Generator::with_config(config);
3071        generator.generate(expr)
3072    }
3073
3074    /// Generate SQL from an expression with source dialect and transpile options.
3075    #[cfg(all(feature = "generate", feature = "transpile"))]
3076    fn generate_with_transpile_options(
3077        &self,
3078        expr: &Expression,
3079        source: DialectType,
3080        opts: &TranspileOptions,
3081    ) -> Result<String> {
3082        let mut config = self.get_config_for_expr(expr);
3083        config.source_dialect = Some(source);
3084        config.pretty = opts.pretty;
3085        config.unsupported_level = opts.unsupported_level;
3086        config.max_unsupported = opts.max_unsupported.max(1);
3087        config.complexity_guard = opts.complexity_guard;
3088        let mut generator = Generator::with_config(config);
3089        generator.generate(expr)
3090    }
3091
3092    /// Generate SQL from an expression with forced identifier quoting (identify=True)
3093    #[cfg(feature = "generate")]
3094    pub fn generate_with_identify(&self, expr: &Expression) -> Result<String> {
3095        let mut config = self.get_config_for_expr(expr);
3096        config.always_quote_identifiers = true;
3097        let mut generator = Generator::with_config(config);
3098        generator.generate(expr)
3099    }
3100
3101    /// Generate SQL from an expression with pretty printing and forced identifier quoting
3102    #[cfg(feature = "generate")]
3103    pub fn generate_pretty_with_identify(&self, expr: &Expression) -> Result<String> {
3104        let mut config = (*self.generator_config).clone();
3105        config.pretty = true;
3106        config.always_quote_identifiers = true;
3107        let mut generator = Generator::with_config(config);
3108        generator.generate(expr)
3109    }
3110
3111    /// Generate SQL from an expression with caller-specified config overrides
3112    #[cfg(feature = "generate")]
3113    pub fn generate_with_overrides(
3114        &self,
3115        expr: &Expression,
3116        overrides: impl FnOnce(&mut GeneratorConfig),
3117    ) -> Result<String> {
3118        let mut config = self.get_config_for_expr(expr);
3119        overrides(&mut config);
3120        let mut generator = Generator::with_config(config);
3121        generator.generate(expr)
3122    }
3123
3124    /// Transforms an expression tree to conform to this dialect's syntax and semantics.
3125    ///
3126    /// The transformation proceeds in two phases:
3127    /// 1. **Preprocessing** -- whole-tree structural rewrites such as eliminating QUALIFY,
3128    ///    ensuring boolean predicates, or converting DISTINCT ON to a window-function pattern.
3129    /// 2. **Recursive per-node transform** -- a bottom-up pass via [`transform_recursive`]
3130    ///    that applies this dialect's [`DialectImpl::transform_expr`] to every node.
3131    ///
3132    /// This method is used both during transpilation (to rewrite an AST for a target dialect)
3133    /// and for identity transforms (normalizing SQL within the same dialect).
3134    #[cfg(feature = "transpile")]
3135    pub fn transform(&self, expr: Expression) -> Result<Expression> {
3136        self.transform_with_guard(expr, self.default_complexity_guard())
3137    }
3138
3139    #[cfg(feature = "transpile")]
3140    fn transform_with_guard(
3141        &self,
3142        expr: Expression,
3143        complexity_guard: ComplexityGuardOptions,
3144    ) -> Result<Expression> {
3145        enforce_generate_ast(&expr, &complexity_guard)?;
3146        // Apply preprocessing transforms based on dialect
3147        let preprocessed = self.preprocess(expr)?;
3148        // Then apply recursive transformation
3149        transform_recursive(preprocessed, &self.transformer)
3150    }
3151
3152    /// Apply dialect-specific preprocessing transforms
3153    #[cfg(feature = "transpile")]
3154    fn preprocess(&self, expr: Expression) -> Result<Expression> {
3155        // If a custom preprocess function is set, use it instead of the built-in logic
3156        if let Some(ref custom_preprocess) = self.custom_preprocess {
3157            return custom_preprocess(expr);
3158        }
3159
3160        #[cfg(any(
3161            feature = "dialect-mysql",
3162            feature = "dialect-postgresql",
3163            feature = "dialect-bigquery",
3164            feature = "dialect-snowflake",
3165            feature = "dialect-tsql",
3166            feature = "dialect-spark",
3167            feature = "dialect-databricks",
3168            feature = "dialect-hive",
3169            feature = "dialect-sqlite",
3170            feature = "dialect-trino",
3171            feature = "dialect-presto",
3172            feature = "dialect-duckdb",
3173            feature = "dialect-redshift",
3174            feature = "dialect-starrocks",
3175            feature = "dialect-oracle",
3176            feature = "dialect-clickhouse",
3177            feature = "dialect-fabric",
3178        ))]
3179        use crate::transforms;
3180
3181        match self.dialect_type {
3182            // MySQL doesn't support QUALIFY, DISTINCT ON, FULL OUTER JOIN
3183            // MySQL doesn't natively support GENERATE_DATE_ARRAY (expand to recursive CTE)
3184            #[cfg(feature = "dialect-mysql")]
3185            DialectType::MySQL => {
3186                let expr = transforms::eliminate_qualify(expr)?;
3187                let expr = transforms::eliminate_full_outer_join(expr)?;
3188                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3189                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3190                Ok(expr)
3191            }
3192            // PostgreSQL doesn't support QUALIFY
3193            // PostgreSQL: UNNEST(GENERATE_SERIES) -> subquery wrapping
3194            // PostgreSQL: Normalize SET ... TO to SET ... = in CREATE FUNCTION
3195            #[cfg(feature = "dialect-postgresql")]
3196            DialectType::PostgreSQL => {
3197                let expr = transforms::eliminate_qualify(expr)?;
3198                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3199                let expr = transforms::unwrap_unnest_generate_series_for_postgres(expr)?;
3200                // Normalize SET ... TO to SET ... = in CREATE FUNCTION
3201                // Only normalize when sqlglot would fully parse (no body) —
3202                // sqlglot falls back to Command for complex function bodies,
3203                // preserving the original text including TO.
3204                let expr = if let Expression::CreateFunction(mut cf) = expr {
3205                    if cf.body.is_none() {
3206                        for opt in &mut cf.set_options {
3207                            if let crate::expressions::FunctionSetValue::Value { use_to, .. } =
3208                                &mut opt.value
3209                            {
3210                                *use_to = false;
3211                            }
3212                        }
3213                    }
3214                    Expression::CreateFunction(cf)
3215                } else {
3216                    expr
3217                };
3218                Ok(expr)
3219            }
3220            // BigQuery doesn't support DISTINCT ON or CTE column aliases
3221            #[cfg(feature = "dialect-bigquery")]
3222            DialectType::BigQuery => {
3223                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3224                let expr = transforms::pushdown_cte_column_names(expr)?;
3225                let expr = transforms::explode_projection_to_unnest(expr, DialectType::BigQuery)?;
3226                Ok(expr)
3227            }
3228            // Snowflake
3229            #[cfg(feature = "dialect-snowflake")]
3230            DialectType::Snowflake => {
3231                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3232                let expr = transforms::eliminate_window_clause(expr)?;
3233                let expr = transforms::snowflake_flatten_projection_to_unnest(expr)?;
3234                Ok(expr)
3235            }
3236            // TSQL doesn't support QUALIFY
3237            // TSQL requires boolean expressions in WHERE/HAVING (no implicit truthiness)
3238            // TSQL doesn't support CTEs in subqueries (hoist to top level)
3239            // NOTE: no_limit_order_by_union is handled in cross_dialect_normalize (not preprocess)
3240            // to avoid breaking TSQL identity tests where ORDER BY on UNION is valid
3241            #[cfg(feature = "dialect-tsql")]
3242            DialectType::TSQL => {
3243                let expr = transforms::eliminate_qualify(expr)?;
3244                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3245                let expr = transforms::normalize_grouping_sets_for_tsql(expr)?;
3246                let expr =
3247                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::TSQL)?;
3248                let expr = transforms::ensure_bools(expr)?;
3249                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3250                let expr = transforms::strip_cte_materialization(expr)?;
3251                let expr = transforms::move_ctes_to_top_level(expr)?;
3252                let expr = transforms::qualify_derived_table_outputs(expr)?;
3253                Ok(expr)
3254            }
3255            // Fabric shares T-SQL predicate rules and CTE placement restrictions,
3256            // but keeps Fabric-specific APPLY and derived-table behavior separate.
3257            #[cfg(feature = "dialect-fabric")]
3258            DialectType::Fabric => {
3259                let expr = transforms::normalize_grouping_sets_for_tsql(expr)?;
3260                let expr =
3261                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::Fabric)?;
3262                let expr = transforms::ensure_bools(expr)?;
3263                let expr = transforms::strip_cte_materialization(expr)?;
3264                let expr = transforms::move_ctes_to_top_level(expr)?;
3265                Ok(expr)
3266            }
3267            // Spark doesn't support QUALIFY (but Databricks does)
3268            // Spark doesn't support CTEs in subqueries (hoist to top level)
3269            #[cfg(feature = "dialect-spark")]
3270            DialectType::Spark => {
3271                let expr = transforms::eliminate_qualify(expr)?;
3272                let expr = transforms::add_auto_table_alias(expr)?;
3273                let expr = transforms::simplify_nested_paren_values(expr)?;
3274                let expr = transforms::move_ctes_to_top_level(expr)?;
3275                Ok(expr)
3276            }
3277            // Databricks supports QUALIFY natively
3278            // Databricks doesn't support CTEs in subqueries (hoist to top level)
3279            #[cfg(feature = "dialect-databricks")]
3280            DialectType::Databricks => {
3281                let expr = transforms::add_auto_table_alias(expr)?;
3282                let expr = transforms::simplify_nested_paren_values(expr)?;
3283                let expr = transforms::move_ctes_to_top_level(expr)?;
3284                Ok(expr)
3285            }
3286            // Hive doesn't support QUALIFY or CTEs in subqueries
3287            #[cfg(feature = "dialect-hive")]
3288            DialectType::Hive => {
3289                let expr = transforms::eliminate_qualify(expr)?;
3290                let expr = transforms::move_ctes_to_top_level(expr)?;
3291                Ok(expr)
3292            }
3293            // SQLite doesn't support QUALIFY
3294            #[cfg(feature = "dialect-sqlite")]
3295            DialectType::SQLite => {
3296                let expr = transforms::eliminate_qualify(expr)?;
3297                Ok(expr)
3298            }
3299            // Trino doesn't support QUALIFY
3300            #[cfg(feature = "dialect-trino")]
3301            DialectType::Trino => {
3302                let expr = transforms::eliminate_qualify(expr)?;
3303                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Trino)?;
3304                Ok(expr)
3305            }
3306            // Presto doesn't support QUALIFY or WINDOW clause
3307            #[cfg(feature = "dialect-presto")]
3308            DialectType::Presto => {
3309                let expr = transforms::eliminate_qualify(expr)?;
3310                let expr = transforms::eliminate_window_clause(expr)?;
3311                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Presto)?;
3312                Ok(expr)
3313            }
3314            // DuckDB supports QUALIFY - no elimination needed
3315            // Expand POSEXPLODE to GENERATE_SUBSCRIPTS + UNNEST
3316            // Expand LIKE ANY / ILIKE ANY to OR chains (DuckDB doesn't support quantifiers)
3317            #[cfg(feature = "dialect-duckdb")]
3318            DialectType::DuckDB => {
3319                let expr = transforms::expand_posexplode_duckdb(expr)?;
3320                let expr = transforms::expand_like_any(expr)?;
3321                Ok(expr)
3322            }
3323            // Redshift doesn't support QUALIFY, WINDOW clause, or GENERATE_DATE_ARRAY
3324            #[cfg(feature = "dialect-redshift")]
3325            DialectType::Redshift => {
3326                let expr = transforms::eliminate_qualify(expr)?;
3327                let expr = transforms::eliminate_window_clause(expr)?;
3328                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3329                Ok(expr)
3330            }
3331            // StarRocks doesn't support BETWEEN in DELETE statements or QUALIFY
3332            #[cfg(feature = "dialect-starrocks")]
3333            DialectType::StarRocks => {
3334                let expr = transforms::eliminate_qualify(expr)?;
3335                let expr = transforms::expand_between_in_delete(expr)?;
3336                let expr = transforms::eliminate_distinct_on_for_dialect(
3337                    expr,
3338                    Some(DialectType::StarRocks),
3339                    Some(DialectType::StarRocks),
3340                )?;
3341                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3342                Ok(expr)
3343            }
3344            // DataFusion supports QUALIFY and semi/anti joins natively
3345            #[cfg(feature = "dialect-datafusion")]
3346            DialectType::DataFusion => Ok(expr),
3347            // Oracle doesn't support QUALIFY
3348            #[cfg(feature = "dialect-oracle")]
3349            DialectType::Oracle => {
3350                let expr = transforms::eliminate_qualify(expr)?;
3351                Ok(expr)
3352            }
3353            // Drill - no special preprocessing needed
3354            #[cfg(feature = "dialect-drill")]
3355            DialectType::Drill => Ok(expr),
3356            // Teradata - no special preprocessing needed
3357            #[cfg(feature = "dialect-teradata")]
3358            DialectType::Teradata => Ok(expr),
3359            // ClickHouse doesn't support ORDER BY/LIMIT directly on UNION
3360            #[cfg(feature = "dialect-clickhouse")]
3361            DialectType::ClickHouse => {
3362                let expr = transforms::no_limit_order_by_union(expr)?;
3363                Ok(expr)
3364            }
3365            // Other dialects - no preprocessing
3366            _ => Ok(expr),
3367        }
3368    }
3369
3370    /// Transpile SQL from this dialect to the given target dialect.
3371    ///
3372    /// The target may be specified as either a built-in [`DialectType`] enum variant
3373    /// or as a reference to a [`Dialect`] handle (built-in or custom). Both work:
3374    ///
3375    /// ```rust,ignore
3376    /// let pg = Dialect::get(DialectType::PostgreSQL);
3377    /// pg.transpile("SELECT NOW()", DialectType::BigQuery)?;   // enum
3378    /// pg.transpile("SELECT NOW()", &custom_dialect)?;         // handle
3379    /// ```
3380    ///
3381    /// For pretty-printing or other options, use [`transpile_with`](Self::transpile_with).
3382    #[cfg(feature = "transpile")]
3383    pub fn transpile<T: TranspileTarget>(&self, sql: &str, target: T) -> Result<Vec<String>> {
3384        self.transpile_with(sql, target, TranspileOptions::default())
3385    }
3386
3387    /// Transpile SQL with configurable [`TranspileOptions`] (e.g. pretty-printing).
3388    #[cfg(feature = "transpile")]
3389    pub fn transpile_with<T: TranspileTarget>(
3390        &self,
3391        sql: &str,
3392        target: T,
3393        opts: TranspileOptions,
3394    ) -> Result<Vec<String>> {
3395        target.with_dialect(|td| self.transpile_inner(sql, td, &opts))
3396    }
3397
3398    #[cfg(feature = "transpile")]
3399    fn transpile_inner(
3400        &self,
3401        sql: &str,
3402        target_dialect: &Dialect,
3403        opts: &TranspileOptions,
3404    ) -> Result<Vec<String>> {
3405        let mut effective_opts = opts.clone();
3406        effective_opts.complexity_guard =
3407            self.default_transpile_complexity_guard(target_dialect, opts.complexity_guard);
3408        let opts = &effective_opts;
3409        let target = target_dialect.dialect_type;
3410        if matches!(self.dialect_type, DialectType::PostgreSQL)
3411            && matches!(target, DialectType::SQLite)
3412        {
3413            self.reject_pgvector_distance_operators_for_sqlite(sql)?;
3414        }
3415        let expressions = self.parse_with_guard(sql, opts.complexity_guard)?;
3416        let generic_identity =
3417            self.dialect_type == DialectType::Generic && target == DialectType::Generic;
3418
3419        if generic_identity {
3420            return expressions
3421                .into_iter()
3422                .map(|expr| {
3423                    Self::reject_strict_unsupported(&expr, self.dialect_type, target, opts)?;
3424                    target_dialect.generate_with_transpile_options(&expr, self.dialect_type, opts)
3425                })
3426                .collect();
3427        }
3428
3429        expressions
3430            .into_iter()
3431            .map(|expr| {
3432                // DuckDB source: normalize VARCHAR/CHAR to TEXT (DuckDB doesn't support
3433                // VARCHAR length constraints). This emulates Python sqlglot's DuckDB parser
3434                // where VARCHAR_LENGTH = None and VARCHAR maps to TEXT.
3435                let expr = if matches!(self.dialect_type, DialectType::DuckDB) {
3436                    use crate::expressions::DataType as DT;
3437                    transform_recursive(expr, &|e| match e {
3438                        Expression::DataType(DT::VarChar { .. }) => {
3439                            Ok(Expression::DataType(DT::Text))
3440                        }
3441                        Expression::DataType(DT::Char { .. }) => Ok(Expression::DataType(DT::Text)),
3442                        _ => Ok(e),
3443                    })?
3444                } else {
3445                    expr
3446                };
3447
3448                Self::reject_clickhouse_session_semantics(&expr, self.dialect_type, target, opts)?;
3449                Self::reject_postgres_tsql_strict_regex_predicates(
3450                    &expr,
3451                    self.dialect_type,
3452                    target,
3453                    opts,
3454                )?;
3455                Self::reject_tsql_strict_json_constructor_return_types(
3456                    &expr,
3457                    self.dialect_type,
3458                    target,
3459                    opts,
3460                )?;
3461                Self::reject_postgres_tsql_strict_json_aggregate_modifiers(
3462                    &expr,
3463                    self.dialect_type,
3464                    target,
3465                    opts,
3466                )?;
3467
3468                // When source and target differ, first normalize the source dialect's
3469                // AST constructs to standard SQL, so that the target dialect can handle them.
3470                // This handles cases like Snowflake's SQUARE -> POWER, DIV0 -> CASE, etc.
3471                let normalized =
3472                    if self.dialect_type != target && self.dialect_type != DialectType::Generic {
3473                        self.transform_with_guard(expr, opts.complexity_guard)?
3474                    } else {
3475                        expr
3476                    };
3477
3478                // For TSQL source targeting non-TSQL: unwrap ISNULL(JSON_QUERY(...), JSON_VALUE(...))
3479                // to just JSON_QUERY(...) so cross_dialect_normalize can convert it cleanly.
3480                // The TSQL read transform wraps JsonQuery in ISNULL for identity, but for
3481                // cross-dialect transpilation we need the unwrapped JSON_QUERY.
3482                let normalized =
3483                    if matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3484                        && !matches!(target, DialectType::TSQL | DialectType::Fabric)
3485                    {
3486                        transform_recursive(normalized, &|e| {
3487                            if let Expression::Function(ref f) = e {
3488                                if f.name.eq_ignore_ascii_case("ISNULL") && f.args.len() == 2 {
3489                                    // Check if first arg is JSON_QUERY and second is JSON_VALUE
3490                                    if let (
3491                                        Expression::Function(ref jq),
3492                                        Expression::Function(ref jv),
3493                                    ) = (&f.args[0], &f.args[1])
3494                                    {
3495                                        if jq.name.eq_ignore_ascii_case("JSON_QUERY")
3496                                            && jv.name.eq_ignore_ascii_case("JSON_VALUE")
3497                                        {
3498                                            // Unwrap: return just JSON_QUERY(...)
3499                                            return Ok(f.args[0].clone());
3500                                        }
3501                                    }
3502                                }
3503                            }
3504                            Ok(e)
3505                        })?
3506                    } else {
3507                        normalized
3508                    };
3509
3510                // Snowflake source to non-Snowflake target: CURRENT_TIME -> LOCALTIME.
3511                // Preserve precision for ClickHouse's Time64 lowering; retain the existing
3512                // precision-less LOCALTIME compatibility output for other targets.
3513                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3514                    && !matches!(target, DialectType::Snowflake)
3515                {
3516                    transform_recursive(normalized, &|e| match e {
3517                        Expression::Function(ref f)
3518                            if f.name.eq_ignore_ascii_case("CURRENT_TIME") =>
3519                        {
3520                            let precision = if matches!(target, DialectType::ClickHouse) {
3521                                f.args.first().cloned().map(Box::new)
3522                            } else {
3523                                None
3524                            };
3525                            Ok(Expression::Localtime(Box::new(
3526                                crate::expressions::Localtime { this: precision },
3527                            )))
3528                        }
3529                        Expression::Localtime(ref localtime)
3530                            if !matches!(target, DialectType::ClickHouse)
3531                                && localtime.this.is_some() =>
3532                        {
3533                            Ok(Expression::Localtime(Box::new(
3534                                crate::expressions::Localtime { this: None },
3535                            )))
3536                        }
3537                        _ => Ok(e),
3538                    })?
3539                } else {
3540                    normalized
3541                };
3542
3543                // Snowflake source to DuckDB target: REPEAT(' ', n) -> REPEAT(' ', CAST(n AS BIGINT))
3544                // Snowflake's SPACE(n) is converted to REPEAT(' ', n) by the Snowflake source
3545                // transform. DuckDB requires the count argument to be BIGINT.
3546                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3547                    && matches!(target, DialectType::DuckDB)
3548                {
3549                    transform_recursive(normalized, &|e| {
3550                        if let Expression::Function(ref f) = e {
3551                            if f.name.eq_ignore_ascii_case("REPEAT") && f.args.len() == 2 {
3552                                // Check if first arg is space string literal
3553                                if let Expression::Literal(ref lit) = f.args[0] {
3554                                    if let crate::expressions::Literal::String(ref s) = lit.as_ref()
3555                                    {
3556                                        if s == " " {
3557                                            // Wrap second arg in CAST(... AS BIGINT) if not already
3558                                            if !matches!(f.args[1], Expression::Cast(_)) {
3559                                                let mut new_args = f.args.clone();
3560                                                new_args[1] = Expression::Cast(Box::new(
3561                                                    crate::expressions::Cast {
3562                                                        this: new_args[1].clone(),
3563                                                        to: crate::expressions::DataType::BigInt {
3564                                                            length: None,
3565                                                        },
3566                                                        trailing_comments: Vec::new(),
3567                                                        double_colon_syntax: false,
3568                                                        format: None,
3569                                                        default: None,
3570                                                        inferred_type: None,
3571                                                    },
3572                                                ));
3573                                                return Ok(Expression::Function(Box::new(
3574                                                    crate::expressions::Function {
3575                                                        name: f.name.clone(),
3576                                                        args: new_args,
3577                                                        distinct: f.distinct,
3578                                                        trailing_comments: f
3579                                                            .trailing_comments
3580                                                            .clone(),
3581                                                        use_bracket_syntax: f.use_bracket_syntax,
3582                                                        no_parens: f.no_parens,
3583                                                        quoted: f.quoted,
3584                                                        span: None,
3585                                                        inferred_type: None,
3586                                                    },
3587                                                )));
3588                                            }
3589                                        }
3590                                    }
3591                                }
3592                            }
3593                        }
3594                        Ok(e)
3595                    })?
3596                } else {
3597                    normalized
3598                };
3599
3600                // Propagate struct field names in arrays (for BigQuery source to non-BigQuery target)
3601                // BigQuery->BigQuery should NOT propagate names (BigQuery handles implicit inheritance)
3602                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3603                    && !matches!(target, DialectType::BigQuery)
3604                {
3605                    crate::transforms::propagate_struct_field_names(normalized)?
3606                } else {
3607                    normalized
3608                };
3609
3610                // Snowflake source to DuckDB target: RANDOM()/RANDOM(seed) -> scaled RANDOM()
3611                // Snowflake RANDOM() returns integer in [-2^63, 2^63-1], DuckDB RANDOM() returns float [0, 1)
3612                // Skip RANDOM inside UNIFORM/NORMAL/ZIPF/RANDSTR generator args since those
3613                // functions handle their generator args differently (as float seeds).
3614                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3615                    && matches!(target, DialectType::DuckDB)
3616                {
3617                    fn make_scaled_random() -> Expression {
3618                        let lower =
3619                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3620                                "-9.223372036854776E+18".to_string(),
3621                            )));
3622                        let upper =
3623                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3624                                "9.223372036854776e+18".to_string(),
3625                            )));
3626                        let random_call = Expression::Random(crate::expressions::Random);
3627                        let range_size = Expression::Paren(Box::new(crate::expressions::Paren {
3628                            this: Expression::Sub(Box::new(crate::expressions::BinaryOp {
3629                                left: upper,
3630                                right: lower.clone(),
3631                                left_comments: vec![],
3632                                operator_comments: vec![],
3633                                trailing_comments: vec![],
3634                                inferred_type: None,
3635                            })),
3636                            trailing_comments: vec![],
3637                        }));
3638                        let scaled = Expression::Mul(Box::new(crate::expressions::BinaryOp {
3639                            left: random_call,
3640                            right: range_size,
3641                            left_comments: vec![],
3642                            operator_comments: vec![],
3643                            trailing_comments: vec![],
3644                            inferred_type: None,
3645                        }));
3646                        let shifted = Expression::Add(Box::new(crate::expressions::BinaryOp {
3647                            left: lower,
3648                            right: scaled,
3649                            left_comments: vec![],
3650                            operator_comments: vec![],
3651                            trailing_comments: vec![],
3652                            inferred_type: None,
3653                        }));
3654                        Expression::Cast(Box::new(crate::expressions::Cast {
3655                            this: shifted,
3656                            to: crate::expressions::DataType::BigInt { length: None },
3657                            trailing_comments: vec![],
3658                            double_colon_syntax: false,
3659                            format: None,
3660                            default: None,
3661                            inferred_type: None,
3662                        }))
3663                    }
3664
3665                    // Pre-process: protect seeded RANDOM(seed) inside UNIFORM/NORMAL/ZIPF/RANDSTR
3666                    // by converting Rand{seed: Some(s)} to Function{name:"RANDOM", args:[s]}.
3667                    // This prevents transform_recursive (which is bottom-up) from expanding
3668                    // seeded RANDOM into make_scaled_random() and losing the seed value.
3669                    // Unseeded RANDOM()/Rand{seed:None} is left as-is so it gets expanded
3670                    // and then un-expanded back to Expression::Random by the code below.
3671                    let normalized = transform_recursive(normalized, &|e| {
3672                        if let Expression::Function(ref f) = e {
3673                            let n = f.name.to_ascii_uppercase();
3674                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" || n == "RANDSTR" {
3675                                if let Expression::Function(mut f) = e {
3676                                    for arg in f.args.iter_mut() {
3677                                        if let Expression::Rand(ref r) = arg {
3678                                            if r.lower.is_none() && r.upper.is_none() {
3679                                                if let Some(ref seed) = r.seed {
3680                                                    // Convert Rand{seed: Some(s)} to Function("RANDOM", [s])
3681                                                    // so it won't be expanded by the RANDOM expansion below
3682                                                    *arg = Expression::Function(Box::new(
3683                                                        crate::expressions::Function::new(
3684                                                            "RANDOM".to_string(),
3685                                                            vec![*seed.clone()],
3686                                                        ),
3687                                                    ));
3688                                                }
3689                                            }
3690                                        }
3691                                    }
3692                                    return Ok(Expression::Function(f));
3693                                }
3694                            }
3695                        }
3696                        Ok(e)
3697                    })?;
3698
3699                    // transform_recursive processes bottom-up, so RANDOM() (unseeded) inside
3700                    // generator functions (UNIFORM, NORMAL, ZIPF) gets expanded before
3701                    // we see the parent. We detect this and undo the expansion by replacing
3702                    // the expanded pattern back with Expression::Random.
3703                    // Seeded RANDOM(seed) was already protected above as Function("RANDOM", [seed]).
3704                    // Note: RANDSTR is NOT included here — it needs the expanded form for unseeded
3705                    // RANDOM() since the DuckDB handler uses the expanded SQL as-is in the hash.
3706                    transform_recursive(normalized, &|e| {
3707                        if let Expression::Function(ref f) = e {
3708                            let n = f.name.to_ascii_uppercase();
3709                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" {
3710                                if let Expression::Function(mut f) = e {
3711                                    for arg in f.args.iter_mut() {
3712                                        // Detect expanded RANDOM pattern: CAST(-9.22... + RANDOM() * (...) AS BIGINT)
3713                                        if let Expression::Cast(ref cast) = arg {
3714                                            if matches!(
3715                                                cast.to,
3716                                                crate::expressions::DataType::BigInt { .. }
3717                                            ) {
3718                                                if let Expression::Add(ref add) = cast.this {
3719                                                    if let Expression::Literal(ref lit) = add.left {
3720                                                        if let crate::expressions::Literal::Number(
3721                                                            ref num,
3722                                                        ) = lit.as_ref()
3723                                                        {
3724                                                            if num == "-9.223372036854776E+18" {
3725                                                                *arg = Expression::Random(
3726                                                                    crate::expressions::Random,
3727                                                                );
3728                                                            }
3729                                                        }
3730                                                    }
3731                                                }
3732                                            }
3733                                        }
3734                                    }
3735                                    return Ok(Expression::Function(f));
3736                                }
3737                                return Ok(e);
3738                            }
3739                        }
3740                        match e {
3741                            Expression::Random(_) => Ok(make_scaled_random()),
3742                            // Rand(seed) with no bounds: drop seed and expand
3743                            // (DuckDB RANDOM doesn't support seeds)
3744                            Expression::Rand(ref r) if r.lower.is_none() && r.upper.is_none() => {
3745                                Ok(make_scaled_random())
3746                            }
3747                            _ => Ok(e),
3748                        }
3749                    })?
3750                } else {
3751                    normalized
3752                };
3753
3754                // Apply cross-dialect semantic normalizations
3755                let normalized = normalization::normalize(
3756                    normalized,
3757                    self.dialect_type,
3758                    target,
3759                    matches!(
3760                        opts.unsupported_level,
3761                        UnsupportedLevel::Raise | UnsupportedLevel::Immediate
3762                    ),
3763                )?;
3764
3765                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3766                    Self::normalize_tsql_fetch_overlaps_date_bin(normalized)?
3767                } else {
3768                    normalized
3769                };
3770
3771                let normalized =
3772                    if matches!(
3773                        self.dialect_type,
3774                        DialectType::PostgreSQL | DialectType::CockroachDB
3775                    ) && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
3776                    {
3777                        Self::normalize_postgres_type_function_casts(normalized, target)?
3778                    } else {
3779                        normalized
3780                    };
3781
3782                let normalized = if matches!(self.dialect_type, DialectType::SQLite)
3783                    && !matches!(target, DialectType::SQLite)
3784                {
3785                    Self::normalize_sqlite_double_quoted_defaults(normalized)?
3786                } else {
3787                    normalized
3788                };
3789
3790                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3791                    && matches!(target, DialectType::SQLite)
3792                {
3793                    Self::normalize_postgres_to_sqlite_types(normalized)?
3794                } else {
3795                    normalized
3796                };
3797
3798                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3799                    && matches!(target, DialectType::Fabric)
3800                {
3801                    Self::normalize_postgres_to_fabric_types(normalized)?
3802                } else {
3803                    normalized
3804                };
3805
3806                // For DuckDB target from BigQuery source: wrap UNNEST of struct arrays in
3807                // (SELECT UNNEST(..., max_depth => 2)) subquery
3808                // Must run BEFORE unnest_alias_to_column_alias since it changes alias structure
3809                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3810                    && matches!(target, DialectType::DuckDB)
3811                {
3812                    crate::transforms::wrap_duckdb_unnest_struct(normalized)?
3813                } else {
3814                    normalized
3815                };
3816
3817                // Convert BigQuery UNNEST aliases to column-alias format for DuckDB/Presto/Spark
3818                // UNNEST(arr) AS x -> UNNEST(arr) AS _t0(x)
3819                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3820                    && matches!(
3821                        target,
3822                        DialectType::DuckDB
3823                            | DialectType::Presto
3824                            | DialectType::Trino
3825                            | DialectType::Athena
3826                            | DialectType::Spark
3827                            | DialectType::Databricks
3828                    ) {
3829                    crate::transforms::unnest_alias_to_column_alias(normalized)?
3830                } else if matches!(self.dialect_type, DialectType::BigQuery)
3831                    && matches!(target, DialectType::BigQuery | DialectType::Redshift)
3832                {
3833                    // For BigQuery/Redshift targets: move UNNEST FROM items to CROSS JOINs
3834                    // but don't convert alias format (no _t0 wrapper)
3835                    let result = crate::transforms::unnest_from_to_cross_join(normalized)?;
3836                    // For Redshift: strip UNNEST when arg is a column reference path
3837                    if matches!(target, DialectType::Redshift) {
3838                        crate::transforms::strip_unnest_column_refs(result)?
3839                    } else {
3840                        result
3841                    }
3842                } else {
3843                    normalized
3844                };
3845
3846                // For Presto/Trino targets from PostgreSQL/Redshift source:
3847                // Wrap UNNEST aliases from GENERATE_SERIES conversion: AS s -> AS _u(s)
3848                let normalized = if matches!(
3849                    self.dialect_type,
3850                    DialectType::PostgreSQL | DialectType::Redshift
3851                ) && matches!(
3852                    target,
3853                    DialectType::Presto | DialectType::Trino | DialectType::Athena
3854                ) {
3855                    crate::transforms::wrap_unnest_join_aliases(normalized)?
3856                } else {
3857                    normalized
3858                };
3859
3860                // Eliminate DISTINCT ON with target-dialect awareness
3861                // This must happen after source transform (which may produce DISTINCT ON)
3862                // and before target transform, with knowledge of the target dialect's NULL ordering behavior
3863                let normalized = crate::transforms::eliminate_distinct_on_for_dialect(
3864                    normalized,
3865                    Some(target),
3866                    Some(self.dialect_type),
3867                )?;
3868
3869                // GENERATE_DATE_ARRAY in UNNEST -> Snowflake ARRAY_GENERATE_RANGE + DATEADD
3870                let normalized = if matches!(target, DialectType::Snowflake) {
3871                    Self::transform_generate_date_array_snowflake(normalized)?
3872                } else {
3873                    normalized
3874                };
3875
3876                // CROSS JOIN UNNEST -> LATERAL VIEW EXPLODE/INLINE for Spark/Hive/Databricks
3877                let normalized = if matches!(
3878                    target,
3879                    DialectType::Spark | DialectType::Databricks | DialectType::Hive
3880                ) {
3881                    crate::transforms::unnest_to_explode_select(normalized)?
3882                } else {
3883                    normalized
3884                };
3885
3886                // Wrap UNION with ORDER BY/LIMIT in a subquery for dialects that require it
3887                let normalized = if matches!(target, DialectType::ClickHouse | DialectType::TSQL) {
3888                    crate::transforms::no_limit_order_by_union(normalized)?
3889                } else {
3890                    normalized
3891                };
3892
3893                let normalized = if matches!(
3894                    self.dialect_type,
3895                    DialectType::PostgreSQL | DialectType::CockroachDB
3896                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3897                {
3898                    Self::normalize_postgres_boolean_semantics_for_tsql(normalized)?
3899                } else {
3900                    normalized
3901                };
3902
3903                let normalized = if self.dialect_type == DialectType::PostgreSQL
3904                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
3905                {
3906                    Self::normalize_postgres_bytea_literals_for_tsql(normalized)?
3907                } else {
3908                    normalized
3909                };
3910
3911                let normalized = if matches!(
3912                    self.dialect_type,
3913                    DialectType::PostgreSQL | DialectType::CockroachDB
3914                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3915                {
3916                    Self::normalize_postgres_string_semantics_for_tsql(normalized)?
3917                } else {
3918                    normalized
3919                };
3920
3921                // TSQL: Convert COUNT(*) -> COUNT_BIG(*) when source is not TSQL/Fabric
3922                // Python sqlglot does this in the TSQL generator, but we can't do it there
3923                // because it would break TSQL -> TSQL identity
3924                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3925                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3926                {
3927                    transform_recursive(normalized, &|e| {
3928                        if let Expression::Count(ref c) = e {
3929                            // Build COUNT_BIG(...) as an AggregateFunction
3930                            let args = if c.star {
3931                                vec![Expression::Star(crate::expressions::Star {
3932                                    table: None,
3933                                    except: None,
3934                                    replace: None,
3935                                    rename: None,
3936                                    trailing_comments: Vec::new(),
3937                                    span: None,
3938                                })]
3939                            } else if let Some(ref this) = c.this {
3940                                vec![this.clone()]
3941                            } else {
3942                                vec![]
3943                            };
3944                            Ok(Expression::AggregateFunction(Box::new(
3945                                crate::expressions::AggregateFunction {
3946                                    name: "COUNT_BIG".to_string(),
3947                                    args,
3948                                    distinct: c.distinct,
3949                                    filter: c.filter.clone(),
3950                                    order_by: Vec::new(),
3951                                    limit: None,
3952                                    ignore_nulls: None,
3953                                    inferred_type: None,
3954                                },
3955                            )))
3956                        } else {
3957                            Ok(e)
3958                        }
3959                    })?
3960                } else {
3961                    normalized
3962                };
3963
3964                // T-SQL/Fabric do not have a scalar boolean type. Keep predicate
3965                // contexts intact, but materialize boolean-valued expressions used
3966                // as values before target transforms add ORDER BY null sort keys.
3967                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
3968                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3969                {
3970                    let normalized = if self.dialect_type == DialectType::PostgreSQL {
3971                        Self::rewrite_postgres_row_value_equality_for_tsql(normalized)?
3972                    } else {
3973                        normalized
3974                    };
3975                    Self::rewrite_boolean_values_for_tsql(normalized)?
3976                } else {
3977                    normalized
3978                };
3979
3980                let normalized = if matches!(
3981                    self.dialect_type,
3982                    DialectType::PostgreSQL | DialectType::CockroachDB
3983                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3984                {
3985                    Self::rewrite_postgres_format_for_tsql(normalized, target)?
3986                } else {
3987                    normalized
3988                };
3989
3990                let normalized = if self.dialect_type == DialectType::PostgreSQL
3991                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
3992                {
3993                    Self::normalize_postgres_only_for_tsql(normalized)?
3994                } else {
3995                    normalized
3996                };
3997
3998                let transformed =
3999                    target_dialect.transform_with_guard(normalized, opts.complexity_guard)?;
4000
4001                // T-SQL and Fabric do not support aggregate FILTER clauses. Rewrite any
4002                // remaining filters after target transforms so special aggregate rewrites
4003                // (for example BOOL_OR/BOOL_AND) can consume their filters first.
4004                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4005                    Self::rewrite_aggregate_filters_for_tsql(transformed)?
4006                } else {
4007                    transformed
4008                };
4009
4010                let transformed = if matches!(
4011                    self.dialect_type,
4012                    DialectType::PostgreSQL | DialectType::CockroachDB
4013                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4014                {
4015                    crate::transforms::grouped_percentiles_to_tsql_windows(transformed)?
4016                } else {
4017                    transformed
4018                };
4019
4020                let transformed = if matches!(
4021                    self.dialect_type,
4022                    DialectType::PostgreSQL | DialectType::CockroachDB
4023                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4024                {
4025                    Self::normalize_postgres_trim_for_tsql(transformed)?
4026                } else {
4027                    transformed
4028                };
4029
4030                let transformed = if matches!(
4031                    self.dialect_type,
4032                    DialectType::PostgreSQL | DialectType::CockroachDB
4033                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4034                {
4035                    Self::rewrite_postgres_json_array_elements_select_for_tsql(transformed)?
4036                } else {
4037                    transformed
4038                };
4039
4040                // DuckDB target: when FROM is RANGE(n), replace SEQ's ROW_NUMBER pattern with `range`
4041                let transformed = if matches!(target, DialectType::DuckDB) {
4042                    Self::seq_rownum_to_range(transformed)?
4043                } else {
4044                    transformed
4045                };
4046
4047                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4048                    Self::reject_tsql_interval_casts(&transformed, target, opts)?;
4049                }
4050
4051                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4052                    Self::rewrite_tsql_interval_casts_to_varchar(transformed)?
4053                } else {
4054                    transformed
4055                };
4056
4057                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4058                    Self::legalize_tsql_nested_order_by(transformed)?
4059                } else {
4060                    transformed
4061                };
4062
4063                Self::reject_strict_unsupported(&transformed, self.dialect_type, target, opts)?;
4064
4065                let mut sql = target_dialect.generate_with_transpile_options(
4066                    &transformed,
4067                    self.dialect_type,
4068                    opts,
4069                )?;
4070
4071                // Align a known Snowflake pretty-print edge case with Python sqlglot output.
4072                if opts.pretty && target == DialectType::Snowflake {
4073                    sql = Self::normalize_snowflake_pretty(sql);
4074                }
4075
4076                Ok(sql)
4077            })
4078            .collect()
4079    }
4080}
4081
4082// Transpile-only methods: cross-dialect normalization and helpers
4083#[cfg(feature = "transpile")]
4084impl Dialect {
4085    fn legalize_tsql_nested_order_by(expr: Expression) -> Result<Expression> {
4086        let preserve_root_order = matches!(&expr, Expression::Select(select) if Self::tsql_select_needs_order_offset(select));
4087
4088        let mut transformed = transform_recursive(expr, &|node| match node {
4089            Expression::Select(mut select) => {
4090                Self::legalize_tsql_select_offset(&mut select);
4091                if Self::tsql_select_needs_order_offset(&select) {
4092                    select.offset = Some(Offset {
4093                        this: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
4094                        rows: Some(true),
4095                    });
4096                }
4097                Ok(Expression::Select(select))
4098            }
4099            Expression::Subquery(mut subquery) => {
4100                Self::legalize_tsql_offset(&mut subquery.order_by, &mut subquery.offset, false);
4101                Ok(Expression::Subquery(subquery))
4102            }
4103            Expression::Union(mut union) => {
4104                Self::legalize_tsql_set_offset(&mut union.order_by, &mut union.offset);
4105                Ok(Expression::Union(union))
4106            }
4107            Expression::Intersect(mut intersect) => {
4108                Self::legalize_tsql_set_offset(&mut intersect.order_by, &mut intersect.offset);
4109                Ok(Expression::Intersect(intersect))
4110            }
4111            Expression::Except(mut except) => {
4112                Self::legalize_tsql_set_offset(&mut except.order_by, &mut except.offset);
4113                Ok(Expression::Except(except))
4114            }
4115            other => Ok(other),
4116        })?;
4117
4118        if preserve_root_order {
4119            if let Expression::Select(select) = &mut transformed {
4120                select.offset = None;
4121            }
4122        }
4123
4124        Self::drop_tsql_unbounded_nested_set_order_by(transformed)
4125    }
4126
4127    fn drop_tsql_unbounded_nested_set_order_by(mut expr: Expression) -> Result<Expression> {
4128        let root_order_by = Self::take_tsql_root_set_order_by(&mut expr);
4129
4130        let mut transformed = transform_recursive(expr, &|node| match node {
4131            Expression::Union(mut union) => {
4132                if union.limit.is_none() && union.offset.is_none() {
4133                    union.order_by = None;
4134                }
4135                Ok(Expression::Union(union))
4136            }
4137            Expression::Intersect(mut intersect) => {
4138                if intersect.limit.is_none() && intersect.offset.is_none() {
4139                    intersect.order_by = None;
4140                }
4141                Ok(Expression::Intersect(intersect))
4142            }
4143            Expression::Except(mut except) => {
4144                if except.limit.is_none() && except.offset.is_none() {
4145                    except.order_by = None;
4146                }
4147                Ok(Expression::Except(except))
4148            }
4149            other => Ok(other),
4150        })?;
4151
4152        if let Some(order_by) = root_order_by {
4153            Self::restore_tsql_root_set_order_by(&mut transformed, order_by);
4154        }
4155
4156        Ok(transformed)
4157    }
4158
4159    fn take_tsql_root_set_order_by(expr: &mut Expression) -> Option<OrderBy> {
4160        match expr {
4161            Expression::Union(union) => union.order_by.take(),
4162            Expression::Intersect(intersect) => intersect.order_by.take(),
4163            Expression::Except(except) => except.order_by.take(),
4164            Expression::Subquery(subquery) if subquery.alias.is_none() => {
4165                Self::take_tsql_root_set_order_by(&mut subquery.this)
4166            }
4167            Expression::Paren(paren) => Self::take_tsql_root_set_order_by(&mut paren.this),
4168            _ => None,
4169        }
4170    }
4171
4172    fn restore_tsql_root_set_order_by(expr: &mut Expression, order_by: OrderBy) {
4173        match expr {
4174            Expression::Union(union) => union.order_by = Some(order_by),
4175            Expression::Intersect(intersect) => intersect.order_by = Some(order_by),
4176            Expression::Except(except) => except.order_by = Some(order_by),
4177            Expression::Subquery(subquery) if subquery.alias.is_none() => {
4178                Self::restore_tsql_root_set_order_by(&mut subquery.this, order_by);
4179            }
4180            Expression::Paren(paren) => {
4181                Self::restore_tsql_root_set_order_by(&mut paren.this, order_by);
4182            }
4183            _ => {}
4184        }
4185    }
4186
4187    fn legalize_tsql_select_offset(select: &mut crate::expressions::Select) {
4188        let has_fetch = select.fetch.is_some();
4189        Self::legalize_tsql_offset(&mut select.order_by, &mut select.offset, has_fetch);
4190    }
4191
4192    fn legalize_tsql_offset(
4193        order_by: &mut Option<OrderBy>,
4194        offset: &mut Option<Offset>,
4195        retain_inert_offset: bool,
4196    ) {
4197        if order_by.is_some() {
4198            return;
4199        }
4200
4201        if offset
4202            .as_ref()
4203            .is_some_and(|offset| Self::tsql_offset_is_inert(&offset.this))
4204            && !retain_inert_offset
4205        {
4206            *offset = None;
4207        } else if offset.is_some() {
4208            *order_by = Some(Generator::dummy_tsql_order_by());
4209        }
4210    }
4211
4212    fn legalize_tsql_set_offset(
4213        order_by: &mut Option<OrderBy>,
4214        offset: &mut Option<Box<Expression>>,
4215    ) {
4216        if order_by.is_some() {
4217            return;
4218        }
4219
4220        if offset.as_deref().is_some_and(Self::tsql_offset_is_inert) {
4221            *offset = None;
4222        } else if offset.is_some() {
4223            *order_by = Some(Generator::dummy_tsql_order_by());
4224        }
4225    }
4226
4227    fn tsql_offset_is_inert(expr: &Expression) -> bool {
4228        match expr {
4229            Expression::Null(_) => true,
4230            Expression::Literal(literal) => match literal.as_ref() {
4231                Literal::Number(value) => value.parse::<i128>().is_ok_and(|value| value == 0),
4232                _ => false,
4233            },
4234            _ => false,
4235        }
4236    }
4237
4238    fn tsql_select_needs_order_offset(select: &crate::expressions::Select) -> bool {
4239        select.order_by.is_some()
4240            && select.top.is_none()
4241            && select.limit.is_none()
4242            && select.offset.is_none()
4243            && select.fetch.is_none()
4244            && select.for_xml.is_empty()
4245            && select.for_json.is_empty()
4246    }
4247
4248    fn reject_clickhouse_session_semantics(
4249        expr: &Expression,
4250        source: DialectType,
4251        target: DialectType,
4252        opts: &TranspileOptions,
4253    ) -> Result<()> {
4254        if !matches!(
4255            opts.unsupported_level,
4256            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4257        ) || target != DialectType::ClickHouse
4258            || source == DialectType::ClickHouse
4259        {
4260            return Ok(());
4261        }
4262
4263        const JOIN_USE_NULLS_DIAGNOSTIC: &str = "ClickHouse outer joins require the target setting join_use_nulls = 1 to preserve unmatched-row NULL semantics";
4264        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";
4265
4266        fn is_current_session_time(node: &Expression) -> bool {
4267            match node {
4268                Expression::CurrentDate(_)
4269                | Expression::CurrentTime(_)
4270                | Expression::CurrentTimestamp(_)
4271                | Expression::CurrentTimestampLTZ(_)
4272                | Expression::Localtime(_)
4273                | Expression::Localtimestamp(_)
4274                | Expression::Systimestamp(_) => true,
4275                Expression::Function(function) if !function.quoted => matches!(
4276                    function.name.to_ascii_uppercase().as_str(),
4277                    "CURRENT_DATE"
4278                        | "CURRENT_TIME"
4279                        | "LOCALTIME"
4280                        | "CURRENT_TIMESTAMP"
4281                        | "CURRENT_TIMESTAMP_LTZ"
4282                        | "LOCALTIMESTAMP"
4283                        | "NOW"
4284                        | "GETDATE"
4285                        | "SYSTIMESTAMP"
4286                ),
4287                _ => false,
4288            }
4289        }
4290
4291        fn is_session_week_part(expression: &Expression) -> bool {
4292            let part = match expression {
4293                Expression::Literal(literal) => match literal.as_ref() {
4294                    Literal::String(value) => Some(value.as_str()),
4295                    _ => None,
4296                },
4297                Expression::Identifier(identifier) => Some(identifier.name.as_str()),
4298                Expression::Var(var) => Some(var.this.as_str()),
4299                Expression::Column(column) if column.table.is_none() => {
4300                    Some(column.name.name.as_str())
4301                }
4302                _ => None,
4303            };
4304            part.is_some_and(|part| {
4305                matches!(
4306                    part.to_ascii_uppercase().as_str(),
4307                    "WEEK" | "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY"
4308                )
4309            })
4310        }
4311
4312        fn is_session_week_trunc(node: &Expression) -> bool {
4313            match node {
4314                Expression::DateTrunc(date_trunc) => {
4315                    matches!(date_trunc.unit, DateTimeField::Week)
4316                }
4317                Expression::Function(function)
4318                    if !function.quoted && function.name.eq_ignore_ascii_case("DATE_TRUNC") =>
4319                {
4320                    function.args.first().is_some_and(is_session_week_part)
4321                }
4322                _ => false,
4323            }
4324        }
4325
4326        fn is_null_extending_clickhouse_join(node: &Expression) -> bool {
4327            fn join_is_null_extending(join: &Join) -> bool {
4328                matches!(
4329                    join.kind,
4330                    JoinKind::Left
4331                        | JoinKind::Right
4332                        | JoinKind::Full
4333                        | JoinKind::NaturalLeft
4334                        | JoinKind::NaturalRight
4335                        | JoinKind::NaturalFull
4336                        | JoinKind::AsOfLeft
4337                        | JoinKind::AsOfRight
4338                )
4339            }
4340
4341            fn joins_are_null_extending(joins: &[Join]) -> bool {
4342                joins.iter().any(join_is_null_extending)
4343            }
4344
4345            match node {
4346                Expression::Join(join) => join_is_null_extending(join),
4347                Expression::Select(select) => joins_are_null_extending(&select.joins),
4348                Expression::JoinedTable(joined) => joins_are_null_extending(&joined.joins),
4349                Expression::Update(update) => {
4350                    joins_are_null_extending(&update.table_joins)
4351                        || joins_are_null_extending(&update.from_joins)
4352                }
4353                Expression::Delete(delete) => joins_are_null_extending(&delete.joins),
4354                _ => false,
4355            }
4356        }
4357
4358        fn is_count_like_aggregate(node: &Expression) -> bool {
4359            match node {
4360                Expression::Count(_)
4361                | Expression::CountIf(_)
4362                | Expression::ApproxDistinct(_)
4363                | Expression::ApproxCountDistinct(_) => true,
4364                Expression::AggregateFunction(function) => matches!(
4365                    function.name.to_ascii_uppercase().as_str(),
4366                    "COUNT" | "COUNT_IF" | "COUNTIF" | "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT"
4367                ),
4368                _ => false,
4369            }
4370        }
4371
4372        fn is_empty_input_sensitive_aggregate(node: &Expression, source: DialectType) -> bool {
4373            Dialect::node_is_aggregate_function(node)
4374                && !is_count_like_aggregate(node)
4375                && !matches!(node, Expression::BitwiseCount(_))
4376                && !matches!(node, Expression::Median(_) if source == DialectType::Snowflake)
4377                && !matches!(
4378                    node,
4379                    Expression::AggregateFunction(function)
4380                        if function.name.to_ascii_lowercase().ends_with("ornull")
4381                )
4382        }
4383
4384        fn setting_is_enabled(setting: &Expression, name: &str) -> bool {
4385            let Expression::Eq(equality) = setting else {
4386                return false;
4387            };
4388            let setting_name = match &equality.left {
4389                Expression::Identifier(identifier) => identifier.name.as_str(),
4390                Expression::Column(column) if column.table.is_none() => column.name.name.as_str(),
4391                Expression::Var(var) => var.this.as_str(),
4392                _ => return false,
4393            };
4394            let enabled = match &equality.right {
4395                Expression::Literal(literal) => {
4396                    matches!(literal.as_ref(), Literal::Number(value) if value.parse::<i128>().is_ok_and(|value| value == 1))
4397                }
4398                Expression::Boolean(boolean) => boolean.value,
4399                _ => false,
4400            };
4401            setting_name.eq_ignore_ascii_case(name) && enabled
4402        }
4403
4404        fn query_has_enabled_setting(expr: &Expression, name: &str) -> bool {
4405            expr.dfs().any(|node| {
4406                matches!(
4407                    node,
4408                    Expression::Select(select)
4409                        if select.settings.as_ref().is_some_and(|settings| {
4410                            settings.iter().any(|setting| setting_is_enabled(setting, name))
4411                        })
4412                )
4413            })
4414        }
4415
4416        let mut diagnostics = Vec::new();
4417        let join_use_nulls_enabled = query_has_enabled_setting(expr, "join_use_nulls");
4418        let aggregate_null_for_empty_enabled =
4419            query_has_enabled_setting(expr, "aggregate_functions_null_for_empty");
4420        for node in expr.dfs() {
4421            if source == DialectType::Snowflake && is_current_session_time(node) {
4422                Self::push_unsupported_diagnostic(
4423                    &mut diagnostics,
4424                    "Snowflake current date/time expressions depend on the session TIMEZONE, which cannot be preserved for ClickHouse",
4425                );
4426            }
4427            if source == DialectType::Snowflake && is_session_week_trunc(node) {
4428                Self::push_unsupported_diagnostic(
4429                    &mut diagnostics,
4430                    "Snowflake DATE_TRUNC with a week unit depends on the session WEEK_START, which cannot be preserved for ClickHouse",
4431                );
4432            }
4433            if !join_use_nulls_enabled && is_null_extending_clickhouse_join(node) {
4434                Self::push_unsupported_diagnostic(&mut diagnostics, JOIN_USE_NULLS_DIAGNOSTIC);
4435            }
4436            if !aggregate_null_for_empty_enabled && is_empty_input_sensitive_aggregate(node, source)
4437            {
4438                Self::push_unsupported_diagnostic(
4439                    &mut diagnostics,
4440                    AGGREGATE_NULL_FOR_EMPTY_DIAGNOSTIC,
4441                );
4442            }
4443            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4444                break;
4445            }
4446        }
4447
4448        if diagnostics.is_empty() {
4449            return Ok(());
4450        }
4451
4452        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4453            1
4454        } else {
4455            opts.max_unsupported.max(1)
4456        };
4457        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4458        if diagnostics.len() > limit {
4459            messages.push(format!("... and {} more", diagnostics.len() - limit));
4460        }
4461
4462        Err(crate::error::Error::unsupported(
4463            messages.join("; "),
4464            target.to_string(),
4465        ))
4466    }
4467
4468    fn reject_strict_unsupported(
4469        expr: &Expression,
4470        source: DialectType,
4471        target: DialectType,
4472        opts: &TranspileOptions,
4473    ) -> Result<()> {
4474        if !matches!(
4475            opts.unsupported_level,
4476            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4477        ) {
4478            return Ok(());
4479        }
4480
4481        let mut diagnostics = Vec::new();
4482        if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4483            && matches!(target, DialectType::TSQL | DialectType::Fabric)
4484            && Self::tsql_apply_has_invalid_outer_aggregate(expr)
4485        {
4486            Self::push_unsupported_diagnostic(
4487                &mut diagnostics,
4488                "APPLY aggregate expressions that combine an outer reference with another column reference",
4489            );
4490        }
4491        let structural_grouping_tuples =
4492            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4493                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4494            {
4495                Self::collect_tsql_grouping_tuple_nodes(expr)
4496            } else {
4497                HashSet::new()
4498            };
4499
4500        for node in expr.dfs() {
4501            if source == DialectType::Snowflake && target == DialectType::ClickHouse {
4502                if Self::node_is_function_named(node, "TO_CHAR") {
4503                    Self::push_unsupported_diagnostic(
4504                        &mut diagnostics,
4505                        "Snowflake TO_CHAR overload or dynamic format",
4506                    );
4507                }
4508                if Self::node_is_function_named(node, "TRY_TO_DOUBLE")
4509                    || matches!(node, Expression::ToDouble(to_double) if to_double.safe.is_some() && to_double.format.is_some())
4510                {
4511                    Self::push_unsupported_diagnostic(
4512                        &mut diagnostics,
4513                        "Snowflake TRY_TO_DOUBLE with a format model",
4514                    );
4515                }
4516                if Self::node_is_function_named(node, "TRY_TO_NUMBER")
4517                    || Self::node_is_function_named(node, "TRY_TO_NUMERIC")
4518                    || Self::node_is_function_named(node, "TRY_TO_DECIMAL")
4519                    || matches!(node, Expression::ToNumber(to_number) if to_number.safe.is_some())
4520                {
4521                    Self::push_unsupported_diagnostic(
4522                        &mut diagnostics,
4523                        "Snowflake TRY_TO_NUMBER decimal conversion semantics",
4524                    );
4525                }
4526                if Self::node_is_function_named(node, "FLATTEN") {
4527                    Self::push_unsupported_diagnostic(
4528                        &mut diagnostics,
4529                        "Snowflake FLATTEN table-function semantics",
4530                    );
4531                }
4532            }
4533
4534            if matches!(target, DialectType::Fabric | DialectType::Hive)
4535                && Self::node_has_recursive_with(node)
4536            {
4537                Self::push_unsupported_diagnostic(&mut diagnostics, "recursive CTEs");
4538            }
4539
4540            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4541                && Self::node_has_lateral(node)
4542            {
4543                Self::push_unsupported_diagnostic(&mut diagnostics, "LATERAL joins and subqueries");
4544            }
4545
4546            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4547                if Self::node_has_join_using(node) {
4548                    Self::push_unsupported_diagnostic(&mut diagnostics, "JOIN USING clauses");
4549                }
4550                if Self::node_has_natural_join(node) {
4551                    Self::push_unsupported_diagnostic(&mut diagnostics, "NATURAL JOIN");
4552                }
4553                if Self::node_has_unsupported_relation_column_aliases(node) {
4554                    Self::push_unsupported_diagnostic(
4555                        &mut diagnostics,
4556                        "column alias lists on base or joined table references",
4557                    );
4558                }
4559                if Self::node_has_qualified_whole_row_aggregate_argument(node) {
4560                    Self::push_unsupported_diagnostic(
4561                        &mut diagnostics,
4562                        "qualified whole-row aggregate arguments",
4563                    );
4564                }
4565                if Self::node_has_subquery_in_aggregate_argument(node) {
4566                    Self::push_unsupported_diagnostic(
4567                        &mut diagnostics,
4568                        "aggregate arguments containing subqueries",
4569                    );
4570                }
4571            }
4572
4573            if !Self::target_supports_distinct_on(target) && Self::node_has_distinct_on(node) {
4574                Self::push_unsupported_diagnostic(&mut diagnostics, "DISTINCT ON");
4575            }
4576
4577            if !Self::target_supports_remaining_unnest(target) && Self::node_is_unnest(node) {
4578                Self::push_unsupported_diagnostic(&mut diagnostics, "UNNEST");
4579            }
4580
4581            if !Self::target_supports_remaining_explode(target) && Self::node_is_explode(node) {
4582                Self::push_unsupported_diagnostic(&mut diagnostics, "EXPLODE");
4583            }
4584
4585            if Self::target_lacks_array_agg(target) && Self::node_is_array_agg(node) {
4586                Self::push_unsupported_diagnostic(&mut diagnostics, "ARRAY_AGG");
4587            }
4588
4589            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4590                && Self::node_is_distinct_string_agg(node)
4591            {
4592                Self::push_unsupported_diagnostic(&mut diagnostics, "STRING_AGG with DISTINCT");
4593            }
4594
4595            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4596                && matches!(node, Expression::NthValue(_))
4597            {
4598                Self::push_unsupported_diagnostic(&mut diagnostics, "NTH_VALUE");
4599            }
4600
4601            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4602                if let Some(frame) = Self::node_window_frame(node) {
4603                    if matches!(frame.kind, WindowFrameKind::Groups) {
4604                        Self::push_unsupported_diagnostic(&mut diagnostics, "GROUPS window frames");
4605                    }
4606                    if matches!(frame.kind, WindowFrameKind::Range)
4607                        && (Self::window_frame_bound_has_value_offset(&frame.start)
4608                            || frame
4609                                .end
4610                                .as_ref()
4611                                .is_some_and(Self::window_frame_bound_has_value_offset))
4612                    {
4613                        Self::push_unsupported_diagnostic(
4614                            &mut diagnostics,
4615                            "value-offset RANGE window frames",
4616                        );
4617                    }
4618                    if frame.exclude.is_some() {
4619                        Self::push_unsupported_diagnostic(
4620                            &mut diagnostics,
4621                            "window frame EXCLUDE clauses",
4622                        );
4623                    }
4624                }
4625            }
4626
4627            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4628                && Self::node_is_regex_predicate(node)
4629            {
4630                Self::push_unsupported_diagnostic(
4631                    &mut diagnostics,
4632                    "regular expression predicates",
4633                );
4634            }
4635
4636            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4637                && Self::node_is_non_subquery_any(node)
4638            {
4639                Self::push_unsupported_diagnostic(
4640                    &mut diagnostics,
4641                    "ANY over non-subquery expressions",
4642                );
4643            }
4644
4645            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4646                && Self::node_is_row_value_subquery_comparison(node)
4647            {
4648                Self::push_unsupported_diagnostic(
4649                    &mut diagnostics,
4650                    "row-value subquery comparisons",
4651                );
4652            }
4653
4654            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4655                && Self::node_is_row_value_values_membership(node)
4656            {
4657                Self::push_unsupported_diagnostic(
4658                    &mut diagnostics,
4659                    "row-value VALUES membership comparisons",
4660                );
4661            }
4662
4663            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4664                && Self::node_has_fetch_with_ties(node)
4665            {
4666                Self::push_unsupported_diagnostic(&mut diagnostics, "FETCH WITH TIES without TOP");
4667            }
4668
4669            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4670                && Self::node_is_overlaps(node)
4671            {
4672                Self::push_unsupported_diagnostic(&mut diagnostics, "OVERLAPS");
4673            }
4674
4675            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4676                && Self::node_is_date_bin(node)
4677            {
4678                Self::push_unsupported_diagnostic(&mut diagnostics, "DATE_BIN");
4679            }
4680
4681            if source == DialectType::PostgreSQL
4682                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4683                && Self::node_is_unresolved_postgres_date_subtraction(node)
4684            {
4685                Self::push_unsupported_diagnostic(
4686                    &mut diagnostics,
4687                    "PostgreSQL date subtraction with an unresolved column type",
4688                );
4689            }
4690
4691            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4692                && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
4693            {
4694                if Self::node_is_postgres_json_build_object(node)
4695                    && !(matches!(target, DialectType::TSQL | DialectType::Fabric)
4696                        && Self::postgres_json_build_object_can_lower_to_json_object(node))
4697                {
4698                    Self::push_unsupported_diagnostic(
4699                        &mut diagnostics,
4700                        "PostgreSQL JSON_BUILD_OBJECT",
4701                    );
4702                }
4703                if Self::node_is_function_named(node, "TO_TSVECTOR") {
4704                    Self::push_unsupported_diagnostic(&mut diagnostics, "PostgreSQL TO_TSVECTOR");
4705                }
4706                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4707                    if let Some(composite_semantics) =
4708                        Self::postgres_tsql_unsupported_composite_semantics(
4709                            node,
4710                            structural_grouping_tuples.contains(&(node as *const Expression)),
4711                        )
4712                    {
4713                        Self::push_unsupported_diagnostic(
4714                            &mut diagnostics,
4715                            &format!("PostgreSQL {composite_semantics}"),
4716                        );
4717                    }
4718                    if Self::node_is_postgres_unknown_cast(node) {
4719                        Self::push_unsupported_diagnostic(
4720                            &mut diagnostics,
4721                            "PostgreSQL unresolved UNKNOWN casts",
4722                        );
4723                    }
4724                    if let Some(collation_name) =
4725                        Self::postgres_tsql_unsupported_collation_name(node)
4726                    {
4727                        Self::push_unsupported_diagnostic(
4728                            &mut diagnostics,
4729                            &format!("PostgreSQL collation \"{collation_name}\""),
4730                        );
4731                    }
4732                    if let Some(array_semantics) =
4733                        Self::postgres_tsql_unsupported_array_semantics(node)
4734                    {
4735                        Self::push_unsupported_diagnostic(
4736                            &mut diagnostics,
4737                            &format!("PostgreSQL {array_semantics}"),
4738                        );
4739                    }
4740                    if let Some(string_semantics) =
4741                        Self::postgres_tsql_unsupported_string_semantics(node)
4742                    {
4743                        Self::push_unsupported_diagnostic(
4744                            &mut diagnostics,
4745                            &format!("PostgreSQL {string_semantics}"),
4746                        );
4747                    }
4748                    if source == DialectType::PostgreSQL {
4749                        if let Some(binary_semantics) =
4750                            Self::postgres_tsql_unsupported_binary_semantics(node)
4751                        {
4752                            Self::push_unsupported_diagnostic(
4753                                &mut diagnostics,
4754                                &format!("PostgreSQL {binary_semantics}"),
4755                            );
4756                        }
4757                    }
4758                    if let Some(function_name) =
4759                        Self::postgres_tsql_unsupported_function_name(node, target)
4760                    {
4761                        Self::push_unsupported_diagnostic(
4762                            &mut diagnostics,
4763                            &format!("PostgreSQL {function_name}"),
4764                        );
4765                    }
4766                }
4767                if matches!(target, DialectType::TSQL | DialectType::Fabric)
4768                    && Self::node_is_postgres_type_function_cast(node)
4769                {
4770                    Self::push_unsupported_diagnostic(
4771                        &mut diagnostics,
4772                        "PostgreSQL type-name function casts",
4773                    );
4774                }
4775            }
4776
4777            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4778                break;
4779            }
4780        }
4781
4782        if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4783            Self::collect_tsql_unsupported_ordered_sets(expr, &mut diagnostics);
4784            Self::collect_tsql_windows_missing_order(expr, &HashMap::new(), &mut diagnostics);
4785        }
4786
4787        if diagnostics.is_empty() {
4788            return Ok(());
4789        }
4790
4791        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4792            1
4793        } else {
4794            opts.max_unsupported.max(1)
4795        };
4796        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4797        if diagnostics.len() > limit {
4798            messages.push(format!("... and {} more", diagnostics.len() - limit));
4799        }
4800
4801        Err(crate::error::Error::unsupported(
4802            messages.join("; "),
4803            target.to_string(),
4804        ))
4805    }
4806
4807    fn reject_postgres_tsql_strict_regex_predicates(
4808        expr: &Expression,
4809        source: DialectType,
4810        target: DialectType,
4811        opts: &TranspileOptions,
4812    ) -> Result<()> {
4813        if !matches!(
4814            opts.unsupported_level,
4815            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4816        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4817            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4818        {
4819            return Ok(());
4820        }
4821
4822        if expr.dfs().any(Self::node_is_regex_predicate) {
4823            return Err(crate::error::Error::unsupported(
4824                "regular expression predicates",
4825                target.to_string(),
4826            ));
4827        }
4828
4829        Ok(())
4830    }
4831
4832    fn reject_tsql_strict_json_constructor_return_types(
4833        expr: &Expression,
4834        source: DialectType,
4835        target: DialectType,
4836        opts: &TranspileOptions,
4837    ) -> Result<()> {
4838        if !matches!(
4839            opts.unsupported_level,
4840            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4841        ) || source == target
4842            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4843        {
4844            return Ok(());
4845        }
4846
4847        let mut diagnostics = Vec::new();
4848        for node in expr.dfs() {
4849            if let Some(return_type) =
4850                normalization::unsupported_tsql_json_constructor_return_type(node)
4851            {
4852                let message =
4853                    format!("SQL/JSON constructor RETURNING {return_type} cannot be preserved");
4854                Self::push_unsupported_diagnostic(&mut diagnostics, &message);
4855                if opts.unsupported_level == UnsupportedLevel::Immediate {
4856                    break;
4857                }
4858            }
4859        }
4860
4861        if diagnostics.is_empty() {
4862            return Ok(());
4863        }
4864
4865        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4866            1
4867        } else {
4868            opts.max_unsupported.max(1)
4869        };
4870        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4871        if diagnostics.len() > limit {
4872            messages.push(format!("... and {} more", diagnostics.len() - limit));
4873        }
4874
4875        Err(crate::error::Error::unsupported(
4876            messages.join("; "),
4877            target.to_string(),
4878        ))
4879    }
4880
4881    fn reject_postgres_tsql_strict_json_aggregate_modifiers(
4882        expr: &Expression,
4883        source: DialectType,
4884        target: DialectType,
4885        opts: &TranspileOptions,
4886    ) -> Result<()> {
4887        if !matches!(
4888            opts.unsupported_level,
4889            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4890        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4891            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4892        {
4893            return Ok(());
4894        }
4895
4896        let mut diagnostics = Vec::new();
4897        for node in expr.dfs() {
4898            match node {
4899                Expression::Function(function)
4900                    if !function.quoted
4901                        && matches!(
4902                            function.name.to_ascii_uppercase().as_str(),
4903                            "JSON_AGG" | "JSONB_AGG"
4904                        ) =>
4905                {
4906                    let name = function.name.to_ascii_uppercase();
4907                    if function.args.len() != 1 {
4908                        Self::push_unsupported_diagnostic(
4909                            &mut diagnostics,
4910                            &format!("PostgreSQL {name} with invalid argument count"),
4911                        );
4912                    }
4913                    if function.distinct {
4914                        Self::push_unsupported_diagnostic(
4915                            &mut diagnostics,
4916                            &format!("PostgreSQL {name} with DISTINCT"),
4917                        );
4918                    }
4919                }
4920                Expression::AggregateFunction(function)
4921                    if matches!(
4922                        function.name.to_ascii_uppercase().as_str(),
4923                        "JSON_AGG" | "JSONB_AGG"
4924                    ) =>
4925                {
4926                    let name = function.name.to_ascii_uppercase();
4927                    if function.args.len() != 1 {
4928                        Self::push_unsupported_diagnostic(
4929                            &mut diagnostics,
4930                            &format!("PostgreSQL {name} with invalid argument count"),
4931                        );
4932                    }
4933                    if function.distinct {
4934                        Self::push_unsupported_diagnostic(
4935                            &mut diagnostics,
4936                            &format!("PostgreSQL {name} with DISTINCT"),
4937                        );
4938                    }
4939                    if function.filter.is_some() {
4940                        Self::push_unsupported_diagnostic(
4941                            &mut diagnostics,
4942                            &format!("PostgreSQL {name} with FILTER"),
4943                        );
4944                    }
4945                    if function.limit.is_some() || function.ignore_nulls.is_some() {
4946                        Self::push_unsupported_diagnostic(
4947                            &mut diagnostics,
4948                            &format!("PostgreSQL {name} with unsupported aggregate modifiers"),
4949                        );
4950                    }
4951                }
4952                Expression::Filter(filter) => {
4953                    if let Some(name) = Self::postgres_json_aggregate_name(&filter.this) {
4954                        Self::push_unsupported_diagnostic(
4955                            &mut diagnostics,
4956                            &format!("PostgreSQL {name} with FILTER"),
4957                        );
4958                    }
4959                }
4960                _ => {}
4961            }
4962
4963            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4964                break;
4965            }
4966        }
4967
4968        if diagnostics.is_empty() {
4969            return Ok(());
4970        }
4971
4972        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4973            1
4974        } else {
4975            opts.max_unsupported.max(1)
4976        };
4977        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4978        if diagnostics.len() > limit {
4979            messages.push(format!("... and {} more", diagnostics.len() - limit));
4980        }
4981
4982        Err(crate::error::Error::unsupported(
4983            messages.join("; "),
4984            target.to_string(),
4985        ))
4986    }
4987
4988    fn postgres_json_aggregate_name(expr: &Expression) -> Option<String> {
4989        let name = match expr {
4990            Expression::Function(function) if !function.quoted => &function.name,
4991            Expression::AggregateFunction(function) => &function.name,
4992            _ => return None,
4993        };
4994        let name = name.to_ascii_uppercase();
4995        matches!(name.as_str(), "JSON_AGG" | "JSONB_AGG").then_some(name)
4996    }
4997
4998    fn push_unsupported_diagnostic(diagnostics: &mut Vec<String>, message: &str) {
4999        if !diagnostics.iter().any(|existing| existing == message) {
5000            diagnostics.push(message.to_string());
5001        }
5002    }
5003
5004    fn node_is_unresolved_postgres_date_subtraction(expr: &Expression) -> bool {
5005        let Expression::Sub(op) = expr else {
5006            return false;
5007        };
5008
5009        (Self::is_explicit_date_expr(&op.left) && Self::is_column_expr(&op.right))
5010            || (Self::is_column_expr(&op.left) && Self::is_explicit_date_expr(&op.right))
5011    }
5012
5013    fn is_column_expr(expr: &Expression) -> bool {
5014        match expr {
5015            Expression::Column(_) => true,
5016            Expression::Paren(paren) => Self::is_column_expr(&paren.this),
5017            _ => false,
5018        }
5019    }
5020
5021    fn node_window_frame(expr: &Expression) -> Option<&WindowFrame> {
5022        match expr {
5023            Expression::WindowFunction(window) => window.over.frame.as_ref(),
5024            Expression::Window(window) | Expression::WindowSpec(window) => window.frame.as_ref(),
5025            _ => None,
5026        }
5027    }
5028
5029    fn window_frame_bound_has_value_offset(bound: &WindowFrameBound) -> bool {
5030        matches!(
5031            bound,
5032            WindowFrameBound::Preceding(_)
5033                | WindowFrameBound::Following(_)
5034                | WindowFrameBound::Value(_)
5035                | WindowFrameBound::BarePreceding
5036                | WindowFrameBound::BareFollowing
5037        )
5038    }
5039
5040    fn collect_tsql_windows_missing_order(
5041        expr: &Expression,
5042        active_windows: &HashMap<String, Over>,
5043        diagnostics: &mut Vec<String>,
5044    ) {
5045        if let Expression::Select(select) = expr {
5046            let local_windows = select
5047                .windows
5048                .as_ref()
5049                .map(|windows| {
5050                    windows
5051                        .iter()
5052                        .map(|window| (window.name.name.to_ascii_lowercase(), window.spec.clone()))
5053                        .collect()
5054                })
5055                .unwrap_or_default();
5056
5057            for child in expr.children() {
5058                Self::collect_tsql_windows_missing_order(child, &local_windows, diagnostics);
5059            }
5060            return;
5061        }
5062
5063        if let Expression::WindowFunction(window) = expr {
5064            let (has_order, has_frame) = Self::effective_window_order_and_frame(
5065                &window.over,
5066                active_windows,
5067                &mut Vec::new(),
5068            );
5069
5070            if !has_order {
5071                if has_frame {
5072                    Self::push_unsupported_diagnostic(
5073                        diagnostics,
5074                        "window frames without ORDER BY",
5075                    );
5076                }
5077                if let Some(function_name) =
5078                    Self::tsql_window_function_requiring_order(&window.this)
5079                {
5080                    Self::push_unsupported_diagnostic(
5081                        diagnostics,
5082                        &format!("{function_name} without ORDER BY"),
5083                    );
5084                }
5085            }
5086        }
5087
5088        for child in expr.children() {
5089            Self::collect_tsql_windows_missing_order(child, active_windows, diagnostics);
5090        }
5091    }
5092
5093    fn effective_window_order_and_frame(
5094        over: &Over,
5095        active_windows: &HashMap<String, Over>,
5096        seen: &mut Vec<String>,
5097    ) -> (bool, bool) {
5098        let inherited = over
5099            .window_name
5100            .as_ref()
5101            .and_then(|name| {
5102                let key = name.name.to_ascii_lowercase();
5103                if seen.iter().any(|seen_name| seen_name == &key) {
5104                    return None;
5105                }
5106                let named = active_windows.get(&key)?;
5107                seen.push(key);
5108                let properties =
5109                    Self::effective_window_order_and_frame(named, active_windows, seen);
5110                seen.pop();
5111                Some(properties)
5112            })
5113            .unwrap_or((false, false));
5114
5115        (
5116            !over.order_by.is_empty() || inherited.0,
5117            over.frame.is_some() || inherited.1,
5118        )
5119    }
5120
5121    fn tsql_window_function_requiring_order(expr: &Expression) -> Option<&'static str> {
5122        match expr {
5123            Expression::FirstValue(_) => Some("FIRST_VALUE"),
5124            Expression::LastValue(_) => Some("LAST_VALUE"),
5125            Expression::Function(function) if function.name.eq_ignore_ascii_case("FIRST_VALUE") => {
5126                Some("FIRST_VALUE")
5127            }
5128            Expression::Function(function) if function.name.eq_ignore_ascii_case("LAST_VALUE") => {
5129                Some("LAST_VALUE")
5130            }
5131            _ => None,
5132        }
5133    }
5134
5135    fn collect_tsql_unsupported_ordered_sets(expr: &Expression, diagnostics: &mut Vec<String>) {
5136        match expr {
5137            Expression::WindowFunction(window) => {
5138                if let Expression::WithinGroup(within_group) = &window.this {
5139                    if Self::within_group_is_hypothetical_set(within_group) {
5140                        Self::push_unsupported_diagnostic(
5141                            diagnostics,
5142                            "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
5143                        );
5144                        return;
5145                    }
5146
5147                    if Self::within_group_is_mode(within_group) {
5148                        Self::push_unsupported_diagnostic(
5149                            diagnostics,
5150                            "MODE ordered-set aggregates",
5151                        );
5152                        return;
5153                    }
5154
5155                    if Self::within_group_is_percentile(within_group) {
5156                        if !window.over.order_by.is_empty() || window.over.frame.is_some() {
5157                            Self::push_unsupported_diagnostic(
5158                                diagnostics,
5159                                "PERCENTILE_CONT/PERCENTILE_DISC window ORDER BY or frame clauses",
5160                            );
5161                        }
5162                        return;
5163                    }
5164                }
5165            }
5166            Expression::WithinGroup(within_group) => {
5167                if Self::within_group_is_hypothetical_set(within_group) {
5168                    Self::push_unsupported_diagnostic(
5169                        diagnostics,
5170                        "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
5171                    );
5172                    return;
5173                }
5174
5175                if Self::within_group_is_mode(within_group) {
5176                    Self::push_unsupported_diagnostic(diagnostics, "MODE ordered-set aggregates");
5177                    return;
5178                }
5179
5180                if Self::within_group_is_percentile(within_group) {
5181                    Self::push_unsupported_diagnostic(
5182                        diagnostics,
5183                        "PERCENTILE_CONT/PERCENTILE_DISC ordered-set aggregates without OVER",
5184                    );
5185                    return;
5186                }
5187            }
5188            _ => {}
5189        }
5190
5191        for child in expr.children() {
5192            Self::collect_tsql_unsupported_ordered_sets(child, diagnostics);
5193        }
5194    }
5195
5196    fn within_group_is_hypothetical_set(within_group: &crate::expressions::WithinGroup) -> bool {
5197        match &within_group.this {
5198            Expression::Function(function) => Self::is_hypothetical_set_name(&function.name),
5199            Expression::AggregateFunction(function) => {
5200                Self::is_hypothetical_set_name(&function.name)
5201            }
5202            Expression::Rank(_)
5203            | Expression::DenseRank(_)
5204            | Expression::CumeDist(_)
5205            | Expression::PercentRank(_) => true,
5206            _ => false,
5207        }
5208    }
5209
5210    fn within_group_is_percentile(within_group: &crate::expressions::WithinGroup) -> bool {
5211        match &within_group.this {
5212            Expression::Function(function) => Self::is_percentile_ordered_set_name(&function.name),
5213            Expression::AggregateFunction(function) => {
5214                Self::is_percentile_ordered_set_name(&function.name)
5215            }
5216            Expression::PercentileCont(_) | Expression::PercentileDisc(_) => true,
5217            _ => false,
5218        }
5219    }
5220
5221    fn within_group_is_mode(within_group: &crate::expressions::WithinGroup) -> bool {
5222        match &within_group.this {
5223            Expression::Function(function) => function.name.eq_ignore_ascii_case("MODE"),
5224            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case("MODE"),
5225            Expression::Mode(_) => true,
5226            _ => false,
5227        }
5228    }
5229
5230    fn is_percentile_ordered_set_name(name: &str) -> bool {
5231        name.eq_ignore_ascii_case("PERCENTILE_CONT") || name.eq_ignore_ascii_case("PERCENTILE_DISC")
5232    }
5233
5234    fn is_hypothetical_set_name(name: &str) -> bool {
5235        name.eq_ignore_ascii_case("RANK")
5236            || name.eq_ignore_ascii_case("DENSE_RANK")
5237            || name.eq_ignore_ascii_case("CUME_DIST")
5238            || name.eq_ignore_ascii_case("PERCENT_RANK")
5239    }
5240
5241    fn target_supports_distinct_on(target: DialectType) -> bool {
5242        matches!(target, DialectType::PostgreSQL | DialectType::DuckDB)
5243    }
5244
5245    fn node_has_distinct_on(expr: &Expression) -> bool {
5246        matches!(
5247            expr,
5248            Expression::Select(select)
5249                if select
5250                    .distinct_on
5251                    .as_ref()
5252                    .is_some_and(|distinct_on| !distinct_on.is_empty())
5253        )
5254    }
5255
5256    fn node_has_recursive_with(expr: &Expression) -> bool {
5257        fn recursive(with: &Option<With>) -> bool {
5258            with.as_ref().is_some_and(|with| with.recursive)
5259        }
5260
5261        match expr {
5262            Expression::With(with) => with.recursive,
5263            Expression::Select(select) => recursive(&select.with),
5264            Expression::Union(union) => recursive(&union.with),
5265            Expression::Intersect(intersect) => recursive(&intersect.with),
5266            Expression::Except(except) => recursive(&except.with),
5267            Expression::Pivot(pivot) => recursive(&pivot.with),
5268            Expression::Insert(insert) => recursive(&insert.with),
5269            Expression::Update(update) => recursive(&update.with),
5270            Expression::Delete(delete) => recursive(&delete.with),
5271            _ => false,
5272        }
5273    }
5274
5275    fn node_has_lateral(expr: &Expression) -> bool {
5276        fn join_has_lateral(join: &Join) -> bool {
5277            matches!(
5278                join.kind,
5279                crate::expressions::JoinKind::Lateral | crate::expressions::JoinKind::LeftLateral
5280            ) || Dialect::node_has_lateral(&join.this)
5281                || join.on.as_ref().is_some_and(Dialect::node_has_lateral)
5282                || join
5283                    .match_condition
5284                    .as_ref()
5285                    .is_some_and(Dialect::node_has_lateral)
5286                || join.pivots.iter().any(Dialect::node_has_lateral)
5287        }
5288
5289        fn joins_have_lateral(joins: &[Join]) -> bool {
5290            joins.iter().any(join_has_lateral)
5291        }
5292
5293        match expr {
5294            Expression::Subquery(subquery) => {
5295                subquery.lateral || Dialect::node_has_lateral(&subquery.this)
5296            }
5297            Expression::Lateral(_) | Expression::LateralView(_) => true,
5298            Expression::Join(join) => join_has_lateral(join),
5299            Expression::Select(select) => {
5300                !select.lateral_views.is_empty()
5301                    || joins_have_lateral(&select.joins)
5302                    || select
5303                        .from
5304                        .as_ref()
5305                        .is_some_and(|from| from.expressions.iter().any(Dialect::node_has_lateral))
5306            }
5307            Expression::JoinedTable(joined) => {
5308                !joined.lateral_views.is_empty()
5309                    || Dialect::node_has_lateral(&joined.left)
5310                    || joins_have_lateral(&joined.joins)
5311            }
5312            Expression::Update(update) => {
5313                joins_have_lateral(&update.table_joins) || joins_have_lateral(&update.from_joins)
5314            }
5315            _ => false,
5316        }
5317    }
5318
5319    fn node_has_join_using(expr: &Expression) -> bool {
5320        fn has_using(joins: &[Join]) -> bool {
5321            joins.iter().any(|join| !join.using.is_empty())
5322        }
5323
5324        match expr {
5325            Expression::Join(join) => !join.using.is_empty(),
5326            Expression::Select(select) => has_using(&select.joins),
5327            Expression::JoinedTable(joined) => has_using(&joined.joins),
5328            Expression::Update(update) => {
5329                has_using(&update.table_joins) || has_using(&update.from_joins)
5330            }
5331            Expression::Delete(delete) => has_using(&delete.joins),
5332            _ => false,
5333        }
5334    }
5335
5336    fn node_has_natural_join(expr: &Expression) -> bool {
5337        fn is_natural(join: &Join) -> bool {
5338            matches!(
5339                join.kind,
5340                crate::expressions::JoinKind::Natural
5341                    | crate::expressions::JoinKind::NaturalLeft
5342                    | crate::expressions::JoinKind::NaturalRight
5343                    | crate::expressions::JoinKind::NaturalFull
5344            )
5345        }
5346
5347        fn has_natural(joins: &[Join]) -> bool {
5348            joins.iter().any(is_natural)
5349        }
5350
5351        match expr {
5352            Expression::Join(join) => is_natural(join),
5353            Expression::Select(select) => has_natural(&select.joins),
5354            Expression::JoinedTable(joined) => has_natural(&joined.joins),
5355            Expression::Update(update) => {
5356                has_natural(&update.table_joins) || has_natural(&update.from_joins)
5357            }
5358            Expression::Delete(delete) => has_natural(&delete.joins),
5359            _ => false,
5360        }
5361    }
5362
5363    fn node_has_unsupported_relation_column_aliases(expr: &Expression) -> bool {
5364        match expr {
5365            Expression::Table(table) => !table.column_aliases.is_empty(),
5366            Expression::Alias(alias) => {
5367                !alias.column_aliases.is_empty()
5368                    && matches!(
5369                        alias.this,
5370                        Expression::Table(_) | Expression::JoinedTable(_)
5371                    )
5372            }
5373            _ => false,
5374        }
5375    }
5376
5377    fn node_is_aggregate_function(expr: &Expression) -> bool {
5378        is_aggregate(expr)
5379    }
5380
5381    fn node_has_qualified_whole_row_aggregate_argument(expr: &Expression) -> bool {
5382        fn contains_qualified_star(expr: &Expression) -> bool {
5383            match expr {
5384                Expression::Star(star) => star.table.is_some(),
5385                // A star projected by an embedded query is not an argument of
5386                // the surrounding aggregate (for example, inside EXISTS).
5387                Expression::Select(_)
5388                | Expression::Subquery(_)
5389                | Expression::Union(_)
5390                | Expression::Intersect(_)
5391                | Expression::Except(_) => false,
5392                _ => expr.children().into_iter().any(contains_qualified_star),
5393            }
5394        }
5395
5396        Self::node_is_aggregate_function(expr)
5397            && expr.children().into_iter().any(contains_qualified_star)
5398    }
5399
5400    fn node_has_subquery_in_aggregate_argument(expr: &Expression) -> bool {
5401        fn contains_query(expr: &Expression) -> bool {
5402            matches!(
5403                expr,
5404                Expression::Select(_)
5405                    | Expression::Subquery(_)
5406                    | Expression::Union(_)
5407                    | Expression::Intersect(_)
5408                    | Expression::Except(_)
5409            ) || expr.children().into_iter().any(contains_query)
5410        }
5411
5412        Self::node_is_aggregate_function(expr) && expr.children().into_iter().any(contains_query)
5413    }
5414
5415    fn tsql_apply_has_invalid_outer_aggregate(expr: &Expression) -> bool {
5416        // SQL Server error 8124: if an aggregate expression contains an outer
5417        // reference, that reference must be the only column used by the expression.
5418        fn collect_source_names(expr: &Expression, names: &mut HashSet<String>) {
5419            let mut insert = |name: &Identifier| {
5420                if !name.name.is_empty() {
5421                    names.insert(name.name.to_ascii_lowercase());
5422                }
5423            };
5424
5425            match expr {
5426                Expression::Table(table) => {
5427                    insert(table.alias.as_ref().unwrap_or(&table.name));
5428                }
5429                Expression::Subquery(subquery) => {
5430                    if let Some(alias) = &subquery.alias {
5431                        insert(alias);
5432                    }
5433                }
5434                Expression::Alias(alias) => insert(&alias.alias),
5435                Expression::JoinedTable(joined) => {
5436                    if let Some(alias) = &joined.alias {
5437                        insert(alias);
5438                    } else {
5439                        collect_source_names(&joined.left, names);
5440                        for join in &joined.joins {
5441                            collect_source_names(&join.this, names);
5442                        }
5443                    }
5444                }
5445                Expression::Paren(paren) => collect_source_names(&paren.this, names),
5446                Expression::Pivot(pivot) => {
5447                    if let Some(alias) = &pivot.alias {
5448                        insert(alias);
5449                    } else {
5450                        collect_source_names(&pivot.this, names);
5451                    }
5452                }
5453                Expression::Unpivot(unpivot) => {
5454                    if let Some(alias) = &unpivot.alias {
5455                        insert(alias);
5456                    } else {
5457                        collect_source_names(&unpivot.this, names);
5458                    }
5459                }
5460                _ => {}
5461            }
5462        }
5463
5464        fn collect_columns<'a>(
5465            expr: &'a Expression,
5466            columns: &mut Vec<&'a crate::expressions::Column>,
5467        ) {
5468            match expr {
5469                Expression::Column(column) => columns.push(column),
5470                // Query expressions introduce a new name-resolution scope. Their
5471                // columns are checked when their own SELECT node is visited.
5472                Expression::Select(_)
5473                | Expression::Subquery(_)
5474                | Expression::Union(_)
5475                | Expression::Intersect(_)
5476                | Expression::Except(_) => {}
5477                _ => {
5478                    for child in expr.children() {
5479                        collect_columns(child, columns);
5480                    }
5481                }
5482            }
5483        }
5484
5485        fn same_column(
5486            left: &crate::expressions::Column,
5487            right: &crate::expressions::Column,
5488        ) -> bool {
5489            let same_identifier =
5490                |left: &Identifier, right: &Identifier| left.name.eq_ignore_ascii_case(&right.name);
5491
5492            same_identifier(&left.name, &right.name)
5493                && match (&left.table, &right.table) {
5494                    (Some(left), Some(right)) => same_identifier(left, right),
5495                    (None, None) => true,
5496                    _ => false,
5497                }
5498        }
5499
5500        fn aggregate_is_invalid(expr: &Expression, local_sources: &HashSet<String>) -> bool {
5501            let mut columns = Vec::new();
5502            collect_columns(expr, &mut columns);
5503
5504            let outer_column = columns.iter().copied().find(|column| match &column.table {
5505                Some(table) => !local_sources.contains(&table.name.to_ascii_lowercase()),
5506                // Without a local source, an unqualified reference in a lateral
5507                // query can only resolve against an outer scope.
5508                None => local_sources.is_empty(),
5509            });
5510
5511            outer_column
5512                .is_some_and(|outer| columns.iter().any(|column| !same_column(outer, column)))
5513        }
5514
5515        fn expression_has_invalid_aggregate(
5516            expr: &Expression,
5517            local_sources: &HashSet<String>,
5518        ) -> bool {
5519            if Dialect::node_is_aggregate_function(expr)
5520                && aggregate_is_invalid(expr, local_sources)
5521            {
5522                return true;
5523            }
5524
5525            match expr {
5526                Expression::Select(_)
5527                | Expression::Subquery(_)
5528                | Expression::Union(_)
5529                | Expression::Intersect(_)
5530                | Expression::Except(_) => false,
5531                _ => expr
5532                    .children()
5533                    .into_iter()
5534                    .any(|child| expression_has_invalid_aggregate(child, local_sources)),
5535            }
5536        }
5537
5538        fn select_has_invalid_aggregate(select: &Select) -> bool {
5539            let mut local_sources = HashSet::new();
5540            if let Some(from) = &select.from {
5541                for source in &from.expressions {
5542                    collect_source_names(source, &mut local_sources);
5543                }
5544            }
5545            for join in &select.joins {
5546                collect_source_names(&join.this, &mut local_sources);
5547            }
5548
5549            let invalid =
5550                |expr: &Expression| expression_has_invalid_aggregate(expr, &local_sources);
5551
5552            select.expressions.iter().any(invalid)
5553                || select.prewhere.as_ref().is_some_and(invalid)
5554                || select
5555                    .where_clause
5556                    .as_ref()
5557                    .is_some_and(|where_clause| invalid(&where_clause.this))
5558                || select
5559                    .group_by
5560                    .as_ref()
5561                    .is_some_and(|group_by| group_by.expressions.iter().any(invalid))
5562                || select
5563                    .having
5564                    .as_ref()
5565                    .is_some_and(|having| invalid(&having.this))
5566                || select
5567                    .qualify
5568                    .as_ref()
5569                    .is_some_and(|qualify| invalid(&qualify.this))
5570                || select.order_by.as_ref().is_some_and(|order_by| {
5571                    order_by
5572                        .expressions
5573                        .iter()
5574                        .any(|ordered| invalid(&ordered.this))
5575                })
5576        }
5577
5578        fn apply_rhs_is_invalid(rhs: &Expression) -> bool {
5579            rhs.dfs().any(|node| match node {
5580                Expression::Select(select) => select_has_invalid_aggregate(select),
5581                _ => false,
5582            })
5583        }
5584
5585        fn joins_have_invalid_aggregate(joins: &[Join]) -> bool {
5586            joins.iter().any(|join| {
5587                matches!(
5588                    join.kind,
5589                    crate::expressions::JoinKind::CrossApply
5590                        | crate::expressions::JoinKind::OuterApply
5591                ) && apply_rhs_is_invalid(&join.this)
5592            })
5593        }
5594
5595        expr.dfs().any(|node| match node {
5596            Expression::Select(select) => joins_have_invalid_aggregate(&select.joins),
5597            Expression::JoinedTable(joined) => joins_have_invalid_aggregate(&joined.joins),
5598            Expression::Update(update) => {
5599                joins_have_invalid_aggregate(&update.table_joins)
5600                    || joins_have_invalid_aggregate(&update.from_joins)
5601            }
5602            Expression::Delete(delete) => joins_have_invalid_aggregate(&delete.joins),
5603            _ => false,
5604        })
5605    }
5606
5607    fn target_supports_remaining_unnest(target: DialectType) -> bool {
5608        matches!(
5609            target,
5610            DialectType::PostgreSQL
5611                | DialectType::BigQuery
5612                | DialectType::DuckDB
5613                | DialectType::Presto
5614                | DialectType::Trino
5615                | DialectType::Athena
5616        )
5617    }
5618
5619    fn target_supports_remaining_explode(target: DialectType) -> bool {
5620        matches!(
5621            target,
5622            DialectType::Spark | DialectType::Databricks | DialectType::Hive
5623        )
5624    }
5625
5626    fn target_lacks_array_agg(target: DialectType) -> bool {
5627        matches!(
5628            target,
5629            DialectType::Fabric
5630                | DialectType::TSQL
5631                | DialectType::MySQL
5632                | DialectType::SQLite
5633                | DialectType::Oracle
5634        )
5635    }
5636
5637    fn node_is_unnest(expr: &Expression) -> bool {
5638        matches!(expr, Expression::Unnest(_)) || Self::node_is_function_named(expr, "UNNEST")
5639    }
5640
5641    fn node_is_explode(expr: &Expression) -> bool {
5642        matches!(expr, Expression::Explode(_) | Expression::ExplodeOuter(_))
5643            || Self::node_is_function_named(expr, "EXPLODE")
5644            || Self::node_is_function_named(expr, "EXPLODE_OUTER")
5645    }
5646
5647    fn node_is_array_agg(expr: &Expression) -> bool {
5648        matches!(expr, Expression::ArrayAgg(_)) || Self::node_is_function_named(expr, "ARRAY_AGG")
5649    }
5650
5651    fn node_is_distinct_string_agg(expr: &Expression) -> bool {
5652        match expr {
5653            Expression::StringAgg(agg) => agg.distinct,
5654            Expression::Function(function) => {
5655                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
5656            }
5657            Expression::AggregateFunction(function) => {
5658                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
5659            }
5660            _ => false,
5661        }
5662    }
5663
5664    fn postgres_tsql_unsupported_collation_name(expr: &Expression) -> Option<&'static str> {
5665        let Expression::Collation(collation) = expr else {
5666            return None;
5667        };
5668
5669        if collation.collation.eq_ignore_ascii_case("C") {
5670            Some("C")
5671        } else if collation.collation.eq_ignore_ascii_case("POSIX") {
5672            Some("POSIX")
5673        } else {
5674            None
5675        }
5676    }
5677
5678    fn collect_tsql_grouping_tuple_nodes(expr: &Expression) -> HashSet<*const Expression> {
5679        let mut tuples = HashSet::new();
5680
5681        for node in expr.dfs() {
5682            let Expression::Select(select) = node else {
5683                continue;
5684            };
5685            let Some(group_by) = &select.group_by else {
5686                continue;
5687            };
5688
5689            for expression in &group_by.expressions {
5690                Self::collect_tsql_grouping_element_tuples(expression, &mut tuples);
5691            }
5692        }
5693
5694        tuples
5695    }
5696
5697    fn collect_tsql_grouping_element_tuples(
5698        expr: &Expression,
5699        tuples: &mut HashSet<*const Expression>,
5700    ) {
5701        match expr {
5702            Expression::GroupingSets(grouping_sets) => {
5703                for expression in &grouping_sets.expressions {
5704                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5705                }
5706            }
5707            Expression::Rollup(rollup) => {
5708                for expression in &rollup.expressions {
5709                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5710                }
5711            }
5712            Expression::Cube(cube) => {
5713                for expression in &cube.expressions {
5714                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5715                }
5716            }
5717            Expression::Function(function)
5718                if !function.quoted
5719                    && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5720                        || function.name.eq_ignore_ascii_case("ROLLUP")
5721                        || function.name.eq_ignore_ascii_case("CUBE")) =>
5722            {
5723                for expression in &function.args {
5724                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5725                }
5726            }
5727            _ => {}
5728        }
5729    }
5730
5731    fn collect_tsql_grouping_unit_tuples(
5732        expr: &Expression,
5733        tuples: &mut HashSet<*const Expression>,
5734    ) {
5735        match expr {
5736            Expression::Tuple(tuple) => {
5737                tuples.insert(expr as *const Expression);
5738                for expression in &tuple.expressions {
5739                    match expression {
5740                        Expression::Tuple(_) | Expression::Paren(_) => {
5741                            Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5742                        }
5743                        Expression::GroupingSets(_)
5744                        | Expression::Rollup(_)
5745                        | Expression::Cube(_) => {
5746                            Self::collect_tsql_grouping_element_tuples(expression, tuples);
5747                        }
5748                        Expression::Function(function)
5749                            if !function.quoted
5750                                && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5751                                    || function.name.eq_ignore_ascii_case("ROLLUP")
5752                                    || function.name.eq_ignore_ascii_case("CUBE")) =>
5753                        {
5754                            Self::collect_tsql_grouping_element_tuples(expression, tuples);
5755                        }
5756                        _ => {}
5757                    }
5758                }
5759            }
5760            Expression::Paren(paren) => {
5761                Self::collect_tsql_grouping_unit_tuples(&paren.this, tuples);
5762            }
5763            Expression::GroupingSets(_) | Expression::Rollup(_) | Expression::Cube(_) => {
5764                Self::collect_tsql_grouping_element_tuples(expr, tuples);
5765            }
5766            Expression::Function(function)
5767                if !function.quoted
5768                    && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5769                        || function.name.eq_ignore_ascii_case("ROLLUP")
5770                        || function.name.eq_ignore_ascii_case("CUBE")) =>
5771            {
5772                Self::collect_tsql_grouping_element_tuples(expr, tuples);
5773            }
5774            _ => {}
5775        }
5776    }
5777
5778    fn postgres_tsql_unsupported_composite_semantics(
5779        expr: &Expression,
5780        structural_grouping_tuple: bool,
5781    ) -> Option<&'static str> {
5782        match expr {
5783            Expression::Tuple(_) if !structural_grouping_tuple => Some("row/composite values"),
5784            Expression::Struct(_) | Expression::StructFunc(_) => Some("row/composite values"),
5785            Expression::Function(function)
5786                if !function.quoted && function.name.eq_ignore_ascii_case("ROW") =>
5787            {
5788                Some("row/composite values")
5789            }
5790            Expression::StructExtract(_) => Some("row/composite field access"),
5791            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) if matches!(&cast.this, Expression::Star(star) if star.table.is_some()) => {
5792                Some("qualified whole-row casts")
5793            }
5794            _ => None,
5795        }
5796    }
5797
5798    fn node_is_postgres_unknown_cast(expr: &Expression) -> bool {
5799        match expr {
5800            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
5801                normalization::is_postgres_unknown_type(&cast.to)
5802            }
5803            _ => false,
5804        }
5805    }
5806
5807    fn postgres_tsql_unsupported_array_semantics(expr: &Expression) -> Option<&'static str> {
5808        match expr {
5809            Expression::Array(_) | Expression::ArrayFunc(_) => Some("array literals"),
5810            Expression::Subscript(_) => Some("array subscripts"),
5811            Expression::ArraySlice(_) => Some("array slices"),
5812            Expression::DataType(DataType::Array { .. }) => Some("array data types"),
5813            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
5814                if matches!(&cast.to, DataType::Array { .. }) =>
5815            {
5816                Some("array data types")
5817            }
5818            Expression::ArrayLength(_) | Expression::ArraySize(_) => Some("ARRAY_LENGTH"),
5819            Expression::Cardinality(_) => Some("CARDINALITY"),
5820            Expression::ArrayToString(_) | Expression::ArrayJoin(_) => Some("ARRAY_TO_STRING"),
5821            Expression::StringToArray(_) => Some("STRING_TO_ARRAY"),
5822            Expression::ArrayContains(_)
5823            | Expression::ArrayPosition(_)
5824            | Expression::ArrayAppend(_)
5825            | Expression::ArrayPrepend(_)
5826            | Expression::ArrayConcat(_)
5827            | Expression::ArraySort(_)
5828            | Expression::ArrayReverse(_)
5829            | Expression::ArrayDistinct(_)
5830            | Expression::ArrayFilter(_)
5831            | Expression::ArrayTransform(_)
5832            | Expression::ArrayFlatten(_)
5833            | Expression::ArrayCompact(_)
5834            | Expression::ArrayIntersect(_)
5835            | Expression::ArrayUnion(_)
5836            | Expression::ArrayExcept(_)
5837            | Expression::ArrayRemove(_)
5838            | Expression::ArrayZip(_)
5839            | Expression::ArrayAll(_)
5840            | Expression::ArrayAny(_)
5841            | Expression::ArrayConstructCompact(_)
5842            | Expression::ArraySum(_) => Some("array functions"),
5843            Expression::ArrayContainsAll(_)
5844            | Expression::ArrayContainedBy(_)
5845            | Expression::ArrayOverlaps(_) => Some("array operators"),
5846            Expression::Function(function) => {
5847                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5848            }
5849            Expression::AggregateFunction(function) => {
5850                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5851            }
5852            _ => None,
5853        }
5854    }
5855
5856    fn postgres_tsql_unsupported_array_function_name_str(name: &str) -> Option<&'static str> {
5857        if name.eq_ignore_ascii_case("ARRAY") {
5858            Some("array literals")
5859        } else if name.eq_ignore_ascii_case("ARRAY_LENGTH")
5860            || name.eq_ignore_ascii_case("ARRAY_SIZE")
5861        {
5862            Some("ARRAY_LENGTH")
5863        } else if name.eq_ignore_ascii_case("CARDINALITY") {
5864            Some("CARDINALITY")
5865        } else if name.eq_ignore_ascii_case("ARRAY_TO_STRING")
5866            || name.eq_ignore_ascii_case("ARRAY_JOIN")
5867        {
5868            Some("ARRAY_TO_STRING")
5869        } else if name.eq_ignore_ascii_case("STRING_TO_ARRAY") {
5870            Some("STRING_TO_ARRAY")
5871        } else {
5872            None
5873        }
5874    }
5875
5876    fn node_is_regex_predicate(expr: &Expression) -> bool {
5877        matches!(
5878            expr,
5879            Expression::SimilarTo(_) | Expression::RegexpLike(_) | Expression::RegexpILike(_)
5880        ) || Self::node_is_function_named(expr, "REGEXP_LIKE")
5881            || Self::node_is_function_named(expr, "REGEXP_I_LIKE")
5882            || Self::node_is_function_named(expr, "REGEXP_ILIKE")
5883    }
5884
5885    fn node_is_non_subquery_any(expr: &Expression) -> bool {
5886        matches!(
5887            expr,
5888            Expression::Any(q) if !Self::quantified_rhs_is_subquery(&q.subquery)
5889        )
5890    }
5891
5892    fn quantified_rhs_is_subquery(expr: &Expression) -> bool {
5893        match expr {
5894            Expression::Select(_) | Expression::Subquery(_) => true,
5895            Expression::Paren(paren) => Self::quantified_rhs_is_subquery(&paren.this),
5896            _ => false,
5897        }
5898    }
5899
5900    fn node_is_row_value_subquery_comparison(expr: &Expression) -> bool {
5901        match expr {
5902            Expression::In(in_expr) => {
5903                Self::in_rhs_is_subquery_like(in_expr) && Self::expr_is_row_value(&in_expr.this)
5904            }
5905            Expression::Eq(op) | Expression::Neq(op) => {
5906                (Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right))
5907                    || (Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left))
5908            }
5909            _ => false,
5910        }
5911    }
5912
5913    fn node_is_row_value_values_membership(expr: &Expression) -> bool {
5914        matches!(
5915            expr,
5916            Expression::In(in_expr)
5917                if Self::expr_is_row_value(&in_expr.this)
5918                    && Self::in_rhs_is_values_like(in_expr)
5919        )
5920    }
5921
5922    fn expr_is_row_value(expr: &Expression) -> bool {
5923        match expr {
5924            Expression::Tuple(tuple) => tuple.expressions.len() > 1,
5925            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
5926                function.args.len() > 1
5927            }
5928            Expression::Paren(paren) => Self::expr_is_row_value(&paren.this),
5929            _ => false,
5930        }
5931    }
5932
5933    fn expr_is_subquery_like(expr: &Expression) -> bool {
5934        match expr {
5935            Expression::Select(_) | Expression::Subquery(_) => true,
5936            Expression::Paren(paren) => Self::expr_is_subquery_like(&paren.this),
5937            _ => false,
5938        }
5939    }
5940
5941    fn in_rhs_is_subquery_like(in_expr: &crate::expressions::In) -> bool {
5942        if in_expr
5943            .query
5944            .as_ref()
5945            .is_some_and(Self::expr_is_subquery_like)
5946        {
5947            return true;
5948        }
5949
5950        in_expr.expressions.len() == 1 && Self::expr_is_subquery_like(&in_expr.expressions[0])
5951    }
5952
5953    fn in_rhs_is_values_like(in_expr: &crate::expressions::In) -> bool {
5954        if in_expr
5955            .query
5956            .as_ref()
5957            .is_some_and(Self::expr_is_values_like)
5958        {
5959            return true;
5960        }
5961
5962        (in_expr.expressions.len() == 1
5963            && Self::expr_is_values_like(&in_expr.expressions[0]))
5964            || in_expr.expressions.first().is_some_and(|expr| {
5965                matches!(expr, Expression::Function(function) if function.name.eq_ignore_ascii_case("VALUES"))
5966            })
5967    }
5968
5969    fn expr_is_values_like(expr: &Expression) -> bool {
5970        match expr {
5971            Expression::Values(_) => true,
5972            Expression::Paren(paren) => Self::expr_is_values_like(&paren.this),
5973            Expression::Subquery(subquery) => Self::expr_is_values_like(&subquery.this),
5974            _ => false,
5975        }
5976    }
5977
5978    fn normalize_tsql_fetch_overlaps_date_bin(expr: Expression) -> Result<Expression> {
5979        transform_recursive(expr, &|e| match e {
5980            Expression::Select(mut select) => {
5981                if select.top.is_none() && select.offset.is_none() {
5982                    if let Some(fetch) = select.fetch.take() {
5983                        if let Some(top) = Self::fetch_with_ties_to_top(fetch.clone()) {
5984                            select.top = Some(top);
5985                        } else {
5986                            select.fetch = Some(fetch);
5987                        }
5988                    }
5989                }
5990                Self::rewrite_tsql_overlaps_in_select_predicates(&mut select)?;
5991                Ok(Expression::Select(select))
5992            }
5993            Expression::DateBin(date_bin) => {
5994                let date_bin = *date_bin;
5995                if let Some(rewritten) = Self::date_bin_to_date_bucket(date_bin.clone()) {
5996                    Ok(rewritten)
5997                } else {
5998                    Ok(Expression::DateBin(Box::new(date_bin)))
5999                }
6000            }
6001            Expression::Function(function) => {
6002                let function = *function;
6003                if function.name.eq_ignore_ascii_case("DATE_BIN") {
6004                    if let Some(rewritten) = Self::date_bin_function_to_date_bucket(&function) {
6005                        Ok(rewritten)
6006                    } else {
6007                        Ok(Expression::Function(Box::new(function)))
6008                    }
6009                } else {
6010                    Ok(Expression::Function(Box::new(function)))
6011                }
6012            }
6013            _ => Ok(e),
6014        })
6015    }
6016
6017    fn rewrite_tsql_overlaps_in_select_predicates(
6018        select: &mut crate::expressions::Select,
6019    ) -> Result<()> {
6020        if let Some(where_clause) = &mut select.where_clause {
6021            where_clause.this = Self::rewrite_tsql_overlaps_predicate(where_clause.this.clone())?;
6022        }
6023        if let Some(having) = &mut select.having {
6024            having.this = Self::rewrite_tsql_overlaps_predicate(having.this.clone())?;
6025        }
6026        if let Some(qualify) = &mut select.qualify {
6027            qualify.this = Self::rewrite_tsql_overlaps_predicate(qualify.this.clone())?;
6028        }
6029        for join in &mut select.joins {
6030            if let Some(on) = join.on.take() {
6031                join.on = Some(Self::rewrite_tsql_overlaps_predicate(on)?);
6032            }
6033            if let Some(match_condition) = join.match_condition.take() {
6034                join.match_condition =
6035                    Some(Self::rewrite_tsql_overlaps_predicate(match_condition)?);
6036            }
6037        }
6038        Ok(())
6039    }
6040
6041    fn rewrite_tsql_overlaps_predicate(expr: Expression) -> Result<Expression> {
6042        transform_recursive(expr, &|e| match e {
6043            Expression::Overlaps(overlaps) => {
6044                let overlaps = *overlaps;
6045                if let Some(rewritten) = Self::rewrite_full_overlaps_for_tsql(&overlaps) {
6046                    Ok(rewritten)
6047                } else {
6048                    Ok(Expression::Overlaps(Box::new(overlaps)))
6049                }
6050            }
6051            _ => Ok(e),
6052        })
6053    }
6054
6055    fn fetch_with_ties_to_top(fetch: Fetch) -> Option<Top> {
6056        if !fetch.with_ties {
6057            return None;
6058        }
6059
6060        fetch.count.map(|count| Top {
6061            this: count,
6062            percent: fetch.percent,
6063            with_ties: true,
6064            parenthesized: true,
6065        })
6066    }
6067
6068    fn rewrite_full_overlaps_for_tsql(
6069        overlaps: &crate::expressions::OverlapsExpr,
6070    ) -> Option<Expression> {
6071        let (left_start, left_end, right_start, right_end) =
6072            if let (Some(left_start), Some(left_end), Some(right_start), Some(right_end)) = (
6073                overlaps.left_start.as_ref(),
6074                overlaps.left_end.as_ref(),
6075                overlaps.right_start.as_ref(),
6076                overlaps.right_end.as_ref(),
6077            ) {
6078                (left_start, left_end, right_start, right_end)
6079            } else if let (
6080                Some(Expression::Tuple(left_tuple)),
6081                Some(Expression::Tuple(right_tuple)),
6082            ) = (&overlaps.this, &overlaps.expression)
6083            {
6084                if left_tuple.expressions.len() != 2 || right_tuple.expressions.len() != 2 {
6085                    return None;
6086                }
6087                (
6088                    &left_tuple.expressions[0],
6089                    &left_tuple.expressions[1],
6090                    &right_tuple.expressions[0],
6091                    &right_tuple.expressions[1],
6092                )
6093            } else {
6094                return None;
6095            };
6096
6097        let left_min = Self::case_min(left_start.clone(), left_end.clone());
6098        let left_max = Self::case_max(left_start.clone(), left_end.clone());
6099        let right_min = Self::case_min(right_start.clone(), right_end.clone());
6100        let right_max = Self::case_max(right_start.clone(), right_end.clone());
6101
6102        Some(Expression::And(Box::new(BinaryOp::new(
6103            Expression::Lte(Box::new(BinaryOp::new(left_min, right_max))),
6104            Expression::Lte(Box::new(BinaryOp::new(right_min, left_max))),
6105        ))))
6106    }
6107
6108    fn case_min(left: Expression, right: Expression) -> Expression {
6109        Expression::Case(Box::new(Case {
6110            operand: None,
6111            whens: vec![(
6112                Expression::Lte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
6113                left,
6114            )],
6115            else_: Some(right),
6116            comments: Vec::new(),
6117            inferred_type: None,
6118        }))
6119    }
6120
6121    fn case_max(left: Expression, right: Expression) -> Expression {
6122        Expression::Case(Box::new(Case {
6123            operand: None,
6124            whens: vec![(
6125                Expression::Gte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
6126                left,
6127            )],
6128            else_: Some(right),
6129            comments: Vec::new(),
6130            inferred_type: None,
6131        }))
6132    }
6133
6134    fn date_bin_to_date_bucket(date_bin: DateBin) -> Option<Expression> {
6135        if date_bin.unit.is_some() || date_bin.zone.is_some() {
6136            return None;
6137        }
6138
6139        let (datepart, number) = Self::date_bucket_parts(&date_bin.this)?;
6140        let mut args = vec![
6141            Self::date_bucket_datepart(datepart),
6142            number,
6143            *date_bin.expression,
6144        ];
6145        if let Some(origin) = date_bin.origin {
6146            args.push(*origin);
6147        }
6148
6149        Some(Expression::Function(Box::new(Function::new(
6150            "DATE_BUCKET".to_string(),
6151            args,
6152        ))))
6153    }
6154
6155    fn date_bin_function_to_date_bucket(function: &Function) -> Option<Expression> {
6156        if !(2..=3).contains(&function.args.len()) {
6157            return None;
6158        }
6159
6160        let (datepart, number) = Self::date_bucket_parts(&function.args[0])?;
6161        let mut args = vec![
6162            Self::date_bucket_datepart(datepart),
6163            number,
6164            function.args[1].clone(),
6165        ];
6166        if let Some(origin) = function.args.get(2) {
6167            args.push(origin.clone());
6168        }
6169
6170        Some(Expression::Function(Box::new(Function::new(
6171            "DATE_BUCKET".to_string(),
6172            args,
6173        ))))
6174    }
6175
6176    fn date_bucket_parts(stride: &Expression) -> Option<(&'static str, Expression)> {
6177        match stride {
6178            Expression::Literal(lit) => match lit.as_ref() {
6179                Literal::String(value) => Self::date_bucket_parts_from_string(value),
6180                _ => None,
6181            },
6182            Expression::Interval(interval) => Self::date_bucket_parts_from_interval(interval),
6183            _ => None,
6184        }
6185    }
6186
6187    fn date_bucket_parts_from_interval(interval: &Interval) -> Option<(&'static str, Expression)> {
6188        match &interval.unit {
6189            Some(IntervalUnitSpec::Simple { unit, .. }) => {
6190                let datepart = Self::date_bucket_datepart_from_unit(*unit)?;
6191                let amount = interval
6192                    .this
6193                    .as_ref()
6194                    .and_then(Self::date_bucket_amount_expr)?;
6195                Some((datepart, amount))
6196            }
6197            None => interval.this.as_ref().and_then(|expr| match expr {
6198                Expression::Literal(lit) => match lit.as_ref() {
6199                    Literal::String(value) => Self::date_bucket_parts_from_string(value),
6200                    _ => None,
6201                },
6202                _ => None,
6203            }),
6204            _ => None,
6205        }
6206    }
6207
6208    fn date_bucket_parts_from_string(value: &str) -> Option<(&'static str, Expression)> {
6209        let mut parts = value.split_whitespace();
6210        let amount = parts.next()?;
6211        let unit = parts.next()?;
6212        if parts.next().is_some() {
6213            return None;
6214        }
6215
6216        Some((
6217            Self::date_bucket_datepart_from_name(unit)?,
6218            Self::positive_integer_expr(amount)?,
6219        ))
6220    }
6221
6222    fn date_bucket_amount_expr(expr: &Expression) -> Option<Expression> {
6223        match expr {
6224            Expression::Literal(lit) => match lit.as_ref() {
6225                Literal::Number(value) => Self::positive_integer_expr(value),
6226                Literal::String(value) => Self::positive_integer_expr(value),
6227                _ => None,
6228            },
6229            _ => Some(expr.clone()),
6230        }
6231    }
6232
6233    fn positive_integer_expr(value: &str) -> Option<Expression> {
6234        let parsed = value.trim().parse::<i64>().ok()?;
6235        (parsed > 0).then(|| Expression::number(parsed))
6236    }
6237
6238    fn date_bucket_datepart(datepart: &str) -> Expression {
6239        Expression::Var(Box::new(Var {
6240            this: datepart.to_string(),
6241        }))
6242    }
6243
6244    fn date_bucket_datepart_from_unit(unit: IntervalUnit) -> Option<&'static str> {
6245        match unit {
6246            IntervalUnit::Week => Some("WEEK"),
6247            IntervalUnit::Day => Some("DAY"),
6248            IntervalUnit::Hour => Some("HOUR"),
6249            IntervalUnit::Minute => Some("MINUTE"),
6250            IntervalUnit::Second => Some("SECOND"),
6251            IntervalUnit::Millisecond => Some("MILLISECOND"),
6252            _ => None,
6253        }
6254    }
6255
6256    fn date_bucket_datepart_from_name(unit: &str) -> Option<&'static str> {
6257        match unit.trim().to_ascii_uppercase().as_str() {
6258            "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" => Some("WEEK"),
6259            "DAY" | "DAYS" | "D" | "DD" => Some("DAY"),
6260            "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some("HOUR"),
6261            "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some("MINUTE"),
6262            "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some("SECOND"),
6263            "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MILLISEC" | "MILLISECS" => {
6264                Some("MILLISECOND")
6265            }
6266            _ => None,
6267        }
6268    }
6269
6270    fn node_has_fetch_with_ties(expr: &Expression) -> bool {
6271        matches!(
6272            expr,
6273            Expression::Select(select)
6274                if select
6275                    .fetch
6276                    .as_ref()
6277                    .is_some_and(|fetch| fetch.with_ties)
6278        )
6279    }
6280
6281    fn node_is_overlaps(expr: &Expression) -> bool {
6282        matches!(expr, Expression::Overlaps(_))
6283    }
6284
6285    fn node_is_date_bin(expr: &Expression) -> bool {
6286        matches!(expr, Expression::DateBin(_)) || Self::node_is_function_named(expr, "DATE_BIN")
6287    }
6288
6289    fn node_is_function_named(expr: &Expression, name: &str) -> bool {
6290        match expr {
6291            Expression::Function(function) => function.name.eq_ignore_ascii_case(name),
6292            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case(name),
6293            _ => false,
6294        }
6295    }
6296
6297    fn node_is_postgres_json_build_object(expr: &Expression) -> bool {
6298        match expr {
6299            Expression::Function(function) => {
6300                function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
6301                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT")
6302            }
6303            _ => false,
6304        }
6305    }
6306
6307    fn postgres_json_build_object_can_lower_to_json_object(expr: &Expression) -> bool {
6308        matches!(
6309            expr,
6310            Expression::Function(function)
6311                if (function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
6312                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT"))
6313                    && !function.distinct
6314                    && function.args.len() % 2 == 0
6315        )
6316    }
6317
6318    fn node_is_postgres_json_array_elements(expr: &Expression) -> bool {
6319        matches!(
6320            expr,
6321            Expression::Function(function)
6322                if function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS")
6323                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS")
6324                    || function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT")
6325                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT")
6326        )
6327    }
6328
6329    fn postgres_tsql_unsupported_function_name(
6330        expr: &Expression,
6331        target: DialectType,
6332    ) -> Option<&'static str> {
6333        match expr {
6334            Expression::Lpad(_) => Some("LPAD"),
6335            Expression::Rpad(_) => Some("RPAD"),
6336            Expression::SplitPart(_) => Some("SPLIT_PART"),
6337            Expression::Initcap(_) => Some("INITCAP"),
6338            Expression::RegexpReplace(_) => Some("REGEXP_REPLACE"),
6339            Expression::RegexpInstr(_) => Some("REGEXP_INSTR"),
6340            Expression::RegexpCount(_) => Some("REGEXP_COUNT"),
6341            Expression::RegexpSplit(_) => Some("REGEXP_SPLIT"),
6342            Expression::DecodeCase(_) => Some("DECODE"),
6343            Expression::ToJson(_) => Some("TO_JSON"),
6344            Expression::JSONBObjectAgg(_) => Some("JSONB_OBJECT_AGG"),
6345            Expression::ToNumber(_) => Some("TO_NUMBER"),
6346            Expression::WidthBucket(_) => Some("WIDTH_BUCKET"),
6347            Expression::BitwiseAndAgg(_) => Some("BIT_AND"),
6348            Expression::BitwiseOrAgg(_) => Some("BIT_OR"),
6349            Expression::BitwiseXorAgg(_) => Some("BIT_XOR"),
6350            Expression::Corr(_) => Some("CORR"),
6351            Expression::CovarPop(_) => Some("COVAR_POP"),
6352            Expression::CovarSamp(_) => Some("COVAR_SAMP"),
6353            Expression::RegrAvgx(_) => Some("REGR_AVGX"),
6354            Expression::RegrAvgy(_) => Some("REGR_AVGY"),
6355            Expression::RegrCount(_) => Some("REGR_COUNT"),
6356            Expression::RegrIntercept(_) => Some("REGR_INTERCEPT"),
6357            Expression::RegrR2(_) => Some("REGR_R2"),
6358            Expression::RegrSlope(_) => Some("REGR_SLOPE"),
6359            Expression::RegrSxx(_) => Some("REGR_SXX"),
6360            Expression::RegrSxy(_) => Some("REGR_SXY"),
6361            Expression::RegrSyy(_) => Some("REGR_SYY"),
6362            Expression::Function(function) => {
6363                Self::postgres_tsql_unsupported_function_name_str(&function.name, target)
6364            }
6365            Expression::AggregateFunction(function) => {
6366                Self::postgres_tsql_unsupported_function_name_str(&function.name, target)
6367            }
6368            _ => None,
6369        }
6370    }
6371
6372    fn postgres_tsql_unsupported_function_name_str(
6373        name: &str,
6374        target: DialectType,
6375    ) -> Option<&'static str> {
6376        if name.eq_ignore_ascii_case("LPAD") {
6377            Some("LPAD")
6378        } else if name.eq_ignore_ascii_case("RPAD") {
6379            Some("RPAD")
6380        } else if name.eq_ignore_ascii_case("SPLIT_PART") {
6381            Some("SPLIT_PART")
6382        } else if name.eq_ignore_ascii_case("INITCAP") {
6383            Some("INITCAP")
6384        } else if name.eq_ignore_ascii_case("TO_JSON") {
6385            Some("TO_JSON")
6386        } else if name.eq_ignore_ascii_case("TO_JSONB") {
6387            Some("TO_JSONB")
6388        } else if name.eq_ignore_ascii_case("JSONB_OBJECT_AGG") {
6389            Some("JSONB_OBJECT_AGG")
6390        } else if name.eq_ignore_ascii_case("ROW_TO_JSON") {
6391            Some("ROW_TO_JSON")
6392        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS") {
6393            Some("JSON_ARRAY_ELEMENTS")
6394        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS") {
6395            Some("JSONB_ARRAY_ELEMENTS")
6396        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT") {
6397            Some("JSON_ARRAY_ELEMENTS_TEXT")
6398        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT") {
6399            Some("JSONB_ARRAY_ELEMENTS_TEXT")
6400        } else if name.eq_ignore_ascii_case("ENCODE") {
6401            Some("ENCODE")
6402        } else if name.eq_ignore_ascii_case("DECODE") {
6403            Some("DECODE")
6404        } else if name.eq_ignore_ascii_case("REGEXP_REPLACE") {
6405            Some("REGEXP_REPLACE")
6406        } else if name.eq_ignore_ascii_case("REGEXP_COUNT") {
6407            Some("REGEXP_COUNT")
6408        } else if name.eq_ignore_ascii_case("REGEXP_INSTR") {
6409            Some("REGEXP_INSTR")
6410        } else if name.eq_ignore_ascii_case("REGEXP_SUBSTR") {
6411            Some("REGEXP_SUBSTR")
6412        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT") {
6413            Some("REGEXP_SPLIT")
6414        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT_TO_ARRAY") {
6415            Some("REGEXP_SPLIT_TO_ARRAY")
6416        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT_TO_TABLE") {
6417            Some("REGEXP_SPLIT_TO_TABLE")
6418        } else if name.eq_ignore_ascii_case("SHA224") {
6419            Some("SHA224")
6420        } else if name.eq_ignore_ascii_case("SHA384") {
6421            Some("SHA384")
6422        } else if name.eq_ignore_ascii_case("TO_BIN") {
6423            Some("TO_BIN")
6424        } else if name.eq_ignore_ascii_case("TO_OCT") {
6425            Some("TO_OCT")
6426        } else if target == DialectType::TSQL && name.eq_ignore_ascii_case("UNISTR") {
6427            Some("UNISTR")
6428        } else if name.eq_ignore_ascii_case("AGE") {
6429            Some("AGE")
6430        } else if name.eq_ignore_ascii_case("ERF") {
6431            Some("ERF")
6432        } else if name.eq_ignore_ascii_case("SINH") {
6433            Some("SINH")
6434        } else if name.eq_ignore_ascii_case("COSH") {
6435            Some("COSH")
6436        } else if name.eq_ignore_ascii_case("TANH") {
6437            Some("TANH")
6438        } else if name.eq_ignore_ascii_case("ASINH") {
6439            Some("ASINH")
6440        } else if name.eq_ignore_ascii_case("ACOSH") {
6441            Some("ACOSH")
6442        } else if name.eq_ignore_ascii_case("ATANH") {
6443            Some("ATANH")
6444        } else if name.eq_ignore_ascii_case("GCD") {
6445            Some("GCD")
6446        } else if name.eq_ignore_ascii_case("LCM") {
6447            Some("LCM")
6448        } else if name.eq_ignore_ascii_case("QUOTE_LITERAL") {
6449            Some("QUOTE_LITERAL")
6450        } else if name.eq_ignore_ascii_case("WIDTH_BUCKET") {
6451            Some("WIDTH_BUCKET")
6452        } else if name.eq_ignore_ascii_case("SCALE") {
6453            Some("SCALE")
6454        } else if name.eq_ignore_ascii_case("TRIM_SCALE") {
6455            Some("TRIM_SCALE")
6456        } else if name.eq_ignore_ascii_case("MIN_SCALE") {
6457            Some("MIN_SCALE")
6458        } else if name.eq_ignore_ascii_case("FACTORIAL") {
6459            Some("FACTORIAL")
6460        } else if name.eq_ignore_ascii_case("PG_LSN") {
6461            Some("PG_LSN")
6462        } else if name.eq_ignore_ascii_case("TO_CHAR") {
6463            Some("TO_CHAR")
6464        } else if name.eq_ignore_ascii_case("PG_TYPEOF") {
6465            Some("PG_TYPEOF")
6466        } else if name.eq_ignore_ascii_case("BIT_AND") {
6467            Some("BIT_AND")
6468        } else if name.eq_ignore_ascii_case("BIT_OR") {
6469            Some("BIT_OR")
6470        } else if name.eq_ignore_ascii_case("BIT_XOR") {
6471            Some("BIT_XOR")
6472        } else if name.eq_ignore_ascii_case("CORR") {
6473            Some("CORR")
6474        } else if name.eq_ignore_ascii_case("COVAR_POP") {
6475            Some("COVAR_POP")
6476        } else if name.eq_ignore_ascii_case("COVAR_SAMP") {
6477            Some("COVAR_SAMP")
6478        } else if name.eq_ignore_ascii_case("REGR_AVGX") {
6479            Some("REGR_AVGX")
6480        } else if name.eq_ignore_ascii_case("REGR_AVGY") {
6481            Some("REGR_AVGY")
6482        } else if name.eq_ignore_ascii_case("REGR_COUNT") {
6483            Some("REGR_COUNT")
6484        } else if name.eq_ignore_ascii_case("REGR_INTERCEPT") {
6485            Some("REGR_INTERCEPT")
6486        } else if name.eq_ignore_ascii_case("REGR_R2") {
6487            Some("REGR_R2")
6488        } else if name.eq_ignore_ascii_case("REGR_SLOPE") {
6489            Some("REGR_SLOPE")
6490        } else if name.eq_ignore_ascii_case("REGR_SXX") {
6491            Some("REGR_SXX")
6492        } else if name.eq_ignore_ascii_case("REGR_SXY") {
6493            Some("REGR_SXY")
6494        } else if name.eq_ignore_ascii_case("REGR_SYY") {
6495            Some("REGR_SYY")
6496        } else if name.eq_ignore_ascii_case("FLOAT8_ACCUM") {
6497            Some("FLOAT8_ACCUM")
6498        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_ACCUM") {
6499            Some("FLOAT8_REGR_ACCUM")
6500        } else if name.eq_ignore_ascii_case("FLOAT8_COMBINE") {
6501            Some("FLOAT8_COMBINE")
6502        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_COMBINE") {
6503            Some("FLOAT8_REGR_COMBINE")
6504        } else if name.eq_ignore_ascii_case("BOOLAND_STATEFUNC") {
6505            Some("BOOLAND_STATEFUNC")
6506        } else if name.eq_ignore_ascii_case("BOOLOR_STATEFUNC") {
6507            Some("BOOLOR_STATEFUNC")
6508        } else {
6509            None
6510        }
6511    }
6512
6513    fn normalize_postgres_trim_for_tsql(expr: Expression) -> Result<Expression> {
6514        transform_recursive(expr, &|e| match e {
6515            Expression::Trim(trim) => {
6516                let mut trim = *trim;
6517                trim.characters = trim.characters.map(Self::strip_postgres_text_literal_cast);
6518                match trim.position {
6519                    crate::expressions::TrimPosition::Both
6520                        if trim.position_explicit && trim.characters.is_some() =>
6521                    {
6522                        trim.position_explicit = false;
6523                        trim.sql_standard_syntax = true;
6524                        Ok(Expression::Trim(Box::new(trim)))
6525                    }
6526                    crate::expressions::TrimPosition::Leading if trim.characters.is_some() => {
6527                        let characters = trim.characters.take().expect("checked above");
6528                        Ok(Expression::Function(Box::new(Function::new(
6529                            "LTRIM",
6530                            vec![trim.this, characters],
6531                        ))))
6532                    }
6533                    crate::expressions::TrimPosition::Trailing if trim.characters.is_some() => {
6534                        let characters = trim.characters.take().expect("checked above");
6535                        Ok(Expression::Function(Box::new(Function::new(
6536                            "RTRIM",
6537                            vec![trim.this, characters],
6538                        ))))
6539                    }
6540                    _ => Ok(Expression::Trim(Box::new(trim))),
6541                }
6542            }
6543            other => Ok(other),
6544        })
6545    }
6546
6547    fn normalize_postgres_string_semantics_for_tsql(expr: Expression) -> Result<Expression> {
6548        transform_recursive(expr, &|e| match e {
6549            Expression::Like(mut op) => {
6550                Self::recover_postgres_like_escape(&mut op);
6551                Ok(Expression::Like(op))
6552            }
6553            Expression::ILike(mut op) => {
6554                Self::recover_postgres_like_escape(&mut op);
6555                Ok(Expression::ILike(op))
6556            }
6557            Expression::Substring(mut substring)
6558                if substring.length.is_none()
6559                    && Self::is_explicitly_numeric_expression(&substring.start) =>
6560            {
6561                substring.length = Some(Expression::number(i32::MAX as i64));
6562                Ok(Expression::Substring(substring))
6563            }
6564            Expression::Trim(mut trim) => {
6565                trim.characters = trim.characters.map(Self::strip_postgres_text_literal_cast);
6566                Ok(Expression::Trim(trim))
6567            }
6568            Expression::Function(mut function)
6569                if !function.quoted
6570                    && matches!(
6571                        function.name.to_ascii_uppercase().as_str(),
6572                        "BTRIM" | "LTRIM" | "RTRIM"
6573                    )
6574                    && function.args.len() == 2 =>
6575            {
6576                function.args[1] = Self::strip_postgres_text_literal_cast(function.args[1].clone());
6577                Ok(Expression::Function(function))
6578            }
6579            Expression::Translate(translate) => {
6580                Ok(Self::normalize_postgres_translate_for_tsql(*translate))
6581            }
6582            Expression::Function(function)
6583                if !function.quoted
6584                    && function.name.eq_ignore_ascii_case("TRANSLATE")
6585                    && function.args.len() == 3 =>
6586            {
6587                Ok(Self::normalize_postgres_translate_function_for_tsql(
6588                    *function,
6589                ))
6590            }
6591            other => Ok(other),
6592        })
6593    }
6594
6595    fn normalize_postgres_bytea_literals_for_tsql(expr: Expression) -> Result<Expression> {
6596        transform_recursive(expr, &|e| match e {
6597            Expression::Cast(cast) if Self::is_postgres_bytea_data_type(&cast.to) => {
6598                let Some(value) = Self::postgres_plain_string_literal_value(&cast.this) else {
6599                    return Ok(Expression::Cast(cast));
6600                };
6601                let Some(hex) = Self::postgres_bytea_hex_payload(value) else {
6602                    return Ok(Expression::Cast(cast));
6603                };
6604
6605                // Replace the complete BYTEA cast. Keeping a bare T-SQL
6606                // CAST(... AS VARBINARY) would apply SQL Server's default length
6607                // and could truncate payloads longer than 30 bytes.
6608                Ok(Expression::Literal(Box::new(Literal::HexString(hex))))
6609            }
6610            other => Ok(other),
6611        })
6612    }
6613
6614    fn postgres_plain_string_literal_value(expr: &Expression) -> Option<&str> {
6615        match expr {
6616            Expression::Literal(literal) => match literal.as_ref() {
6617                Literal::String(value) => Some(value),
6618                _ => None,
6619            },
6620            Expression::Paren(paren) => Self::postgres_plain_string_literal_value(&paren.this),
6621            _ => None,
6622        }
6623    }
6624
6625    fn postgres_bytea_hex_payload(value: &str) -> Option<String> {
6626        let payload = value.strip_prefix("\\x")?;
6627        if payload.is_empty() {
6628            return Some(String::new());
6629        }
6630
6631        let mut chars = payload.chars().peekable();
6632        let mut hex = String::with_capacity(payload.len());
6633        loop {
6634            let high = chars.next()?;
6635            let low = chars.next()?;
6636            if !high.is_ascii_hexdigit() || !low.is_ascii_hexdigit() {
6637                return None;
6638            }
6639            hex.push(high);
6640            hex.push(low);
6641
6642            let Some(next) = chars.peek().copied() else {
6643                return Some(hex);
6644            };
6645            if next.is_ascii_whitespace() {
6646                while chars
6647                    .peek()
6648                    .is_some_and(|character| character.is_ascii_whitespace())
6649                {
6650                    chars.next();
6651                }
6652                // PostgreSQL permits whitespace between byte pairs, not after
6653                // the prefix or after the final pair.
6654                chars.peek()?;
6655            }
6656        }
6657    }
6658
6659    fn is_postgres_bytea_data_type(data_type: &DataType) -> bool {
6660        match data_type {
6661            DataType::VarBinary { length: None } => true,
6662            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("BYTEA"),
6663            _ => false,
6664        }
6665    }
6666
6667    fn postgres_tsql_unsupported_binary_semantics(expr: &Expression) -> Option<&'static str> {
6668        let cast = match expr {
6669            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
6670                if Self::is_postgres_bytea_data_type(&cast.to) =>
6671            {
6672                cast
6673            }
6674            _ => return None,
6675        };
6676
6677        let literal = match &cast.this {
6678            Expression::Literal(literal) => literal.as_ref(),
6679            Expression::Paren(paren) => match &paren.this {
6680                Expression::Literal(literal) => literal.as_ref(),
6681                _ => return None,
6682            },
6683            _ => return None,
6684        };
6685        let value = match literal {
6686            Literal::String(value) | Literal::EscapeString(value) => value,
6687            _ => return None,
6688        };
6689
6690        if value.starts_with("\\x") {
6691            Some("bytea hex literals with invalid or unsupported formatting")
6692        } else if value.contains('\\') {
6693            Some("bytea escape-format literals")
6694        } else {
6695            None
6696        }
6697    }
6698
6699    fn recover_postgres_like_escape(op: &mut crate::expressions::LikeOp) {
6700        if op.escape.is_some() {
6701            return;
6702        }
6703
6704        let Expression::Function(function) = &op.right else {
6705            return;
6706        };
6707        if function.quoted
6708            || function.distinct
6709            || !function.name.eq_ignore_ascii_case("LIKE_ESCAPE")
6710            || function.args.len() != 2
6711        {
6712            return;
6713        }
6714
6715        let pattern = function.args[0].clone();
6716        let escape = function.args[1].clone();
6717        op.right = Self::strip_postgres_text_literal_cast(pattern);
6718        op.escape = Some(Self::strip_postgres_text_literal_cast(escape));
6719    }
6720
6721    fn normalize_postgres_translate_for_tsql(
6722        mut translate: crate::expressions::Translate,
6723    ) -> Expression {
6724        let (Some(from), Some(to)) = (&translate.from_, &translate.to) else {
6725            return Expression::Translate(Box::new(translate));
6726        };
6727
6728        let (Some(from_value), Some(to_value)) = (
6729            Self::postgres_text_literal_value(from),
6730            Self::postgres_text_literal_value(to),
6731        ) else {
6732            return Expression::Translate(Box::new(translate));
6733        };
6734        let from_value = from_value.to_string();
6735        let to_value = to_value.to_string();
6736
6737        if from_value.chars().count() > to_value.chars().count() {
6738            if let Some(input) = Self::postgres_text_literal_value(&translate.this) {
6739                return Expression::string(Self::translate_postgres_literal(
6740                    input,
6741                    &from_value,
6742                    &to_value,
6743                ));
6744            }
6745            return Expression::Translate(Box::new(translate));
6746        }
6747
6748        translate.from_ = Some(Box::new(Self::strip_postgres_text_literal_cast(
6749            *translate.from_.expect("checked above"),
6750        )));
6751        let normalized_to = if from_value.chars().count() < to_value.chars().count() {
6752            Expression::string(
6753                to_value
6754                    .chars()
6755                    .take(from_value.chars().count())
6756                    .collect::<String>(),
6757            )
6758        } else {
6759            Self::strip_postgres_text_literal_cast(*translate.to.expect("checked above"))
6760        };
6761        translate.to = Some(Box::new(normalized_to));
6762        Expression::Translate(Box::new(translate))
6763    }
6764
6765    fn normalize_postgres_translate_function_for_tsql(mut function: Function) -> Expression {
6766        let from = Self::postgres_text_literal_value(&function.args[1]);
6767        let to = Self::postgres_text_literal_value(&function.args[2]);
6768        let (Some(from), Some(to)) = (from, to) else {
6769            return Expression::Function(Box::new(function));
6770        };
6771        let from = from.to_string();
6772        let to = to.to_string();
6773
6774        if from.chars().count() > to.chars().count() {
6775            if let Some(input) = Self::postgres_text_literal_value(&function.args[0]) {
6776                return Expression::string(Self::translate_postgres_literal(input, &from, &to));
6777            }
6778            return Expression::Function(Box::new(function));
6779        }
6780
6781        function.args[1] = Self::strip_postgres_text_literal_cast(function.args[1].clone());
6782        function.args[2] = if from.chars().count() < to.chars().count() {
6783            Expression::string(to.chars().take(from.chars().count()).collect::<String>())
6784        } else {
6785            Self::strip_postgres_text_literal_cast(function.args[2].clone())
6786        };
6787        Expression::Function(Box::new(function))
6788    }
6789
6790    fn translate_postgres_literal(input: &str, from: &str, to: &str) -> String {
6791        let from = from.chars().collect::<Vec<_>>();
6792        let to = to.chars().collect::<Vec<_>>();
6793        let mut output = String::with_capacity(input.len());
6794
6795        for ch in input.chars() {
6796            match from.iter().position(|candidate| *candidate == ch) {
6797                Some(index) if index < to.len() => output.push(to[index]),
6798                Some(_) => {}
6799                None => output.push(ch),
6800            }
6801        }
6802
6803        output
6804    }
6805
6806    fn postgres_tsql_unsupported_string_semantics(expr: &Expression) -> Option<&'static str> {
6807        match expr {
6808            Expression::Substring(substring) if substring.length.is_none() => {
6809                if Self::postgres_text_literal_value(&substring.start).is_some() {
6810                    Some("regular-expression SUBSTRING")
6811                } else {
6812                    Some("SUBSTRING without a statically numeric start position")
6813                }
6814            }
6815            Expression::Translate(translate) => {
6816                let from = translate
6817                    .from_
6818                    .as_deref()
6819                    .and_then(Self::postgres_text_literal_value);
6820                let to = translate
6821                    .to
6822                    .as_deref()
6823                    .and_then(Self::postgres_text_literal_value);
6824                match (from, to) {
6825                    (Some(from), Some(to)) if from.chars().count() == to.chars().count() => None,
6826                    _ => Some("TRANSLATE with source and replacement lengths that differ or cannot be proven equal"),
6827                }
6828            }
6829            Expression::Function(function)
6830                if !function.quoted && function.name.eq_ignore_ascii_case("LIKE_ESCAPE") =>
6831            {
6832                Some("LIKE_ESCAPE helper outside a LIKE predicate")
6833            }
6834            Expression::Function(function)
6835                if !function.quoted
6836                    && function.name.eq_ignore_ascii_case("TRANSLATE")
6837                    && function.args.len() == 3 =>
6838            {
6839                let from = Self::postgres_text_literal_value(&function.args[1]);
6840                let to = Self::postgres_text_literal_value(&function.args[2]);
6841                match (from, to) {
6842                    (Some(from), Some(to)) if from.chars().count() == to.chars().count() => None,
6843                    _ => Some("TRANSLATE with source and replacement lengths that differ or cannot be proven equal"),
6844                }
6845            }
6846            Expression::Trim(trim)
6847                if trim
6848                    .characters
6849                    .as_ref()
6850                    .is_some_and(Self::is_unbounded_text_cast) =>
6851            {
6852                Some("TRIM character set cast to an unbounded text type")
6853            }
6854            Expression::Function(function)
6855                if !function.quoted
6856                    && matches!(
6857                        function.name.to_ascii_uppercase().as_str(),
6858                        "LTRIM" | "RTRIM"
6859                    )
6860                    && function.args.len() == 2
6861                    && Self::is_unbounded_text_cast(&function.args[1]) =>
6862            {
6863                Some("TRIM character set cast to an unbounded text type")
6864            }
6865            _ => None,
6866        }
6867    }
6868
6869    fn strip_postgres_text_literal_cast(expr: Expression) -> Expression {
6870        match expr {
6871            Expression::Cast(cast)
6872                if Self::is_text_data_type(&cast.to)
6873                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
6874            {
6875                Self::strip_postgres_text_literal_cast(cast.this)
6876            }
6877            Expression::TryCast(cast)
6878                if Self::is_text_data_type(&cast.to)
6879                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
6880            {
6881                Self::strip_postgres_text_literal_cast(cast.this)
6882            }
6883            Expression::SafeCast(cast)
6884                if Self::is_text_data_type(&cast.to)
6885                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
6886            {
6887                Self::strip_postgres_text_literal_cast(cast.this)
6888            }
6889            Expression::Paren(mut paren)
6890                if Self::postgres_text_literal_value(&paren.this).is_some() =>
6891            {
6892                paren.this = Self::strip_postgres_text_literal_cast(paren.this);
6893                Expression::Paren(paren)
6894            }
6895            other => other,
6896        }
6897    }
6898
6899    fn postgres_text_literal_value(expr: &Expression) -> Option<&str> {
6900        match expr {
6901            Expression::Literal(literal) if literal.is_string() => Some(literal.value_str()),
6902            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
6903                if Self::is_text_data_type(&cast.to) =>
6904            {
6905                Self::postgres_text_literal_value(&cast.this)
6906            }
6907            Expression::Alias(alias) => Self::postgres_text_literal_value(&alias.this),
6908            Expression::Paren(paren) => Self::postgres_text_literal_value(&paren.this),
6909            _ => None,
6910        }
6911    }
6912
6913    fn is_text_data_type(data_type: &DataType) -> bool {
6914        match data_type {
6915            DataType::Char { .. }
6916            | DataType::VarChar { .. }
6917            | DataType::String { .. }
6918            | DataType::Text
6919            | DataType::TextWithLength { .. } => true,
6920            DataType::Custom { name } => {
6921                let base = name
6922                    .split_once('(')
6923                    .map_or(name.as_str(), |(base, _)| base)
6924                    .trim();
6925                matches!(
6926                    base.to_ascii_uppercase().as_str(),
6927                    "CHAR"
6928                        | "NCHAR"
6929                        | "VARCHAR"
6930                        | "NVARCHAR"
6931                        | "TEXT"
6932                        | "NTEXT"
6933                        | "STRING"
6934                        | "CHARACTER VARYING"
6935                )
6936            }
6937            _ => false,
6938        }
6939    }
6940
6941    fn is_unbounded_text_cast(expr: &Expression) -> bool {
6942        let data_type = match expr {
6943            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
6944                &cast.to
6945            }
6946            Expression::Paren(paren) => return Self::is_unbounded_text_cast(&paren.this),
6947            _ => return false,
6948        };
6949
6950        match data_type {
6951            DataType::Text => true,
6952            DataType::VarChar { length: None, .. } | DataType::String { length: None } => true,
6953            DataType::Custom { name } => name.to_ascii_uppercase().contains("(MAX)"),
6954            _ => false,
6955        }
6956    }
6957
6958    fn is_explicitly_numeric_expression(expr: &Expression) -> bool {
6959        if expr.inferred_type().is_some_and(Self::is_numeric_data_type) {
6960            return true;
6961        }
6962
6963        match expr {
6964            Expression::Literal(literal) => literal.is_number(),
6965            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
6966                Self::is_numeric_data_type(&cast.to)
6967            }
6968            Expression::Alias(alias) => Self::is_explicitly_numeric_expression(&alias.this),
6969            Expression::Paren(paren) => Self::is_explicitly_numeric_expression(&paren.this),
6970            Expression::Neg(unary) => Self::is_explicitly_numeric_expression(&unary.this),
6971            _ => false,
6972        }
6973    }
6974
6975    fn is_numeric_data_type(data_type: &DataType) -> bool {
6976        match data_type {
6977            DataType::TinyInt { .. }
6978            | DataType::SmallInt { .. }
6979            | DataType::Int { .. }
6980            | DataType::BigInt { .. }
6981            | DataType::Float { .. }
6982            | DataType::Double { .. }
6983            | DataType::Decimal { .. } => true,
6984            DataType::Custom { name } => {
6985                let base = name
6986                    .split_once('(')
6987                    .map_or(name.as_str(), |(base, _)| base)
6988                    .trim();
6989                matches!(
6990                    base.to_ascii_uppercase().as_str(),
6991                    "TINYINT"
6992                        | "SMALLINT"
6993                        | "INT"
6994                        | "INTEGER"
6995                        | "BIGINT"
6996                        | "DECIMAL"
6997                        | "NUMERIC"
6998                        | "REAL"
6999                        | "FLOAT"
7000                        | "MONEY"
7001                        | "SMALLMONEY"
7002                )
7003            }
7004            _ => false,
7005        }
7006    }
7007
7008    fn normalize_postgres_only_for_tsql(expr: Expression) -> Result<Expression> {
7009        transform_recursive(expr, &|e| match e {
7010            Expression::Table(mut table) if table.only => {
7011                table.only = false;
7012                Ok(Expression::Table(table))
7013            }
7014            other => Ok(other),
7015        })
7016    }
7017
7018    fn rewrite_postgres_json_array_elements_select_for_tsql(
7019        expr: Expression,
7020    ) -> Result<Expression> {
7021        let Expression::Select(select) = expr else {
7022            return Ok(expr);
7023        };
7024        let mut select = *select;
7025        if !Self::is_plain_single_projection_select(&select) {
7026            return Ok(Expression::Select(Box::new(select)));
7027        }
7028
7029        let Some(json_arg) =
7030            Self::postgres_json_array_elements_projection_arg(&select.expressions[0])
7031        else {
7032            return Ok(Expression::Select(Box::new(select)));
7033        };
7034
7035        select.expressions = vec![Expression::column("value")];
7036        select.from = Some(From {
7037            expressions: vec![Expression::OpenJSON(Box::new(
7038                crate::expressions::OpenJSON {
7039                    this: Box::new(json_arg),
7040                    path: None,
7041                    expressions: Vec::new(),
7042                },
7043            ))],
7044        });
7045
7046        Ok(Expression::Select(Box::new(select)))
7047    }
7048
7049    fn is_plain_single_projection_select(select: &crate::expressions::Select) -> bool {
7050        select.expressions.len() == 1
7051            && select.from.is_none()
7052            && select.joins.is_empty()
7053            && select.lateral_views.is_empty()
7054            && select.prewhere.is_none()
7055            && select.where_clause.is_none()
7056            && select.group_by.is_none()
7057            && select.having.is_none()
7058            && select.qualify.is_none()
7059            && select.order_by.is_none()
7060            && select.distribute_by.is_none()
7061            && select.cluster_by.is_none()
7062            && select.sort_by.is_none()
7063            && select.limit.is_none()
7064            && select.offset.is_none()
7065            && select.limit_by.is_none()
7066            && select.fetch.is_none()
7067            && !select.distinct
7068            && select.distinct_on.is_none()
7069            && select.top.is_none()
7070            && select.with.is_none()
7071            && select.sample.is_none()
7072            && select.into.is_none()
7073            && select.locks.is_empty()
7074            && select.for_xml.is_empty()
7075            && select.for_json.is_empty()
7076            && select.exclude.is_none()
7077    }
7078
7079    fn postgres_json_array_elements_projection_arg(expr: &Expression) -> Option<Expression> {
7080        match expr {
7081            Expression::Function(function)
7082                if Self::node_is_postgres_json_array_elements(expr) && function.args.len() == 1 =>
7083            {
7084                Some(function.args[0].clone())
7085            }
7086            Expression::Alias(alias) => {
7087                Self::postgres_json_array_elements_projection_arg(&alias.this)
7088            }
7089            _ => None,
7090        }
7091    }
7092
7093    fn normalize_postgres_type_function_casts(
7094        expr: Expression,
7095        target: DialectType,
7096    ) -> Result<Expression> {
7097        transform_recursive(expr, &|e| match e {
7098            Expression::Function(function) => {
7099                let mut function = *function;
7100                if function.args.len() == 1
7101                    && !function.distinct
7102                    && !function.quoted
7103                    && !function.use_bracket_syntax
7104                    && !function.name.contains('.')
7105                {
7106                    if let Some(to) = Self::postgres_type_function_data_type(&function.name) {
7107                        let this = function.args.remove(0);
7108                        let cast = Cast {
7109                            this,
7110                            to,
7111                            trailing_comments: function.trailing_comments,
7112                            double_colon_syntax: false,
7113                            format: None,
7114                            default: None,
7115                            inferred_type: function.inferred_type,
7116                        };
7117                        return Ok(
7118                            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
7119                                normalization::rewrite_postgres_float_to_integer_cast(cast)
7120                            } else {
7121                                Expression::Cast(Box::new(cast))
7122                            },
7123                        );
7124                    }
7125                }
7126                Ok(Expression::Function(Box::new(function)))
7127            }
7128            _ => Ok(e),
7129        })
7130    }
7131
7132    fn node_is_postgres_type_function_cast(expr: &Expression) -> bool {
7133        matches!(
7134            expr,
7135            Expression::Function(function)
7136                if !function.quoted
7137                    && !function.use_bracket_syntax
7138                    && !function.name.contains('.')
7139                    && Self::postgres_type_function_data_type(&function.name).is_some()
7140        )
7141    }
7142
7143    fn postgres_type_function_data_type(name: &str) -> Option<DataType> {
7144        match name.to_ascii_uppercase().as_str() {
7145            "NUMERIC" | "DECIMAL" | "DEC" => Some(DataType::Decimal {
7146                precision: None,
7147                scale: None,
7148            }),
7149            "INT2" | "SMALLINT" => Some(DataType::SmallInt { length: None }),
7150            "INT4" | "INT" => Some(DataType::Int {
7151                length: None,
7152                integer_spelling: false,
7153            }),
7154            "INTEGER" => Some(DataType::Int {
7155                length: None,
7156                integer_spelling: true,
7157            }),
7158            "INT8" | "BIGINT" => Some(DataType::BigInt { length: None }),
7159            "FLOAT4" | "REAL" => Some(DataType::Float {
7160                precision: None,
7161                scale: None,
7162                real_spelling: true,
7163            }),
7164            "FLOAT8" => Some(DataType::Double {
7165                precision: None,
7166                scale: None,
7167            }),
7168            "BOOL" | "BOOLEAN" => Some(DataType::Boolean),
7169            "TEXT" => Some(DataType::Text),
7170            "VARCHAR" => Some(DataType::VarChar {
7171                length: None,
7172                parenthesized_length: false,
7173            }),
7174            "UUID" => Some(DataType::Uuid),
7175            _ => None,
7176        }
7177    }
7178
7179    fn rewrite_boolean_values_for_tsql(expr: Expression) -> Result<Expression> {
7180        match expr {
7181            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
7182            Expression::Subquery(mut subquery) => {
7183                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
7184                Ok(Expression::Subquery(subquery))
7185            }
7186            Expression::Union(mut union) => {
7187                let left = std::mem::replace(&mut union.left, Expression::null());
7188                let right = std::mem::replace(&mut union.right, Expression::null());
7189                union.left = Self::rewrite_boolean_values_for_tsql(left)?;
7190                union.right = Self::rewrite_boolean_values_for_tsql(right)?;
7191                if let Some(mut with) = union.with.take() {
7192                    with.ctes = with
7193                        .ctes
7194                        .into_iter()
7195                        .map(|mut cte| {
7196                            cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
7197                            Ok(cte)
7198                        })
7199                        .collect::<Result<Vec<_>>>()?;
7200                    union.with = Some(with);
7201                }
7202                Ok(Expression::Union(union))
7203            }
7204            Expression::Intersect(mut intersect) => {
7205                let left = std::mem::replace(&mut intersect.left, Expression::null());
7206                let right = std::mem::replace(&mut intersect.right, Expression::null());
7207                intersect.left = Self::rewrite_boolean_values_for_tsql(left)?;
7208                intersect.right = Self::rewrite_boolean_values_for_tsql(right)?;
7209                Ok(Expression::Intersect(intersect))
7210            }
7211            Expression::Except(mut except) => {
7212                let left = std::mem::replace(&mut except.left, Expression::null());
7213                let right = std::mem::replace(&mut except.right, Expression::null());
7214                except.left = Self::rewrite_boolean_values_for_tsql(left)?;
7215                except.right = Self::rewrite_boolean_values_for_tsql(right)?;
7216                Ok(Expression::Except(except))
7217            }
7218            other => Self::rewrite_tsql_boolean_nested_contexts(other),
7219        }
7220    }
7221
7222    fn rewrite_postgres_row_value_equality_for_tsql(expr: Expression) -> Result<Expression> {
7223        transform_recursive(expr, &|e| match e {
7224            Expression::Eq(op) => {
7225                let op = *op;
7226                Ok(Self::postgres_row_value_equality_to_tsql_scalar(&op)
7227                    .unwrap_or_else(|| Expression::Eq(Box::new(op))))
7228            }
7229            other => Ok(other),
7230        })
7231    }
7232
7233    fn postgres_row_value_equality_to_tsql_scalar(op: &BinaryOp) -> Option<Expression> {
7234        let (row, query) =
7235            if Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right) {
7236                (&op.left, &op.right)
7237            } else if Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left) {
7238                (&op.right, &op.left)
7239            } else {
7240                return None;
7241            };
7242
7243        let row_values = Self::row_value_expressions(row)?;
7244        let projection_count = Self::subquery_projection_count(query)?;
7245        if row_values.is_empty() || row_values.len() != projection_count {
7246            return None;
7247        }
7248
7249        // Keep the complete original query behind a derived table. The outer scalar
7250        // SELECT therefore returns the same number of rows as the PostgreSQL
7251        // single-row subquery: zero rows stay NULL and multiple rows still raise a
7252        // scalar-subquery cardinality error in T-SQL/Fabric.
7253        let mut taken_names = HashSet::new();
7254        Self::collect_generated_alias_conflicts(row, &mut taken_names);
7255        Self::collect_generated_alias_conflicts(query, &mut taken_names);
7256
7257        let source_alias = find_new_name(&taken_names, "_polyglot_row");
7258        taken_names.insert(source_alias.to_ascii_lowercase());
7259        let column_aliases = (1..=row_values.len())
7260            .map(|index| {
7261                let name = find_new_name(&taken_names, &format!("_polyglot_row_value_{index}"));
7262                taken_names.insert(name.to_ascii_lowercase());
7263                Identifier::new(name)
7264            })
7265            .collect::<Vec<_>>();
7266        let source = Self::subquery_as_derived_table(
7267            query,
7268            Identifier::new(&source_alias),
7269            column_aliases.clone(),
7270        )?;
7271
7272        let mut equal_components = Vec::with_capacity(row_values.len());
7273        let mut unequal_components = Vec::with_capacity(row_values.len());
7274        for (column, row_value) in column_aliases.into_iter().zip(row_values) {
7275            let projected = Expression::qualified_column(source_alias.clone(), column.name);
7276            equal_components.push(Expression::Eq(Box::new(BinaryOp::new(
7277                projected.clone(),
7278                row_value.clone(),
7279            ))));
7280            unequal_components.push(Expression::Neq(Box::new(BinaryOp::new(
7281                projected, row_value,
7282            ))));
7283        }
7284
7285        let all_equal = equal_components
7286            .into_iter()
7287            .reduce(|left, right| Expression::And(Box::new(BinaryOp::new(left, right))))?;
7288        let any_unequal = unequal_components
7289            .into_iter()
7290            .reduce(|left, right| Expression::Or(Box::new(BinaryOp::new(left, right))))?;
7291        let comparison = Expression::Case(Box::new(Case {
7292            operand: None,
7293            whens: vec![
7294                (all_equal, Expression::number(1)),
7295                (any_unequal, Expression::number(0)),
7296            ],
7297            else_: Some(Expression::null()),
7298            comments: Vec::new(),
7299            inferred_type: None,
7300        }));
7301
7302        let scalar_select = Select::new().column(comparison).from(source);
7303        let scalar_subquery = Expression::Subquery(Box::new(Subquery {
7304            this: Expression::Select(Box::new(scalar_select)),
7305            alias: None,
7306            column_aliases: Vec::new(),
7307            alias_explicit_as: false,
7308            alias_keyword: None,
7309            order_by: None,
7310            limit: None,
7311            offset: None,
7312            distribute_by: None,
7313            sort_by: None,
7314            cluster_by: None,
7315            lateral: false,
7316            modifiers_inside: false,
7317            trailing_comments: Vec::new(),
7318            inferred_type: Some(DataType::Boolean),
7319        }));
7320
7321        Some(Expression::Cast(Box::new(Cast {
7322            this: scalar_subquery,
7323            to: DataType::Boolean,
7324            trailing_comments: Vec::new(),
7325            double_colon_syntax: false,
7326            format: None,
7327            default: None,
7328            inferred_type: Some(DataType::Boolean),
7329        })))
7330    }
7331
7332    fn row_value_expressions(expr: &Expression) -> Option<Vec<Expression>> {
7333        match expr {
7334            Expression::Tuple(tuple) => Some(tuple.expressions.clone()),
7335            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
7336                Some(function.args.clone())
7337            }
7338            Expression::Paren(paren) => Self::row_value_expressions(&paren.this),
7339            _ => None,
7340        }
7341    }
7342
7343    fn subquery_projection_count(expr: &Expression) -> Option<usize> {
7344        match expr {
7345            Expression::Select(select) => Some(select.expressions.len()),
7346            Expression::Subquery(subquery) => Self::subquery_projection_count(&subquery.this),
7347            Expression::Paren(paren) => Self::subquery_projection_count(&paren.this),
7348            _ => None,
7349        }
7350    }
7351
7352    fn subquery_as_derived_table(
7353        expr: &Expression,
7354        alias: Identifier,
7355        column_aliases: Vec<Identifier>,
7356    ) -> Option<Expression> {
7357        match expr.clone() {
7358            Expression::Subquery(mut subquery) => {
7359                subquery.alias = Some(alias);
7360                subquery.column_aliases = column_aliases;
7361                subquery.alias_explicit_as = true;
7362                subquery.alias_keyword = None;
7363                Some(Expression::Subquery(subquery))
7364            }
7365            Expression::Select(_) | Expression::Paren(_) => {
7366                Some(Expression::Subquery(Box::new(Subquery {
7367                    this: expr.clone(),
7368                    alias: Some(alias),
7369                    column_aliases,
7370                    alias_explicit_as: true,
7371                    alias_keyword: None,
7372                    order_by: None,
7373                    limit: None,
7374                    offset: None,
7375                    distribute_by: None,
7376                    sort_by: None,
7377                    cluster_by: None,
7378                    lateral: false,
7379                    modifiers_inside: false,
7380                    trailing_comments: Vec::new(),
7381                    inferred_type: None,
7382                })))
7383            }
7384            _ => None,
7385        }
7386    }
7387
7388    fn collect_generated_alias_conflicts(expr: &Expression, names: &mut HashSet<String>) {
7389        fn insert(names: &mut HashSet<String>, identifier: &Identifier) {
7390            if !identifier.name.is_empty() {
7391                names.insert(identifier.name.to_ascii_lowercase());
7392            }
7393        }
7394
7395        for node in expr.dfs() {
7396            match node {
7397                Expression::Identifier(identifier) => insert(names, identifier),
7398                Expression::Column(column) => {
7399                    insert(names, &column.name);
7400                    if let Some(table) = &column.table {
7401                        insert(names, table);
7402                    }
7403                }
7404                Expression::Table(table) => {
7405                    insert(names, &table.name);
7406                    if let Some(schema) = &table.schema {
7407                        insert(names, schema);
7408                    }
7409                    if let Some(catalog) = &table.catalog {
7410                        insert(names, catalog);
7411                    }
7412                    if let Some(alias) = &table.alias {
7413                        insert(names, alias);
7414                    }
7415                    for alias in &table.column_aliases {
7416                        insert(names, alias);
7417                    }
7418                }
7419                Expression::Alias(alias) => {
7420                    insert(names, &alias.alias);
7421                    for column_alias in &alias.column_aliases {
7422                        insert(names, column_alias);
7423                    }
7424                }
7425                Expression::Subquery(subquery) => {
7426                    if let Some(alias) = &subquery.alias {
7427                        insert(names, alias);
7428                    }
7429                    for column_alias in &subquery.column_aliases {
7430                        insert(names, column_alias);
7431                    }
7432                }
7433                Expression::Cte(cte) => {
7434                    insert(names, &cte.alias);
7435                    for column in &cte.columns {
7436                        insert(names, column);
7437                    }
7438                    for key in &cte.key_expressions {
7439                        insert(names, key);
7440                    }
7441                }
7442                Expression::Values(values) => {
7443                    if let Some(alias) = &values.alias {
7444                        insert(names, alias);
7445                    }
7446                    for column_alias in &values.column_aliases {
7447                        insert(names, column_alias);
7448                    }
7449                }
7450                Expression::Unnest(unnest) => {
7451                    if let Some(alias) = &unnest.alias {
7452                        insert(names, alias);
7453                    }
7454                    if let Some(offset_alias) = &unnest.offset_alias {
7455                        insert(names, offset_alias);
7456                    }
7457                }
7458                _ => {}
7459            }
7460        }
7461    }
7462
7463    fn rewrite_postgres_format_for_tsql(
7464        expr: Expression,
7465        target: DialectType,
7466    ) -> Result<Expression> {
7467        transform_recursive(expr, &|e| match e {
7468            Expression::Function(f) if f.name.eq_ignore_ascii_case("FORMAT") => {
7469                Self::postgres_format_function_to_tsql(*f, target)
7470            }
7471            other => Ok(other),
7472        })
7473    }
7474
7475    fn postgres_format_function_to_tsql(f: Function, target: DialectType) -> Result<Expression> {
7476        let Some(format_expr) = f.args.first() else {
7477            return Err(Self::unsupported_postgres_format_for_tsql(
7478                target,
7479                "missing format string",
7480            ));
7481        };
7482
7483        let format = match format_expr {
7484            Expression::Literal(lit) if lit.is_string() => lit.value_str(),
7485            _ => {
7486                return Err(Self::unsupported_postgres_format_for_tsql(
7487                    target,
7488                    "dynamic format strings",
7489                ))
7490            }
7491        };
7492
7493        let value_args = &f.args[1..];
7494        let mut arg_index = 0usize;
7495        let mut literal = String::new();
7496        let mut segments = Vec::new();
7497        let mut chars = format.chars();
7498
7499        while let Some(ch) = chars.next() {
7500            if ch != '%' {
7501                literal.push(ch);
7502                continue;
7503            }
7504
7505            let Some(specifier) = chars.next() else {
7506                return Err(Self::unsupported_postgres_format_for_tsql(
7507                    target,
7508                    "unterminated format specifier",
7509                ));
7510            };
7511
7512            match specifier {
7513                '%' => literal.push('%'),
7514                's' => {
7515                    if !literal.is_empty() {
7516                        segments.push(Expression::string(std::mem::take(&mut literal)));
7517                    }
7518                    let Some(arg) = value_args.get(arg_index) else {
7519                        return Err(Self::unsupported_postgres_format_for_tsql(
7520                            target,
7521                            "not enough arguments",
7522                        ));
7523                    };
7524                    segments.push(arg.clone());
7525                    arg_index += 1;
7526                }
7527                other => {
7528                    return Err(Self::unsupported_postgres_format_for_tsql(
7529                        target,
7530                        format!("unsupported format specifier %{other}"),
7531                    ))
7532                }
7533            }
7534        }
7535
7536        if !literal.is_empty() {
7537            segments.push(Expression::string(literal));
7538        }
7539
7540        if arg_index != value_args.len() {
7541            return Err(Self::unsupported_postgres_format_for_tsql(
7542                target,
7543                "unused format arguments",
7544            ));
7545        }
7546
7547        Ok(Self::postgres_format_segments_to_tsql_concat(segments))
7548    }
7549
7550    fn postgres_format_segments_to_tsql_concat(mut segments: Vec<Expression>) -> Expression {
7551        if segments.is_empty() {
7552            return Expression::string("");
7553        }
7554
7555        if segments.len() == 1 {
7556            let only = segments.pop().expect("one segment");
7557            if matches!(&only, Expression::Literal(lit) if lit.is_string()) {
7558                return only;
7559            }
7560
7561            return Expression::Function(Box::new(Function::new(
7562                "CONCAT".to_string(),
7563                vec![only, Expression::string("")],
7564            )));
7565        }
7566
7567        Expression::Function(Box::new(Function::new("CONCAT".to_string(), segments)))
7568    }
7569
7570    fn unsupported_postgres_format_for_tsql(
7571        target: DialectType,
7572        reason: impl Into<String>,
7573    ) -> crate::error::Error {
7574        crate::error::Error::unsupported(
7575            format!("PostgreSQL format() ({})", reason.into()),
7576            target.to_string(),
7577        )
7578    }
7579
7580    fn rewrite_boolean_values_in_tsql_select(
7581        mut select: Box<crate::expressions::Select>,
7582    ) -> Result<Expression> {
7583        if let Some(mut with) = select.with.take() {
7584            with.ctes = with
7585                .ctes
7586                .into_iter()
7587                .map(|mut cte| {
7588                    cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
7589                    Ok(cte)
7590                })
7591                .collect::<Result<Vec<_>>>()?;
7592            select.with = Some(with);
7593        }
7594
7595        select.expressions = select
7596            .expressions
7597            .into_iter()
7598            .map(Self::rewrite_tsql_boolean_scalar_value)
7599            .collect::<Result<Vec<_>>>()?;
7600
7601        if let Some(mut from) = select.from.take() {
7602            from.expressions = from
7603                .expressions
7604                .into_iter()
7605                .map(Self::rewrite_tsql_boolean_nested_contexts)
7606                .collect::<Result<Vec<_>>>()?;
7607            select.from = Some(from);
7608        }
7609
7610        select.joins = select
7611            .joins
7612            .into_iter()
7613            .map(|mut join| {
7614                join.this = Self::rewrite_tsql_boolean_nested_contexts(join.this)?;
7615                if let Some(on) = join.on.take() {
7616                    join.on = Some(Self::rewrite_tsql_boolean_predicate_context(on)?);
7617                }
7618                if let Some(match_condition) = join.match_condition.take() {
7619                    join.match_condition = Some(Self::rewrite_tsql_boolean_predicate_context(
7620                        match_condition,
7621                    )?);
7622                }
7623                join.pivots = join
7624                    .pivots
7625                    .into_iter()
7626                    .map(Self::rewrite_tsql_boolean_nested_contexts)
7627                    .collect::<Result<Vec<_>>>()?;
7628                Ok(join)
7629            })
7630            .collect::<Result<Vec<_>>>()?;
7631
7632        select.lateral_views = select
7633            .lateral_views
7634            .into_iter()
7635            .map(|mut lateral_view| {
7636                lateral_view.this = Self::rewrite_tsql_boolean_nested_contexts(lateral_view.this)?;
7637                Ok(lateral_view)
7638            })
7639            .collect::<Result<Vec<_>>>()?;
7640
7641        if let Some(prewhere) = select.prewhere.take() {
7642            select.prewhere = Some(Self::rewrite_tsql_boolean_predicate_context(prewhere)?);
7643        }
7644
7645        if let Some(mut where_clause) = select.where_clause.take() {
7646            where_clause.this = Self::rewrite_tsql_boolean_predicate_context(where_clause.this)?;
7647            select.where_clause = Some(where_clause);
7648        }
7649
7650        if let Some(mut group_by) = select.group_by.take() {
7651            group_by.expressions = group_by
7652                .expressions
7653                .into_iter()
7654                .map(Self::rewrite_tsql_boolean_scalar_value)
7655                .collect::<Result<Vec<_>>>()?;
7656            select.group_by = Some(group_by);
7657        }
7658
7659        if let Some(mut having) = select.having.take() {
7660            having.this = Self::rewrite_tsql_boolean_predicate_context(having.this)?;
7661            select.having = Some(having);
7662        }
7663
7664        if let Some(mut qualify) = select.qualify.take() {
7665            qualify.this = Self::rewrite_tsql_boolean_predicate_context(qualify.this)?;
7666            select.qualify = Some(qualify);
7667        }
7668
7669        if let Some(mut order_by) = select.order_by.take() {
7670            order_by.expressions = Self::rewrite_tsql_boolean_ordered_values(order_by.expressions)?;
7671            select.order_by = Some(order_by);
7672        }
7673
7674        if let Some(mut distribute_by) = select.distribute_by.take() {
7675            distribute_by.expressions = distribute_by
7676                .expressions
7677                .into_iter()
7678                .map(Self::rewrite_tsql_boolean_scalar_value)
7679                .collect::<Result<Vec<_>>>()?;
7680            select.distribute_by = Some(distribute_by);
7681        }
7682
7683        if let Some(mut cluster_by) = select.cluster_by.take() {
7684            cluster_by.expressions =
7685                Self::rewrite_tsql_boolean_ordered_values(cluster_by.expressions)?;
7686            select.cluster_by = Some(cluster_by);
7687        }
7688
7689        if let Some(mut sort_by) = select.sort_by.take() {
7690            sort_by.expressions = Self::rewrite_tsql_boolean_ordered_values(sort_by.expressions)?;
7691            select.sort_by = Some(sort_by);
7692        }
7693
7694        if let Some(limit_by) = select.limit_by.take() {
7695            select.limit_by = Some(
7696                limit_by
7697                    .into_iter()
7698                    .map(Self::rewrite_tsql_boolean_scalar_value)
7699                    .collect::<Result<Vec<_>>>()?,
7700            );
7701        }
7702
7703        if let Some(distinct_on) = select.distinct_on.take() {
7704            select.distinct_on = Some(
7705                distinct_on
7706                    .into_iter()
7707                    .map(Self::rewrite_tsql_boolean_scalar_value)
7708                    .collect::<Result<Vec<_>>>()?,
7709            );
7710        }
7711
7712        if let Some(mut sample) = select.sample.take() {
7713            sample.size = Self::rewrite_tsql_boolean_nested_contexts(sample.size)?;
7714            if let Some(offset) = sample.offset.take() {
7715                sample.offset = Some(Self::rewrite_tsql_boolean_nested_contexts(offset)?);
7716            }
7717            if let Some(bucket_numerator) = sample.bucket_numerator.take() {
7718                sample.bucket_numerator = Some(Box::new(
7719                    Self::rewrite_tsql_boolean_nested_contexts(*bucket_numerator)?,
7720                ));
7721            }
7722            if let Some(bucket_denominator) = sample.bucket_denominator.take() {
7723                sample.bucket_denominator = Some(Box::new(
7724                    Self::rewrite_tsql_boolean_nested_contexts(*bucket_denominator)?,
7725                ));
7726            }
7727            if let Some(bucket_field) = sample.bucket_field.take() {
7728                sample.bucket_field = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7729                    *bucket_field,
7730                )?));
7731            }
7732            select.sample = Some(sample);
7733        }
7734
7735        if let Some(settings) = select.settings.take() {
7736            select.settings = Some(
7737                settings
7738                    .into_iter()
7739                    .map(Self::rewrite_tsql_boolean_nested_contexts)
7740                    .collect::<Result<Vec<_>>>()?,
7741            );
7742        }
7743
7744        if let Some(format) = select.format.take() {
7745            select.format = Some(Self::rewrite_tsql_boolean_nested_contexts(format)?);
7746        }
7747
7748        if let Some(mut windows) = select.windows.take() {
7749            for window in windows.iter_mut() {
7750                Self::rewrite_tsql_boolean_over_values(&mut window.spec)?;
7751            }
7752            select.windows = Some(windows);
7753        }
7754
7755        Ok(Expression::Select(select))
7756    }
7757
7758    fn normalize_postgres_boolean_semantics_for_tsql(expr: Expression) -> Result<Expression> {
7759        transform_recursive(expr, &|e| match e {
7760            Expression::Function(function)
7761                if function.args.len() == 2
7762                    && (function.name.eq_ignore_ascii_case("BOOLEQ")
7763                        || function.name.eq_ignore_ascii_case("BOOLNE")) =>
7764            {
7765                let is_equal = function.name.eq_ignore_ascii_case("BOOLEQ");
7766                let mut args = function.args.into_iter();
7767                let op = BinaryOp {
7768                    left: args.next().expect("checked boolean operator arity"),
7769                    right: args.next().expect("checked boolean operator arity"),
7770                    left_comments: Vec::new(),
7771                    operator_comments: Vec::new(),
7772                    trailing_comments: function.trailing_comments,
7773                    inferred_type: None,
7774                };
7775                if is_equal {
7776                    Ok(Expression::Eq(Box::new(op)))
7777                } else {
7778                    Ok(Expression::Neq(Box::new(op)))
7779                }
7780            }
7781            Expression::Cast(cast)
7782                if matches!(cast.to, DataType::Text)
7783                    && Self::is_known_postgres_boolean_expression(&cast.this) =>
7784            {
7785                Ok(Self::postgres_boolean_text_value(cast.this))
7786            }
7787            other => Ok(other),
7788        })
7789    }
7790
7791    fn is_known_postgres_boolean_expression(expr: &Expression) -> bool {
7792        match expr {
7793            Expression::Boolean(_) => true,
7794            Expression::Cast(cast) => matches!(cast.to, DataType::Boolean),
7795            Expression::Paren(paren) => Self::is_known_postgres_boolean_expression(&paren.this),
7796            other => Self::is_tsql_boolean_value_expression(other),
7797        }
7798    }
7799
7800    fn postgres_boolean_text_value(predicate: Expression) -> Expression {
7801        if let Expression::Boolean(boolean) = predicate {
7802            return Expression::string(if boolean.value { "true" } else { "false" });
7803        }
7804
7805        Self::three_valued_boolean_case(
7806            predicate,
7807            Expression::string("true"),
7808            Expression::string("false"),
7809        )
7810    }
7811
7812    fn rewrite_tsql_boolean_scalar_value(expr: Expression) -> Result<Expression> {
7813        if let Expression::Boolean(boolean) = expr {
7814            return Ok(Expression::Cast(Box::new(Cast {
7815                this: Expression::Boolean(boolean),
7816                to: DataType::Boolean,
7817                trailing_comments: Vec::new(),
7818                double_colon_syntax: false,
7819                format: None,
7820                default: None,
7821                inferred_type: None,
7822            })));
7823        }
7824
7825        if Self::is_tsql_boolean_value_expression(&expr) {
7826            // Tuple/subquery equality currently lowers only its positive branch to EXISTS.
7827            // Keep its established two-way scalar fallback until that rewrite models UNKNOWN.
7828            let can_be_unknown = Self::tsql_boolean_expression_can_be_unknown(&expr)
7829                && !Self::node_is_row_value_subquery_comparison(&expr);
7830            let predicate = Self::rewrite_tsql_boolean_predicate_context(expr)?;
7831            return Ok(Self::tsql_boolean_value_case(predicate, can_be_unknown));
7832        }
7833
7834        match expr {
7835            Expression::Alias(mut alias) => {
7836                alias.this = Self::rewrite_tsql_boolean_scalar_value(alias.this)?;
7837                Ok(Expression::Alias(alias))
7838            }
7839            Expression::Paren(mut paren) => {
7840                paren.this = Self::rewrite_tsql_boolean_scalar_value(paren.this)?;
7841                Ok(Expression::Paren(paren))
7842            }
7843            Expression::Cast(mut cast) => {
7844                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7845                if let Some(format) = cast.format.take() {
7846                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7847                        *format,
7848                    )?));
7849                }
7850                if let Some(default) = cast.default.take() {
7851                    cast.default =
7852                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
7853                }
7854                Ok(Expression::Cast(cast))
7855            }
7856            Expression::TryCast(mut cast) => {
7857                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7858                if let Some(format) = cast.format.take() {
7859                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7860                        *format,
7861                    )?));
7862                }
7863                if let Some(default) = cast.default.take() {
7864                    cast.default =
7865                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
7866                }
7867                Ok(Expression::TryCast(cast))
7868            }
7869            Expression::SafeCast(mut cast) => {
7870                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7871                if let Some(format) = cast.format.take() {
7872                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7873                        *format,
7874                    )?));
7875                }
7876                if let Some(default) = cast.default.take() {
7877                    cast.default =
7878                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
7879                }
7880                Ok(Expression::SafeCast(cast))
7881            }
7882            Expression::Case(mut case) => {
7883                let is_simple_case = case.operand.is_some();
7884                if let Some(operand) = case.operand.take() {
7885                    case.operand = Some(Self::rewrite_tsql_boolean_scalar_value(operand)?);
7886                }
7887                case.whens = case
7888                    .whens
7889                    .into_iter()
7890                    .map(|(condition, result)| {
7891                        let condition = if is_simple_case {
7892                            Self::rewrite_tsql_boolean_scalar_value(condition)?
7893                        } else {
7894                            Self::rewrite_tsql_boolean_predicate_context(condition)?
7895                        };
7896                        Ok((condition, Self::rewrite_tsql_boolean_scalar_value(result)?))
7897                    })
7898                    .collect::<Result<Vec<_>>>()?;
7899                if let Some(else_) = case.else_.take() {
7900                    case.else_ = Some(Self::rewrite_tsql_boolean_scalar_value(else_)?);
7901                }
7902                Ok(Expression::Case(case))
7903            }
7904            Expression::IfFunc(mut if_func) => {
7905                if_func.condition =
7906                    Self::rewrite_tsql_boolean_predicate_context(if_func.condition)?;
7907                if_func.true_value = Self::rewrite_tsql_boolean_scalar_value(if_func.true_value)?;
7908                if let Some(false_value) = if_func.false_value.take() {
7909                    if_func.false_value =
7910                        Some(Self::rewrite_tsql_boolean_scalar_value(false_value)?);
7911                }
7912                Ok(Expression::IfFunc(if_func))
7913            }
7914            Expression::WindowFunction(mut window_function) => {
7915                window_function.this =
7916                    Self::rewrite_tsql_boolean_nested_contexts(window_function.this)?;
7917                Self::rewrite_tsql_boolean_over_values(&mut window_function.over)?;
7918                if let Some(mut keep) = window_function.keep.take() {
7919                    keep.order_by = Self::rewrite_tsql_boolean_ordered_values(keep.order_by)?;
7920                    window_function.keep = Some(keep);
7921                }
7922                Ok(Expression::WindowFunction(window_function))
7923            }
7924            Expression::WithinGroup(mut within_group) => {
7925                within_group.this = Self::rewrite_tsql_boolean_nested_contexts(within_group.this)?;
7926                within_group.order_by =
7927                    Self::rewrite_tsql_boolean_ordered_values(within_group.order_by)?;
7928                Ok(Expression::WithinGroup(within_group))
7929            }
7930            Expression::Subquery(mut subquery) => {
7931                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
7932                Ok(Expression::Subquery(subquery))
7933            }
7934            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
7935            other => Self::rewrite_tsql_boolean_nested_contexts(other),
7936        }
7937    }
7938
7939    fn rewrite_tsql_boolean_predicate_context(expr: Expression) -> Result<Expression> {
7940        let expr = Self::rewrite_tsql_boolean_nested_contexts(expr)?;
7941        Ok(crate::transforms::ensure_bool_condition(expr))
7942    }
7943
7944    fn rewrite_tsql_boolean_nested_contexts(expr: Expression) -> Result<Expression> {
7945        transform_recursive(expr, &|e| match e {
7946            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
7947            Expression::Subquery(mut subquery) => {
7948                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
7949                Ok(Expression::Subquery(subquery))
7950            }
7951            Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => {
7952                Self::rewrite_boolean_values_for_tsql(e)
7953            }
7954            other => Self::rewrite_tsql_boolean_cast_operand(other),
7955        })
7956    }
7957
7958    fn rewrite_tsql_boolean_cast_operand(expr: Expression) -> Result<Expression> {
7959        macro_rules! rewrite_cast_operand {
7960            ($variant:ident, $cast:expr) => {{
7961                let mut cast = $cast;
7962                if Self::is_tsql_boolean_value_expression(&cast.this) {
7963                    cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7964                }
7965                Ok(Expression::$variant(cast))
7966            }};
7967        }
7968
7969        match expr {
7970            Expression::Cast(cast) => rewrite_cast_operand!(Cast, cast),
7971            Expression::TryCast(cast) => rewrite_cast_operand!(TryCast, cast),
7972            Expression::SafeCast(cast) => rewrite_cast_operand!(SafeCast, cast),
7973            other => Ok(other),
7974        }
7975    }
7976
7977    fn rewrite_tsql_boolean_ordered_values(
7978        ordered: Vec<crate::expressions::Ordered>,
7979    ) -> Result<Vec<crate::expressions::Ordered>> {
7980        ordered
7981            .into_iter()
7982            .map(|mut ordered| {
7983                ordered.this = Self::rewrite_tsql_boolean_scalar_value(ordered.this)?;
7984                if let Some(with_fill) = ordered.with_fill.take() {
7985                    ordered.with_fill = Some(Box::new(
7986                        Self::rewrite_tsql_boolean_with_fill_values(*with_fill)?,
7987                    ));
7988                }
7989                Ok(ordered)
7990            })
7991            .collect()
7992    }
7993
7994    fn rewrite_tsql_boolean_with_fill_values(
7995        mut with_fill: crate::expressions::WithFill,
7996    ) -> Result<crate::expressions::WithFill> {
7997        if let Some(from) = with_fill.from_.take() {
7998            with_fill.from_ = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*from)?));
7999        }
8000        if let Some(to) = with_fill.to.take() {
8001            with_fill.to = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*to)?));
8002        }
8003        if let Some(step) = with_fill.step.take() {
8004            with_fill.step = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*step)?));
8005        }
8006        if let Some(staleness) = with_fill.staleness.take() {
8007            with_fill.staleness = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
8008                *staleness,
8009            )?));
8010        }
8011        if let Some(interpolate) = with_fill.interpolate.take() {
8012            with_fill.interpolate = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
8013                *interpolate,
8014            )?));
8015        }
8016        Ok(with_fill)
8017    }
8018
8019    fn rewrite_tsql_boolean_over_values(over: &mut crate::expressions::Over) -> Result<()> {
8020        over.partition_by = std::mem::take(&mut over.partition_by)
8021            .into_iter()
8022            .map(Self::rewrite_tsql_boolean_scalar_value)
8023            .collect::<Result<Vec<_>>>()?;
8024        over.order_by =
8025            Self::rewrite_tsql_boolean_ordered_values(std::mem::take(&mut over.order_by))?;
8026        Ok(())
8027    }
8028
8029    fn is_tsql_boolean_value_expression(expr: &Expression) -> bool {
8030        match expr {
8031            Expression::Paren(paren) => Self::is_tsql_boolean_value_expression(&paren.this),
8032            Expression::Eq(_)
8033            | Expression::Neq(_)
8034            | Expression::Lt(_)
8035            | Expression::Lte(_)
8036            | Expression::Gt(_)
8037            | Expression::Gte(_)
8038            | Expression::Is(_)
8039            | Expression::IsNull(_)
8040            | Expression::IsTrue(_)
8041            | Expression::IsFalse(_)
8042            | Expression::Like(_)
8043            | Expression::ILike(_)
8044            | Expression::StartsWith(_)
8045            | Expression::SimilarTo(_)
8046            | Expression::Glob(_)
8047            | Expression::RegexpLike(_)
8048            | Expression::In(_)
8049            | Expression::Between(_)
8050            | Expression::Exists(_)
8051            | Expression::And(_)
8052            | Expression::Or(_)
8053            | Expression::Not(_)
8054            | Expression::Any(_)
8055            | Expression::All(_)
8056            | Expression::NullSafeEq(_)
8057            | Expression::NullSafeNeq(_)
8058            | Expression::EqualNull(_) => true,
8059            _ => false,
8060        }
8061    }
8062
8063    fn tsql_boolean_expression_can_be_unknown(expr: &Expression) -> bool {
8064        match expr {
8065            Expression::Boolean(_)
8066            | Expression::IsNull(_)
8067            | Expression::IsTrue(_)
8068            | Expression::IsFalse(_)
8069            | Expression::Exists(_)
8070            | Expression::NullSafeEq(_)
8071            | Expression::NullSafeNeq(_)
8072            | Expression::EqualNull(_) => false,
8073            Expression::Paren(paren) => Self::tsql_boolean_expression_can_be_unknown(&paren.this),
8074            Expression::Not(op) => Self::tsql_boolean_expression_can_be_unknown(&op.this),
8075            Expression::And(op) | Expression::Or(op) => {
8076                Self::tsql_boolean_expression_can_be_unknown(&op.left)
8077                    || Self::tsql_boolean_expression_can_be_unknown(&op.right)
8078            }
8079            _ => true,
8080        }
8081    }
8082
8083    fn tsql_boolean_value_case(predicate: Expression, can_be_unknown: bool) -> Expression {
8084        let case = if can_be_unknown {
8085            Self::three_valued_boolean_case(predicate, Expression::number(1), Expression::number(0))
8086        } else {
8087            Expression::Case(Box::new(crate::expressions::Case {
8088                operand: None,
8089                whens: vec![(predicate, Expression::number(1))],
8090                else_: Some(Expression::number(0)),
8091                comments: Vec::new(),
8092                inferred_type: None,
8093            }))
8094        };
8095
8096        Expression::Cast(Box::new(Cast {
8097            this: case,
8098            to: DataType::Boolean,
8099            trailing_comments: Vec::new(),
8100            double_colon_syntax: false,
8101            format: None,
8102            default: None,
8103            inferred_type: None,
8104        }))
8105    }
8106
8107    fn three_valued_boolean_case(
8108        predicate: Expression,
8109        true_value: Expression,
8110        false_value: Expression,
8111    ) -> Expression {
8112        let false_operand = if matches!(predicate, Expression::And(_) | Expression::Or(_)) {
8113            Expression::Paren(Box::new(crate::expressions::Paren {
8114                this: predicate.clone(),
8115                trailing_comments: Vec::new(),
8116            }))
8117        } else {
8118            predicate.clone()
8119        };
8120        let false_predicate = Expression::Not(Box::new(crate::expressions::UnaryOp {
8121            this: false_operand,
8122            inferred_type: None,
8123        }));
8124
8125        Expression::Case(Box::new(crate::expressions::Case {
8126            operand: None,
8127            whens: vec![(predicate, true_value), (false_predicate, false_value)],
8128            else_: Some(Expression::null()),
8129            comments: Vec::new(),
8130            inferred_type: None,
8131        }))
8132    }
8133
8134    fn rewrite_aggregate_filters_for_tsql(expr: Expression) -> Result<Expression> {
8135        transform_recursive(expr, &|e| Self::rewrite_aggregate_filter_for_tsql(e))
8136    }
8137
8138    fn rewrite_aggregate_filter_for_tsql(expr: Expression) -> Result<Expression> {
8139        macro_rules! rewrite_agg_filter {
8140            ($variant:ident, $agg:expr) => {{
8141                let mut agg = $agg;
8142                if let Some(filter) = agg.filter.take() {
8143                    let this = std::mem::replace(&mut agg.this, Expression::null());
8144                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8145                }
8146                Ok(Expression::$variant(agg))
8147            }};
8148        }
8149
8150        match expr {
8151            Expression::Filter(filter) => {
8152                let condition = match *filter.expression {
8153                    Expression::Where(where_) => where_.this,
8154                    other => other,
8155                };
8156                Ok(Self::push_filter_into_tsql_aggregate(
8157                    *filter.this,
8158                    condition,
8159                ))
8160            }
8161            Expression::AggregateFunction(mut agg) => {
8162                if let Some(filter) = agg.filter.take() {
8163                    Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
8164                }
8165                Ok(Expression::AggregateFunction(agg))
8166            }
8167            Expression::Count(mut count) => {
8168                if let Some(filter) = count.filter.take() {
8169                    let value = if count.star {
8170                        Expression::number(1)
8171                    } else {
8172                        count.this.take().unwrap_or_else(|| Expression::number(1))
8173                    };
8174                    count.star = false;
8175                    count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
8176                }
8177                Ok(Expression::Count(count))
8178            }
8179            Expression::Sum(agg) => rewrite_agg_filter!(Sum, agg),
8180            Expression::Avg(agg) => rewrite_agg_filter!(Avg, agg),
8181            Expression::Min(agg) => rewrite_agg_filter!(Min, agg),
8182            Expression::Max(agg) => rewrite_agg_filter!(Max, agg),
8183            Expression::ArrayAgg(agg) => rewrite_agg_filter!(ArrayAgg, agg),
8184            Expression::CountIf(agg) => Ok(Expression::CountIf(agg)),
8185            Expression::Stddev(agg) => rewrite_agg_filter!(Stddev, agg),
8186            Expression::StddevPop(agg) => rewrite_agg_filter!(StddevPop, agg),
8187            Expression::StddevSamp(agg) => rewrite_agg_filter!(StddevSamp, agg),
8188            Expression::Variance(agg) => rewrite_agg_filter!(Variance, agg),
8189            Expression::VarPop(agg) => rewrite_agg_filter!(VarPop, agg),
8190            Expression::VarSamp(agg) => rewrite_agg_filter!(VarSamp, agg),
8191            Expression::Median(agg) => rewrite_agg_filter!(Median, agg),
8192            Expression::Mode(agg) => rewrite_agg_filter!(Mode, agg),
8193            Expression::First(agg) => rewrite_agg_filter!(First, agg),
8194            Expression::Last(agg) => rewrite_agg_filter!(Last, agg),
8195            Expression::AnyValue(agg) => rewrite_agg_filter!(AnyValue, agg),
8196            Expression::ApproxDistinct(agg) => rewrite_agg_filter!(ApproxDistinct, agg),
8197            Expression::ApproxCountDistinct(agg) => {
8198                rewrite_agg_filter!(ApproxCountDistinct, agg)
8199            }
8200            Expression::LogicalAnd(agg) => rewrite_agg_filter!(LogicalAnd, agg),
8201            Expression::LogicalOr(agg) => rewrite_agg_filter!(LogicalOr, agg),
8202            Expression::Skewness(agg) => rewrite_agg_filter!(Skewness, agg),
8203            Expression::ArrayConcatAgg(agg) => rewrite_agg_filter!(ArrayConcatAgg, agg),
8204            Expression::ArrayUniqueAgg(agg) => rewrite_agg_filter!(ArrayUniqueAgg, agg),
8205            Expression::BoolXorAgg(agg) => rewrite_agg_filter!(BoolXorAgg, agg),
8206            Expression::BitwiseAndAgg(agg) => rewrite_agg_filter!(BitwiseAndAgg, agg),
8207            Expression::BitwiseOrAgg(agg) => rewrite_agg_filter!(BitwiseOrAgg, agg),
8208            Expression::BitwiseXorAgg(agg) => rewrite_agg_filter!(BitwiseXorAgg, agg),
8209            Expression::StringAgg(mut agg) => {
8210                if let Some(filter) = agg.filter.take() {
8211                    let this = std::mem::replace(&mut agg.this, Expression::null());
8212                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8213                }
8214                Ok(Expression::StringAgg(agg))
8215            }
8216            Expression::GroupConcat(mut agg) => {
8217                if let Some(filter) = agg.filter.take() {
8218                    let this = std::mem::replace(&mut agg.this, Expression::null());
8219                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8220                }
8221                Ok(Expression::GroupConcat(agg))
8222            }
8223            Expression::ListAgg(mut agg) => {
8224                if let Some(filter) = agg.filter.take() {
8225                    let this = std::mem::replace(&mut agg.this, Expression::null());
8226                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8227                }
8228                Ok(Expression::ListAgg(agg))
8229            }
8230            Expression::WithinGroup(mut within_group) => {
8231                within_group.this = Self::rewrite_aggregate_filters_for_tsql(within_group.this)?;
8232                Ok(Expression::WithinGroup(within_group))
8233            }
8234            other => Ok(other),
8235        }
8236    }
8237
8238    fn push_filter_into_tsql_aggregate(expr: Expression, filter: Expression) -> Expression {
8239        macro_rules! push_agg_filter {
8240            ($variant:ident, $agg:expr) => {{
8241                let mut agg = $agg;
8242                let this = std::mem::replace(&mut agg.this, Expression::null());
8243                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8244                agg.filter = None;
8245                Expression::$variant(agg)
8246            }};
8247        }
8248
8249        match expr {
8250            Expression::AggregateFunction(mut agg) => {
8251                Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
8252                Expression::AggregateFunction(agg)
8253            }
8254            Expression::Count(mut count) => {
8255                let value = if count.star {
8256                    Expression::number(1)
8257                } else {
8258                    count.this.take().unwrap_or_else(|| Expression::number(1))
8259                };
8260                count.star = false;
8261                count.filter = None;
8262                count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
8263                Expression::Count(count)
8264            }
8265            Expression::Sum(agg) => push_agg_filter!(Sum, agg),
8266            Expression::Avg(agg) => push_agg_filter!(Avg, agg),
8267            Expression::Min(agg) => push_agg_filter!(Min, agg),
8268            Expression::Max(agg) => push_agg_filter!(Max, agg),
8269            Expression::ArrayAgg(agg) => push_agg_filter!(ArrayAgg, agg),
8270            Expression::CountIf(mut agg) => {
8271                agg.filter = Some(filter);
8272                Expression::CountIf(agg)
8273            }
8274            Expression::Stddev(agg) => push_agg_filter!(Stddev, agg),
8275            Expression::StddevPop(agg) => push_agg_filter!(StddevPop, agg),
8276            Expression::StddevSamp(agg) => push_agg_filter!(StddevSamp, agg),
8277            Expression::Variance(agg) => push_agg_filter!(Variance, agg),
8278            Expression::VarPop(agg) => push_agg_filter!(VarPop, agg),
8279            Expression::VarSamp(agg) => push_agg_filter!(VarSamp, agg),
8280            Expression::Median(agg) => push_agg_filter!(Median, agg),
8281            Expression::Mode(agg) => push_agg_filter!(Mode, agg),
8282            Expression::First(agg) => push_agg_filter!(First, agg),
8283            Expression::Last(agg) => push_agg_filter!(Last, agg),
8284            Expression::AnyValue(agg) => push_agg_filter!(AnyValue, agg),
8285            Expression::ApproxDistinct(agg) => push_agg_filter!(ApproxDistinct, agg),
8286            Expression::ApproxCountDistinct(agg) => {
8287                push_agg_filter!(ApproxCountDistinct, agg)
8288            }
8289            Expression::LogicalAnd(agg) => push_agg_filter!(LogicalAnd, agg),
8290            Expression::LogicalOr(agg) => push_agg_filter!(LogicalOr, agg),
8291            Expression::Skewness(agg) => push_agg_filter!(Skewness, agg),
8292            Expression::ArrayConcatAgg(agg) => push_agg_filter!(ArrayConcatAgg, agg),
8293            Expression::ArrayUniqueAgg(agg) => push_agg_filter!(ArrayUniqueAgg, agg),
8294            Expression::BoolXorAgg(agg) => push_agg_filter!(BoolXorAgg, agg),
8295            Expression::BitwiseAndAgg(agg) => push_agg_filter!(BitwiseAndAgg, agg),
8296            Expression::BitwiseOrAgg(agg) => push_agg_filter!(BitwiseOrAgg, agg),
8297            Expression::BitwiseXorAgg(agg) => push_agg_filter!(BitwiseXorAgg, agg),
8298            Expression::StringAgg(mut agg) => {
8299                let this = std::mem::replace(&mut agg.this, Expression::null());
8300                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8301                agg.filter = None;
8302                Expression::StringAgg(agg)
8303            }
8304            Expression::GroupConcat(mut agg) => {
8305                let this = std::mem::replace(&mut agg.this, Expression::null());
8306                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8307                agg.filter = None;
8308                Expression::GroupConcat(agg)
8309            }
8310            Expression::ListAgg(mut agg) => {
8311                let this = std::mem::replace(&mut agg.this, Expression::null());
8312                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8313                agg.filter = None;
8314                Expression::ListAgg(agg)
8315            }
8316            Expression::WithinGroup(mut within_group) => {
8317                within_group.this =
8318                    Self::push_filter_into_tsql_aggregate(within_group.this, filter);
8319                Expression::WithinGroup(within_group)
8320            }
8321            other => Expression::Filter(Box::new(crate::expressions::Filter {
8322                this: Box::new(other),
8323                expression: Box::new(filter),
8324            })),
8325        }
8326    }
8327
8328    fn rewrite_generic_aggregate_filter_for_tsql(
8329        agg: &mut crate::expressions::AggregateFunction,
8330        filter: Expression,
8331    ) {
8332        let is_count =
8333            agg.name.eq_ignore_ascii_case("COUNT") || agg.name.eq_ignore_ascii_case("COUNT_BIG");
8334        let is_count_star = is_count
8335            && (agg.args.is_empty()
8336                || (agg.args.len() == 1 && matches!(agg.args[0], Expression::Star(_))));
8337
8338        if is_count_star {
8339            agg.args = vec![Self::conditional_aggregate_value_for_tsql(
8340                filter,
8341                Expression::number(1),
8342            )];
8343        } else if !agg.args.is_empty() {
8344            agg.args = agg
8345                .args
8346                .drain(..)
8347                .map(|arg| Self::conditional_aggregate_value_for_tsql(filter.clone(), arg))
8348                .collect();
8349        } else {
8350            agg.filter = Some(filter);
8351        }
8352    }
8353
8354    fn conditional_aggregate_value_for_tsql(filter: Expression, value: Expression) -> Expression {
8355        let filter = crate::transforms::ensure_bool_condition(filter);
8356        Expression::Case(Box::new(crate::expressions::Case {
8357            operand: None,
8358            whens: vec![(filter, value)],
8359            else_: None,
8360            comments: Vec::new(),
8361            inferred_type: None,
8362        }))
8363    }
8364
8365    fn reject_pgvector_distance_operators_for_sqlite(&self, sql: &str) -> Result<()> {
8366        let tokens = self.tokenize(sql)?;
8367        for (i, token) in tokens.iter().enumerate() {
8368            if token.token_type == TokenType::NullsafeEq {
8369                return Err(crate::error::Error::unsupported(
8370                    "PostgreSQL pgvector cosine distance operator <=>",
8371                    "SQLite",
8372                ));
8373            }
8374            if token.token_type == TokenType::Lt
8375                && tokens
8376                    .get(i + 1)
8377                    .is_some_and(|token| token.token_type == TokenType::Tilde)
8378                && tokens
8379                    .get(i + 2)
8380                    .is_some_and(|token| token.token_type == TokenType::Gt)
8381            {
8382                return Err(crate::error::Error::unsupported(
8383                    "PostgreSQL pgvector Hamming distance operator <~>",
8384                    "SQLite",
8385                ));
8386            }
8387        }
8388        Ok(())
8389    }
8390
8391    fn normalize_sqlite_double_quoted_defaults(expr: Expression) -> Result<Expression> {
8392        fn normalize_default_expr(expr: Expression) -> Result<Expression> {
8393            transform_recursive(expr, &|e| match e {
8394                Expression::Column(col)
8395                    if col.table.is_none() && col.name.quoted && !col.join_mark =>
8396                {
8397                    Ok(Expression::Literal(Box::new(Literal::String(
8398                        col.name.name,
8399                    ))))
8400                }
8401                Expression::Identifier(id) if id.quoted => {
8402                    Ok(Expression::Literal(Box::new(Literal::String(id.name))))
8403                }
8404                _ => Ok(e),
8405            })
8406        }
8407
8408        fn normalize_column_default(col: &mut crate::expressions::ColumnDef) -> Result<()> {
8409            if let Some(default) = col.default.take() {
8410                col.default = Some(normalize_default_expr(default)?);
8411            }
8412
8413            for constraint in &mut col.constraints {
8414                if let ColumnConstraint::Default(default) = constraint {
8415                    *default = normalize_default_expr(default.clone())?;
8416                }
8417            }
8418
8419            Ok(())
8420        }
8421
8422        transform_recursive(expr, &|e| match e {
8423            Expression::CreateTable(mut ct) => {
8424                for column in &mut ct.columns {
8425                    normalize_column_default(column)?;
8426                }
8427                Ok(Expression::CreateTable(ct))
8428            }
8429            Expression::ColumnDef(mut col) => {
8430                normalize_column_default(&mut col)?;
8431                Ok(Expression::ColumnDef(col))
8432            }
8433            _ => Ok(e),
8434        })
8435    }
8436
8437    fn normalize_postgres_to_sqlite_types(expr: Expression) -> Result<Expression> {
8438        fn sqlite_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
8439            use crate::expressions::DataType;
8440
8441            match dt {
8442                DataType::Bit { .. } => DataType::Int {
8443                    length: None,
8444                    integer_spelling: true,
8445                },
8446                DataType::TextWithLength { .. } => DataType::Text,
8447                DataType::VarChar { .. } => DataType::Text,
8448                DataType::Char { .. } => DataType::Text,
8449                DataType::Timestamp { timezone: true, .. } => DataType::Text,
8450                DataType::Custom { name } => {
8451                    let base = name
8452                        .split_once('(')
8453                        .map_or(name.as_str(), |(base, _)| base)
8454                        .trim();
8455                    if base.eq_ignore_ascii_case("TSVECTOR")
8456                        || base.eq_ignore_ascii_case("TIMESTAMPTZ")
8457                        || base.eq_ignore_ascii_case("TIMESTAMP WITH TIME ZONE")
8458                        || base.eq_ignore_ascii_case("NVARCHAR")
8459                        || base.eq_ignore_ascii_case("NCHAR")
8460                    {
8461                        DataType::Text
8462                    } else {
8463                        DataType::Custom { name }
8464                    }
8465                }
8466                _ => dt,
8467            }
8468        }
8469
8470        transform_recursive(expr, &|e| match e {
8471            Expression::DataType(dt) => Ok(Expression::DataType(sqlite_type(dt))),
8472            Expression::CreateTable(mut ct) => {
8473                for column in &mut ct.columns {
8474                    column.data_type = sqlite_type(column.data_type.clone());
8475                }
8476                Ok(Expression::CreateTable(ct))
8477            }
8478            _ => Ok(e),
8479        })
8480    }
8481
8482    fn normalize_postgres_to_fabric_types(expr: Expression) -> Result<Expression> {
8483        fn fabric_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
8484            use crate::expressions::DataType;
8485
8486            match dt {
8487                DataType::Decimal {
8488                    precision: None,
8489                    scale: None,
8490                } => DataType::Decimal {
8491                    precision: Some(38),
8492                    scale: Some(10),
8493                },
8494                DataType::Json | DataType::JsonB => DataType::Custom {
8495                    name: "VARCHAR(MAX)".to_string(),
8496                },
8497                _ => dt,
8498            }
8499        }
8500
8501        transform_recursive(expr, &|e| match e {
8502            Expression::DataType(dt) => Ok(Expression::DataType(fabric_type(dt))),
8503            Expression::CreateTable(mut ct) => {
8504                for column in &mut ct.columns {
8505                    column.data_type = fabric_type(column.data_type.clone());
8506                }
8507                Ok(Expression::CreateTable(ct))
8508            }
8509            Expression::ColumnDef(mut col) => {
8510                col.data_type = fabric_type(col.data_type);
8511                Ok(Expression::ColumnDef(col))
8512            }
8513            _ => Ok(e),
8514        })
8515    }
8516
8517    /// For DuckDB target: when FROM clause contains RANGE(n), replace
8518    /// `(ROW_NUMBER() OVER (ORDER BY 1 NULLS FIRST) - 1)` with `range` in select expressions.
8519    /// This handles SEQ1/2/4/8 → RANGE transpilation from Snowflake.
8520    fn seq_rownum_to_range(expr: Expression) -> Result<Expression> {
8521        if let Expression::Select(mut select) = expr {
8522            // Check if FROM contains a RANGE function
8523            let has_range_from = if let Some(ref from) = select.from {
8524                from.expressions.iter().any(|e| {
8525                    // Check for direct RANGE(...) or aliased RANGE(...)
8526                    match e {
8527                        Expression::Function(f) => f.name.eq_ignore_ascii_case("RANGE"),
8528                        Expression::Alias(a) => {
8529                            matches!(&a.this, Expression::Function(f) if f.name.eq_ignore_ascii_case("RANGE"))
8530                        }
8531                        _ => false,
8532                    }
8533                })
8534            } else {
8535                false
8536            };
8537
8538            if has_range_from {
8539                // Replace the ROW_NUMBER pattern in select expressions
8540                select.expressions = select
8541                    .expressions
8542                    .into_iter()
8543                    .map(|e| Self::replace_rownum_with_range(e))
8544                    .collect();
8545            }
8546
8547            Ok(Expression::Select(select))
8548        } else {
8549            Ok(expr)
8550        }
8551    }
8552
8553    /// Replace `(ROW_NUMBER() OVER (...) - 1)` with `range` column reference
8554    fn replace_rownum_with_range(expr: Expression) -> Expression {
8555        match expr {
8556            // Match: (ROW_NUMBER() OVER (...) - 1) % N → range % N
8557            Expression::Mod(op) => {
8558                let new_left = Self::try_replace_rownum_paren(&op.left);
8559                Expression::Mod(Box::new(crate::expressions::BinaryOp {
8560                    left: new_left,
8561                    right: op.right,
8562                    left_comments: op.left_comments,
8563                    operator_comments: op.operator_comments,
8564                    trailing_comments: op.trailing_comments,
8565                    inferred_type: op.inferred_type,
8566                }))
8567            }
8568            // Match: (CASE WHEN (ROW...) % N >= ... THEN ... ELSE ... END)
8569            Expression::Paren(p) => {
8570                let inner = Self::replace_rownum_with_range(p.this);
8571                Expression::Paren(Box::new(crate::expressions::Paren {
8572                    this: inner,
8573                    trailing_comments: p.trailing_comments,
8574                }))
8575            }
8576            Expression::Case(mut c) => {
8577                // Replace ROW_NUMBER in WHEN conditions and THEN expressions
8578                c.whens = c
8579                    .whens
8580                    .into_iter()
8581                    .map(|(cond, then)| {
8582                        (
8583                            Self::replace_rownum_with_range(cond),
8584                            Self::replace_rownum_with_range(then),
8585                        )
8586                    })
8587                    .collect();
8588                if let Some(else_) = c.else_ {
8589                    c.else_ = Some(Self::replace_rownum_with_range(else_));
8590                }
8591                Expression::Case(c)
8592            }
8593            Expression::Gte(op) => Expression::Gte(Box::new(crate::expressions::BinaryOp {
8594                left: Self::replace_rownum_with_range(op.left),
8595                right: op.right,
8596                left_comments: op.left_comments,
8597                operator_comments: op.operator_comments,
8598                trailing_comments: op.trailing_comments,
8599                inferred_type: op.inferred_type,
8600            })),
8601            Expression::Sub(op) => Expression::Sub(Box::new(crate::expressions::BinaryOp {
8602                left: Self::replace_rownum_with_range(op.left),
8603                right: op.right,
8604                left_comments: op.left_comments,
8605                operator_comments: op.operator_comments,
8606                trailing_comments: op.trailing_comments,
8607                inferred_type: op.inferred_type,
8608            })),
8609            Expression::Alias(mut a) => {
8610                a.this = Self::replace_rownum_with_range(a.this);
8611                Expression::Alias(a)
8612            }
8613            other => other,
8614        }
8615    }
8616
8617    /// Check if an expression is `(ROW_NUMBER() OVER (...) - 1)` and replace with `range`
8618    fn try_replace_rownum_paren(expr: &Expression) -> Expression {
8619        if let Expression::Paren(ref p) = expr {
8620            if let Expression::Sub(ref sub) = p.this {
8621                if let Expression::WindowFunction(ref wf) = sub.left {
8622                    if let Expression::Function(ref f) = wf.this {
8623                        if f.name.eq_ignore_ascii_case("ROW_NUMBER") {
8624                            if let Expression::Literal(ref lit) = sub.right {
8625                                if let crate::expressions::Literal::Number(ref n) = lit.as_ref() {
8626                                    if n == "1" {
8627                                        return Expression::column("range");
8628                                    }
8629                                }
8630                            }
8631                        }
8632                    }
8633                }
8634            }
8635        }
8636        expr.clone()
8637    }
8638
8639    /// Transform BigQuery GENERATE_DATE_ARRAY in UNNEST for Snowflake target.
8640    /// Converts:
8641    ///   SELECT ..., alias, ... FROM t CROSS JOIN UNNEST(GENERATE_DATE_ARRAY(start, end, INTERVAL '1' unit)) AS alias
8642    /// To:
8643    ///   SELECT ..., DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE)) AS alias, ...
8644    ///   FROM t, LATERAL FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)) AS _t0(seq, key, path, index, alias, this)
8645    fn transform_generate_date_array_snowflake(expr: Expression) -> Result<Expression> {
8646        use crate::expressions::*;
8647        transform_recursive(expr, &|e| {
8648            // Handle ARRAY_SIZE(GENERATE_DATE_ARRAY(...)) -> ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM subquery))
8649            if let Expression::ArraySize(ref af) = e {
8650                if let Expression::Function(ref f) = af.this {
8651                    if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
8652                        let result = Self::convert_array_size_gda_snowflake(f)?;
8653                        return Ok(result);
8654                    }
8655                }
8656            }
8657
8658            let Expression::Select(mut sel) = e else {
8659                return Ok(e);
8660            };
8661
8662            // Find joins with UNNEST containing GenerateSeries (from GENERATE_DATE_ARRAY conversion)
8663            let mut gda_info: Option<(String, Expression, Expression, String)> = None; // (alias_name, start_expr, end_expr, unit)
8664            let mut gda_join_idx: Option<usize> = None;
8665
8666            for (idx, join) in sel.joins.iter().enumerate() {
8667                // The join.this may be:
8668                // 1. Unnest(UnnestFunc { alias: Some("mnth"), ... })
8669                // 2. Alias(Alias { this: Unnest(UnnestFunc { alias: None, ... }), alias: "mnth", ... })
8670                let (unnest_ref, alias_name) = match &join.this {
8671                    Expression::Unnest(ref unnest) => {
8672                        let alias = unnest.alias.as_ref().map(|id| id.name.clone());
8673                        (Some(unnest.as_ref()), alias)
8674                    }
8675                    Expression::Alias(ref a) => {
8676                        if let Expression::Unnest(ref unnest) = a.this {
8677                            (Some(unnest.as_ref()), Some(a.alias.name.clone()))
8678                        } else {
8679                            (None, None)
8680                        }
8681                    }
8682                    _ => (None, None),
8683                };
8684
8685                if let (Some(unnest), Some(alias)) = (unnest_ref, alias_name) {
8686                    // Check the main expression (this) of the UNNEST for GENERATE_DATE_ARRAY function
8687                    if let Expression::Function(ref f) = unnest.this {
8688                        if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
8689                            let start_expr = f.args[0].clone();
8690                            let end_expr = f.args[1].clone();
8691                            let step = f.args.get(2).cloned();
8692
8693                            // Extract unit from step interval
8694                            let unit = if let Some(Expression::Interval(ref iv)) = step {
8695                                if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
8696                                    Some(format!("{:?}", unit).to_ascii_uppercase())
8697                                } else if let Some(ref this) = iv.this {
8698                                    // The interval may be stored as a string like "1 MONTH"
8699                                    if let Expression::Literal(lit) = this {
8700                                        if let Literal::String(ref s) = lit.as_ref() {
8701                                            let parts: Vec<&str> = s.split_whitespace().collect();
8702                                            if parts.len() == 2 {
8703                                                Some(parts[1].to_ascii_uppercase())
8704                                            } else if parts.len() == 1 {
8705                                                // Single word like "MONTH" or just "1"
8706                                                let upper = parts[0].to_ascii_uppercase();
8707                                                if matches!(
8708                                                    upper.as_str(),
8709                                                    "YEAR"
8710                                                        | "QUARTER"
8711                                                        | "MONTH"
8712                                                        | "WEEK"
8713                                                        | "DAY"
8714                                                        | "HOUR"
8715                                                        | "MINUTE"
8716                                                        | "SECOND"
8717                                                ) {
8718                                                    Some(upper)
8719                                                } else {
8720                                                    None
8721                                                }
8722                                            } else {
8723                                                None
8724                                            }
8725                                        } else {
8726                                            None
8727                                        }
8728                                    } else {
8729                                        None
8730                                    }
8731                                } else {
8732                                    None
8733                                }
8734                            } else {
8735                                None
8736                            };
8737
8738                            if let Some(unit_str) = unit {
8739                                gda_info = Some((alias, start_expr, end_expr, unit_str));
8740                                gda_join_idx = Some(idx);
8741                            }
8742                        }
8743                    }
8744                }
8745                if gda_info.is_some() {
8746                    break;
8747                }
8748            }
8749
8750            let Some((alias_name, start_expr, end_expr, unit_str)) = gda_info else {
8751                // Also check FROM clause for UNNEST(GENERATE_DATE_ARRAY(...)) patterns
8752                // This handles Generic->Snowflake where GENERATE_DATE_ARRAY is in FROM, not in JOIN
8753                let result = Self::try_transform_from_gda_snowflake(sel);
8754                return result;
8755            };
8756            let join_idx = gda_join_idx.unwrap();
8757
8758            // Build ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)
8759            // ARRAY_GENERATE_RANGE uses exclusive end, and we need DATEDIFF + 1 values
8760            // (inclusive date range), so the exclusive end is DATEDIFF + 1.
8761            let datediff = Expression::Function(Box::new(Function::new(
8762                "DATEDIFF".to_string(),
8763                vec![
8764                    Expression::boxed_column(Column {
8765                        name: Identifier::new(&unit_str),
8766                        table: None,
8767                        join_mark: false,
8768                        trailing_comments: vec![],
8769                        span: None,
8770                        inferred_type: None,
8771                    }),
8772                    start_expr.clone(),
8773                    end_expr.clone(),
8774                ],
8775            )));
8776            let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
8777                left: datediff,
8778                right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
8779                left_comments: vec![],
8780                operator_comments: vec![],
8781                trailing_comments: vec![],
8782                inferred_type: None,
8783            }));
8784
8785            let array_gen_range = Expression::Function(Box::new(Function::new(
8786                "ARRAY_GENERATE_RANGE".to_string(),
8787                vec![
8788                    Expression::Literal(Box::new(Literal::Number("0".to_string()))),
8789                    datediff_plus_one,
8790                ],
8791            )));
8792
8793            // Build FLATTEN(INPUT => ARRAY_GENERATE_RANGE(...))
8794            let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
8795                name: Identifier::new("INPUT"),
8796                value: array_gen_range,
8797                separator: crate::expressions::NamedArgSeparator::DArrow,
8798            }));
8799            let flatten = Expression::Function(Box::new(Function::new(
8800                "FLATTEN".to_string(),
8801                vec![flatten_input],
8802            )));
8803
8804            // Build LATERAL FLATTEN(...) AS _t0(seq, key, path, index, alias, this)
8805            let alias_table = Alias {
8806                this: flatten,
8807                alias: Identifier::new("_t0"),
8808                column_aliases: vec![
8809                    Identifier::new("seq"),
8810                    Identifier::new("key"),
8811                    Identifier::new("path"),
8812                    Identifier::new("index"),
8813                    Identifier::new(&alias_name),
8814                    Identifier::new("this"),
8815                ],
8816                alias_explicit_as: false,
8817                alias_keyword: None,
8818                pre_alias_comments: vec![],
8819                trailing_comments: vec![],
8820                inferred_type: None,
8821            };
8822            let lateral_expr = Expression::Lateral(Box::new(Lateral {
8823                this: Box::new(Expression::Alias(Box::new(alias_table))),
8824                view: None,
8825                outer: None,
8826                alias: None,
8827                alias_quoted: false,
8828                cross_apply: None,
8829                ordinality: None,
8830                column_aliases: vec![],
8831            }));
8832
8833            // Remove the original join and add to FROM expressions
8834            sel.joins.remove(join_idx);
8835            if let Some(ref mut from) = sel.from {
8836                from.expressions.push(lateral_expr);
8837            }
8838
8839            // Build DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE))
8840            let dateadd_expr = Expression::Function(Box::new(Function::new(
8841                "DATEADD".to_string(),
8842                vec![
8843                    Expression::boxed_column(Column {
8844                        name: Identifier::new(&unit_str),
8845                        table: None,
8846                        join_mark: false,
8847                        trailing_comments: vec![],
8848                        span: None,
8849                        inferred_type: None,
8850                    }),
8851                    Expression::Cast(Box::new(Cast {
8852                        this: Expression::boxed_column(Column {
8853                            name: Identifier::new(&alias_name),
8854                            table: None,
8855                            join_mark: false,
8856                            trailing_comments: vec![],
8857                            span: None,
8858                            inferred_type: None,
8859                        }),
8860                        to: DataType::Int {
8861                            length: None,
8862                            integer_spelling: false,
8863                        },
8864                        trailing_comments: vec![],
8865                        double_colon_syntax: false,
8866                        format: None,
8867                        default: None,
8868                        inferred_type: None,
8869                    })),
8870                    Expression::Cast(Box::new(Cast {
8871                        this: start_expr.clone(),
8872                        to: DataType::Date,
8873                        trailing_comments: vec![],
8874                        double_colon_syntax: false,
8875                        format: None,
8876                        default: None,
8877                        inferred_type: None,
8878                    })),
8879                ],
8880            )));
8881
8882            // Replace references to the alias in the SELECT list
8883            let new_exprs: Vec<Expression> = sel
8884                .expressions
8885                .iter()
8886                .map(|expr| Self::replace_column_ref_with_dateadd(expr, &alias_name, &dateadd_expr))
8887                .collect();
8888            sel.expressions = new_exprs;
8889
8890            Ok(Expression::Select(sel))
8891        })
8892    }
8893
8894    /// Helper: replace column references to `alias_name` with dateadd expression
8895    fn replace_column_ref_with_dateadd(
8896        expr: &Expression,
8897        alias_name: &str,
8898        dateadd: &Expression,
8899    ) -> Expression {
8900        use crate::expressions::*;
8901        match expr {
8902            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
8903                // Plain column reference -> DATEADD(...) AS alias_name
8904                Expression::Alias(Box::new(Alias {
8905                    this: dateadd.clone(),
8906                    alias: Identifier::new(alias_name),
8907                    column_aliases: vec![],
8908                    alias_explicit_as: false,
8909                    alias_keyword: None,
8910                    pre_alias_comments: vec![],
8911                    trailing_comments: vec![],
8912                    inferred_type: None,
8913                }))
8914            }
8915            Expression::Alias(a) => {
8916                // Check if the inner expression references the alias
8917                let new_this = Self::replace_column_ref_inner(&a.this, alias_name, dateadd);
8918                Expression::Alias(Box::new(Alias {
8919                    this: new_this,
8920                    alias: a.alias.clone(),
8921                    column_aliases: a.column_aliases.clone(),
8922                    alias_explicit_as: false,
8923                    alias_keyword: None,
8924                    pre_alias_comments: a.pre_alias_comments.clone(),
8925                    trailing_comments: a.trailing_comments.clone(),
8926                    inferred_type: None,
8927                }))
8928            }
8929            _ => expr.clone(),
8930        }
8931    }
8932
8933    /// Helper: replace column references in inner expression (not top-level)
8934    fn replace_column_ref_inner(
8935        expr: &Expression,
8936        alias_name: &str,
8937        dateadd: &Expression,
8938    ) -> Expression {
8939        use crate::expressions::*;
8940        match expr {
8941            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
8942                dateadd.clone()
8943            }
8944            Expression::Add(op) => {
8945                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
8946                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
8947                Expression::Add(Box::new(BinaryOp {
8948                    left,
8949                    right,
8950                    left_comments: op.left_comments.clone(),
8951                    operator_comments: op.operator_comments.clone(),
8952                    trailing_comments: op.trailing_comments.clone(),
8953                    inferred_type: None,
8954                }))
8955            }
8956            Expression::Sub(op) => {
8957                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
8958                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
8959                Expression::Sub(Box::new(BinaryOp {
8960                    left,
8961                    right,
8962                    left_comments: op.left_comments.clone(),
8963                    operator_comments: op.operator_comments.clone(),
8964                    trailing_comments: op.trailing_comments.clone(),
8965                    inferred_type: None,
8966                }))
8967            }
8968            Expression::Mul(op) => {
8969                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
8970                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
8971                Expression::Mul(Box::new(BinaryOp {
8972                    left,
8973                    right,
8974                    left_comments: op.left_comments.clone(),
8975                    operator_comments: op.operator_comments.clone(),
8976                    trailing_comments: op.trailing_comments.clone(),
8977                    inferred_type: None,
8978                }))
8979            }
8980            _ => expr.clone(),
8981        }
8982    }
8983
8984    /// Handle UNNEST(GENERATE_DATE_ARRAY(...)) in FROM clause for Snowflake target.
8985    /// Converts to a subquery with DATEADD + TABLE(FLATTEN(ARRAY_GENERATE_RANGE(...))).
8986    fn try_transform_from_gda_snowflake(
8987        mut sel: Box<crate::expressions::Select>,
8988    ) -> Result<Expression> {
8989        use crate::expressions::*;
8990
8991        // Extract GDA info from FROM clause
8992        let mut gda_info: Option<(
8993            usize,
8994            String,
8995            Expression,
8996            Expression,
8997            String,
8998            Option<(String, Vec<Identifier>)>,
8999        )> = None; // (from_idx, col_name, start, end, unit, outer_alias)
9000
9001        if let Some(ref from) = sel.from {
9002            for (idx, table_expr) in from.expressions.iter().enumerate() {
9003                // Pattern 1: UNNEST(GENERATE_DATE_ARRAY(...))
9004                // Pattern 2: Alias(UNNEST(GENERATE_DATE_ARRAY(...))) AS _q(date_week)
9005                let (unnest_opt, outer_alias_info) = match table_expr {
9006                    Expression::Unnest(ref unnest) => (Some(unnest.as_ref()), None),
9007                    Expression::Alias(ref a) => {
9008                        if let Expression::Unnest(ref unnest) = a.this {
9009                            let alias_info = (a.alias.name.clone(), a.column_aliases.clone());
9010                            (Some(unnest.as_ref()), Some(alias_info))
9011                        } else {
9012                            (None, None)
9013                        }
9014                    }
9015                    _ => (None, None),
9016                };
9017
9018                if let Some(unnest) = unnest_opt {
9019                    // Check for GENERATE_DATE_ARRAY function
9020                    let func_opt = match &unnest.this {
9021                        Expression::Function(ref f)
9022                            if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY")
9023                                && f.args.len() >= 2 =>
9024                        {
9025                            Some(f)
9026                        }
9027                        // Also check for GenerateSeries (from earlier normalization)
9028                        _ => None,
9029                    };
9030
9031                    if let Some(f) = func_opt {
9032                        let start_expr = f.args[0].clone();
9033                        let end_expr = f.args[1].clone();
9034                        let step = f.args.get(2).cloned();
9035
9036                        // Extract unit and column name
9037                        let unit = Self::extract_interval_unit_str(&step);
9038                        let col_name = outer_alias_info
9039                            .as_ref()
9040                            .and_then(|(_, cols)| cols.first().map(|id| id.name.clone()))
9041                            .unwrap_or_else(|| "value".to_string());
9042
9043                        if let Some(unit_str) = unit {
9044                            gda_info = Some((
9045                                idx,
9046                                col_name,
9047                                start_expr,
9048                                end_expr,
9049                                unit_str,
9050                                outer_alias_info,
9051                            ));
9052                            break;
9053                        }
9054                    }
9055                }
9056            }
9057        }
9058
9059        let Some((from_idx, col_name, start_expr, end_expr, unit_str, outer_alias_info)) = gda_info
9060        else {
9061            return Ok(Expression::Select(sel));
9062        };
9063
9064        // Build the Snowflake subquery:
9065        // (SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
9066        //  FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(seq, key, path, index, col_name, this))
9067
9068        // DATEDIFF(unit, start, end)
9069        let datediff = Expression::Function(Box::new(Function::new(
9070            "DATEDIFF".to_string(),
9071            vec![
9072                Expression::boxed_column(Column {
9073                    name: Identifier::new(&unit_str),
9074                    table: None,
9075                    join_mark: false,
9076                    trailing_comments: vec![],
9077                    span: None,
9078                    inferred_type: None,
9079                }),
9080                start_expr.clone(),
9081                end_expr.clone(),
9082            ],
9083        )));
9084        // DATEDIFF(...) + 1
9085        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
9086            left: datediff,
9087            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
9088            left_comments: vec![],
9089            operator_comments: vec![],
9090            trailing_comments: vec![],
9091            inferred_type: None,
9092        }));
9093
9094        let array_gen_range = Expression::Function(Box::new(Function::new(
9095            "ARRAY_GENERATE_RANGE".to_string(),
9096            vec![
9097                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
9098                datediff_plus_one,
9099            ],
9100        )));
9101
9102        // TABLE(FLATTEN(INPUT => ...))
9103        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
9104            name: Identifier::new("INPUT"),
9105            value: array_gen_range,
9106            separator: crate::expressions::NamedArgSeparator::DArrow,
9107        }));
9108        let flatten = Expression::Function(Box::new(Function::new(
9109            "FLATTEN".to_string(),
9110            vec![flatten_input],
9111        )));
9112
9113        // Determine alias name for the table: use outer alias or _t0
9114        let table_alias_name = outer_alias_info
9115            .as_ref()
9116            .map(|(name, _)| name.clone())
9117            .unwrap_or_else(|| "_t0".to_string());
9118
9119        // TABLE(FLATTEN(...)) AS _t0(seq, key, path, index, col_name, this)
9120        let table_func =
9121            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
9122        let flatten_aliased = Expression::Alias(Box::new(Alias {
9123            this: table_func,
9124            alias: Identifier::new(&table_alias_name),
9125            column_aliases: vec![
9126                Identifier::new("seq"),
9127                Identifier::new("key"),
9128                Identifier::new("path"),
9129                Identifier::new("index"),
9130                Identifier::new(&col_name),
9131                Identifier::new("this"),
9132            ],
9133            alias_explicit_as: false,
9134            alias_keyword: None,
9135            pre_alias_comments: vec![],
9136            trailing_comments: vec![],
9137            inferred_type: None,
9138        }));
9139
9140        // SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
9141        let dateadd_expr = Expression::Function(Box::new(Function::new(
9142            "DATEADD".to_string(),
9143            vec![
9144                Expression::boxed_column(Column {
9145                    name: Identifier::new(&unit_str),
9146                    table: None,
9147                    join_mark: false,
9148                    trailing_comments: vec![],
9149                    span: None,
9150                    inferred_type: None,
9151                }),
9152                Expression::Cast(Box::new(Cast {
9153                    this: Expression::boxed_column(Column {
9154                        name: Identifier::new(&col_name),
9155                        table: None,
9156                        join_mark: false,
9157                        trailing_comments: vec![],
9158                        span: None,
9159                        inferred_type: None,
9160                    }),
9161                    to: DataType::Int {
9162                        length: None,
9163                        integer_spelling: false,
9164                    },
9165                    trailing_comments: vec![],
9166                    double_colon_syntax: false,
9167                    format: None,
9168                    default: None,
9169                    inferred_type: None,
9170                })),
9171                // Use start_expr directly - it's already been normalized (DATE literal -> CAST)
9172                start_expr.clone(),
9173            ],
9174        )));
9175        let dateadd_aliased = Expression::Alias(Box::new(Alias {
9176            this: dateadd_expr,
9177            alias: Identifier::new(&col_name),
9178            column_aliases: vec![],
9179            alias_explicit_as: false,
9180            alias_keyword: None,
9181            pre_alias_comments: vec![],
9182            trailing_comments: vec![],
9183            inferred_type: None,
9184        }));
9185
9186        // Build inner SELECT
9187        let mut inner_select = Select::new();
9188        inner_select.expressions = vec![dateadd_aliased];
9189        inner_select.from = Some(From {
9190            expressions: vec![flatten_aliased],
9191        });
9192
9193        let inner_select_expr = Expression::Select(Box::new(inner_select));
9194        let subquery = Expression::Subquery(Box::new(Subquery {
9195            this: inner_select_expr,
9196            alias: None,
9197            column_aliases: vec![],
9198            alias_explicit_as: false,
9199            alias_keyword: None,
9200            order_by: None,
9201            limit: None,
9202            offset: None,
9203            distribute_by: None,
9204            sort_by: None,
9205            cluster_by: None,
9206            lateral: false,
9207            modifiers_inside: false,
9208            trailing_comments: vec![],
9209            inferred_type: None,
9210        }));
9211
9212        // If there was an outer alias (e.g., AS _q(date_week)), wrap with alias
9213        let replacement = if let Some((alias_name, col_aliases)) = outer_alias_info {
9214            Expression::Alias(Box::new(Alias {
9215                this: subquery,
9216                alias: Identifier::new(&alias_name),
9217                column_aliases: col_aliases,
9218                alias_explicit_as: false,
9219                alias_keyword: None,
9220                pre_alias_comments: vec![],
9221                trailing_comments: vec![],
9222                inferred_type: None,
9223            }))
9224        } else {
9225            subquery
9226        };
9227
9228        // Replace the FROM expression
9229        if let Some(ref mut from) = sel.from {
9230            from.expressions[from_idx] = replacement;
9231        }
9232
9233        Ok(Expression::Select(sel))
9234    }
9235
9236    /// Convert ARRAY_SIZE(GENERATE_DATE_ARRAY(start, end, step)) for Snowflake.
9237    /// Produces: ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM (SELECT DATEADD(unit, CAST(value AS INT), start) AS value
9238    ///   FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(...))))
9239    fn convert_array_size_gda_snowflake(f: &crate::expressions::Function) -> Result<Expression> {
9240        use crate::expressions::*;
9241
9242        let start_expr = f.args[0].clone();
9243        let end_expr = f.args[1].clone();
9244        let step = f.args.get(2).cloned();
9245        let unit_str = Self::extract_interval_unit_str(&step).unwrap_or_else(|| "DAY".to_string());
9246        let col_name = "value";
9247
9248        // Build the inner subquery: same as try_transform_from_gda_snowflake
9249        let datediff = Expression::Function(Box::new(Function::new(
9250            "DATEDIFF".to_string(),
9251            vec![
9252                Expression::boxed_column(Column {
9253                    name: Identifier::new(&unit_str),
9254                    table: None,
9255                    join_mark: false,
9256                    trailing_comments: vec![],
9257                    span: None,
9258                    inferred_type: None,
9259                }),
9260                start_expr.clone(),
9261                end_expr.clone(),
9262            ],
9263        )));
9264        // DATEDIFF(...) + 1
9265        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
9266            left: datediff,
9267            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
9268            left_comments: vec![],
9269            operator_comments: vec![],
9270            trailing_comments: vec![],
9271            inferred_type: None,
9272        }));
9273
9274        let array_gen_range = Expression::Function(Box::new(Function::new(
9275            "ARRAY_GENERATE_RANGE".to_string(),
9276            vec![
9277                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
9278                datediff_plus_one,
9279            ],
9280        )));
9281
9282        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
9283            name: Identifier::new("INPUT"),
9284            value: array_gen_range,
9285            separator: crate::expressions::NamedArgSeparator::DArrow,
9286        }));
9287        let flatten = Expression::Function(Box::new(Function::new(
9288            "FLATTEN".to_string(),
9289            vec![flatten_input],
9290        )));
9291
9292        let table_func =
9293            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
9294        let flatten_aliased = Expression::Alias(Box::new(Alias {
9295            this: table_func,
9296            alias: Identifier::new("_t0"),
9297            column_aliases: vec![
9298                Identifier::new("seq"),
9299                Identifier::new("key"),
9300                Identifier::new("path"),
9301                Identifier::new("index"),
9302                Identifier::new(col_name),
9303                Identifier::new("this"),
9304            ],
9305            alias_explicit_as: false,
9306            alias_keyword: None,
9307            pre_alias_comments: vec![],
9308            trailing_comments: vec![],
9309            inferred_type: None,
9310        }));
9311
9312        let dateadd_expr = Expression::Function(Box::new(Function::new(
9313            "DATEADD".to_string(),
9314            vec![
9315                Expression::boxed_column(Column {
9316                    name: Identifier::new(&unit_str),
9317                    table: None,
9318                    join_mark: false,
9319                    trailing_comments: vec![],
9320                    span: None,
9321                    inferred_type: None,
9322                }),
9323                Expression::Cast(Box::new(Cast {
9324                    this: Expression::boxed_column(Column {
9325                        name: Identifier::new(col_name),
9326                        table: None,
9327                        join_mark: false,
9328                        trailing_comments: vec![],
9329                        span: None,
9330                        inferred_type: None,
9331                    }),
9332                    to: DataType::Int {
9333                        length: None,
9334                        integer_spelling: false,
9335                    },
9336                    trailing_comments: vec![],
9337                    double_colon_syntax: false,
9338                    format: None,
9339                    default: None,
9340                    inferred_type: None,
9341                })),
9342                start_expr.clone(),
9343            ],
9344        )));
9345        let dateadd_aliased = Expression::Alias(Box::new(Alias {
9346            this: dateadd_expr,
9347            alias: Identifier::new(col_name),
9348            column_aliases: vec![],
9349            alias_explicit_as: false,
9350            alias_keyword: None,
9351            pre_alias_comments: vec![],
9352            trailing_comments: vec![],
9353            inferred_type: None,
9354        }));
9355
9356        // Inner SELECT: SELECT DATEADD(...) AS value FROM TABLE(FLATTEN(...)) AS _t0(...)
9357        let mut inner_select = Select::new();
9358        inner_select.expressions = vec![dateadd_aliased];
9359        inner_select.from = Some(From {
9360            expressions: vec![flatten_aliased],
9361        });
9362
9363        // Wrap in subquery for the inner part
9364        let inner_subquery = Expression::Subquery(Box::new(Subquery {
9365            this: Expression::Select(Box::new(inner_select)),
9366            alias: None,
9367            column_aliases: vec![],
9368            alias_explicit_as: false,
9369            alias_keyword: None,
9370            order_by: None,
9371            limit: None,
9372            offset: None,
9373            distribute_by: None,
9374            sort_by: None,
9375            cluster_by: None,
9376            lateral: false,
9377            modifiers_inside: false,
9378            trailing_comments: vec![],
9379            inferred_type: None,
9380        }));
9381
9382        // Outer: SELECT ARRAY_AGG(*) FROM (inner_subquery)
9383        let star = Expression::Star(Star {
9384            table: None,
9385            except: None,
9386            replace: None,
9387            rename: None,
9388            trailing_comments: vec![],
9389            span: None,
9390        });
9391        let array_agg = Expression::ArrayAgg(Box::new(AggFunc {
9392            this: star,
9393            distinct: false,
9394            filter: None,
9395            order_by: vec![],
9396            name: Some("ARRAY_AGG".to_string()),
9397            ignore_nulls: None,
9398            having_max: None,
9399            limit: None,
9400            inferred_type: None,
9401        }));
9402
9403        let mut outer_select = Select::new();
9404        outer_select.expressions = vec![array_agg];
9405        outer_select.from = Some(From {
9406            expressions: vec![inner_subquery],
9407        });
9408
9409        // Wrap in a subquery
9410        let outer_subquery = Expression::Subquery(Box::new(Subquery {
9411            this: Expression::Select(Box::new(outer_select)),
9412            alias: None,
9413            column_aliases: vec![],
9414            alias_explicit_as: false,
9415            alias_keyword: None,
9416            order_by: None,
9417            limit: None,
9418            offset: None,
9419            distribute_by: None,
9420            sort_by: None,
9421            cluster_by: None,
9422            lateral: false,
9423            modifiers_inside: false,
9424            trailing_comments: vec![],
9425            inferred_type: None,
9426        }));
9427
9428        // ARRAY_SIZE(subquery)
9429        Ok(Expression::ArraySize(Box::new(UnaryFunc::new(
9430            outer_subquery,
9431        ))))
9432    }
9433
9434    /// Extract interval unit string from an optional step expression.
9435    fn extract_interval_unit_str(step: &Option<Expression>) -> Option<String> {
9436        use crate::expressions::*;
9437        if let Some(Expression::Interval(ref iv)) = step {
9438            if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
9439                return Some(format!("{:?}", unit).to_ascii_uppercase());
9440            }
9441            if let Some(ref this) = iv.this {
9442                if let Expression::Literal(lit) = this {
9443                    if let Literal::String(ref s) = lit.as_ref() {
9444                        let parts: Vec<&str> = s.split_whitespace().collect();
9445                        if parts.len() == 2 {
9446                            return Some(parts[1].to_ascii_uppercase());
9447                        } else if parts.len() == 1 {
9448                            let upper = parts[0].to_ascii_uppercase();
9449                            if matches!(
9450                                upper.as_str(),
9451                                "YEAR"
9452                                    | "QUARTER"
9453                                    | "MONTH"
9454                                    | "WEEK"
9455                                    | "DAY"
9456                                    | "HOUR"
9457                                    | "MINUTE"
9458                                    | "SECOND"
9459                            ) {
9460                                return Some(upper);
9461                            }
9462                        }
9463                    }
9464                }
9465            }
9466        }
9467        // Default to DAY if no step or no interval
9468        if step.is_none() {
9469            return Some("DAY".to_string());
9470        }
9471        None
9472    }
9473
9474    fn normalize_snowflake_pretty(mut sql: String) -> String {
9475        if sql.contains("LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)")
9476            && sql.contains("ARRAY_GENERATE_RANGE(0, (GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1) + 1)")
9477        {
9478            sql = sql.replace(
9479                "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')",
9480                "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    )",
9481            );
9482
9483            sql = sql.replace(
9484                "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)",
9485                "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)",
9486            );
9487
9488            sql = sql.replace(
9489                "OR (_u.pos > (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1)\n  AND _u_2.pos_2 = (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1))",
9490                "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  )",
9491            );
9492        }
9493
9494        sql
9495    }
9496
9497    #[cfg(feature = "transpile")]
9498    fn wrap_tsql_top_level_values(expr: Expression) -> Expression {
9499        match expr {
9500            Expression::Values(values) => Self::tsql_values_as_select(*values),
9501            Expression::Union(mut union) => {
9502                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
9503                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
9504                union.left = Self::wrap_tsql_values_set_operand(left);
9505                union.right = Self::wrap_tsql_values_set_operand(right);
9506                Expression::Union(union)
9507            }
9508            Expression::Intersect(mut intersect) => {
9509                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
9510                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
9511                intersect.left = Self::wrap_tsql_values_set_operand(left);
9512                intersect.right = Self::wrap_tsql_values_set_operand(right);
9513                Expression::Intersect(intersect)
9514            }
9515            Expression::Except(mut except) => {
9516                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
9517                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
9518                except.left = Self::wrap_tsql_values_set_operand(left);
9519                except.right = Self::wrap_tsql_values_set_operand(right);
9520                Expression::Except(except)
9521            }
9522            other => other,
9523        }
9524    }
9525
9526    #[cfg(feature = "transpile")]
9527    fn wrap_tsql_values_set_operand(expr: Expression) -> Expression {
9528        match expr {
9529            Expression::Values(values) => Self::tsql_values_as_select(*values),
9530            Expression::Select(mut select)
9531                if Self::is_parser_wrapped_values_set_operand(&select) =>
9532            {
9533                let mut from = select.from.take().expect("checked as present");
9534                let Expression::Values(values) = from
9535                    .expressions
9536                    .pop()
9537                    .expect("checked as a single VALUES source")
9538                else {
9539                    unreachable!("checked as a VALUES source");
9540                };
9541                Self::tsql_values_as_select(*values)
9542            }
9543            Expression::Annotated(mut annotated) => {
9544                annotated.this = Self::wrap_tsql_values_set_operand(annotated.this);
9545                Expression::Annotated(annotated)
9546            }
9547            Expression::Union(mut union) => {
9548                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
9549                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
9550                union.left = Self::wrap_tsql_values_set_operand(left);
9551                union.right = Self::wrap_tsql_values_set_operand(right);
9552                Expression::Union(union)
9553            }
9554            Expression::Intersect(mut intersect) => {
9555                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
9556                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
9557                intersect.left = Self::wrap_tsql_values_set_operand(left);
9558                intersect.right = Self::wrap_tsql_values_set_operand(right);
9559                Expression::Intersect(intersect)
9560            }
9561            Expression::Except(mut except) => {
9562                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
9563                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
9564                except.left = Self::wrap_tsql_values_set_operand(left);
9565                except.right = Self::wrap_tsql_values_set_operand(right);
9566                Expression::Except(except)
9567            }
9568            other => other,
9569        }
9570    }
9571
9572    #[cfg(feature = "transpile")]
9573    fn is_parser_wrapped_values_set_operand(select: &Select) -> bool {
9574        let Some(from) = &select.from else {
9575            return false;
9576        };
9577        let [Expression::Values(values)] = from.expressions.as_slice() else {
9578            return false;
9579        };
9580        if !values
9581            .alias
9582            .as_ref()
9583            .is_some_and(|alias| alias.name == "_values")
9584        {
9585            return false;
9586        }
9587
9588        let mut parser_wrapper = Select::new();
9589        parser_wrapper.expressions = vec![Expression::star()];
9590        parser_wrapper.from = Some(from.clone());
9591        select == &parser_wrapper
9592    }
9593
9594    #[cfg(feature = "transpile")]
9595    fn tsql_values_as_select(mut values: crate::expressions::Values) -> Expression {
9596        let column_aliases = if values.column_aliases.is_empty() {
9597            let column_count = values
9598                .expressions
9599                .first()
9600                .map(|row| row.expressions.len())
9601                .unwrap_or(0);
9602            (1..=column_count)
9603                .map(|index| Identifier::new(format!("column{index}")))
9604                .collect()
9605        } else {
9606            std::mem::take(&mut values.column_aliases)
9607        };
9608
9609        values.alias = None;
9610
9611        let values_subquery = Expression::Subquery(Box::new(crate::expressions::Subquery {
9612            this: Expression::Values(Box::new(values)),
9613            alias: Some(Identifier::new("_v")),
9614            column_aliases,
9615            alias_explicit_as: false,
9616            alias_keyword: None,
9617            order_by: None,
9618            limit: None,
9619            offset: None,
9620            distribute_by: None,
9621            sort_by: None,
9622            cluster_by: None,
9623            lateral: false,
9624            modifiers_inside: false,
9625            trailing_comments: Vec::new(),
9626            inferred_type: None,
9627        }));
9628
9629        let mut select = crate::expressions::Select::new();
9630        select.expressions = vec![Expression::star()];
9631        select.from = Some(From {
9632            expressions: vec![values_subquery],
9633        });
9634
9635        Expression::Select(Box::new(select))
9636    }
9637
9638    fn extract_interval_parts(
9639        interval_expr: &Expression,
9640    ) -> Option<(Expression, crate::expressions::IntervalUnit)> {
9641        use crate::expressions::{DataType, IntervalUnit, IntervalUnitSpec, Literal};
9642
9643        fn unit_from_str(unit: &str) -> Option<IntervalUnit> {
9644            match unit.trim().to_ascii_uppercase().as_str() {
9645                "YEAR" | "YEARS" | "Y" | "YR" | "YRS" | "YY" | "YYYY" => Some(IntervalUnit::Year),
9646                "QUARTER" | "QUARTERS" | "Q" | "QTR" | "QTRS" | "QQ" => Some(IntervalUnit::Quarter),
9647                "MONTH" | "MONTHS" | "MON" | "MONS" | "MM" => Some(IntervalUnit::Month),
9648                "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" | "ISOWEEK" => {
9649                    Some(IntervalUnit::Week)
9650                }
9651                "DAY" | "DAYS" | "D" | "DD" => Some(IntervalUnit::Day),
9652                "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some(IntervalUnit::Hour),
9653                "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some(IntervalUnit::Minute),
9654                "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some(IntervalUnit::Second),
9655                "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MSECOND"
9656                | "MSECONDS" | "MILLISEC" | "MILLISECS" | "MILLISECON" => {
9657                    Some(IntervalUnit::Millisecond)
9658                }
9659                "MICROSECOND" | "MICROSECONDS" | "US" | "USEC" | "USECS" | "USECOND"
9660                | "USECONDS" | "MICROSEC" | "MICROSECS" | "MCS" => Some(IntervalUnit::Microsecond),
9661                "NANOSECOND" | "NANOSECONDS" | "NS" | "NSEC" | "NSECS" | "NSECOND" | "NSECONDS"
9662                | "NANOSEC" | "NANOSECS" => Some(IntervalUnit::Nanosecond),
9663                _ => None,
9664            }
9665        }
9666
9667        fn parts_from_literal_string(s: &str) -> Option<(Expression, IntervalUnit)> {
9668            let mut parts = s.split_whitespace();
9669            let value = parts.next()?;
9670            let unit = unit_from_str(parts.next()?)?;
9671            Some((
9672                Expression::Literal(Box::new(Literal::String(value.to_string()))),
9673                unit,
9674            ))
9675        }
9676
9677        fn unit_from_spec(unit: &IntervalUnitSpec) -> Option<IntervalUnit> {
9678            match unit {
9679                IntervalUnitSpec::Simple { unit, .. } => Some(*unit),
9680                IntervalUnitSpec::Expr(expr) => match expr.as_ref() {
9681                    Expression::Day(_) => Some(IntervalUnit::Day),
9682                    Expression::Month(_) => Some(IntervalUnit::Month),
9683                    Expression::Year(_) => Some(IntervalUnit::Year),
9684                    Expression::Identifier(id) => unit_from_str(&id.name),
9685                    Expression::Var(v) => unit_from_str(&v.this),
9686                    Expression::Column(col) => unit_from_str(&col.name.name),
9687                    _ => None,
9688                },
9689                _ => None,
9690            }
9691        }
9692
9693        match interval_expr {
9694            Expression::Interval(iv) => {
9695                let val = iv.this.clone().unwrap_or(Expression::number(0));
9696                if let Expression::Literal(lit) = &val {
9697                    if let Literal::String(s) = lit.as_ref() {
9698                        if let Some(parts) = parts_from_literal_string(s) {
9699                            return Some(parts);
9700                        }
9701                    }
9702                }
9703                let unit = iv
9704                    .unit
9705                    .as_ref()
9706                    .and_then(unit_from_spec)
9707                    .unwrap_or(IntervalUnit::Day);
9708                Some((val, unit))
9709            }
9710            Expression::Cast(cast) if matches!(cast.to, DataType::Interval { .. }) => {
9711                if let Expression::Literal(lit) = &cast.this {
9712                    if let Literal::String(s) = lit.as_ref() {
9713                        if let Some(parts) = parts_from_literal_string(s) {
9714                            return Some(parts);
9715                        }
9716                    }
9717                }
9718                let unit = match &cast.to {
9719                    DataType::Interval {
9720                        unit: Some(unit), ..
9721                    } => unit_from_str(unit).unwrap_or(IntervalUnit::Day),
9722                    _ => IntervalUnit::Day,
9723                };
9724                Some((cast.this.clone(), unit))
9725            }
9726            _ => None,
9727        }
9728    }
9729
9730    fn data_type_is_interval(dt: &DataType) -> bool {
9731        match dt {
9732            DataType::Interval { .. } => true,
9733            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("INTERVAL"),
9734            _ => false,
9735        }
9736    }
9737
9738    fn node_is_interval_cast(node: &Expression) -> bool {
9739        match node {
9740            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
9741                Self::data_type_is_interval(&c.to)
9742            }
9743            _ => false,
9744        }
9745    }
9746
9747    fn reject_tsql_interval_casts(
9748        expr: &Expression,
9749        target: DialectType,
9750        opts: &TranspileOptions,
9751    ) -> Result<()> {
9752        if !matches!(
9753            opts.unsupported_level,
9754            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
9755        ) {
9756            return Ok(());
9757        }
9758
9759        if expr.dfs().any(Self::node_is_interval_cast) {
9760            return Err(crate::error::Error::unsupported(
9761                "INTERVAL casts",
9762                target.to_string(),
9763            ));
9764        }
9765
9766        Ok(())
9767    }
9768
9769    fn tsql_varchar_max_type() -> DataType {
9770        DataType::Custom {
9771            name: "VARCHAR(MAX)".to_string(),
9772        }
9773    }
9774
9775    fn rewrite_tsql_interval_casts_to_varchar(expr: Expression) -> Result<Expression> {
9776        transform_recursive(expr, &|e| match e {
9777            Expression::Cast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9778                cast.to = Self::tsql_varchar_max_type();
9779                cast.double_colon_syntax = false;
9780                Ok(Expression::Cast(cast))
9781            }
9782            Expression::TryCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9783                cast.to = Self::tsql_varchar_max_type();
9784                cast.double_colon_syntax = false;
9785                Ok(Expression::TryCast(cast))
9786            }
9787            Expression::SafeCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9788                cast.to = Self::tsql_varchar_max_type();
9789                cast.double_colon_syntax = false;
9790                Ok(Expression::SafeCast(cast))
9791            }
9792            _ => Ok(e),
9793        })
9794    }
9795
9796    fn rewrite_tsql_interval_arithmetic_legacy(
9797        expr: &Expression,
9798        source: DialectType,
9799    ) -> Option<Expression> {
9800        match expr {
9801            Expression::Add(op) => {
9802                if Self::extract_interval_parts(&op.right).is_some() {
9803                    return Some(Self::build_tsql_dateadd_from_interval(
9804                        op.left.clone(),
9805                        &op.right,
9806                        false,
9807                    ));
9808                }
9809
9810                if Self::is_postgres_family_source(source) {
9811                    if Self::is_explicit_date_expr(&op.left)
9812                        && Self::is_integer_day_offset_expr(&op.right)
9813                    {
9814                        return Some(Self::build_tsql_dateadd_days(
9815                            op.left.clone(),
9816                            op.right.clone(),
9817                            false,
9818                        ));
9819                    }
9820
9821                    if Self::is_integer_day_offset_expr(&op.left)
9822                        && Self::is_explicit_date_expr(&op.right)
9823                    {
9824                        return Some(Self::build_tsql_dateadd_days(
9825                            op.right.clone(),
9826                            op.left.clone(),
9827                            false,
9828                        ));
9829                    }
9830                }
9831
9832                None
9833            }
9834            Expression::Sub(op) => {
9835                if Self::extract_interval_parts(&op.right).is_some() {
9836                    return Some(Self::build_tsql_dateadd_from_interval(
9837                        op.left.clone(),
9838                        &op.right,
9839                        true,
9840                    ));
9841                }
9842
9843                if Self::is_postgres_family_source(source) {
9844                    if Self::is_explicit_date_expr(&op.left)
9845                        && Self::is_explicit_date_expr(&op.right)
9846                    {
9847                        return Some(Self::build_tsql_datediff_days(
9848                            op.right.clone(),
9849                            op.left.clone(),
9850                        ));
9851                    }
9852
9853                    if Self::is_explicit_date_expr(&op.left)
9854                        && Self::is_integer_day_offset_expr(&op.right)
9855                    {
9856                        return Some(Self::build_tsql_dateadd_days(
9857                            op.left.clone(),
9858                            op.right.clone(),
9859                            true,
9860                        ));
9861                    }
9862                }
9863
9864                None
9865            }
9866            _ => None,
9867        }
9868    }
9869
9870    fn is_postgres_family_source(source: DialectType) -> bool {
9871        matches!(
9872            source,
9873            DialectType::PostgreSQL
9874                | DialectType::Redshift
9875                | DialectType::Materialize
9876                | DialectType::RisingWave
9877                | DialectType::CockroachDB
9878        )
9879    }
9880
9881    fn is_explicit_date_expr(expr: &Expression) -> bool {
9882        use crate::expressions::Literal;
9883
9884        match expr {
9885            Expression::Literal(lit) => matches!(lit.as_ref(), Literal::Date(_)),
9886            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
9887                matches!(c.to, crate::expressions::DataType::Date)
9888            }
9889            Expression::Paren(p) => Self::is_explicit_date_expr(&p.this),
9890            Expression::CurrentDate(_)
9891            | Expression::Date(_)
9892            | Expression::MakeDate(_)
9893            | Expression::ToDate(_)
9894            | Expression::DateStrToDate(_) => true,
9895            _ => false,
9896        }
9897    }
9898
9899    fn is_integer_day_offset_expr(expr: &Expression) -> bool {
9900        use crate::expressions::Literal;
9901
9902        match expr {
9903            Expression::Literal(lit) => match lit.as_ref() {
9904                Literal::Number(n) => n.parse::<i64>().is_ok(),
9905                _ => false,
9906            },
9907            Expression::Parameter(_) | Expression::Placeholder(_) => true,
9908            Expression::Neg(op) => Self::is_integer_day_offset_expr(&op.this),
9909            Expression::Paren(p) => Self::is_integer_day_offset_expr(&p.this),
9910            _ => false,
9911        }
9912    }
9913
9914    fn build_tsql_datediff_days(start: Expression, end: Expression) -> Expression {
9915        Expression::Function(Box::new(Function::new(
9916            "DATEDIFF".to_string(),
9917            vec![Expression::Identifier(Identifier::new("DAY")), start, end],
9918        )))
9919    }
9920
9921    fn build_tsql_dateadd_days(date: Expression, amount: Expression, subtract: bool) -> Expression {
9922        Expression::Function(Box::new(Function::new(
9923            "DATEADD".to_string(),
9924            vec![
9925                Expression::Identifier(Identifier::new("DAY")),
9926                Self::tsql_dateadd_amount(amount, subtract),
9927                date,
9928            ],
9929        )))
9930    }
9931
9932    fn build_tsql_dateadd_from_interval(
9933        date: Expression,
9934        interval: &Expression,
9935        subtract: bool,
9936    ) -> Expression {
9937        let (value, unit) = Self::extract_interval_parts(interval)
9938            .unwrap_or_else(|| (interval.clone(), crate::expressions::IntervalUnit::Day));
9939        let unit = normalization::temporal::interval_unit_to_string(&unit);
9940        let amount = Self::tsql_dateadd_amount(value, subtract);
9941
9942        Expression::Function(Box::new(Function::new(
9943            "DATEADD".to_string(),
9944            vec![Expression::Identifier(Identifier::new(unit)), amount, date],
9945        )))
9946    }
9947
9948    fn tsql_dateadd_amount(value: Expression, negate: bool) -> Expression {
9949        use crate::expressions::{Parameter, ParameterStyle, UnaryOp};
9950
9951        fn numeric_literal_value(value: &Expression) -> Option<&str> {
9952            match value {
9953                Expression::Literal(lit) => match lit.as_ref() {
9954                    crate::expressions::Literal::Number(n)
9955                    | crate::expressions::Literal::String(n) => Some(n.as_str()),
9956                    _ => None,
9957                },
9958                _ => None,
9959            }
9960        }
9961
9962        fn colon_parameter(value: &Expression) -> Option<Expression> {
9963            let Expression::Literal(lit) = value else {
9964                return None;
9965            };
9966            let crate::expressions::Literal::String(s) = lit.as_ref() else {
9967                return None;
9968            };
9969            let name = s.strip_prefix(':')?;
9970            if name.is_empty()
9971                || !name
9972                    .chars()
9973                    .all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
9974            {
9975                return None;
9976            }
9977
9978            Some(Expression::Parameter(Box::new(Parameter {
9979                name: if name.chars().all(|ch| ch.is_ascii_digit()) {
9980                    None
9981                } else {
9982                    Some(name.to_string())
9983                },
9984                index: name.parse::<u32>().ok(),
9985                style: ParameterStyle::Colon,
9986                quoted: false,
9987                string_quoted: false,
9988                expression: None,
9989            })))
9990        }
9991
9992        let value = colon_parameter(&value).unwrap_or(value);
9993
9994        if let Some(n) = numeric_literal_value(&value) {
9995            if let Ok(parsed) = n.parse::<f64>() {
9996                let normalized = if negate { -parsed } else { parsed };
9997                let rendered = if normalized.fract() == 0.0 {
9998                    format!("{}", normalized as i64)
9999                } else {
10000                    normalized.to_string()
10001                };
10002                return Expression::Literal(Box::new(crate::expressions::Literal::Number(
10003                    rendered,
10004                )));
10005            }
10006        }
10007
10008        if !negate {
10009            return value;
10010        }
10011
10012        match value {
10013            Expression::Neg(op) => op.this,
10014            other => Expression::Neg(Box::new(UnaryOp {
10015                this: other,
10016                inferred_type: None,
10017            })),
10018        }
10019    }
10020
10021    /// Internal TO_DATE function that won't be converted to CAST by the Snowflake handler.
10022    /// Uses the name `_POLYGLOT_TO_DATE` which is not recognized by the TO_DATE -> CAST logic.
10023    /// The Snowflake DATEDIFF handler converts these back to TO_DATE.
10024    const PRESERVED_TO_DATE: &'static str = "_POLYGLOT_TO_DATE";
10025}
10026
10027#[cfg(test)]
10028mod tests {
10029    use super::*;
10030
10031    #[test]
10032    fn built_in_dialect_instances_share_tokenizer_config() {
10033        let first = Dialect::get(DialectType::PostgreSQL);
10034        let second = Dialect::get(DialectType::PostgreSQL);
10035
10036        assert!(first.tokenizer.shares_config_with(&second.tokenizer));
10037    }
10038
10039    #[test]
10040    fn test_dialect_type_from_str() {
10041        assert_eq!(
10042            "postgres".parse::<DialectType>().unwrap(),
10043            DialectType::PostgreSQL
10044        );
10045        assert_eq!(
10046            "postgresql".parse::<DialectType>().unwrap(),
10047            DialectType::PostgreSQL
10048        );
10049        assert_eq!("mysql".parse::<DialectType>().unwrap(), DialectType::MySQL);
10050        assert_eq!(
10051            "bigquery".parse::<DialectType>().unwrap(),
10052            DialectType::BigQuery
10053        );
10054    }
10055
10056    #[test]
10057    fn test_basic_transpile() {
10058        let dialect = Dialect::get(DialectType::Generic);
10059        let result = dialect
10060            .transpile("SELECT 1", DialectType::PostgreSQL)
10061            .unwrap();
10062        assert_eq!(result.len(), 1);
10063        assert_eq!(result[0], "SELECT 1");
10064    }
10065
10066    #[test]
10067    fn test_sqlite_double_quoted_column_defaults_to_postgres_strings() {
10068        let sqlite = Dialect::get(DialectType::SQLite);
10069        let result = sqlite
10070            .transpile(
10071                r#"CREATE TABLE "_collections" (
10072                    "type" TEXT DEFAULT "base" NOT NULL,
10073                    "fields" JSON DEFAULT "[]" NOT NULL,
10074                    "options" JSON DEFAULT "{}" NOT NULL
10075                )"#,
10076                DialectType::PostgreSQL,
10077            )
10078            .unwrap();
10079
10080        assert!(result[0].contains(r#""type" TEXT DEFAULT 'base' NOT NULL"#));
10081        assert!(result[0].contains(r#""fields" JSON DEFAULT '[]' NOT NULL"#));
10082        assert!(result[0].contains(r#""options" JSON DEFAULT '{}' NOT NULL"#));
10083    }
10084
10085    #[test]
10086    fn test_sqlite_identity_preserves_double_quoted_column_defaults() {
10087        let sqlite = Dialect::get(DialectType::SQLite);
10088        let result = sqlite
10089            .transpile(
10090                r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#,
10091                DialectType::SQLite,
10092            )
10093            .unwrap();
10094
10095        assert_eq!(
10096            result[0],
10097            r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#
10098        );
10099    }
10100
10101    #[test]
10102    fn test_function_transformation_mysql() {
10103        // NVL should be transformed to IFNULL in MySQL
10104        let dialect = Dialect::get(DialectType::Generic);
10105        let result = dialect
10106            .transpile("SELECT NVL(a, b)", DialectType::MySQL)
10107            .unwrap();
10108        assert_eq!(result[0], "SELECT IFNULL(a, b)");
10109    }
10110
10111    #[test]
10112    fn test_get_path_duckdb() {
10113        // Test: step by step
10114        let snowflake = Dialect::get(DialectType::Snowflake);
10115
10116        // Step 1: Parse and check what Snowflake produces as intermediate
10117        let result_sf_sf = snowflake
10118            .transpile(
10119                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
10120                DialectType::Snowflake,
10121            )
10122            .unwrap();
10123        eprintln!("Snowflake->Snowflake colon: {}", result_sf_sf[0]);
10124
10125        // Step 2: DuckDB target
10126        let result_sf_dk = snowflake
10127            .transpile(
10128                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
10129                DialectType::DuckDB,
10130            )
10131            .unwrap();
10132        eprintln!("Snowflake->DuckDB colon: {}", result_sf_dk[0]);
10133
10134        // Step 3: GET_PATH directly
10135        let result_gp = snowflake
10136            .transpile(
10137                "SELECT GET_PATH(PARSE_JSON('{\"fruit\":\"banana\"}'), 'fruit')",
10138                DialectType::DuckDB,
10139            )
10140            .unwrap();
10141        eprintln!("Snowflake->DuckDB explicit GET_PATH: {}", result_gp[0]);
10142    }
10143
10144    #[test]
10145    fn test_function_transformation_postgres() {
10146        // IFNULL should be transformed to COALESCE in PostgreSQL
10147        let dialect = Dialect::get(DialectType::Generic);
10148        let result = dialect
10149            .transpile("SELECT IFNULL(a, b)", DialectType::PostgreSQL)
10150            .unwrap();
10151        assert_eq!(result[0], "SELECT COALESCE(a, b)");
10152
10153        // NVL should also be transformed to COALESCE
10154        let result = dialect
10155            .transpile("SELECT NVL(a, b)", DialectType::PostgreSQL)
10156            .unwrap();
10157        assert_eq!(result[0], "SELECT COALESCE(a, b)");
10158    }
10159
10160    #[test]
10161    fn test_hive_cast_to_trycast() {
10162        // Hive CAST should become TRY_CAST for targets that support it
10163        let hive = Dialect::get(DialectType::Hive);
10164        let result = hive
10165            .transpile("CAST(1 AS INT)", DialectType::DuckDB)
10166            .unwrap();
10167        assert_eq!(result[0], "TRY_CAST(1 AS INT)");
10168
10169        let result = hive
10170            .transpile("CAST(1 AS INT)", DialectType::Presto)
10171            .unwrap();
10172        assert_eq!(result[0], "TRY_CAST(1 AS INTEGER)");
10173    }
10174
10175    #[test]
10176    fn test_hive_array_identity() {
10177        // Hive ARRAY<DATE> should preserve angle bracket syntax
10178        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')";
10179        let hive = Dialect::get(DialectType::Hive);
10180
10181        // Test via transpile (this works)
10182        let result = hive.transpile(sql, DialectType::Hive).unwrap();
10183        eprintln!("Hive ARRAY via transpile: {}", result[0]);
10184        assert!(
10185            result[0].contains("ARRAY<DATE>"),
10186            "transpile: Expected ARRAY<DATE>, got: {}",
10187            result[0]
10188        );
10189
10190        // Test via parse -> transform -> generate (identity test path)
10191        let ast = hive.parse(sql).unwrap();
10192        let transformed = hive.transform(ast[0].clone()).unwrap();
10193        let output = hive.generate(&transformed).unwrap();
10194        eprintln!("Hive ARRAY via identity path: {}", output);
10195        assert!(
10196            output.contains("ARRAY<DATE>"),
10197            "identity path: Expected ARRAY<DATE>, got: {}",
10198            output
10199        );
10200    }
10201
10202    #[test]
10203    fn test_starrocks_delete_between_expansion() {
10204        // StarRocks doesn't support BETWEEN in DELETE statements
10205        let dialect = Dialect::get(DialectType::Generic);
10206
10207        // BETWEEN should be expanded to >= AND <= in DELETE
10208        let result = dialect
10209            .transpile(
10210                "DELETE FROM t WHERE a BETWEEN b AND c",
10211                DialectType::StarRocks,
10212            )
10213            .unwrap();
10214        assert_eq!(result[0], "DELETE FROM t WHERE a >= b AND a <= c");
10215
10216        // NOT BETWEEN should be expanded to < OR > in DELETE
10217        let result = dialect
10218            .transpile(
10219                "DELETE FROM t WHERE a NOT BETWEEN b AND c",
10220                DialectType::StarRocks,
10221            )
10222            .unwrap();
10223        assert_eq!(result[0], "DELETE FROM t WHERE a < b OR a > c");
10224
10225        // BETWEEN in SELECT should NOT be expanded (StarRocks supports it there)
10226        let result = dialect
10227            .transpile(
10228                "SELECT * FROM t WHERE a BETWEEN b AND c",
10229                DialectType::StarRocks,
10230            )
10231            .unwrap();
10232        assert!(
10233            result[0].contains("BETWEEN"),
10234            "BETWEEN should be preserved in SELECT"
10235        );
10236    }
10237
10238    #[test]
10239    fn test_snowflake_ltrim_rtrim_parse() {
10240        let sf = Dialect::get(DialectType::Snowflake);
10241        let sql = "SELECT LTRIM(RTRIM(col)) FROM t1";
10242        let result = sf.transpile(sql, DialectType::DuckDB);
10243        match &result {
10244            Ok(r) => eprintln!("LTRIM/RTRIM result: {}", r[0]),
10245            Err(e) => eprintln!("LTRIM/RTRIM error: {}", e),
10246        }
10247        assert!(
10248            result.is_ok(),
10249            "Expected successful parse of LTRIM(RTRIM(col)), got error: {:?}",
10250            result.err()
10251        );
10252    }
10253
10254    #[test]
10255    fn test_duckdb_count_if_parse() {
10256        let duck = Dialect::get(DialectType::DuckDB);
10257        let sql = "COUNT_IF(x)";
10258        let result = duck.transpile(sql, DialectType::DuckDB);
10259        match &result {
10260            Ok(r) => eprintln!("COUNT_IF result: {}", r[0]),
10261            Err(e) => eprintln!("COUNT_IF error: {}", e),
10262        }
10263        assert!(
10264            result.is_ok(),
10265            "Expected successful parse of COUNT_IF(x), got error: {:?}",
10266            result.err()
10267        );
10268    }
10269
10270    #[test]
10271    fn test_tsql_cast_tinyint_parse() {
10272        let tsql = Dialect::get(DialectType::TSQL);
10273        let sql = "CAST(X AS TINYINT)";
10274        let result = tsql.transpile(sql, DialectType::DuckDB);
10275        match &result {
10276            Ok(r) => eprintln!("TSQL CAST TINYINT result: {}", r[0]),
10277            Err(e) => eprintln!("TSQL CAST TINYINT error: {}", e),
10278        }
10279        assert!(
10280            result.is_ok(),
10281            "Expected successful transpile, got error: {:?}",
10282            result.err()
10283        );
10284    }
10285
10286    #[test]
10287    fn test_pg_hash_bitwise_xor() {
10288        let dialect = Dialect::get(DialectType::PostgreSQL);
10289        let result = dialect.transpile("x # y", DialectType::PostgreSQL).unwrap();
10290        assert_eq!(result[0], "x # y");
10291    }
10292
10293    #[test]
10294    fn test_pg_array_to_duckdb() {
10295        let dialect = Dialect::get(DialectType::PostgreSQL);
10296        let result = dialect
10297            .transpile("SELECT ARRAY[1, 2, 3] @> ARRAY[1, 2]", DialectType::DuckDB)
10298            .unwrap();
10299        assert_eq!(result[0], "SELECT [1, 2, 3] @> [1, 2]");
10300    }
10301
10302    #[test]
10303    fn test_array_remove_bigquery() {
10304        let dialect = Dialect::get(DialectType::Generic);
10305        let result = dialect
10306            .transpile("ARRAY_REMOVE(the_array, target)", DialectType::BigQuery)
10307            .unwrap();
10308        assert_eq!(
10309            result[0],
10310            "ARRAY(SELECT _u FROM UNNEST(the_array) AS _u WHERE _u <> target)"
10311        );
10312    }
10313
10314    #[test]
10315    fn test_map_clickhouse_case() {
10316        let dialect = Dialect::get(DialectType::Generic);
10317        let parsed = dialect
10318            .parse("CAST(MAP('a', '1') AS MAP(TEXT, TEXT))")
10319            .unwrap();
10320        eprintln!("MAP parsed: {:?}", parsed);
10321        let result = dialect
10322            .transpile(
10323                "CAST(MAP('a', '1') AS MAP(TEXT, TEXT))",
10324                DialectType::ClickHouse,
10325            )
10326            .unwrap();
10327        eprintln!("MAP result: {}", result[0]);
10328    }
10329
10330    #[test]
10331    fn test_generate_date_array_presto() {
10332        let dialect = Dialect::get(DialectType::Generic);
10333        let result = dialect.transpile(
10334            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10335            DialectType::Presto,
10336        ).unwrap();
10337        eprintln!("GDA -> Presto: {}", result[0]);
10338        assert_eq!(result[0], "SELECT * FROM UNNEST(SEQUENCE(CAST('2020-01-01' AS DATE), CAST('2020-02-01' AS DATE), (1 * INTERVAL '7' DAY)))");
10339    }
10340
10341    #[test]
10342    fn test_generate_date_array_postgres() {
10343        let dialect = Dialect::get(DialectType::Generic);
10344        let result = dialect.transpile(
10345            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10346            DialectType::PostgreSQL,
10347        ).unwrap();
10348        eprintln!("GDA -> PostgreSQL: {}", result[0]);
10349    }
10350
10351    #[test]
10352    fn test_generate_date_array_snowflake() {
10353        let dialect = Dialect::get(DialectType::Generic);
10354        let result = dialect
10355            .transpile(
10356                "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10357                DialectType::Snowflake,
10358            )
10359            .unwrap();
10360        eprintln!("GDA -> Snowflake: {}", result[0]);
10361    }
10362
10363    #[test]
10364    fn test_array_length_generate_date_array_snowflake() {
10365        let dialect = Dialect::get(DialectType::Generic);
10366        let result = dialect.transpile(
10367            "SELECT ARRAY_LENGTH(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10368            DialectType::Snowflake,
10369        ).unwrap();
10370        eprintln!("ARRAY_LENGTH(GDA) -> Snowflake: {}", result[0]);
10371    }
10372
10373    #[test]
10374    fn test_generate_date_array_mysql() {
10375        let dialect = Dialect::get(DialectType::Generic);
10376        let result = dialect.transpile(
10377            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10378            DialectType::MySQL,
10379        ).unwrap();
10380        eprintln!("GDA -> MySQL: {}", result[0]);
10381    }
10382
10383    #[test]
10384    fn test_generate_date_array_redshift() {
10385        let dialect = Dialect::get(DialectType::Generic);
10386        let result = dialect.transpile(
10387            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10388            DialectType::Redshift,
10389        ).unwrap();
10390        eprintln!("GDA -> Redshift: {}", result[0]);
10391    }
10392
10393    #[test]
10394    fn test_generate_date_array_tsql() {
10395        let dialect = Dialect::get(DialectType::Generic);
10396        let result = dialect.transpile(
10397            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10398            DialectType::TSQL,
10399        ).unwrap();
10400        eprintln!("GDA -> TSQL: {}", result[0]);
10401    }
10402
10403    #[test]
10404    fn test_struct_colon_syntax() {
10405        let dialect = Dialect::get(DialectType::Generic);
10406        // Test without colon first
10407        let result = dialect.transpile(
10408            "CAST((1, 2, 3, 4) AS STRUCT<a TINYINT, b SMALLINT, c INT, d BIGINT>)",
10409            DialectType::ClickHouse,
10410        );
10411        match result {
10412            Ok(r) => eprintln!("STRUCT no colon -> ClickHouse: {}", r[0]),
10413            Err(e) => eprintln!("STRUCT no colon error: {}", e),
10414        }
10415        // Now test with colon
10416        let result = dialect.transpile(
10417            "CAST((1, 2, 3, 4) AS STRUCT<a: TINYINT, b: SMALLINT, c: INT, d: BIGINT>)",
10418            DialectType::ClickHouse,
10419        );
10420        match result {
10421            Ok(r) => eprintln!("STRUCT colon -> ClickHouse: {}", r[0]),
10422            Err(e) => eprintln!("STRUCT colon error: {}", e),
10423        }
10424    }
10425
10426    #[test]
10427    fn test_generate_date_array_cte_wrapped_mysql() {
10428        let dialect = Dialect::get(DialectType::Generic);
10429        let result = dialect.transpile(
10430            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
10431            DialectType::MySQL,
10432        ).unwrap();
10433        eprintln!("GDA CTE -> MySQL: {}", result[0]);
10434    }
10435
10436    #[test]
10437    fn test_generate_date_array_cte_wrapped_tsql() {
10438        let dialect = Dialect::get(DialectType::Generic);
10439        let result = dialect.transpile(
10440            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
10441            DialectType::TSQL,
10442        ).unwrap();
10443        eprintln!("GDA CTE -> TSQL: {}", result[0]);
10444    }
10445
10446    #[test]
10447    fn test_decode_literal_no_null_check() {
10448        // Oracle DECODE with all literals should produce simple equality, no IS NULL
10449        let dialect = Dialect::get(DialectType::Oracle);
10450        let result = dialect
10451            .transpile("SELECT decode(1,2,3,4)", DialectType::DuckDB)
10452            .unwrap();
10453        assert_eq!(
10454            result[0], "SELECT CASE WHEN 1 = 2 THEN 3 ELSE 4 END",
10455            "Literal DECODE should not have IS NULL checks"
10456        );
10457    }
10458
10459    #[test]
10460    fn test_decode_column_vs_literal_no_null_check() {
10461        // Oracle DECODE with column vs literal should use simple equality (like sqlglot)
10462        let dialect = Dialect::get(DialectType::Oracle);
10463        let result = dialect
10464            .transpile("SELECT decode(col, 2, 3, 4) FROM t", DialectType::DuckDB)
10465            .unwrap();
10466        assert_eq!(
10467            result[0], "SELECT CASE WHEN col = 2 THEN 3 ELSE 4 END FROM t",
10468            "Column vs literal DECODE should not have IS NULL checks"
10469        );
10470    }
10471
10472    #[test]
10473    fn test_decode_column_vs_column_keeps_null_check() {
10474        // Oracle DECODE with column vs column should keep null-safe comparison
10475        let dialect = Dialect::get(DialectType::Oracle);
10476        let result = dialect
10477            .transpile("SELECT decode(col, col2, 3, 4) FROM t", DialectType::DuckDB)
10478            .unwrap();
10479        assert!(
10480            result[0].contains("IS NULL"),
10481            "Column vs column DECODE should have IS NULL checks, got: {}",
10482            result[0]
10483        );
10484    }
10485
10486    #[test]
10487    fn test_decode_null_search() {
10488        // Oracle DECODE with NULL search should use IS NULL
10489        let dialect = Dialect::get(DialectType::Oracle);
10490        let result = dialect
10491            .transpile("SELECT decode(col, NULL, 3, 4) FROM t", DialectType::DuckDB)
10492            .unwrap();
10493        assert_eq!(
10494            result[0],
10495            "SELECT CASE WHEN col IS NULL THEN 3 ELSE 4 END FROM t",
10496        );
10497    }
10498
10499    // =========================================================================
10500    // REGEXP function transpilation tests
10501    // =========================================================================
10502
10503    #[test]
10504    fn test_regexp_substr_snowflake_to_duckdb_2arg() {
10505        let dialect = Dialect::get(DialectType::Snowflake);
10506        let result = dialect
10507            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern')", DialectType::DuckDB)
10508            .unwrap();
10509        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10510    }
10511
10512    #[test]
10513    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos1() {
10514        let dialect = Dialect::get(DialectType::Snowflake);
10515        let result = dialect
10516            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 1)", DialectType::DuckDB)
10517            .unwrap();
10518        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10519    }
10520
10521    #[test]
10522    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos_gt1() {
10523        let dialect = Dialect::get(DialectType::Snowflake);
10524        let result = dialect
10525            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 3)", DialectType::DuckDB)
10526            .unwrap();
10527        assert_eq!(
10528            result[0],
10529            "SELECT REGEXP_EXTRACT(NULLIF(SUBSTRING(s, 3), ''), 'pattern')"
10530        );
10531    }
10532
10533    #[test]
10534    fn test_regexp_substr_snowflake_to_duckdb_4arg_occ_gt1() {
10535        let dialect = Dialect::get(DialectType::Snowflake);
10536        let result = dialect
10537            .transpile(
10538                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 3)",
10539                DialectType::DuckDB,
10540            )
10541            .unwrap();
10542        assert_eq!(
10543            result[0],
10544            "SELECT ARRAY_EXTRACT(REGEXP_EXTRACT_ALL(s, 'pattern'), 3)"
10545        );
10546    }
10547
10548    #[test]
10549    fn test_regexp_substr_snowflake_to_duckdb_5arg_e_flag() {
10550        let dialect = Dialect::get(DialectType::Snowflake);
10551        let result = dialect
10552            .transpile(
10553                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')",
10554                DialectType::DuckDB,
10555            )
10556            .unwrap();
10557        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10558    }
10559
10560    #[test]
10561    fn test_regexp_substr_snowflake_to_duckdb_6arg_group0() {
10562        let dialect = Dialect::get(DialectType::Snowflake);
10563        let result = dialect
10564            .transpile(
10565                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
10566                DialectType::DuckDB,
10567            )
10568            .unwrap();
10569        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10570    }
10571
10572    #[test]
10573    fn test_regexp_substr_snowflake_identity_strip_group0() {
10574        let dialect = Dialect::get(DialectType::Snowflake);
10575        let result = dialect
10576            .transpile(
10577                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
10578                DialectType::Snowflake,
10579            )
10580            .unwrap();
10581        assert_eq!(result[0], "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')");
10582    }
10583
10584    #[test]
10585    fn test_regexp_substr_all_snowflake_to_duckdb_2arg() {
10586        let dialect = Dialect::get(DialectType::Snowflake);
10587        let result = dialect
10588            .transpile(
10589                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')",
10590                DialectType::DuckDB,
10591            )
10592            .unwrap();
10593        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10594    }
10595
10596    #[test]
10597    fn test_regexp_substr_all_snowflake_to_duckdb_3arg_pos_gt1() {
10598        let dialect = Dialect::get(DialectType::Snowflake);
10599        let result = dialect
10600            .transpile(
10601                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 3)",
10602                DialectType::DuckDB,
10603            )
10604            .unwrap();
10605        assert_eq!(
10606            result[0],
10607            "SELECT REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')"
10608        );
10609    }
10610
10611    #[test]
10612    fn test_regexp_substr_all_snowflake_to_duckdb_5arg_e_flag() {
10613        let dialect = Dialect::get(DialectType::Snowflake);
10614        let result = dialect
10615            .transpile(
10616                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')",
10617                DialectType::DuckDB,
10618            )
10619            .unwrap();
10620        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10621    }
10622
10623    #[test]
10624    fn test_regexp_substr_all_snowflake_to_duckdb_6arg_group0() {
10625        let dialect = Dialect::get(DialectType::Snowflake);
10626        let result = dialect
10627            .transpile(
10628                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
10629                DialectType::DuckDB,
10630            )
10631            .unwrap();
10632        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10633    }
10634
10635    #[test]
10636    fn test_regexp_substr_all_snowflake_identity_strip_group0() {
10637        let dialect = Dialect::get(DialectType::Snowflake);
10638        let result = dialect
10639            .transpile(
10640                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
10641                DialectType::Snowflake,
10642            )
10643            .unwrap();
10644        assert_eq!(
10645            result[0],
10646            "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')"
10647        );
10648    }
10649
10650    #[test]
10651    fn test_regexp_count_snowflake_to_duckdb_2arg() {
10652        let dialect = Dialect::get(DialectType::Snowflake);
10653        let result = dialect
10654            .transpile("SELECT REGEXP_COUNT(s, 'pattern')", DialectType::DuckDB)
10655            .unwrap();
10656        assert_eq!(
10657            result[0],
10658            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(s, 'pattern')) END"
10659        );
10660    }
10661
10662    #[test]
10663    fn test_regexp_count_snowflake_to_duckdb_3arg() {
10664        let dialect = Dialect::get(DialectType::Snowflake);
10665        let result = dialect
10666            .transpile("SELECT REGEXP_COUNT(s, 'pattern', 3)", DialectType::DuckDB)
10667            .unwrap();
10668        assert_eq!(
10669            result[0],
10670            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')) END"
10671        );
10672    }
10673
10674    #[test]
10675    fn test_regexp_count_snowflake_to_duckdb_4arg_flags() {
10676        let dialect = Dialect::get(DialectType::Snowflake);
10677        let result = dialect
10678            .transpile(
10679                "SELECT REGEXP_COUNT(s, 'pattern', 1, 'i')",
10680                DialectType::DuckDB,
10681            )
10682            .unwrap();
10683        assert_eq!(
10684            result[0],
10685            "SELECT CASE WHEN '(?i)' || 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 1), '(?i)' || 'pattern')) END"
10686        );
10687    }
10688
10689    #[test]
10690    fn test_regexp_count_snowflake_to_duckdb_4arg_flags_literal_string() {
10691        let dialect = Dialect::get(DialectType::Snowflake);
10692        let result = dialect
10693            .transpile(
10694                "SELECT REGEXP_COUNT('Hello World', 'L', 1, 'im')",
10695                DialectType::DuckDB,
10696            )
10697            .unwrap();
10698        assert_eq!(
10699            result[0],
10700            "SELECT CASE WHEN '(?im)' || 'L' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING('Hello World', 1), '(?im)' || 'L')) END"
10701        );
10702    }
10703
10704    #[test]
10705    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos1_occ1() {
10706        let dialect = Dialect::get(DialectType::Snowflake);
10707        let result = dialect
10708            .transpile(
10709                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 1, 1)",
10710                DialectType::DuckDB,
10711            )
10712            .unwrap();
10713        assert_eq!(result[0], "SELECT REGEXP_REPLACE(s, 'pattern', 'repl')");
10714    }
10715
10716    #[test]
10717    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ0() {
10718        let dialect = Dialect::get(DialectType::Snowflake);
10719        let result = dialect
10720            .transpile(
10721                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 0)",
10722                DialectType::DuckDB,
10723            )
10724            .unwrap();
10725        assert_eq!(
10726            result[0],
10727            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl', 'g')"
10728        );
10729    }
10730
10731    #[test]
10732    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ1() {
10733        let dialect = Dialect::get(DialectType::Snowflake);
10734        let result = dialect
10735            .transpile(
10736                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 1)",
10737                DialectType::DuckDB,
10738            )
10739            .unwrap();
10740        assert_eq!(
10741            result[0],
10742            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl')"
10743        );
10744    }
10745
10746    #[test]
10747    fn test_rlike_snowflake_to_duckdb_2arg() {
10748        let dialect = Dialect::get(DialectType::Snowflake);
10749        let result = dialect
10750            .transpile("SELECT RLIKE(a, b)", DialectType::DuckDB)
10751            .unwrap();
10752        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b)");
10753    }
10754
10755    #[test]
10756    fn test_rlike_snowflake_to_duckdb_3arg_flags() {
10757        let dialect = Dialect::get(DialectType::Snowflake);
10758        let result = dialect
10759            .transpile("SELECT RLIKE(a, b, 'i')", DialectType::DuckDB)
10760            .unwrap();
10761        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b, 'i')");
10762    }
10763
10764    #[test]
10765    fn test_regexp_extract_all_bigquery_to_snowflake_no_capture() {
10766        let dialect = Dialect::get(DialectType::BigQuery);
10767        let result = dialect
10768            .transpile(
10769                "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')",
10770                DialectType::Snowflake,
10771            )
10772            .unwrap();
10773        assert_eq!(result[0], "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')");
10774    }
10775
10776    #[test]
10777    fn test_regexp_extract_all_bigquery_to_snowflake_with_capture() {
10778        let dialect = Dialect::get(DialectType::BigQuery);
10779        let result = dialect
10780            .transpile(
10781                "SELECT REGEXP_EXTRACT_ALL(s, '(a)[0-9]')",
10782                DialectType::Snowflake,
10783            )
10784            .unwrap();
10785        assert_eq!(
10786            result[0],
10787            "SELECT REGEXP_SUBSTR_ALL(s, '(a)[0-9]', 1, 1, 'c', 1)"
10788        );
10789    }
10790
10791    #[test]
10792    fn test_regexp_instr_snowflake_to_duckdb_2arg() {
10793        let dialect = Dialect::get(DialectType::Snowflake);
10794        let result = dialect
10795            .transpile("SELECT REGEXP_INSTR(s, 'pattern')", DialectType::DuckDB)
10796            .unwrap();
10797        assert!(
10798            result[0].contains("CASE WHEN"),
10799            "Expected CASE WHEN in result: {}",
10800            result[0]
10801        );
10802        assert!(
10803            result[0].contains("LIST_SUM"),
10804            "Expected LIST_SUM in result: {}",
10805            result[0]
10806        );
10807    }
10808
10809    #[test]
10810    fn test_array_except_generic_to_duckdb() {
10811        let dialect = Dialect::get(DialectType::Generic);
10812        let result = dialect
10813            .transpile(
10814                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
10815                DialectType::DuckDB,
10816            )
10817            .unwrap();
10818        eprintln!("ARRAY_EXCEPT Generic->DuckDB: {}", result[0]);
10819        assert!(
10820            result[0].contains("CASE WHEN"),
10821            "Expected CASE WHEN: {}",
10822            result[0]
10823        );
10824        assert!(
10825            result[0].contains("LIST_FILTER"),
10826            "Expected LIST_FILTER: {}",
10827            result[0]
10828        );
10829        assert!(
10830            result[0].contains("LIST_DISTINCT"),
10831            "Expected LIST_DISTINCT: {}",
10832            result[0]
10833        );
10834        assert!(
10835            result[0].contains("IS NOT DISTINCT FROM"),
10836            "Expected IS NOT DISTINCT FROM: {}",
10837            result[0]
10838        );
10839        assert!(
10840            result[0].contains("= 0"),
10841            "Expected = 0 filter: {}",
10842            result[0]
10843        );
10844    }
10845
10846    #[test]
10847    fn test_array_except_generic_to_snowflake() {
10848        let dialect = Dialect::get(DialectType::Generic);
10849        let result = dialect
10850            .transpile(
10851                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
10852                DialectType::Snowflake,
10853            )
10854            .unwrap();
10855        eprintln!("ARRAY_EXCEPT Generic->Snowflake: {}", result[0]);
10856        assert_eq!(result[0], "SELECT ARRAY_EXCEPT([1, 2, 3], [2])");
10857    }
10858
10859    #[test]
10860    fn test_array_except_generic_to_presto() {
10861        let dialect = Dialect::get(DialectType::Generic);
10862        let result = dialect
10863            .transpile(
10864                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
10865                DialectType::Presto,
10866            )
10867            .unwrap();
10868        eprintln!("ARRAY_EXCEPT Generic->Presto: {}", result[0]);
10869        assert_eq!(result[0], "SELECT ARRAY_EXCEPT(ARRAY[1, 2, 3], ARRAY[2])");
10870    }
10871
10872    #[test]
10873    fn test_array_except_snowflake_to_duckdb() {
10874        let dialect = Dialect::get(DialectType::Snowflake);
10875        let result = dialect
10876            .transpile("SELECT ARRAY_EXCEPT([1, 2, 3], [2])", DialectType::DuckDB)
10877            .unwrap();
10878        eprintln!("ARRAY_EXCEPT Snowflake->DuckDB: {}", result[0]);
10879        assert!(
10880            result[0].contains("CASE WHEN"),
10881            "Expected CASE WHEN: {}",
10882            result[0]
10883        );
10884        assert!(
10885            result[0].contains("LIST_TRANSFORM"),
10886            "Expected LIST_TRANSFORM: {}",
10887            result[0]
10888        );
10889    }
10890
10891    #[test]
10892    fn test_array_contains_snowflake_to_snowflake() {
10893        let dialect = Dialect::get(DialectType::Snowflake);
10894        let result = dialect
10895            .transpile(
10896                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
10897                DialectType::Snowflake,
10898            )
10899            .unwrap();
10900        eprintln!("ARRAY_CONTAINS Snowflake->Snowflake: {}", result[0]);
10901        assert_eq!(result[0], "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])");
10902    }
10903
10904    #[test]
10905    fn test_array_contains_snowflake_to_duckdb() {
10906        let dialect = Dialect::get(DialectType::Snowflake);
10907        let result = dialect
10908            .transpile(
10909                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
10910                DialectType::DuckDB,
10911            )
10912            .unwrap();
10913        eprintln!("ARRAY_CONTAINS Snowflake->DuckDB: {}", result[0]);
10914        assert!(
10915            result[0].contains("CASE WHEN"),
10916            "Expected CASE WHEN: {}",
10917            result[0]
10918        );
10919        assert!(
10920            result[0].contains("NULLIF"),
10921            "Expected NULLIF: {}",
10922            result[0]
10923        );
10924        assert!(
10925            result[0].contains("ARRAY_CONTAINS"),
10926            "Expected ARRAY_CONTAINS: {}",
10927            result[0]
10928        );
10929    }
10930
10931    #[test]
10932    fn test_array_distinct_snowflake_to_duckdb() {
10933        let dialect = Dialect::get(DialectType::Snowflake);
10934        let result = dialect
10935            .transpile(
10936                "SELECT ARRAY_DISTINCT([1, 2, 2, 3, 1])",
10937                DialectType::DuckDB,
10938            )
10939            .unwrap();
10940        eprintln!("ARRAY_DISTINCT Snowflake->DuckDB: {}", result[0]);
10941        assert!(
10942            result[0].contains("CASE WHEN"),
10943            "Expected CASE WHEN: {}",
10944            result[0]
10945        );
10946        assert!(
10947            result[0].contains("LIST_DISTINCT"),
10948            "Expected LIST_DISTINCT: {}",
10949            result[0]
10950        );
10951        assert!(
10952            result[0].contains("LIST_APPEND"),
10953            "Expected LIST_APPEND: {}",
10954            result[0]
10955        );
10956        assert!(
10957            result[0].contains("LIST_FILTER"),
10958            "Expected LIST_FILTER: {}",
10959            result[0]
10960        );
10961    }
10962}