Skip to main content

polyglot_sql/dialects/
mod.rs

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