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-hana")]
55mod hana;
56#[cfg(feature = "dialect-hive")]
57mod hive;
58#[cfg(feature = "dialect-materialize")]
59mod materialize;
60#[cfg(feature = "dialect-mysql")]
61mod mysql;
62#[cfg(feature = "dialect-oracle")]
63mod oracle;
64#[cfg(feature = "dialect-postgresql")]
65mod postgres;
66#[cfg(feature = "dialect-presto")]
67mod presto;
68#[cfg(feature = "dialect-redshift")]
69mod redshift;
70#[cfg(feature = "dialect-risingwave")]
71mod risingwave;
72#[cfg(feature = "dialect-singlestore")]
73mod singlestore;
74#[cfg(feature = "dialect-snowflake")]
75mod snowflake;
76#[cfg(feature = "dialect-solr")]
77mod solr;
78#[cfg(feature = "dialect-spark")]
79mod spark;
80#[cfg(feature = "dialect-sqlite")]
81mod sqlite;
82#[cfg(feature = "dialect-starrocks")]
83mod starrocks;
84#[cfg(feature = "dialect-tableau")]
85mod tableau;
86#[cfg(feature = "dialect-teradata")]
87mod teradata;
88#[cfg(feature = "dialect-tidb")]
89mod tidb;
90#[cfg(feature = "dialect-trino")]
91mod trino;
92#[cfg(any(feature = "dialect-tsql", feature = "dialect-fabric"))]
93mod tsql;
94#[cfg(feature = "dialect-vertica")]
95mod vertica;
96
97pub use generic::GenericDialect; // Always available
98
99#[cfg(feature = "dialect-athena")]
100pub use athena::AthenaDialect;
101#[cfg(feature = "dialect-bigquery")]
102pub use bigquery::BigQueryDialect;
103#[cfg(feature = "dialect-clickhouse")]
104pub use clickhouse::ClickHouseDialect;
105#[cfg(feature = "dialect-cockroachdb")]
106pub use cockroachdb::CockroachDBDialect;
107#[cfg(feature = "dialect-databricks")]
108pub use databricks::DatabricksDialect;
109#[cfg(feature = "dialect-datafusion")]
110pub use datafusion::DataFusionDialect;
111#[cfg(feature = "dialect-doris")]
112pub use doris::DorisDialect;
113#[cfg(feature = "dialect-dremio")]
114pub use dremio::DremioDialect;
115#[cfg(feature = "dialect-drill")]
116pub use drill::DrillDialect;
117#[cfg(feature = "dialect-druid")]
118pub use druid::DruidDialect;
119#[cfg(feature = "dialect-duckdb")]
120pub use duckdb::DuckDBDialect;
121#[cfg(feature = "dialect-dune")]
122pub use dune::DuneDialect;
123#[cfg(feature = "dialect-exasol")]
124pub use exasol::ExasolDialect;
125#[cfg(feature = "dialect-fabric")]
126pub use fabric::FabricDialect;
127#[cfg(feature = "dialect-hana")]
128pub use hana::HanaDialect;
129#[cfg(feature = "dialect-hive")]
130pub use hive::HiveDialect;
131#[cfg(feature = "dialect-materialize")]
132pub use materialize::MaterializeDialect;
133#[cfg(feature = "dialect-mysql")]
134pub use mysql::MySQLDialect;
135#[cfg(feature = "dialect-oracle")]
136pub use oracle::OracleDialect;
137#[cfg(feature = "dialect-postgresql")]
138pub use postgres::PostgresDialect;
139#[cfg(feature = "dialect-presto")]
140pub use presto::PrestoDialect;
141#[cfg(feature = "dialect-redshift")]
142pub use redshift::RedshiftDialect;
143#[cfg(feature = "dialect-risingwave")]
144pub use risingwave::RisingWaveDialect;
145#[cfg(feature = "dialect-singlestore")]
146pub use singlestore::SingleStoreDialect;
147#[cfg(feature = "dialect-snowflake")]
148pub use snowflake::SnowflakeDialect;
149#[cfg(feature = "dialect-solr")]
150pub use solr::SolrDialect;
151#[cfg(feature = "dialect-spark")]
152pub use spark::SparkDialect;
153#[cfg(feature = "dialect-sqlite")]
154pub use sqlite::SQLiteDialect;
155#[cfg(feature = "dialect-starrocks")]
156pub use starrocks::StarRocksDialect;
157#[cfg(feature = "dialect-tableau")]
158pub use tableau::TableauDialect;
159#[cfg(feature = "dialect-teradata")]
160pub use teradata::TeradataDialect;
161#[cfg(feature = "dialect-tidb")]
162pub use tidb::TiDBDialect;
163#[cfg(feature = "dialect-trino")]
164pub use trino::TrinoDialect;
165#[cfg(feature = "dialect-tsql")]
166pub use tsql::TSQLDialect;
167#[cfg(feature = "dialect-vertica")]
168pub use vertica::VerticaDialect;
169
170use crate::error::Result;
171#[cfg(feature = "transpile")]
172use crate::expressions::{
173    BinaryOp, Case, Cast, ColumnConstraint, DateBin, DateTimeField, Fetch, Function, Identifier,
174    Interval, IntervalUnit, IntervalUnitSpec, JoinKind, Literal, Offset, Over, Select, Subquery,
175    Top, Var, WindowFrame, WindowFrameBound, WindowFrameKind,
176};
177use crate::expressions::{DataType, Expression};
178#[cfg(any(
179    feature = "transpile",
180    feature = "ast-tools",
181    feature = "generate",
182    feature = "semantic"
183))]
184use crate::expressions::{From, FunctionBody, Join, Null, OrderBy, OutputClause, TableRef, With};
185#[cfg(feature = "transpile")]
186use crate::generator::UnsupportedLevel;
187#[cfg(feature = "generate")]
188use crate::generator::{Generator, GeneratorConfig};
189#[cfg(feature = "transpile")]
190use crate::guard::enforce_generate_ast;
191use crate::guard::{enforce_input, ComplexityGuardOptions, ParseOptions};
192#[cfg(feature = "transpile")]
193use crate::helper::find_new_name;
194use crate::parser::Parser;
195#[cfg(feature = "transpile")]
196use crate::tokens::TokenType;
197use crate::tokens::{Token, Tokenizer, TokenizerConfig};
198#[cfg(feature = "transpile")]
199use crate::traversal::{is_aggregate, ExpressionWalk};
200use serde::{Deserialize, Serialize};
201use std::collections::HashMap;
202#[cfg(feature = "transpile")]
203use std::collections::HashSet;
204use std::sync::{Arc, LazyLock, RwLock};
205
206/// Enumeration of all supported SQL dialects.
207///
208/// Each variant corresponds to a specific SQL database engine or query language.
209/// The `Generic` variant represents standard SQL with no dialect-specific behavior,
210/// and is used as the default when no dialect is specified.
211///
212/// Dialect names are case-insensitive when parsed from strings via [`FromStr`].
213/// Some dialects accept aliases (e.g., "mssql" and "sqlserver" both resolve to [`TSQL`](DialectType::TSQL)).
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
215#[cfg_attr(feature = "bindings", derive(ts_rs::TS))]
216#[serde(rename_all = "lowercase")]
217pub enum DialectType {
218    /// Standard SQL with no dialect-specific behavior (default).
219    Generic,
220    /// PostgreSQL -- advanced open-source relational database.
221    PostgreSQL,
222    /// MySQL -- widely-used open-source relational database (also accepts "mysql").
223    MySQL,
224    /// Google BigQuery -- serverless cloud data warehouse with unique syntax (backtick quoting, STRUCT types, QUALIFY).
225    BigQuery,
226    /// Snowflake -- cloud data platform with QUALIFY clause, FLATTEN, and variant types.
227    Snowflake,
228    /// DuckDB -- in-process analytical database with modern SQL extensions.
229    DuckDB,
230    /// SQLite -- lightweight embedded relational database.
231    SQLite,
232    /// Apache Hive -- data warehouse on Hadoop with HiveQL syntax.
233    Hive,
234    /// Apache Spark SQL -- distributed query engine (also accepts "spark2").
235    Spark,
236    /// Trino -- distributed SQL query engine (formerly PrestoSQL).
237    Trino,
238    /// PrestoDB -- distributed SQL query engine for big data.
239    Presto,
240    /// Amazon Redshift -- cloud data warehouse based on PostgreSQL.
241    Redshift,
242    /// Transact-SQL (T-SQL) -- Microsoft SQL Server and Azure SQL (also accepts "mssql", "sqlserver").
243    TSQL,
244    /// Oracle Database -- commercial relational database with PL/SQL extensions.
245    Oracle,
246    /// ClickHouse -- column-oriented OLAP database for real-time analytics.
247    ClickHouse,
248    /// Databricks SQL -- Spark-based lakehouse platform with QUALIFY support.
249    Databricks,
250    /// Amazon Athena -- serverless query service (hybrid Trino/Hive engine).
251    Athena,
252    /// Teradata -- enterprise data warehouse with proprietary SQL extensions.
253    Teradata,
254    /// Apache Doris -- real-time analytical database (MySQL-compatible).
255    Doris,
256    /// StarRocks -- sub-second OLAP database (MySQL-compatible).
257    StarRocks,
258    /// Materialize -- streaming SQL database built on differential dataflow.
259    Materialize,
260    /// RisingWave -- distributed streaming database with PostgreSQL compatibility.
261    RisingWave,
262    /// SingleStore (formerly MemSQL) -- distributed SQL database (also accepts "memsql").
263    SingleStore,
264    /// CockroachDB -- distributed SQL database with PostgreSQL compatibility (also accepts "cockroach").
265    CockroachDB,
266    /// TiDB -- distributed HTAP database with MySQL compatibility.
267    TiDB,
268    /// Apache Druid -- real-time analytics database.
269    Druid,
270    /// Apache Solr -- search platform with SQL interface.
271    Solr,
272    /// Tableau -- data visualization platform with its own SQL dialect.
273    Tableau,
274    /// Dune Analytics -- blockchain analytics SQL engine.
275    Dune,
276    /// Microsoft Fabric -- unified analytics platform (T-SQL based).
277    Fabric,
278    /// Apache Drill -- schema-free SQL query engine for big data.
279    Drill,
280    /// Dremio -- data lakehouse platform with Arrow-based query engine.
281    Dremio,
282    /// Exasol -- in-memory analytic database.
283    Exasol,
284    /// Apache DataFusion -- Arrow-based query engine with modern SQL extensions.
285    DataFusion,
286    /// SAP HANA Cloud and SAP HANA Platform SQL.
287    HANA,
288    /// Vertica (OpenText Analytics Database) -- columnar MPP analytic database.
289    Vertica,
290}
291
292impl DialectType {
293    /// Whether SELECT projections may use string literals as column aliases.
294    /// DuckDB permits this only when the alias has an explicit AS keyword.
295    pub(crate) const fn supports_string_aliases(self, explicit_as: bool) -> bool {
296        matches!(
297            self,
298            DialectType::TSQL | DialectType::Fabric | DialectType::MySQL | DialectType::SQLite
299        ) || (matches!(self, DialectType::DuckDB) && explicit_as)
300    }
301}
302
303impl Default for DialectType {
304    fn default() -> Self {
305        DialectType::Generic
306    }
307}
308
309impl std::fmt::Display for DialectType {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        match self {
312            DialectType::Generic => write!(f, "generic"),
313            DialectType::PostgreSQL => write!(f, "postgresql"),
314            DialectType::MySQL => write!(f, "mysql"),
315            DialectType::BigQuery => write!(f, "bigquery"),
316            DialectType::Snowflake => write!(f, "snowflake"),
317            DialectType::DuckDB => write!(f, "duckdb"),
318            DialectType::SQLite => write!(f, "sqlite"),
319            DialectType::Hive => write!(f, "hive"),
320            DialectType::Spark => write!(f, "spark"),
321            DialectType::Trino => write!(f, "trino"),
322            DialectType::Presto => write!(f, "presto"),
323            DialectType::Redshift => write!(f, "redshift"),
324            DialectType::TSQL => write!(f, "tsql"),
325            DialectType::Oracle => write!(f, "oracle"),
326            DialectType::ClickHouse => write!(f, "clickhouse"),
327            DialectType::Databricks => write!(f, "databricks"),
328            DialectType::Athena => write!(f, "athena"),
329            DialectType::Teradata => write!(f, "teradata"),
330            DialectType::Doris => write!(f, "doris"),
331            DialectType::StarRocks => write!(f, "starrocks"),
332            DialectType::Materialize => write!(f, "materialize"),
333            DialectType::RisingWave => write!(f, "risingwave"),
334            DialectType::SingleStore => write!(f, "singlestore"),
335            DialectType::CockroachDB => write!(f, "cockroachdb"),
336            DialectType::TiDB => write!(f, "tidb"),
337            DialectType::Druid => write!(f, "druid"),
338            DialectType::Solr => write!(f, "solr"),
339            DialectType::Tableau => write!(f, "tableau"),
340            DialectType::Dune => write!(f, "dune"),
341            DialectType::Fabric => write!(f, "fabric"),
342            DialectType::Drill => write!(f, "drill"),
343            DialectType::Dremio => write!(f, "dremio"),
344            DialectType::Exasol => write!(f, "exasol"),
345            DialectType::DataFusion => write!(f, "datafusion"),
346            DialectType::HANA => write!(f, "hana"),
347            DialectType::Vertica => write!(f, "vertica"),
348        }
349    }
350}
351
352impl std::str::FromStr for DialectType {
353    type Err = crate::error::Error;
354
355    fn from_str(s: &str) -> Result<Self> {
356        match s.to_ascii_lowercase().as_str() {
357            "generic" | "" => Ok(DialectType::Generic),
358            "postgres" | "postgresql" => Ok(DialectType::PostgreSQL),
359            "mysql" => Ok(DialectType::MySQL),
360            "bigquery" => Ok(DialectType::BigQuery),
361            "snowflake" => Ok(DialectType::Snowflake),
362            "duckdb" => Ok(DialectType::DuckDB),
363            "sqlite" => Ok(DialectType::SQLite),
364            "hive" => Ok(DialectType::Hive),
365            "spark" | "spark2" => Ok(DialectType::Spark),
366            "trino" => Ok(DialectType::Trino),
367            "presto" => Ok(DialectType::Presto),
368            "redshift" => Ok(DialectType::Redshift),
369            "tsql" | "mssql" | "sqlserver" => Ok(DialectType::TSQL),
370            "oracle" => Ok(DialectType::Oracle),
371            "clickhouse" => Ok(DialectType::ClickHouse),
372            "databricks" => Ok(DialectType::Databricks),
373            "athena" => Ok(DialectType::Athena),
374            "teradata" => Ok(DialectType::Teradata),
375            "doris" => Ok(DialectType::Doris),
376            "starrocks" => Ok(DialectType::StarRocks),
377            "materialize" => Ok(DialectType::Materialize),
378            "risingwave" => Ok(DialectType::RisingWave),
379            "singlestore" | "memsql" => Ok(DialectType::SingleStore),
380            "cockroachdb" | "cockroach" => Ok(DialectType::CockroachDB),
381            "tidb" => Ok(DialectType::TiDB),
382            "druid" => Ok(DialectType::Druid),
383            "solr" => Ok(DialectType::Solr),
384            "tableau" => Ok(DialectType::Tableau),
385            "dune" => Ok(DialectType::Dune),
386            "fabric" => Ok(DialectType::Fabric),
387            "drill" => Ok(DialectType::Drill),
388            "dremio" => Ok(DialectType::Dremio),
389            "exasol" => Ok(DialectType::Exasol),
390            "datafusion" | "arrow-datafusion" | "arrow_datafusion" => Ok(DialectType::DataFusion),
391            "hana" | "saphana" | "sap_hana" => Ok(DialectType::HANA),
392            "vertica" => Ok(DialectType::Vertica),
393            _ => Err(crate::error::Error::parse(
394                format!("Unknown dialect: {}", s),
395                0,
396                0,
397                0,
398                0,
399            )),
400        }
401    }
402}
403
404/// Trait that each concrete SQL dialect must implement.
405///
406/// `DialectImpl` provides the configuration hooks and per-expression transform logic
407/// that distinguish one dialect from another. Implementors supply:
408///
409/// - A [`DialectType`] identifier.
410/// - Optional overrides for tokenizer and generator configuration (defaults to generic SQL).
411/// - An expression-level transform function ([`transform_expr`](DialectImpl::transform_expr))
412///   that rewrites individual AST nodes for this dialect (e.g., converting `NVL` to `COALESCE`).
413/// - An optional preprocessing step ([`preprocess`](DialectImpl::preprocess)) for whole-tree
414///   rewrites that must run before the recursive per-node transform (e.g., eliminating QUALIFY).
415///
416/// The default implementations are no-ops, so a minimal dialect only needs to provide
417/// [`dialect_type`](DialectImpl::dialect_type) and override the methods that differ from
418/// standard SQL.
419pub trait DialectImpl {
420    /// Returns the [`DialectType`] that identifies this dialect.
421    fn dialect_type(&self) -> DialectType;
422
423    /// Returns the tokenizer configuration for this dialect.
424    ///
425    /// Override to customize identifier quoting characters, string escape rules,
426    /// comment styles, and other lexing behavior.
427    fn tokenizer_config(&self) -> TokenizerConfig {
428        TokenizerConfig::default()
429    }
430
431    /// Returns the generator configuration for this dialect.
432    ///
433    /// Override to customize identifier quoting style, function name casing,
434    /// keyword casing, and other SQL generation behavior.
435    #[cfg(feature = "generate")]
436    fn generator_config(&self) -> GeneratorConfig {
437        GeneratorConfig::default()
438    }
439
440    /// Returns a generator configuration tailored to a specific expression.
441    ///
442    /// Override this for hybrid dialects like Athena that route to different SQL engines
443    /// based on expression type (e.g., Hive-style generation for DDL, Trino-style for DML).
444    /// The default delegates to [`generator_config`](DialectImpl::generator_config).
445    #[cfg(feature = "generate")]
446    fn generator_config_for_expr(&self, _expr: &Expression) -> GeneratorConfig {
447        self.generator_config()
448    }
449
450    /// Transforms a single expression node for this dialect, without recursing into children.
451    ///
452    /// This is the per-node rewrite hook invoked by [`transform_recursive`]. Return the
453    /// expression unchanged if no dialect-specific rewrite is needed. Transformations
454    /// typically include function renaming, operator substitution, and type mapping.
455    #[cfg(feature = "transpile")]
456    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
457        Ok(expr)
458    }
459
460    /// Applies whole-tree preprocessing transforms before the recursive per-node pass.
461    ///
462    /// Override this to apply structural rewrites that must see the entire tree at once,
463    /// such as `eliminate_qualify`, `eliminate_distinct_on`, `ensure_bools`, or
464    /// `explode_projection_to_unnest`. The default is a no-op pass-through.
465    #[cfg(feature = "transpile")]
466    fn preprocess(&self, expr: Expression) -> Result<Expression> {
467        Ok(expr)
468    }
469}
470
471/// Recursively transforms a [`DataType`](crate::expressions::DataType), handling nested
472/// parametric types such as `ARRAY<INT>`, `STRUCT<a INT, b TEXT>`, and `MAP<STRING, INT>`.
473///
474/// The outer type is first passed through `transform_fn` as an `Expression::DataType`,
475/// and then nested element/field types are recursed into. This ensures that dialect-level
476/// type mappings (e.g., `INT` to `INTEGER`) propagate into complex nested types.
477#[cfg(any(
478    feature = "transpile",
479    feature = "ast-tools",
480    feature = "generate",
481    feature = "semantic"
482))]
483pub(crate) fn transform_data_type_recursive<F>(
484    dt: crate::expressions::DataType,
485    transform_fn: &F,
486) -> Result<crate::expressions::DataType>
487where
488    F: Fn(Expression) -> Result<Expression>,
489{
490    use crate::expressions::DataType;
491    // First, transform the outermost type through the expression system
492    let dt_expr = transform_fn(Expression::DataType(dt))?;
493    let dt = match dt_expr {
494        Expression::DataType(d) => d,
495        _ => {
496            return Ok(match dt_expr {
497                _ => DataType::Custom {
498                    name: "UNKNOWN".to_string(),
499                },
500            })
501        }
502    };
503    // Then recurse into nested types
504    match dt {
505        DataType::Array {
506            element_type,
507            dimension,
508        } => {
509            let inner = transform_data_type_recursive(*element_type, transform_fn)?;
510            Ok(DataType::Array {
511                element_type: Box::new(inner),
512                dimension,
513            })
514        }
515        DataType::List { element_type } => {
516            let inner = transform_data_type_recursive(*element_type, transform_fn)?;
517            Ok(DataType::List {
518                element_type: Box::new(inner),
519            })
520        }
521        DataType::Struct { fields, nested } => {
522            let mut new_fields = Vec::new();
523            for mut field in fields {
524                field.data_type = transform_data_type_recursive(field.data_type, transform_fn)?;
525                new_fields.push(field);
526            }
527            Ok(DataType::Struct {
528                fields: new_fields,
529                nested,
530            })
531        }
532        DataType::Map {
533            key_type,
534            value_type,
535        } => {
536            let k = transform_data_type_recursive(*key_type, transform_fn)?;
537            let v = transform_data_type_recursive(*value_type, transform_fn)?;
538            Ok(DataType::Map {
539                key_type: Box::new(k),
540                value_type: Box::new(v),
541            })
542        }
543        other => Ok(other),
544    }
545}
546
547/// Convert DuckDB C-style format strings to Presto C-style format strings.
548/// DuckDB and Presto both use C-style % directives but with different specifiers for some cases.
549#[cfg(feature = "transpile")]
550fn duckdb_to_presto_format(fmt: &str) -> String {
551    // Order matters: handle longer patterns first to avoid partial replacements
552    let mut result = fmt.to_string();
553    // First pass: mark multi-char patterns with placeholders
554    result = result.replace("%-m", "\x01NOPADM\x01");
555    result = result.replace("%-d", "\x01NOPADD\x01");
556    result = result.replace("%-I", "\x01NOPADI\x01");
557    result = result.replace("%-H", "\x01NOPADH\x01");
558    result = result.replace("%H:%M:%S", "\x01HMS\x01");
559    result = result.replace("%Y-%m-%d", "\x01YMD\x01");
560    // Now convert individual specifiers
561    result = result.replace("%M", "%i");
562    result = result.replace("%S", "%s");
563    // Restore multi-char patterns with Presto equivalents
564    result = result.replace("\x01NOPADM\x01", "%c");
565    result = result.replace("\x01NOPADD\x01", "%e");
566    result = result.replace("\x01NOPADI\x01", "%l");
567    result = result.replace("\x01NOPADH\x01", "%k");
568    result = result.replace("\x01HMS\x01", "%T");
569    result = result.replace("\x01YMD\x01", "%Y-%m-%d");
570    result
571}
572
573/// Convert DuckDB C-style format strings to BigQuery format strings.
574/// BigQuery uses a mix of strftime-like directives.
575#[cfg(feature = "transpile")]
576fn duckdb_to_bigquery_format(fmt: &str) -> String {
577    let mut result = fmt.to_string();
578    // Handle longer patterns first
579    result = result.replace("%-d", "%e");
580    result = result.replace("%Y-%m-%d %H:%M:%S", "%F %T");
581    result = result.replace("%Y-%m-%d", "%F");
582    result = result.replace("%H:%M:%S", "%T");
583    result
584}
585
586#[cfg(feature = "transpile")]
587fn presto_to_java_format(fmt: &str) -> String {
588    fmt.replace("%Y", "yyyy")
589        .replace("%m", "MM")
590        .replace("%d", "dd")
591        .replace("%H", "HH")
592        .replace("%i", "mm")
593        .replace("%S", "ss")
594        .replace("%s", "ss")
595        .replace("%y", "yy")
596        .replace("%T", "HH:mm:ss")
597        .replace("%F", "yyyy-MM-dd")
598        .replace("%M", "MMMM")
599}
600
601#[cfg(feature = "transpile")]
602fn presto_to_java_parse_format(fmt: &str) -> String {
603    fmt.replace("%Y", "yyyy")
604        .replace("%m", "M")
605        .replace("%d", "d")
606        .replace("%H", "H")
607        .replace("%i", "m")
608        .replace("%S", "s")
609        .replace("%s", "s")
610        .replace("%y", "yy")
611        .replace("%T", "H:m:s")
612        .replace("%F", "yyyy-M-d")
613        .replace("%M", "MMMM")
614}
615
616#[cfg(feature = "transpile")]
617fn normalize_presto_format(fmt: &str) -> String {
618    fmt.replace("%H:%i:%S", "%T").replace("%H:%i:%s", "%T")
619}
620
621#[cfg(feature = "transpile")]
622fn presto_to_duckdb_format(fmt: &str) -> String {
623    fmt.replace("%i", "%M")
624        .replace("%s", "%S")
625        .replace("%T", "%H:%M:%S")
626}
627
628#[cfg(feature = "transpile")]
629fn presto_to_bigquery_format(fmt: &str) -> String {
630    fmt.replace("%Y-%m-%d", "%F")
631        .replace("%H:%i:%S", "%T")
632        .replace("%H:%i:%s", "%T")
633        .replace("%i", "%M")
634        .replace("%s", "%S")
635}
636
637#[cfg(feature = "transpile")]
638fn is_default_presto_timestamp_format(fmt: &str) -> bool {
639    let normalized = normalize_presto_format(fmt);
640    normalized == "%Y-%m-%d %T"
641        || normalized == "%Y-%m-%d %H:%i:%S"
642        || fmt == "%Y-%m-%d %H:%i:%S"
643        || fmt == "%Y-%m-%d %T"
644}
645
646#[cfg(feature = "transpile")]
647fn is_default_presto_date_format(fmt: &str) -> bool {
648    fmt == "%Y-%m-%d" || fmt == "%F"
649}
650
651/// Applies a dialect transform bottom-up through selected syntax children.
652///
653/// The public entrypoint uses an explicit task stack for the recursion-heavy shapes
654/// that dominate deeply nested SQL (nested SELECT/FROM/SUBQUERY chains, set-operation
655/// trees, and common binary/unary expression chains). Less common shapes currently
656/// reuse the reference recursive implementation so semantics stay identical while
657/// the hot path avoids stack growth.
658#[cfg(any(
659    feature = "transpile",
660    feature = "ast-tools",
661    feature = "generate",
662    feature = "semantic"
663))]
664pub fn transform_recursive<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
665where
666    F: Fn(Expression) -> Result<Expression>,
667{
668    #[cfg(feature = "stacker")]
669    {
670        let red_zone = if cfg!(debug_assertions) {
671            4 * 1024 * 1024
672        } else {
673            1024 * 1024
674        };
675        stacker::maybe_grow(red_zone, 8 * 1024 * 1024, move || {
676            transform_recursive_inner(expr, transform_fn)
677        })
678    }
679    #[cfg(not(feature = "stacker"))]
680    {
681        transform_recursive_inner(expr, transform_fn)
682    }
683}
684
685#[cfg(any(
686    feature = "transpile",
687    feature = "ast-tools",
688    feature = "generate",
689    feature = "semantic"
690))]
691fn transform_recursive_inner<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
692where
693    F: Fn(Expression) -> Result<Expression>,
694{
695    // These are the shapes handled by the former explicit-stack fast path. Other
696    // nodes retain the reference transformer's selective and wrapper-aware child
697    // semantics even though all physical children are visible to traversal APIs.
698    fn uses_generated_dispatch(expression: &Expression) -> bool {
699        match expression {
700            Expression::CreateTable(_) => true,
701            Expression::Select(select) => {
702                select.joins.is_empty()
703                    && select.with.is_none()
704                    && select.order_by.is_none()
705                    && select.windows.is_none()
706                    && select.settings.is_none()
707            }
708            Expression::Union(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
709            Expression::Intersect(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
710            Expression::Except(set_op) => set_op.with.is_none() && set_op.order_by.is_none(),
711            Expression::Vertica(_)
712            | Expression::Literal(_)
713            | Expression::Boolean(_)
714            | Expression::Null(_)
715            | Expression::Identifier(_)
716            | Expression::Star(_)
717            | Expression::Parameter(_)
718            | Expression::Placeholder(_)
719            | Expression::SessionParameter(_)
720            | Expression::Interval(_)
721            | Expression::Alias(_)
722            | Expression::Paren(_)
723            | Expression::Not(_)
724            | Expression::Neg(_)
725            | Expression::IsNull(_)
726            | Expression::IsTrue(_)
727            | Expression::IsFalse(_)
728            | Expression::Subquery(_)
729            | Expression::Exists(_)
730            | Expression::Any(_)
731            | Expression::All(_)
732            | Expression::TableArgument(_)
733            | Expression::And(_)
734            | Expression::Or(_)
735            | Expression::Add(_)
736            | Expression::Sub(_)
737            | Expression::Mul(_)
738            | Expression::Div(_)
739            | Expression::Eq(_)
740            | Expression::NullSafeEq(_)
741            | Expression::NullSafeNeq(_)
742            | Expression::Lt(_)
743            | Expression::Gt(_)
744            | Expression::Neq(_)
745            | Expression::Lte(_)
746            | Expression::Gte(_)
747            | Expression::Mod(_)
748            | Expression::Concat(_)
749            | Expression::BitwiseAnd(_)
750            | Expression::BitwiseOr(_)
751            | Expression::BitwiseXor(_)
752            | Expression::Is(_)
753            | Expression::MemberOf(_)
754            | Expression::ArrayContainsAll(_)
755            | Expression::ArrayContainedBy(_)
756            | Expression::ArrayOverlaps(_)
757            | Expression::TsMatch(_)
758            | Expression::Adjacent(_)
759            | Expression::Like(_)
760            | Expression::ILike(_)
761            | Expression::Function(_)
762            | Expression::AggregateFunction(_)
763            | Expression::JSONValue(_)
764            | Expression::JSONTable(_)
765            | Expression::JSONColumnDef(_)
766            | Expression::JsonQuery(_)
767            | Expression::RegexpLike(_)
768            | Expression::RegexpReplace(_)
769            | Expression::RegexpExtract(_)
770            | Expression::RegexpInstr(_)
771            | Expression::RegexpCount(_)
772            | Expression::Cube(_)
773            | Expression::Rollup(_)
774            | Expression::GroupingSets(_)
775            | Expression::Hint(_)
776            | Expression::PartitionByProperty(_)
777            | Expression::StorageProperty(_)
778            | Expression::Hierarchy(_)
779            | Expression::ViewParameter(_)
780            | Expression::Call(_)
781            | Expression::Lead(_)
782            | Expression::Lag(_)
783            | Expression::Array(_)
784            | Expression::Tuple(_)
785            | Expression::ArrayFunc(_)
786            | Expression::Coalesce(_)
787            | Expression::Greatest(_)
788            | Expression::Least(_)
789            | Expression::ArrayConcat(_)
790            | Expression::ArrayIntersect(_)
791            | Expression::ArrayZip(_)
792            | Expression::MapConcat(_)
793            | Expression::JsonArray(_)
794            | Expression::From(_) => true,
795            _ => false,
796        }
797    }
798
799    crate::traversal::transform_with_dispatch(
800        expr,
801        transform_fn,
802        &uses_generated_dispatch,
803        &transform_recursive_reference,
804    )
805}
806
807#[cfg(any(
808    feature = "transpile",
809    feature = "ast-tools",
810    feature = "generate",
811    feature = "semantic"
812))]
813fn transform_table_ref_recursive<F>(table: TableRef, transform_fn: &F) -> Result<TableRef>
814where
815    F: Fn(Expression) -> Result<Expression>,
816{
817    match transform_recursive(Expression::Table(Box::new(table)), transform_fn)? {
818        Expression::Table(table) => Ok(*table),
819        _ => Err(crate::error::Error::parse(
820            "TableRef transformation returned non-table expression",
821            0,
822            0,
823            0,
824            0,
825        )),
826    }
827}
828
829#[cfg(any(
830    feature = "transpile",
831    feature = "ast-tools",
832    feature = "generate",
833    feature = "semantic"
834))]
835fn transform_from_recursive<F>(from: From, transform_fn: &F) -> Result<From>
836where
837    F: Fn(Expression) -> Result<Expression>,
838{
839    match transform_recursive(Expression::From(Box::new(from)), transform_fn)? {
840        Expression::From(from) => Ok(*from),
841        _ => Err(crate::error::Error::parse(
842            "FROM transformation returned non-FROM expression",
843            0,
844            0,
845            0,
846            0,
847        )),
848    }
849}
850
851#[cfg(any(
852    feature = "transpile",
853    feature = "ast-tools",
854    feature = "generate",
855    feature = "semantic"
856))]
857fn transform_join_recursive<F>(mut join: Join, transform_fn: &F) -> Result<Join>
858where
859    F: Fn(Expression) -> Result<Expression>,
860{
861    join.this = transform_recursive(join.this, transform_fn)?;
862    if let Some(on) = join.on.take() {
863        join.on = Some(transform_recursive(on, transform_fn)?);
864    }
865    if let Some(match_condition) = join.match_condition.take() {
866        join.match_condition = Some(transform_recursive(match_condition, transform_fn)?);
867    }
868    join.pivots = join
869        .pivots
870        .into_iter()
871        .map(|pivot| transform_recursive(pivot, transform_fn))
872        .collect::<Result<Vec<_>>>()?;
873
874    match transform_fn(Expression::Join(Box::new(join)))? {
875        Expression::Join(join) => Ok(*join),
876        _ => Err(crate::error::Error::parse(
877            "Join transformation returned non-join expression",
878            0,
879            0,
880            0,
881            0,
882        )),
883    }
884}
885
886#[cfg(any(
887    feature = "transpile",
888    feature = "ast-tools",
889    feature = "generate",
890    feature = "semantic"
891))]
892fn transform_output_clause_recursive<F>(
893    mut output: OutputClause,
894    transform_fn: &F,
895) -> Result<OutputClause>
896where
897    F: Fn(Expression) -> Result<Expression>,
898{
899    output.columns = output
900        .columns
901        .into_iter()
902        .map(|column| transform_recursive(column, transform_fn))
903        .collect::<Result<Vec<_>>>()?;
904    if let Some(into_table) = output.into_table.take() {
905        output.into_table = Some(transform_recursive(into_table, transform_fn)?);
906    }
907    Ok(output)
908}
909
910#[cfg(any(
911    feature = "transpile",
912    feature = "ast-tools",
913    feature = "generate",
914    feature = "semantic"
915))]
916fn transform_with_recursive<F>(mut with: With, transform_fn: &F) -> Result<With>
917where
918    F: Fn(Expression) -> Result<Expression>,
919{
920    with.ctes = with
921        .ctes
922        .into_iter()
923        .map(|mut cte| {
924            cte.this = transform_recursive(cte.this, transform_fn)?;
925            Ok(cte)
926        })
927        .collect::<Result<Vec<_>>>()?;
928    if let Some(search) = with.search.take() {
929        with.search = Some(Box::new(transform_recursive(*search, transform_fn)?));
930    }
931    Ok(with)
932}
933
934#[cfg(any(
935    feature = "transpile",
936    feature = "ast-tools",
937    feature = "generate",
938    feature = "semantic"
939))]
940fn transform_order_by_recursive<F>(mut order: OrderBy, transform_fn: &F) -> Result<OrderBy>
941where
942    F: Fn(Expression) -> Result<Expression>,
943{
944    order.expressions = order
945        .expressions
946        .into_iter()
947        .map(|mut ordered| {
948            let original = ordered.this.clone();
949            ordered.this = transform_recursive(ordered.this, transform_fn).unwrap_or(original);
950            match transform_fn(Expression::Ordered(Box::new(ordered.clone()))) {
951                Ok(Expression::Ordered(transformed)) => Ok(*transformed),
952                Ok(_) | Err(_) => Ok(ordered),
953            }
954        })
955        .collect::<Result<Vec<_>>>()?;
956    Ok(order)
957}
958
959#[cfg(any(
960    feature = "transpile",
961    feature = "ast-tools",
962    feature = "generate",
963    feature = "semantic"
964))]
965fn transform_recursive_reference<F>(expr: Expression, transform_fn: &F) -> Result<Expression>
966where
967    F: Fn(Expression) -> Result<Expression>,
968{
969    use crate::expressions::BinaryOp;
970
971    // Helper macro to recurse into AggFunc-based expressions (this, filter, order_by, having_max, limit).
972    macro_rules! recurse_agg {
973        ($variant:ident, $f:expr) => {{
974            let mut f = $f;
975            f.this = transform_recursive(f.this, transform_fn)?;
976            if let Some(filter) = f.filter.take() {
977                f.filter = Some(transform_recursive(filter, transform_fn)?);
978            }
979            for ord in &mut f.order_by {
980                ord.this = transform_recursive(
981                    std::mem::replace(&mut ord.this, Expression::Null(crate::expressions::Null)),
982                    transform_fn,
983                )?;
984            }
985            if let Some((ref mut expr, _)) = f.having_max {
986                *expr = Box::new(transform_recursive(
987                    std::mem::replace(expr.as_mut(), Expression::Null(crate::expressions::Null)),
988                    transform_fn,
989                )?);
990            }
991            if let Some(limit) = f.limit.take() {
992                f.limit = Some(Box::new(transform_recursive(*limit, transform_fn)?));
993            }
994            Expression::$variant(f)
995        }};
996    }
997
998    // Helper macro to transform binary ops with Box<BinaryOp>
999    macro_rules! transform_binary {
1000        ($variant:ident, $op:expr) => {{
1001            let left = transform_recursive($op.left, transform_fn)?;
1002            let right = transform_recursive($op.right, transform_fn)?;
1003            Expression::$variant(Box::new(BinaryOp {
1004                left,
1005                right,
1006                left_comments: $op.left_comments,
1007                operator_comments: $op.operator_comments,
1008                trailing_comments: $op.trailing_comments,
1009                inferred_type: $op.inferred_type,
1010            }))
1011        }};
1012    }
1013
1014    // Fast path: leaf nodes never need child traversal, apply transform directly
1015    if matches!(
1016        &expr,
1017        Expression::Literal(_)
1018            | Expression::Boolean(_)
1019            | Expression::Null(_)
1020            | Expression::Identifier(_)
1021            | Expression::Star(_)
1022            | Expression::Parameter(_)
1023            | Expression::Placeholder(_)
1024            | Expression::SessionParameter(_)
1025    ) {
1026        return transform_fn(expr);
1027    }
1028
1029    // First recursively transform children, then apply the transform function
1030    let expr = match expr {
1031        Expression::Select(mut select) => {
1032            select.query_hints = std::mem::take(&mut select.query_hints)
1033                .into_iter()
1034                .map(|hint| transform_recursive(hint, transform_fn))
1035                .collect::<Result<Vec<_>>>()?;
1036            if let Some(extension) = &mut select.vertica {
1037                use crate::ast_children::AstNode;
1038                let mut failure = None;
1039                extension.visit_expressions_mut(&mut |child| {
1040                    if failure.is_none() {
1041                        let old =
1042                            std::mem::replace(child, Expression::Null(crate::expressions::Null));
1043                        match transform_recursive(old, transform_fn) {
1044                            Ok(new) => *child = new,
1045                            Err(error) => failure = Some(error),
1046                        }
1047                    }
1048                });
1049                if let Some(error) = failure {
1050                    return Err(error);
1051                }
1052            }
1053            select.expressions = select
1054                .expressions
1055                .into_iter()
1056                .map(|e| transform_recursive(e, transform_fn))
1057                .collect::<Result<Vec<_>>>()?;
1058
1059            // Transform FROM clause
1060            if let Some(mut from) = select.from.take() {
1061                from.expressions = from
1062                    .expressions
1063                    .into_iter()
1064                    .map(|e| transform_recursive(e, transform_fn))
1065                    .collect::<Result<Vec<_>>>()?;
1066                select.from = Some(from);
1067            }
1068
1069            // Transform JOINs - important for CROSS APPLY / LATERAL transformations
1070            select.joins = select
1071                .joins
1072                .into_iter()
1073                .map(|mut join| {
1074                    join.this = transform_recursive(join.this, transform_fn)?;
1075                    if let Some(on) = join.on.take() {
1076                        join.on = Some(transform_recursive(on, transform_fn)?);
1077                    }
1078                    // Wrap join in Expression::Join to allow transform_fn to transform it
1079                    match transform_fn(Expression::Join(Box::new(join)))? {
1080                        Expression::Join(j) => Ok(*j),
1081                        _ => Err(crate::error::Error::parse(
1082                            "Join transformation returned non-join expression",
1083                            0,
1084                            0,
1085                            0,
1086                            0,
1087                        )),
1088                    }
1089                })
1090                .collect::<Result<Vec<_>>>()?;
1091
1092            // Transform LATERAL VIEW expressions (Hive/Spark)
1093            select.lateral_views = select
1094                .lateral_views
1095                .into_iter()
1096                .map(|mut lv| {
1097                    lv.this = transform_recursive(lv.this, transform_fn)?;
1098                    Ok(lv)
1099                })
1100                .collect::<Result<Vec<_>>>()?;
1101
1102            // Transform WHERE clause
1103            if let Some(mut where_clause) = select.where_clause.take() {
1104                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1105                select.where_clause = Some(where_clause);
1106            }
1107
1108            // Transform GROUP BY
1109            if let Some(mut group_by) = select.group_by.take() {
1110                group_by.expressions = group_by
1111                    .expressions
1112                    .into_iter()
1113                    .map(|e| transform_recursive(e, transform_fn))
1114                    .collect::<Result<Vec<_>>>()?;
1115                select.group_by = Some(group_by);
1116            }
1117
1118            // Transform HAVING
1119            if let Some(mut having) = select.having.take() {
1120                having.this = transform_recursive(having.this, transform_fn)?;
1121                select.having = Some(having);
1122            }
1123
1124            // Transform WITH (CTEs)
1125            if let Some(mut with) = select.with.take() {
1126                with.ctes = with
1127                    .ctes
1128                    .into_iter()
1129                    .map(|mut cte| {
1130                        let original = cte.this.clone();
1131                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1132                        cte
1133                    })
1134                    .collect();
1135                select.with = Some(with);
1136            }
1137
1138            // Transform ORDER BY
1139            if let Some(mut order) = select.order_by.take() {
1140                order.expressions = order
1141                    .expressions
1142                    .into_iter()
1143                    .map(|o| {
1144                        let mut o = o;
1145                        let original = o.this.clone();
1146                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1147                        // Also apply transform to the Ordered wrapper itself (for NULLS FIRST etc.)
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                select.order_by = Some(order);
1155            }
1156
1157            // Transform WINDOW clause order_by
1158            if let Some(ref mut windows) = select.windows {
1159                for nw in windows.iter_mut() {
1160                    nw.spec.order_by = std::mem::take(&mut nw.spec.order_by)
1161                        .into_iter()
1162                        .map(|o| {
1163                            let mut o = o;
1164                            let original = o.this.clone();
1165                            o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1166                            match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1167                                Ok(Expression::Ordered(transformed)) => *transformed,
1168                                Ok(_) | Err(_) => o,
1169                            }
1170                        })
1171                        .collect();
1172                }
1173            }
1174
1175            // Transform QUALIFY
1176            if let Some(mut qual) = select.qualify.take() {
1177                qual.this = transform_recursive(qual.this, transform_fn)?;
1178                select.qualify = Some(qual);
1179            }
1180
1181            Expression::Select(select)
1182        }
1183        Expression::Function(mut f) => {
1184            f.args = f
1185                .args
1186                .into_iter()
1187                .map(|e| transform_recursive(e, transform_fn))
1188                .collect::<Result<Vec<_>>>()?;
1189            Expression::Function(f)
1190        }
1191        Expression::AggregateFunction(mut f) => {
1192            f.args = f
1193                .args
1194                .into_iter()
1195                .map(|e| transform_recursive(e, transform_fn))
1196                .collect::<Result<Vec<_>>>()?;
1197            if let Some(filter) = f.filter {
1198                f.filter = Some(transform_recursive(filter, transform_fn)?);
1199            }
1200            Expression::AggregateFunction(f)
1201        }
1202        Expression::WindowFunction(mut wf) => {
1203            wf.this = transform_recursive(wf.this, transform_fn)?;
1204            wf.over.partition_by = wf
1205                .over
1206                .partition_by
1207                .into_iter()
1208                .map(|e| transform_recursive(e, transform_fn))
1209                .collect::<Result<Vec<_>>>()?;
1210            // Transform order_by items through Expression::Ordered wrapper
1211            wf.over.order_by = wf
1212                .over
1213                .order_by
1214                .into_iter()
1215                .map(|o| {
1216                    let mut o = o;
1217                    o.this = transform_recursive(o.this, transform_fn)?;
1218                    match transform_fn(Expression::Ordered(Box::new(o)))? {
1219                        Expression::Ordered(transformed) => Ok(*transformed),
1220                        _ => Err(crate::error::Error::parse(
1221                            "Ordered transformation returned non-Ordered expression",
1222                            0,
1223                            0,
1224                            0,
1225                            0,
1226                        )),
1227                    }
1228                })
1229                .collect::<Result<Vec<_>>>()?;
1230            Expression::WindowFunction(wf)
1231        }
1232        Expression::Alias(mut a) => {
1233            a.this = transform_recursive(a.this, transform_fn)?;
1234            Expression::Alias(a)
1235        }
1236        Expression::Cast(mut c) => {
1237            c.this = transform_recursive(c.this, transform_fn)?;
1238            // Also transform the target data type (recursively for nested types like ARRAY<INT>, STRUCT<a INT>)
1239            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1240            Expression::Cast(c)
1241        }
1242        Expression::And(op) => transform_binary!(And, *op),
1243        Expression::Or(op) => transform_binary!(Or, *op),
1244        Expression::Add(op) => transform_binary!(Add, *op),
1245        Expression::Sub(op) => transform_binary!(Sub, *op),
1246        Expression::Mul(op) => transform_binary!(Mul, *op),
1247        Expression::Div(op) => transform_binary!(Div, *op),
1248        Expression::Eq(op) => transform_binary!(Eq, *op),
1249        Expression::Lt(op) => transform_binary!(Lt, *op),
1250        Expression::Gt(op) => transform_binary!(Gt, *op),
1251        Expression::Paren(mut p) => {
1252            p.this = transform_recursive(p.this, transform_fn)?;
1253            Expression::Paren(p)
1254        }
1255        Expression::Coalesce(mut f) => {
1256            f.expressions = f
1257                .expressions
1258                .into_iter()
1259                .map(|e| transform_recursive(e, transform_fn))
1260                .collect::<Result<Vec<_>>>()?;
1261            Expression::Coalesce(f)
1262        }
1263        Expression::IfNull(mut f) => {
1264            f.this = transform_recursive(f.this, transform_fn)?;
1265            f.expression = transform_recursive(f.expression, transform_fn)?;
1266            Expression::IfNull(f)
1267        }
1268        Expression::Nvl(mut f) => {
1269            f.this = transform_recursive(f.this, transform_fn)?;
1270            f.expression = transform_recursive(f.expression, transform_fn)?;
1271            Expression::Nvl(f)
1272        }
1273        Expression::In(mut i) => {
1274            i.this = transform_recursive(i.this, transform_fn)?;
1275            i.expressions = i
1276                .expressions
1277                .into_iter()
1278                .map(|e| transform_recursive(e, transform_fn))
1279                .collect::<Result<Vec<_>>>()?;
1280            if let Some(query) = i.query {
1281                i.query = Some(transform_recursive(query, transform_fn)?);
1282            }
1283            Expression::In(i)
1284        }
1285        Expression::Not(mut n) => {
1286            n.this = transform_recursive(n.this, transform_fn)?;
1287            Expression::Not(n)
1288        }
1289        Expression::ArraySlice(mut s) => {
1290            s.this = transform_recursive(s.this, transform_fn)?;
1291            if let Some(start) = s.start {
1292                s.start = Some(transform_recursive(start, transform_fn)?);
1293            }
1294            if let Some(end) = s.end {
1295                s.end = Some(transform_recursive(end, transform_fn)?);
1296            }
1297            Expression::ArraySlice(s)
1298        }
1299        Expression::Subscript(mut s) => {
1300            s.this = transform_recursive(s.this, transform_fn)?;
1301            s.index = transform_recursive(s.index, transform_fn)?;
1302            Expression::Subscript(s)
1303        }
1304        Expression::Array(mut a) => {
1305            a.expressions = a
1306                .expressions
1307                .into_iter()
1308                .map(|e| transform_recursive(e, transform_fn))
1309                .collect::<Result<Vec<_>>>()?;
1310            Expression::Array(a)
1311        }
1312        Expression::Struct(mut s) => {
1313            let mut new_fields = Vec::new();
1314            for (name, expr) in s.fields {
1315                let transformed = transform_recursive(expr, transform_fn)?;
1316                new_fields.push((name, transformed));
1317            }
1318            s.fields = new_fields;
1319            Expression::Struct(s)
1320        }
1321        Expression::NamedArgument(mut na) => {
1322            na.value = transform_recursive(na.value, transform_fn)?;
1323            Expression::NamedArgument(na)
1324        }
1325        Expression::MapFunc(mut m) => {
1326            m.keys = m
1327                .keys
1328                .into_iter()
1329                .map(|e| transform_recursive(e, transform_fn))
1330                .collect::<Result<Vec<_>>>()?;
1331            m.values = m
1332                .values
1333                .into_iter()
1334                .map(|e| transform_recursive(e, transform_fn))
1335                .collect::<Result<Vec<_>>>()?;
1336            Expression::MapFunc(m)
1337        }
1338        Expression::ArrayFunc(mut a) => {
1339            a.expressions = a
1340                .expressions
1341                .into_iter()
1342                .map(|e| transform_recursive(e, transform_fn))
1343                .collect::<Result<Vec<_>>>()?;
1344            Expression::ArrayFunc(a)
1345        }
1346        Expression::Lambda(mut l) => {
1347            l.body = transform_recursive(l.body, transform_fn)?;
1348            Expression::Lambda(l)
1349        }
1350        Expression::JsonExtract(mut f) => {
1351            f.this = transform_recursive(f.this, transform_fn)?;
1352            f.path = transform_recursive(f.path, transform_fn)?;
1353            Expression::JsonExtract(f)
1354        }
1355        Expression::JsonExtractScalar(mut f) => {
1356            f.this = transform_recursive(f.this, transform_fn)?;
1357            f.path = transform_recursive(f.path, transform_fn)?;
1358            Expression::JsonExtractScalar(f)
1359        }
1360
1361        // ===== UnaryFunc-based expressions =====
1362        // These all have a single `this: Expression` child
1363        Expression::Length(mut f) => {
1364            f.this = transform_recursive(f.this, transform_fn)?;
1365            Expression::Length(f)
1366        }
1367        Expression::Upper(mut f) => {
1368            f.this = transform_recursive(f.this, transform_fn)?;
1369            Expression::Upper(f)
1370        }
1371        Expression::Lower(mut f) => {
1372            f.this = transform_recursive(f.this, transform_fn)?;
1373            Expression::Lower(f)
1374        }
1375        Expression::LTrim(mut f) => {
1376            f.this = transform_recursive(f.this, transform_fn)?;
1377            Expression::LTrim(f)
1378        }
1379        Expression::RTrim(mut f) => {
1380            f.this = transform_recursive(f.this, transform_fn)?;
1381            Expression::RTrim(f)
1382        }
1383        Expression::Reverse(mut f) => {
1384            f.this = transform_recursive(f.this, transform_fn)?;
1385            Expression::Reverse(f)
1386        }
1387        Expression::Abs(mut f) => {
1388            f.this = transform_recursive(f.this, transform_fn)?;
1389            Expression::Abs(f)
1390        }
1391        Expression::Ceil(mut f) => {
1392            f.this = transform_recursive(f.this, transform_fn)?;
1393            Expression::Ceil(f)
1394        }
1395        Expression::Floor(mut f) => {
1396            f.this = transform_recursive(f.this, transform_fn)?;
1397            Expression::Floor(f)
1398        }
1399        Expression::Sign(mut f) => {
1400            f.this = transform_recursive(f.this, transform_fn)?;
1401            Expression::Sign(f)
1402        }
1403        Expression::Sqrt(mut f) => {
1404            f.this = transform_recursive(f.this, transform_fn)?;
1405            Expression::Sqrt(f)
1406        }
1407        Expression::Cbrt(mut f) => {
1408            f.this = transform_recursive(f.this, transform_fn)?;
1409            Expression::Cbrt(f)
1410        }
1411        Expression::Ln(mut f) => {
1412            f.this = transform_recursive(f.this, transform_fn)?;
1413            Expression::Ln(f)
1414        }
1415        Expression::Log(mut f) => {
1416            f.this = transform_recursive(f.this, transform_fn)?;
1417            if let Some(base) = f.base {
1418                f.base = Some(transform_recursive(base, transform_fn)?);
1419            }
1420            Expression::Log(f)
1421        }
1422        Expression::Exp(mut f) => {
1423            f.this = transform_recursive(f.this, transform_fn)?;
1424            Expression::Exp(f)
1425        }
1426        Expression::Date(mut f) => {
1427            f.this = transform_recursive(f.this, transform_fn)?;
1428            Expression::Date(f)
1429        }
1430        Expression::Stddev(f) => recurse_agg!(Stddev, f),
1431        Expression::StddevSamp(f) => recurse_agg!(StddevSamp, f),
1432        Expression::Variance(f) => recurse_agg!(Variance, f),
1433
1434        // ===== BinaryFunc-based expressions =====
1435        Expression::ModFunc(mut f) => {
1436            f.this = transform_recursive(f.this, transform_fn)?;
1437            f.expression = transform_recursive(f.expression, transform_fn)?;
1438            Expression::ModFunc(f)
1439        }
1440        Expression::Power(mut f) => {
1441            f.this = transform_recursive(f.this, transform_fn)?;
1442            f.expression = transform_recursive(f.expression, transform_fn)?;
1443            Expression::Power(f)
1444        }
1445        Expression::MapFromArrays(mut f) => {
1446            f.this = transform_recursive(f.this, transform_fn)?;
1447            f.expression = transform_recursive(f.expression, transform_fn)?;
1448            Expression::MapFromArrays(f)
1449        }
1450        Expression::ElementAt(mut f) => {
1451            f.this = transform_recursive(f.this, transform_fn)?;
1452            f.expression = transform_recursive(f.expression, transform_fn)?;
1453            Expression::ElementAt(f)
1454        }
1455        Expression::MapContainsKey(mut f) => {
1456            f.this = transform_recursive(f.this, transform_fn)?;
1457            f.expression = transform_recursive(f.expression, transform_fn)?;
1458            Expression::MapContainsKey(f)
1459        }
1460        Expression::Left(mut f) => {
1461            f.this = transform_recursive(f.this, transform_fn)?;
1462            f.length = transform_recursive(f.length, transform_fn)?;
1463            Expression::Left(f)
1464        }
1465        Expression::Right(mut f) => {
1466            f.this = transform_recursive(f.this, transform_fn)?;
1467            f.length = transform_recursive(f.length, transform_fn)?;
1468            Expression::Right(f)
1469        }
1470        Expression::Repeat(mut f) => {
1471            f.this = transform_recursive(f.this, transform_fn)?;
1472            f.times = transform_recursive(f.times, transform_fn)?;
1473            Expression::Repeat(f)
1474        }
1475
1476        // ===== Complex function expressions =====
1477        Expression::Substring(mut f) => {
1478            f.this = transform_recursive(f.this, transform_fn)?;
1479            f.start = transform_recursive(f.start, transform_fn)?;
1480            if let Some(len) = f.length {
1481                f.length = Some(transform_recursive(len, transform_fn)?);
1482            }
1483            Expression::Substring(f)
1484        }
1485        Expression::Replace(mut f) => {
1486            f.this = transform_recursive(f.this, transform_fn)?;
1487            f.old = transform_recursive(f.old, transform_fn)?;
1488            f.new = transform_recursive(f.new, transform_fn)?;
1489            Expression::Replace(f)
1490        }
1491        Expression::ConcatWs(mut f) => {
1492            f.separator = transform_recursive(f.separator, transform_fn)?;
1493            f.expressions = f
1494                .expressions
1495                .into_iter()
1496                .map(|e| transform_recursive(e, transform_fn))
1497                .collect::<Result<Vec<_>>>()?;
1498            Expression::ConcatWs(f)
1499        }
1500        Expression::Trim(mut f) => {
1501            f.this = transform_recursive(f.this, transform_fn)?;
1502            if let Some(chars) = f.characters {
1503                f.characters = Some(transform_recursive(chars, transform_fn)?);
1504            }
1505            Expression::Trim(f)
1506        }
1507        Expression::Split(mut f) => {
1508            f.this = transform_recursive(f.this, transform_fn)?;
1509            f.delimiter = transform_recursive(f.delimiter, transform_fn)?;
1510            Expression::Split(f)
1511        }
1512        Expression::Lpad(mut f) => {
1513            f.this = transform_recursive(f.this, transform_fn)?;
1514            f.length = transform_recursive(f.length, transform_fn)?;
1515            if let Some(fill) = f.fill {
1516                f.fill = Some(transform_recursive(fill, transform_fn)?);
1517            }
1518            Expression::Lpad(f)
1519        }
1520        Expression::Rpad(mut f) => {
1521            f.this = transform_recursive(f.this, transform_fn)?;
1522            f.length = transform_recursive(f.length, transform_fn)?;
1523            if let Some(fill) = f.fill {
1524                f.fill = Some(transform_recursive(fill, transform_fn)?);
1525            }
1526            Expression::Rpad(f)
1527        }
1528
1529        // ===== Conditional expressions =====
1530        Expression::Case(mut c) => {
1531            if let Some(operand) = c.operand {
1532                c.operand = Some(transform_recursive(operand, transform_fn)?);
1533            }
1534            c.whens = c
1535                .whens
1536                .into_iter()
1537                .map(|(cond, then)| {
1538                    let new_cond = transform_recursive(cond.clone(), transform_fn).unwrap_or(cond);
1539                    let new_then = transform_recursive(then.clone(), transform_fn).unwrap_or(then);
1540                    (new_cond, new_then)
1541                })
1542                .collect();
1543            if let Some(else_expr) = c.else_ {
1544                c.else_ = Some(transform_recursive(else_expr, transform_fn)?);
1545            }
1546            Expression::Case(c)
1547        }
1548        Expression::IfFunc(mut f) => {
1549            f.condition = transform_recursive(f.condition, transform_fn)?;
1550            f.true_value = transform_recursive(f.true_value, transform_fn)?;
1551            if let Some(false_val) = f.false_value {
1552                f.false_value = Some(transform_recursive(false_val, transform_fn)?);
1553            }
1554            Expression::IfFunc(f)
1555        }
1556
1557        // ===== Date/Time expressions =====
1558        Expression::DateAdd(mut f) => {
1559            f.this = transform_recursive(f.this, transform_fn)?;
1560            f.interval = transform_recursive(f.interval, transform_fn)?;
1561            Expression::DateAdd(f)
1562        }
1563        Expression::DateSub(mut f) => {
1564            f.this = transform_recursive(f.this, transform_fn)?;
1565            f.interval = transform_recursive(f.interval, transform_fn)?;
1566            Expression::DateSub(f)
1567        }
1568        Expression::DateDiff(mut f) => {
1569            f.this = transform_recursive(f.this, transform_fn)?;
1570            f.expression = transform_recursive(f.expression, transform_fn)?;
1571            Expression::DateDiff(f)
1572        }
1573        Expression::DateTrunc(mut f) => {
1574            f.this = transform_recursive(f.this, transform_fn)?;
1575            Expression::DateTrunc(f)
1576        }
1577        Expression::Extract(mut f) => {
1578            f.this = transform_recursive(f.this, transform_fn)?;
1579            Expression::Extract(f)
1580        }
1581
1582        // ===== JSON expressions =====
1583        Expression::JsonObject(mut f) => {
1584            f.pairs = f
1585                .pairs
1586                .into_iter()
1587                .map(|(k, v)| {
1588                    let new_k = transform_recursive(k, transform_fn)?;
1589                    let new_v = transform_recursive(v, transform_fn)?;
1590                    Ok((new_k, new_v))
1591                })
1592                .collect::<Result<Vec<_>>>()?;
1593            Expression::JsonObject(f)
1594        }
1595
1596        // ===== Subquery expressions =====
1597        Expression::Subquery(mut s) => {
1598            s.this = transform_recursive(s.this, transform_fn)?;
1599            Expression::Subquery(s)
1600        }
1601        Expression::Exists(mut e) => {
1602            e.this = transform_recursive(e.this, transform_fn)?;
1603            Expression::Exists(e)
1604        }
1605        Expression::Describe(mut d) => {
1606            d.target = transform_recursive(d.target, transform_fn)?;
1607            Expression::Describe(d)
1608        }
1609
1610        // ===== Set operations =====
1611        Expression::Union(mut u) => {
1612            let left = std::mem::replace(&mut u.left, Expression::Null(Null));
1613            u.left = transform_recursive(left, transform_fn)?;
1614            let right = std::mem::replace(&mut u.right, Expression::Null(Null));
1615            u.right = transform_recursive(right, transform_fn)?;
1616            if let Some(mut order) = u.order_by.take() {
1617                order.expressions = order
1618                    .expressions
1619                    .into_iter()
1620                    .map(|o| {
1621                        let mut o = o;
1622                        let original = o.this.clone();
1623                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1624                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1625                            Ok(Expression::Ordered(transformed)) => *transformed,
1626                            Ok(_) | Err(_) => o,
1627                        }
1628                    })
1629                    .collect();
1630                u.order_by = Some(order);
1631            }
1632            if let Some(mut with) = u.with.take() {
1633                with.ctes = with
1634                    .ctes
1635                    .into_iter()
1636                    .map(|mut cte| {
1637                        let original = cte.this.clone();
1638                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1639                        cte
1640                    })
1641                    .collect();
1642                u.with = Some(with);
1643            }
1644            Expression::Union(u)
1645        }
1646        Expression::Intersect(mut i) => {
1647            let left = std::mem::replace(&mut i.left, Expression::Null(Null));
1648            i.left = transform_recursive(left, transform_fn)?;
1649            let right = std::mem::replace(&mut i.right, Expression::Null(Null));
1650            i.right = transform_recursive(right, transform_fn)?;
1651            if let Some(mut order) = i.order_by.take() {
1652                order.expressions = order
1653                    .expressions
1654                    .into_iter()
1655                    .map(|o| {
1656                        let mut o = o;
1657                        let original = o.this.clone();
1658                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1659                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1660                            Ok(Expression::Ordered(transformed)) => *transformed,
1661                            Ok(_) | Err(_) => o,
1662                        }
1663                    })
1664                    .collect();
1665                i.order_by = Some(order);
1666            }
1667            if let Some(mut with) = i.with.take() {
1668                with.ctes = with
1669                    .ctes
1670                    .into_iter()
1671                    .map(|mut cte| {
1672                        let original = cte.this.clone();
1673                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1674                        cte
1675                    })
1676                    .collect();
1677                i.with = Some(with);
1678            }
1679            Expression::Intersect(i)
1680        }
1681        Expression::Except(mut e) => {
1682            let left = std::mem::replace(&mut e.left, Expression::Null(Null));
1683            e.left = transform_recursive(left, transform_fn)?;
1684            let right = std::mem::replace(&mut e.right, Expression::Null(Null));
1685            e.right = transform_recursive(right, transform_fn)?;
1686            if let Some(mut order) = e.order_by.take() {
1687                order.expressions = order
1688                    .expressions
1689                    .into_iter()
1690                    .map(|o| {
1691                        let mut o = o;
1692                        let original = o.this.clone();
1693                        o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
1694                        match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
1695                            Ok(Expression::Ordered(transformed)) => *transformed,
1696                            Ok(_) | Err(_) => o,
1697                        }
1698                    })
1699                    .collect();
1700                e.order_by = Some(order);
1701            }
1702            if let Some(mut with) = e.with.take() {
1703                with.ctes = with
1704                    .ctes
1705                    .into_iter()
1706                    .map(|mut cte| {
1707                        let original = cte.this.clone();
1708                        cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1709                        cte
1710                    })
1711                    .collect();
1712                e.with = Some(with);
1713            }
1714            Expression::Except(e)
1715        }
1716
1717        // ===== DML expressions =====
1718        Expression::Insert(mut ins) => {
1719            // Transform VALUES clause expressions
1720            let mut new_values = Vec::new();
1721            for row in ins.values {
1722                let mut new_row = Vec::new();
1723                for e in row {
1724                    new_row.push(transform_recursive(e, transform_fn)?);
1725                }
1726                new_values.push(new_row);
1727            }
1728            ins.values = new_values;
1729
1730            // Transform query (for INSERT ... SELECT)
1731            if let Some(query) = ins.query {
1732                ins.query = Some(transform_recursive(query, transform_fn)?);
1733            }
1734
1735            // Transform RETURNING clause
1736            let mut new_returning = Vec::new();
1737            for e in ins.returning {
1738                new_returning.push(transform_recursive(e, transform_fn)?);
1739            }
1740            ins.returning = new_returning;
1741
1742            // Transform ON CONFLICT clause
1743            if let Some(on_conflict) = ins.on_conflict {
1744                ins.on_conflict = Some(Box::new(transform_recursive(*on_conflict, transform_fn)?));
1745            }
1746
1747            Expression::Insert(ins)
1748        }
1749        Expression::Update(mut upd) => {
1750            upd.table = transform_table_ref_recursive(upd.table, transform_fn)?;
1751            upd.extra_tables = upd
1752                .extra_tables
1753                .into_iter()
1754                .map(|table| transform_table_ref_recursive(table, transform_fn))
1755                .collect::<Result<Vec<_>>>()?;
1756            upd.table_joins = upd
1757                .table_joins
1758                .into_iter()
1759                .map(|join| transform_join_recursive(join, transform_fn))
1760                .collect::<Result<Vec<_>>>()?;
1761            upd.set = upd
1762                .set
1763                .into_iter()
1764                .map(|(id, val)| {
1765                    let new_val = transform_recursive(val.clone(), transform_fn).unwrap_or(val);
1766                    (id, new_val)
1767                })
1768                .collect();
1769            if let Some(from_clause) = upd.from_clause.take() {
1770                upd.from_clause = Some(transform_from_recursive(from_clause, transform_fn)?);
1771            }
1772            upd.from_joins = upd
1773                .from_joins
1774                .into_iter()
1775                .map(|join| transform_join_recursive(join, transform_fn))
1776                .collect::<Result<Vec<_>>>()?;
1777            if let Some(mut where_clause) = upd.where_clause.take() {
1778                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1779                upd.where_clause = Some(where_clause);
1780            }
1781            upd.returning = upd
1782                .returning
1783                .into_iter()
1784                .map(|expr| transform_recursive(expr, transform_fn))
1785                .collect::<Result<Vec<_>>>()?;
1786            if let Some(output) = upd.output.take() {
1787                upd.output = Some(transform_output_clause_recursive(output, transform_fn)?);
1788            }
1789            if let Some(with) = upd.with.take() {
1790                upd.with = Some(transform_with_recursive(with, transform_fn)?);
1791            }
1792            if let Some(limit) = upd.limit.take() {
1793                upd.limit = Some(transform_recursive(limit, transform_fn)?);
1794            }
1795            if let Some(order_by) = upd.order_by.take() {
1796                upd.order_by = Some(transform_order_by_recursive(order_by, transform_fn)?);
1797            }
1798            Expression::Update(upd)
1799        }
1800        Expression::Delete(mut del) => {
1801            del.table = transform_table_ref_recursive(del.table, transform_fn)?;
1802            del.using = del
1803                .using
1804                .into_iter()
1805                .map(|table| transform_table_ref_recursive(table, transform_fn))
1806                .collect::<Result<Vec<_>>>()?;
1807            if let Some(mut where_clause) = del.where_clause.take() {
1808                where_clause.this = transform_recursive(where_clause.this, transform_fn)?;
1809                del.where_clause = Some(where_clause);
1810            }
1811            if let Some(output) = del.output.take() {
1812                del.output = Some(transform_output_clause_recursive(output, transform_fn)?);
1813            }
1814            if let Some(with) = del.with.take() {
1815                del.with = Some(transform_with_recursive(with, transform_fn)?);
1816            }
1817            if let Some(limit) = del.limit.take() {
1818                del.limit = Some(transform_recursive(limit, transform_fn)?);
1819            }
1820            if let Some(order_by) = del.order_by.take() {
1821                del.order_by = Some(transform_order_by_recursive(order_by, transform_fn)?);
1822            }
1823            del.returning = del
1824                .returning
1825                .into_iter()
1826                .map(|expr| transform_recursive(expr, transform_fn))
1827                .collect::<Result<Vec<_>>>()?;
1828            del.tables = del
1829                .tables
1830                .into_iter()
1831                .map(|table| transform_table_ref_recursive(table, transform_fn))
1832                .collect::<Result<Vec<_>>>()?;
1833            del.joins = del
1834                .joins
1835                .into_iter()
1836                .map(|join| transform_join_recursive(join, transform_fn))
1837                .collect::<Result<Vec<_>>>()?;
1838            Expression::Delete(del)
1839        }
1840
1841        // ===== CTE expressions =====
1842        Expression::With(mut w) => {
1843            w.ctes = w
1844                .ctes
1845                .into_iter()
1846                .map(|mut cte| {
1847                    let original = cte.this.clone();
1848                    cte.this = transform_recursive(cte.this, transform_fn).unwrap_or(original);
1849                    cte
1850                })
1851                .collect();
1852            Expression::With(w)
1853        }
1854        Expression::Cte(mut c) => {
1855            c.this = transform_recursive(c.this, transform_fn)?;
1856            Expression::Cte(c)
1857        }
1858
1859        // ===== Order expressions =====
1860        Expression::Ordered(mut o) => {
1861            o.this = transform_recursive(o.this, transform_fn)?;
1862            Expression::Ordered(o)
1863        }
1864
1865        // ===== Negation =====
1866        Expression::Neg(mut n) => {
1867            n.this = transform_recursive(n.this, transform_fn)?;
1868            Expression::Neg(n)
1869        }
1870
1871        // ===== Between =====
1872        Expression::Between(mut b) => {
1873            b.this = transform_recursive(b.this, transform_fn)?;
1874            b.low = transform_recursive(b.low, transform_fn)?;
1875            b.high = transform_recursive(b.high, transform_fn)?;
1876            Expression::Between(b)
1877        }
1878        Expression::IsNull(mut i) => {
1879            i.this = transform_recursive(i.this, transform_fn)?;
1880            Expression::IsNull(i)
1881        }
1882        Expression::IsTrue(mut i) => {
1883            i.this = transform_recursive(i.this, transform_fn)?;
1884            Expression::IsTrue(i)
1885        }
1886        Expression::IsFalse(mut i) => {
1887            i.this = transform_recursive(i.this, transform_fn)?;
1888            Expression::IsFalse(i)
1889        }
1890
1891        // ===== Like expressions =====
1892        Expression::Like(mut l) => {
1893            l.left = transform_recursive(l.left, transform_fn)?;
1894            l.right = transform_recursive(l.right, transform_fn)?;
1895            Expression::Like(l)
1896        }
1897        Expression::ILike(mut l) => {
1898            l.left = transform_recursive(l.left, transform_fn)?;
1899            l.right = transform_recursive(l.right, transform_fn)?;
1900            Expression::ILike(l)
1901        }
1902
1903        // ===== Additional binary ops not covered by macro =====
1904        Expression::Neq(op) => transform_binary!(Neq, *op),
1905        Expression::Lte(op) => transform_binary!(Lte, *op),
1906        Expression::Gte(op) => transform_binary!(Gte, *op),
1907        Expression::Mod(op) => transform_binary!(Mod, *op),
1908        Expression::Concat(op) => transform_binary!(Concat, *op),
1909        Expression::BitwiseAnd(op) => transform_binary!(BitwiseAnd, *op),
1910        Expression::BitwiseOr(op) => transform_binary!(BitwiseOr, *op),
1911        Expression::BitwiseXor(op) => transform_binary!(BitwiseXor, *op),
1912        Expression::Is(op) => transform_binary!(Is, *op),
1913
1914        // ===== TryCast / SafeCast =====
1915        Expression::TryCast(mut c) => {
1916            c.this = transform_recursive(c.this, transform_fn)?;
1917            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1918            Expression::TryCast(c)
1919        }
1920        Expression::SafeCast(mut c) => {
1921            c.this = transform_recursive(c.this, transform_fn)?;
1922            c.to = transform_data_type_recursive(c.to, transform_fn)?;
1923            Expression::SafeCast(c)
1924        }
1925
1926        // ===== Misc =====
1927        Expression::Unnest(mut f) => {
1928            f.this = transform_recursive(f.this, transform_fn)?;
1929            f.expressions = f
1930                .expressions
1931                .into_iter()
1932                .map(|e| transform_recursive(e, transform_fn))
1933                .collect::<Result<Vec<_>>>()?;
1934            Expression::Unnest(f)
1935        }
1936        Expression::Explode(mut f) => {
1937            f.this = transform_recursive(f.this, transform_fn)?;
1938            Expression::Explode(f)
1939        }
1940        Expression::GroupConcat(mut f) => {
1941            f.this = transform_recursive(f.this, transform_fn)?;
1942            Expression::GroupConcat(f)
1943        }
1944        Expression::StringAgg(mut f) => {
1945            f.this = transform_recursive(f.this, transform_fn)?;
1946            if let Some(order_by) = f.order_by.take() {
1947                f.order_by = Some(
1948                    order_by
1949                        .into_iter()
1950                        .map(|mut ordered| {
1951                            let original = ordered.this.clone();
1952                            ordered.this =
1953                                transform_recursive(ordered.this, transform_fn).unwrap_or(original);
1954                            match transform_fn(Expression::Ordered(Box::new(ordered.clone()))) {
1955                                Ok(Expression::Ordered(transformed)) => Ok(*transformed),
1956                                Ok(_) | Err(_) => Ok(ordered),
1957                            }
1958                        })
1959                        .collect::<Result<Vec<_>>>()?,
1960                );
1961            }
1962            Expression::StringAgg(f)
1963        }
1964        Expression::ListAgg(mut f) => {
1965            f.this = transform_recursive(f.this, transform_fn)?;
1966            Expression::ListAgg(f)
1967        }
1968        Expression::ArrayAgg(mut f) => {
1969            f.this = transform_recursive(f.this, transform_fn)?;
1970            Expression::ArrayAgg(f)
1971        }
1972        Expression::ParseJson(mut f) => {
1973            f.this = transform_recursive(f.this, transform_fn)?;
1974            Expression::ParseJson(f)
1975        }
1976        Expression::ToJson(mut f) => {
1977            f.this = transform_recursive(f.this, transform_fn)?;
1978            Expression::ToJson(f)
1979        }
1980        Expression::JSONExtract(mut e) => {
1981            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1982            e.expression = Box::new(transform_recursive(*e.expression, transform_fn)?);
1983            Expression::JSONExtract(e)
1984        }
1985        Expression::JSONExtractScalar(mut e) => {
1986            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1987            e.expression = Box::new(transform_recursive(*e.expression, transform_fn)?);
1988            Expression::JSONExtractScalar(e)
1989        }
1990
1991        // StrToTime: recurse into this
1992        Expression::StrToTime(mut e) => {
1993            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
1994            Expression::StrToTime(e)
1995        }
1996
1997        // UnixToTime: recurse into this
1998        Expression::UnixToTime(mut e) => {
1999            e.this = Box::new(transform_recursive(*e.this, transform_fn)?);
2000            Expression::UnixToTime(e)
2001        }
2002
2003        Expression::Upsert(mut upsert) => {
2004            upsert.table = transform_table_ref_recursive(upsert.table, transform_fn)?;
2005            upsert.source = transform_recursive(upsert.source, transform_fn)?;
2006            if let Some(partition) = upsert.partition.take() {
2007                upsert.partition = Some(transform_recursive(partition, transform_fn)?);
2008            }
2009            if let Some(condition) = upsert.condition.take() {
2010                upsert.condition = Some(transform_recursive(condition, transform_fn)?);
2011            }
2012            Expression::Upsert(upsert)
2013        }
2014
2015        // CreateTable: recurse into column defaults, on_update expressions, and data types
2016        Expression::CreateTable(mut ct) => {
2017            for col in &mut ct.columns {
2018                if let Some(default_expr) = col.default.take() {
2019                    col.default = Some(transform_recursive(default_expr, transform_fn)?);
2020                }
2021                if let Some(on_update_expr) = col.on_update.take() {
2022                    col.on_update = Some(transform_recursive(on_update_expr, transform_fn)?);
2023                }
2024                // Note: Column data type transformations (INT -> INT64 for BigQuery, etc.)
2025                // are NOT applied here because per-dialect transforms are designed for CAST/expression
2026                // contexts and may not produce correct results for DDL column definitions.
2027                // The DDL type mappings would need dedicated handling per source/target pair.
2028            }
2029            if let Some(as_select) = ct.as_select.take() {
2030                ct.as_select = Some(transform_recursive(as_select, transform_fn)?);
2031            }
2032            Expression::CreateTable(ct)
2033        }
2034
2035        // CreateView: recurse into the view body query
2036        Expression::CreateView(mut cv) => {
2037            cv.query = transform_recursive(cv.query, transform_fn)?;
2038            Expression::CreateView(cv)
2039        }
2040
2041        // CreateTask: recurse into the task body
2042        Expression::CreateTask(mut ct) => {
2043            ct.body = transform_recursive(ct.body, transform_fn)?;
2044            Expression::CreateTask(ct)
2045        }
2046
2047        // Prepare: recurse into the prepared statement body
2048        Expression::Prepare(mut prepare) => {
2049            prepare.statement = transform_recursive(prepare.statement, transform_fn)?;
2050            Expression::Prepare(prepare)
2051        }
2052
2053        // Execute: recurse into procedure/prepared name and argument values
2054        Expression::Execute(mut execute) => {
2055            execute.this = transform_recursive(execute.this, transform_fn)?;
2056            execute.arguments = execute
2057                .arguments
2058                .into_iter()
2059                .map(|argument| transform_recursive(argument, transform_fn))
2060                .collect::<Result<Vec<_>>>()?;
2061            execute.parameters = execute
2062                .parameters
2063                .into_iter()
2064                .map(|mut parameter| {
2065                    parameter.value = transform_recursive(parameter.value, transform_fn)?;
2066                    Ok(parameter)
2067                })
2068                .collect::<Result<Vec<_>>>()?;
2069            Expression::Execute(execute)
2070        }
2071
2072        // CreateProcedure: recurse into body expressions
2073        Expression::CreateProcedure(mut cp) => {
2074            if let Some(body) = cp.body.take() {
2075                cp.body = Some(match body {
2076                    FunctionBody::Expression(expr) => {
2077                        FunctionBody::Expression(transform_recursive(expr, transform_fn)?)
2078                    }
2079                    FunctionBody::Return(expr) => {
2080                        FunctionBody::Return(transform_recursive(expr, transform_fn)?)
2081                    }
2082                    FunctionBody::Statements(stmts) => {
2083                        let transformed_stmts = stmts
2084                            .into_iter()
2085                            .map(|s| transform_recursive(s, transform_fn))
2086                            .collect::<Result<Vec<_>>>()?;
2087                        FunctionBody::Statements(transformed_stmts)
2088                    }
2089                    other => other,
2090                });
2091            }
2092            Expression::CreateProcedure(cp)
2093        }
2094
2095        // CreateFunction: recurse into body expressions
2096        Expression::CreateFunction(mut cf) => {
2097            if let Some(body) = cf.body.take() {
2098                cf.body = Some(match body {
2099                    FunctionBody::Expression(expr) => {
2100                        FunctionBody::Expression(transform_recursive(expr, transform_fn)?)
2101                    }
2102                    FunctionBody::Return(expr) => {
2103                        FunctionBody::Return(transform_recursive(expr, transform_fn)?)
2104                    }
2105                    FunctionBody::Statements(stmts) => {
2106                        let transformed_stmts = stmts
2107                            .into_iter()
2108                            .map(|s| transform_recursive(s, transform_fn))
2109                            .collect::<Result<Vec<_>>>()?;
2110                        FunctionBody::Statements(transformed_stmts)
2111                    }
2112                    other => other,
2113                });
2114            }
2115            Expression::CreateFunction(cf)
2116        }
2117
2118        // MemberOf: recurse into left and right operands
2119        Expression::MemberOf(op) => transform_binary!(MemberOf, *op),
2120        // ArrayContainsAll (@>): recurse into left and right operands
2121        Expression::ArrayContainsAll(op) => transform_binary!(ArrayContainsAll, *op),
2122        // ArrayContainedBy (<@): recurse into left and right operands
2123        Expression::ArrayContainedBy(op) => transform_binary!(ArrayContainedBy, *op),
2124        // ArrayOverlaps (&&): recurse into left and right operands
2125        Expression::ArrayOverlaps(op) => transform_binary!(ArrayOverlaps, *op),
2126        // TsMatch (@@): recurse into left and right operands
2127        Expression::TsMatch(op) => transform_binary!(TsMatch, *op),
2128        // Adjacent (-|-): recurse into left and right operands
2129        Expression::Adjacent(op) => transform_binary!(Adjacent, *op),
2130
2131        // Table: recurse into when (HistoricalData) and changes fields
2132        Expression::Table(mut t) => {
2133            if let Some(when) = t.when.take() {
2134                let transformed =
2135                    transform_recursive(Expression::HistoricalData(when), transform_fn)?;
2136                if let Expression::HistoricalData(hd) = transformed {
2137                    t.when = Some(hd);
2138                }
2139            }
2140            if let Some(changes) = t.changes.take() {
2141                let transformed = transform_recursive(Expression::Changes(changes), transform_fn)?;
2142                if let Expression::Changes(c) = transformed {
2143                    t.changes = Some(c);
2144                }
2145            }
2146            Expression::Table(t)
2147        }
2148
2149        // HistoricalData (Snowflake time travel): recurse into expression
2150        Expression::HistoricalData(mut hd) => {
2151            *hd.expression = transform_recursive(*hd.expression, transform_fn)?;
2152            Expression::HistoricalData(hd)
2153        }
2154
2155        // Changes (Snowflake CHANGES clause): recurse into at_before and end
2156        Expression::Changes(mut c) => {
2157            if let Some(at_before) = c.at_before.take() {
2158                c.at_before = Some(Box::new(transform_recursive(*at_before, transform_fn)?));
2159            }
2160            if let Some(end) = c.end.take() {
2161                c.end = Some(Box::new(transform_recursive(*end, transform_fn)?));
2162            }
2163            Expression::Changes(c)
2164        }
2165
2166        // TableArgument: TABLE(expr) or MODEL(expr)
2167        Expression::TableArgument(mut ta) => {
2168            ta.this = transform_recursive(ta.this, transform_fn)?;
2169            Expression::TableArgument(ta)
2170        }
2171
2172        // JoinedTable: (tbl1 JOIN tbl2 ON ...) - recurse into left and join tables
2173        Expression::JoinedTable(mut jt) => {
2174            jt.left = transform_recursive(jt.left, transform_fn)?;
2175            jt.joins = jt
2176                .joins
2177                .into_iter()
2178                .map(|mut join| {
2179                    join.this = transform_recursive(join.this, transform_fn)?;
2180                    if let Some(on) = join.on.take() {
2181                        join.on = Some(transform_recursive(on, transform_fn)?);
2182                    }
2183                    match transform_fn(Expression::Join(Box::new(join)))? {
2184                        Expression::Join(j) => Ok(*j),
2185                        _ => Err(crate::error::Error::parse(
2186                            "Join transformation returned non-join expression",
2187                            0,
2188                            0,
2189                            0,
2190                            0,
2191                        )),
2192                    }
2193                })
2194                .collect::<Result<Vec<_>>>()?;
2195            jt.lateral_views = jt
2196                .lateral_views
2197                .into_iter()
2198                .map(|mut lv| {
2199                    lv.this = transform_recursive(lv.this, transform_fn)?;
2200                    Ok(lv)
2201                })
2202                .collect::<Result<Vec<_>>>()?;
2203            Expression::JoinedTable(jt)
2204        }
2205
2206        // Lateral: LATERAL func() - recurse into the function expression
2207        Expression::Lateral(mut lat) => {
2208            *lat.this = transform_recursive(*lat.this, transform_fn)?;
2209            Expression::Lateral(lat)
2210        }
2211
2212        // WithinGroup: recurse into order_by items (for NULLS FIRST/LAST etc.)
2213        // but NOT into wg.this - the inner function is handled by StringAggConvert/GroupConcatConvert
2214        // as a unit together with the WithinGroup wrapper
2215        Expression::WithinGroup(mut wg) => {
2216            wg.order_by = wg
2217                .order_by
2218                .into_iter()
2219                .map(|mut o| {
2220                    let original = o.this.clone();
2221                    o.this = transform_recursive(o.this, transform_fn).unwrap_or(original);
2222                    match transform_fn(Expression::Ordered(Box::new(o.clone()))) {
2223                        Ok(Expression::Ordered(transformed)) => *transformed,
2224                        Ok(_) | Err(_) => o,
2225                    }
2226                })
2227                .collect();
2228            Expression::WithinGroup(wg)
2229        }
2230
2231        // Filter: recurse into both the aggregate and the filter condition
2232        Expression::Filter(mut f) => {
2233            f.this = Box::new(transform_recursive(*f.this, transform_fn)?);
2234            f.expression = Box::new(transform_recursive(*f.expression, transform_fn)?);
2235            Expression::Filter(f)
2236        }
2237
2238        // Aggregate functions (AggFunc-based): recurse into the aggregate argument,
2239        // filter, order_by, having_max, and limit.
2240        // Stddev, StddevSamp, Variance, and ArrayAgg are handled earlier in this match.
2241        Expression::Sum(f) => recurse_agg!(Sum, f),
2242        Expression::Avg(f) => recurse_agg!(Avg, f),
2243        Expression::Min(f) => recurse_agg!(Min, f),
2244        Expression::Max(f) => recurse_agg!(Max, f),
2245        Expression::CountIf(f) => recurse_agg!(CountIf, f),
2246        Expression::StddevPop(f) => recurse_agg!(StddevPop, f),
2247        Expression::VarPop(f) => recurse_agg!(VarPop, f),
2248        Expression::VarSamp(f) => recurse_agg!(VarSamp, f),
2249        Expression::Median(f) => recurse_agg!(Median, f),
2250        Expression::Mode(f) => recurse_agg!(Mode, f),
2251        Expression::First(f) => recurse_agg!(First, f),
2252        Expression::Last(f) => recurse_agg!(Last, f),
2253        Expression::AnyValue(f) => recurse_agg!(AnyValue, f),
2254        Expression::ApproxDistinct(f) => recurse_agg!(ApproxDistinct, f),
2255        Expression::ApproxCountDistinct(f) => recurse_agg!(ApproxCountDistinct, f),
2256        Expression::LogicalAnd(f) => recurse_agg!(LogicalAnd, f),
2257        Expression::LogicalOr(f) => recurse_agg!(LogicalOr, f),
2258        Expression::Skewness(f) => recurse_agg!(Skewness, f),
2259        Expression::ArrayConcatAgg(f) => recurse_agg!(ArrayConcatAgg, f),
2260        Expression::ArrayUniqueAgg(f) => recurse_agg!(ArrayUniqueAgg, f),
2261        Expression::BoolXorAgg(f) => recurse_agg!(BoolXorAgg, f),
2262        Expression::BitwiseOrAgg(f) => recurse_agg!(BitwiseOrAgg, f),
2263        Expression::BitwiseAndAgg(f) => recurse_agg!(BitwiseAndAgg, f),
2264        Expression::BitwiseXorAgg(f) => recurse_agg!(BitwiseXorAgg, f),
2265
2266        // Count has its own struct with an Option<Expression> `this` field
2267        Expression::Count(mut c) => {
2268            if let Some(this) = c.this.take() {
2269                c.this = Some(transform_recursive(this, transform_fn)?);
2270            }
2271            if let Some(filter) = c.filter.take() {
2272                c.filter = Some(transform_recursive(filter, transform_fn)?);
2273            }
2274            Expression::Count(c)
2275        }
2276
2277        Expression::PipeOperator(mut pipe) => {
2278            pipe.this = transform_recursive(pipe.this, transform_fn)?;
2279            pipe.expression = transform_recursive(pipe.expression, transform_fn)?;
2280            Expression::PipeOperator(pipe)
2281        }
2282
2283        // ArrayExcept/ArrayContains/ArrayDistinct: recurse into children
2284        Expression::ArrayExcept(mut f) => {
2285            f.this = transform_recursive(f.this, transform_fn)?;
2286            f.expression = transform_recursive(f.expression, transform_fn)?;
2287            Expression::ArrayExcept(f)
2288        }
2289        Expression::ArrayContains(mut f) => {
2290            f.this = transform_recursive(f.this, transform_fn)?;
2291            f.expression = transform_recursive(f.expression, transform_fn)?;
2292            Expression::ArrayContains(f)
2293        }
2294        Expression::ArrayDistinct(mut f) => {
2295            f.this = transform_recursive(f.this, transform_fn)?;
2296            Expression::ArrayDistinct(f)
2297        }
2298        Expression::ArrayPosition(mut f) => {
2299            f.this = transform_recursive(f.this, transform_fn)?;
2300            f.expression = transform_recursive(f.expression, transform_fn)?;
2301            Expression::ArrayPosition(f)
2302        }
2303
2304        // Pass through leaf nodes unchanged
2305        other => other,
2306    };
2307
2308    // Then apply the transform function
2309    transform_fn(expr)
2310}
2311
2312/// Returns the tokenizer config, generator config, and expression transform closure
2313/// for a built-in dialect type. This is the shared implementation used by both
2314/// `Dialect::get()` and custom dialect construction.
2315// ---------------------------------------------------------------------------
2316// Cached dialect configurations
2317// ---------------------------------------------------------------------------
2318
2319/// Pre-computed tokenizer + generator configs for a dialect, cached via `LazyLock`.
2320/// Transform closures are cheap (unit-struct method calls) and created fresh each time.
2321struct CachedDialectConfig {
2322    tokenizer_config: Arc<TokenizerConfig>,
2323    #[cfg(feature = "generate")]
2324    generator_config: Arc<GeneratorConfig>,
2325}
2326
2327struct DialectConfigs {
2328    tokenizer_config: Arc<TokenizerConfig>,
2329    #[cfg(feature = "generate")]
2330    generator_config: Arc<GeneratorConfig>,
2331    #[cfg(feature = "transpile")]
2332    transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2333}
2334
2335/// Declare a per-dialect `LazyLock<CachedDialectConfig>` static.
2336macro_rules! cached_dialect {
2337    ($static_name:ident, $dialect_struct:expr, $feature:literal) => {
2338        #[cfg(feature = $feature)]
2339        static $static_name: LazyLock<CachedDialectConfig> = LazyLock::new(|| {
2340            let d = $dialect_struct;
2341            CachedDialectConfig {
2342                tokenizer_config: Arc::new(d.tokenizer_config()),
2343                #[cfg(feature = "generate")]
2344                generator_config: Arc::new(d.generator_config()),
2345            }
2346        });
2347    };
2348}
2349
2350static CACHED_GENERIC: LazyLock<CachedDialectConfig> = LazyLock::new(|| {
2351    let d = GenericDialect;
2352    CachedDialectConfig {
2353        tokenizer_config: Arc::new(d.tokenizer_config()),
2354        #[cfg(feature = "generate")]
2355        generator_config: Arc::new(d.generator_config()),
2356    }
2357});
2358
2359cached_dialect!(CACHED_POSTGRESQL, PostgresDialect, "dialect-postgresql");
2360cached_dialect!(CACHED_MYSQL, MySQLDialect, "dialect-mysql");
2361cached_dialect!(CACHED_BIGQUERY, BigQueryDialect, "dialect-bigquery");
2362cached_dialect!(CACHED_SNOWFLAKE, SnowflakeDialect, "dialect-snowflake");
2363cached_dialect!(CACHED_DUCKDB, DuckDBDialect, "dialect-duckdb");
2364cached_dialect!(CACHED_TSQL, TSQLDialect, "dialect-tsql");
2365cached_dialect!(CACHED_ORACLE, OracleDialect, "dialect-oracle");
2366cached_dialect!(CACHED_HIVE, HiveDialect, "dialect-hive");
2367cached_dialect!(CACHED_SPARK, SparkDialect, "dialect-spark");
2368cached_dialect!(CACHED_SQLITE, SQLiteDialect, "dialect-sqlite");
2369cached_dialect!(CACHED_PRESTO, PrestoDialect, "dialect-presto");
2370cached_dialect!(CACHED_TRINO, TrinoDialect, "dialect-trino");
2371cached_dialect!(CACHED_REDSHIFT, RedshiftDialect, "dialect-redshift");
2372cached_dialect!(CACHED_CLICKHOUSE, ClickHouseDialect, "dialect-clickhouse");
2373cached_dialect!(CACHED_DATABRICKS, DatabricksDialect, "dialect-databricks");
2374cached_dialect!(CACHED_ATHENA, AthenaDialect, "dialect-athena");
2375cached_dialect!(CACHED_TERADATA, TeradataDialect, "dialect-teradata");
2376cached_dialect!(CACHED_DORIS, DorisDialect, "dialect-doris");
2377cached_dialect!(CACHED_STARROCKS, StarRocksDialect, "dialect-starrocks");
2378cached_dialect!(
2379    CACHED_MATERIALIZE,
2380    MaterializeDialect,
2381    "dialect-materialize"
2382);
2383cached_dialect!(CACHED_RISINGWAVE, RisingWaveDialect, "dialect-risingwave");
2384cached_dialect!(
2385    CACHED_SINGLESTORE,
2386    SingleStoreDialect,
2387    "dialect-singlestore"
2388);
2389cached_dialect!(
2390    CACHED_COCKROACHDB,
2391    CockroachDBDialect,
2392    "dialect-cockroachdb"
2393);
2394cached_dialect!(CACHED_TIDB, TiDBDialect, "dialect-tidb");
2395cached_dialect!(CACHED_DRUID, DruidDialect, "dialect-druid");
2396cached_dialect!(CACHED_SOLR, SolrDialect, "dialect-solr");
2397cached_dialect!(CACHED_TABLEAU, TableauDialect, "dialect-tableau");
2398cached_dialect!(CACHED_DUNE, DuneDialect, "dialect-dune");
2399cached_dialect!(CACHED_FABRIC, FabricDialect, "dialect-fabric");
2400cached_dialect!(CACHED_DRILL, DrillDialect, "dialect-drill");
2401cached_dialect!(CACHED_DREMIO, DremioDialect, "dialect-dremio");
2402cached_dialect!(CACHED_EXASOL, ExasolDialect, "dialect-exasol");
2403cached_dialect!(CACHED_DATAFUSION, DataFusionDialect, "dialect-datafusion");
2404cached_dialect!(CACHED_HANA, HanaDialect, "dialect-hana");
2405cached_dialect!(CACHED_VERTICA, VerticaDialect, "dialect-vertica");
2406
2407fn configs_for_dialect_type(dt: DialectType) -> DialectConfigs {
2408    /// Clone configs from a cached static and pair with a fresh transform closure.
2409    macro_rules! from_cache {
2410        ($cache:expr, $dialect_struct:expr) => {{
2411            let c = &*$cache;
2412            DialectConfigs {
2413                tokenizer_config: c.tokenizer_config.clone(),
2414                #[cfg(feature = "generate")]
2415                generator_config: c.generator_config.clone(),
2416                #[cfg(feature = "transpile")]
2417                transformer: Box::new(move |e| $dialect_struct.transform_expr(e)),
2418            }
2419        }};
2420    }
2421    match dt {
2422        #[cfg(feature = "dialect-postgresql")]
2423        DialectType::PostgreSQL => from_cache!(CACHED_POSTGRESQL, PostgresDialect),
2424        #[cfg(feature = "dialect-mysql")]
2425        DialectType::MySQL => from_cache!(CACHED_MYSQL, MySQLDialect),
2426        #[cfg(feature = "dialect-bigquery")]
2427        DialectType::BigQuery => from_cache!(CACHED_BIGQUERY, BigQueryDialect),
2428        #[cfg(feature = "dialect-snowflake")]
2429        DialectType::Snowflake => from_cache!(CACHED_SNOWFLAKE, SnowflakeDialect),
2430        #[cfg(feature = "dialect-duckdb")]
2431        DialectType::DuckDB => from_cache!(CACHED_DUCKDB, DuckDBDialect),
2432        #[cfg(feature = "dialect-tsql")]
2433        DialectType::TSQL => from_cache!(CACHED_TSQL, TSQLDialect),
2434        #[cfg(feature = "dialect-oracle")]
2435        DialectType::Oracle => from_cache!(CACHED_ORACLE, OracleDialect),
2436        #[cfg(feature = "dialect-hive")]
2437        DialectType::Hive => from_cache!(CACHED_HIVE, HiveDialect),
2438        #[cfg(feature = "dialect-spark")]
2439        DialectType::Spark => from_cache!(CACHED_SPARK, SparkDialect),
2440        #[cfg(feature = "dialect-sqlite")]
2441        DialectType::SQLite => from_cache!(CACHED_SQLITE, SQLiteDialect),
2442        #[cfg(feature = "dialect-presto")]
2443        DialectType::Presto => from_cache!(CACHED_PRESTO, PrestoDialect),
2444        #[cfg(feature = "dialect-trino")]
2445        DialectType::Trino => from_cache!(CACHED_TRINO, TrinoDialect),
2446        #[cfg(feature = "dialect-redshift")]
2447        DialectType::Redshift => from_cache!(CACHED_REDSHIFT, RedshiftDialect),
2448        #[cfg(feature = "dialect-clickhouse")]
2449        DialectType::ClickHouse => from_cache!(CACHED_CLICKHOUSE, ClickHouseDialect),
2450        #[cfg(feature = "dialect-databricks")]
2451        DialectType::Databricks => from_cache!(CACHED_DATABRICKS, DatabricksDialect),
2452        #[cfg(feature = "dialect-athena")]
2453        DialectType::Athena => from_cache!(CACHED_ATHENA, AthenaDialect),
2454        #[cfg(feature = "dialect-teradata")]
2455        DialectType::Teradata => from_cache!(CACHED_TERADATA, TeradataDialect),
2456        #[cfg(feature = "dialect-doris")]
2457        DialectType::Doris => from_cache!(CACHED_DORIS, DorisDialect),
2458        #[cfg(feature = "dialect-starrocks")]
2459        DialectType::StarRocks => from_cache!(CACHED_STARROCKS, StarRocksDialect),
2460        #[cfg(feature = "dialect-materialize")]
2461        DialectType::Materialize => from_cache!(CACHED_MATERIALIZE, MaterializeDialect),
2462        #[cfg(feature = "dialect-risingwave")]
2463        DialectType::RisingWave => from_cache!(CACHED_RISINGWAVE, RisingWaveDialect),
2464        #[cfg(feature = "dialect-singlestore")]
2465        DialectType::SingleStore => from_cache!(CACHED_SINGLESTORE, SingleStoreDialect),
2466        #[cfg(feature = "dialect-cockroachdb")]
2467        DialectType::CockroachDB => from_cache!(CACHED_COCKROACHDB, CockroachDBDialect),
2468        #[cfg(feature = "dialect-tidb")]
2469        DialectType::TiDB => from_cache!(CACHED_TIDB, TiDBDialect),
2470        #[cfg(feature = "dialect-druid")]
2471        DialectType::Druid => from_cache!(CACHED_DRUID, DruidDialect),
2472        #[cfg(feature = "dialect-solr")]
2473        DialectType::Solr => from_cache!(CACHED_SOLR, SolrDialect),
2474        #[cfg(feature = "dialect-tableau")]
2475        DialectType::Tableau => from_cache!(CACHED_TABLEAU, TableauDialect),
2476        #[cfg(feature = "dialect-dune")]
2477        DialectType::Dune => from_cache!(CACHED_DUNE, DuneDialect),
2478        #[cfg(feature = "dialect-fabric")]
2479        DialectType::Fabric => from_cache!(CACHED_FABRIC, FabricDialect),
2480        #[cfg(feature = "dialect-drill")]
2481        DialectType::Drill => from_cache!(CACHED_DRILL, DrillDialect),
2482        #[cfg(feature = "dialect-dremio")]
2483        DialectType::Dremio => from_cache!(CACHED_DREMIO, DremioDialect),
2484        #[cfg(feature = "dialect-exasol")]
2485        DialectType::Exasol => from_cache!(CACHED_EXASOL, ExasolDialect),
2486        #[cfg(feature = "dialect-datafusion")]
2487        DialectType::DataFusion => from_cache!(CACHED_DATAFUSION, DataFusionDialect),
2488        #[cfg(feature = "dialect-hana")]
2489        DialectType::HANA => from_cache!(CACHED_HANA, HanaDialect),
2490        #[cfg(feature = "dialect-vertica")]
2491        DialectType::Vertica => from_cache!(CACHED_VERTICA, VerticaDialect),
2492        _ => from_cache!(CACHED_GENERIC, GenericDialect),
2493    }
2494}
2495
2496// ---------------------------------------------------------------------------
2497// Custom dialect registry
2498// ---------------------------------------------------------------------------
2499
2500static CUSTOM_DIALECT_REGISTRY: LazyLock<RwLock<HashMap<String, Arc<CustomDialectConfig>>>> =
2501    LazyLock::new(|| RwLock::new(HashMap::new()));
2502
2503struct CustomDialectConfig {
2504    name: String,
2505    base_dialect: DialectType,
2506    tokenizer_config: Arc<TokenizerConfig>,
2507    #[cfg(feature = "generate")]
2508    generator_config: GeneratorConfig,
2509    #[cfg(feature = "transpile")]
2510    transform: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2511    #[cfg(feature = "transpile")]
2512    preprocess: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2513}
2514
2515/// Fluent builder for creating and registering custom SQL dialects.
2516///
2517/// A custom dialect is based on an existing built-in dialect and allows selective
2518/// overrides of tokenizer configuration, generator configuration, and expression
2519/// transforms.
2520///
2521/// # Example
2522///
2523/// ```rust,ignore
2524/// use polyglot_sql::dialects::{CustomDialectBuilder, DialectType, Dialect};
2525/// use polyglot_sql::generator::NormalizeFunctions;
2526///
2527/// CustomDialectBuilder::new("my_postgres")
2528///     .based_on(DialectType::PostgreSQL)
2529///     .generator_config_modifier(|gc| {
2530///         gc.normalize_functions = NormalizeFunctions::Lower;
2531///     })
2532///     .register()
2533///     .unwrap();
2534///
2535/// let d = Dialect::get_by_name("my_postgres").unwrap();
2536/// let exprs = d.parse("SELECT COUNT(*)").unwrap();
2537/// let sql = d.generate(&exprs[0]).unwrap();
2538/// assert_eq!(sql, "select count(*)");
2539///
2540/// polyglot_sql::unregister_custom_dialect("my_postgres");
2541/// ```
2542pub struct CustomDialectBuilder {
2543    name: String,
2544    base_dialect: DialectType,
2545    tokenizer_modifier: Option<Box<dyn FnOnce(&mut TokenizerConfig)>>,
2546    #[cfg(feature = "generate")]
2547    generator_modifier: Option<Box<dyn FnOnce(&mut GeneratorConfig)>>,
2548    #[cfg(feature = "transpile")]
2549    transform: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2550    #[cfg(feature = "transpile")]
2551    preprocess: Option<Arc<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2552}
2553
2554impl CustomDialectBuilder {
2555    /// Create a new builder with the given name. Defaults to `Generic` as the base dialect.
2556    pub fn new(name: impl Into<String>) -> Self {
2557        Self {
2558            name: name.into(),
2559            base_dialect: DialectType::Generic,
2560            tokenizer_modifier: None,
2561            #[cfg(feature = "generate")]
2562            generator_modifier: None,
2563            #[cfg(feature = "transpile")]
2564            transform: None,
2565            #[cfg(feature = "transpile")]
2566            preprocess: None,
2567        }
2568    }
2569
2570    /// Set the base built-in dialect to inherit configuration from.
2571    pub fn based_on(mut self, dialect: DialectType) -> Self {
2572        self.base_dialect = dialect;
2573        self
2574    }
2575
2576    /// Provide a closure that modifies the tokenizer configuration inherited from the base dialect.
2577    pub fn tokenizer_config_modifier<F>(mut self, f: F) -> Self
2578    where
2579        F: FnOnce(&mut TokenizerConfig) + 'static,
2580    {
2581        self.tokenizer_modifier = Some(Box::new(f));
2582        self
2583    }
2584
2585    /// Provide a closure that modifies the generator configuration inherited from the base dialect.
2586    #[cfg(feature = "generate")]
2587    pub fn generator_config_modifier<F>(mut self, f: F) -> Self
2588    where
2589        F: FnOnce(&mut GeneratorConfig) + 'static,
2590    {
2591        self.generator_modifier = Some(Box::new(f));
2592        self
2593    }
2594
2595    /// Set a custom per-node expression transform function.
2596    ///
2597    /// This replaces the base dialect's transform. It is called on every expression
2598    /// node during the recursive transform pass.
2599    #[cfg(feature = "transpile")]
2600    pub fn transform_fn<F>(mut self, f: F) -> Self
2601    where
2602        F: Fn(Expression) -> Result<Expression> + Send + Sync + 'static,
2603    {
2604        self.transform = Some(Arc::new(f));
2605        self
2606    }
2607
2608    /// Set a custom whole-tree preprocessing function.
2609    ///
2610    /// This replaces the base dialect's built-in preprocessing. It is called once
2611    /// on the entire expression tree before the recursive per-node transform.
2612    #[cfg(feature = "transpile")]
2613    pub fn preprocess_fn<F>(mut self, f: F) -> Self
2614    where
2615        F: Fn(Expression) -> Result<Expression> + Send + Sync + 'static,
2616    {
2617        self.preprocess = Some(Arc::new(f));
2618        self
2619    }
2620
2621    /// Build the custom dialect configuration and register it in the global registry.
2622    ///
2623    /// Returns an error if:
2624    /// - The name collides with a built-in dialect name
2625    /// - A custom dialect with the same name is already registered
2626    pub fn register(self) -> Result<()> {
2627        // Reject names that collide with built-in dialects
2628        if DialectType::from_str(&self.name).is_ok() {
2629            return Err(crate::error::Error::parse(
2630                format!(
2631                    "Cannot register custom dialect '{}': name collides with built-in dialect",
2632                    self.name
2633                ),
2634                0,
2635                0,
2636                0,
2637                0,
2638            ));
2639        }
2640
2641        // Get base configs
2642        let base_configs = configs_for_dialect_type(self.base_dialect);
2643        let mut tok_config = (*base_configs.tokenizer_config).clone();
2644        #[cfg(feature = "generate")]
2645        let mut gen_config = (*base_configs.generator_config).clone();
2646
2647        // Apply modifiers
2648        if let Some(tok_mod) = self.tokenizer_modifier {
2649            tok_mod(&mut tok_config);
2650        }
2651        #[cfg(feature = "generate")]
2652        if let Some(gen_mod) = self.generator_modifier {
2653            gen_mod(&mut gen_config);
2654        }
2655
2656        let config = CustomDialectConfig {
2657            name: self.name.clone(),
2658            base_dialect: self.base_dialect,
2659            tokenizer_config: Arc::new(tok_config),
2660            #[cfg(feature = "generate")]
2661            generator_config: gen_config,
2662            #[cfg(feature = "transpile")]
2663            transform: self.transform,
2664            #[cfg(feature = "transpile")]
2665            preprocess: self.preprocess,
2666        };
2667
2668        register_custom_dialect(config)
2669    }
2670}
2671
2672use std::str::FromStr;
2673
2674fn register_custom_dialect(config: CustomDialectConfig) -> Result<()> {
2675    let mut registry = CUSTOM_DIALECT_REGISTRY.write().map_err(|e| {
2676        crate::error::Error::parse(format!("Registry lock poisoned: {}", e), 0, 0, 0, 0)
2677    })?;
2678
2679    if registry.contains_key(&config.name) {
2680        return Err(crate::error::Error::parse(
2681            format!("Custom dialect '{}' is already registered", config.name),
2682            0,
2683            0,
2684            0,
2685            0,
2686        ));
2687    }
2688
2689    registry.insert(config.name.clone(), Arc::new(config));
2690    Ok(())
2691}
2692
2693/// Remove a custom dialect from the global registry.
2694///
2695/// Returns `true` if a dialect with that name was found and removed,
2696/// `false` if no such custom dialect existed.
2697pub fn unregister_custom_dialect(name: &str) -> bool {
2698    if let Ok(mut registry) = CUSTOM_DIALECT_REGISTRY.write() {
2699        registry.remove(name).is_some()
2700    } else {
2701        false
2702    }
2703}
2704
2705fn get_custom_dialect_config(name: &str) -> Option<Arc<CustomDialectConfig>> {
2706    CUSTOM_DIALECT_REGISTRY
2707        .read()
2708        .ok()
2709        .and_then(|registry| registry.get(name).cloned())
2710}
2711
2712/// Main entry point for dialect-specific SQL operations.
2713///
2714/// A `Dialect` bundles together a tokenizer, generator configuration, and expression
2715/// transformer for a specific SQL database engine. It is the high-level API through
2716/// which callers parse, generate, transform, and transpile SQL.
2717///
2718/// # Usage
2719///
2720/// ```rust,ignore
2721/// use polyglot_sql::dialects::{Dialect, DialectType};
2722///
2723/// // Parse PostgreSQL SQL into an AST
2724/// let pg = Dialect::get(DialectType::PostgreSQL);
2725/// let exprs = pg.parse("SELECT id, name FROM users WHERE active")?;
2726///
2727/// // Transpile from PostgreSQL to BigQuery
2728/// let results = pg.transpile("SELECT NOW()", DialectType::BigQuery)?;
2729/// assert_eq!(results[0], "SELECT CURRENT_TIMESTAMP()");
2730/// ```
2731///
2732/// Obtain an instance via [`Dialect::get`] or [`Dialect::get_by_name`].
2733/// The struct is `Send + Sync` safe so it can be shared across threads.
2734pub struct Dialect {
2735    dialect_type: DialectType,
2736    tokenizer: Tokenizer,
2737    #[cfg(feature = "generate")]
2738    generator_config: Arc<GeneratorConfig>,
2739    #[cfg(feature = "transpile")]
2740    transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2741    /// Optional function to get expression-specific generator config (for hybrid dialects like Athena).
2742    #[cfg(feature = "generate")]
2743    generator_config_for_expr: Option<Box<dyn Fn(&Expression) -> GeneratorConfig + Send + Sync>>,
2744    /// Optional custom preprocessing function (overrides built-in preprocess for custom dialects).
2745    #[cfg(feature = "transpile")]
2746    custom_preprocess: Option<Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>>,
2747}
2748
2749/// Options for [`Dialect::transpile_with`].
2750///
2751/// Use [`TranspileOptions::default`] for defaults, then tweak the fields you need.
2752/// The struct is marked `#[non_exhaustive]` so new fields can be added without
2753/// breaking the API.
2754///
2755/// The struct derives `Serialize`/`Deserialize` using camelCase field names so
2756/// it can be round-tripped over JSON bridges (C FFI, WASM) without mapping.
2757#[cfg(feature = "transpile")]
2758#[derive(Debug, Clone, Serialize, Deserialize)]
2759#[serde(rename_all = "camelCase", default)]
2760#[non_exhaustive]
2761pub struct TranspileOptions {
2762    /// Whether to pretty-print the output SQL.
2763    pub pretty: bool,
2764    /// How unsupported target-dialect constructs should be handled.
2765    ///
2766    /// The default is [`UnsupportedLevel::Warn`], which preserves the current
2767    /// compatibility behavior and continues transpilation.
2768    pub unsupported_level: UnsupportedLevel,
2769    /// Maximum number of unsupported diagnostics to include in raised errors.
2770    pub max_unsupported: usize,
2771    /// Complexity guard limits used while parsing, transforming, and generating.
2772    pub complexity_guard: ComplexityGuardOptions,
2773}
2774
2775#[cfg(feature = "transpile")]
2776impl Default for TranspileOptions {
2777    fn default() -> Self {
2778        Self {
2779            pretty: false,
2780            unsupported_level: UnsupportedLevel::Warn,
2781            max_unsupported: 3,
2782            complexity_guard: ComplexityGuardOptions::default(),
2783        }
2784    }
2785}
2786
2787#[cfg(feature = "transpile")]
2788impl TranspileOptions {
2789    /// Construct options with pretty-printing enabled.
2790    pub fn pretty() -> Self {
2791        Self {
2792            pretty: true,
2793            ..Default::default()
2794        }
2795    }
2796
2797    /// Construct options that raise when known unsupported constructs remain.
2798    pub fn strict() -> Self {
2799        Self {
2800            unsupported_level: UnsupportedLevel::Raise,
2801            ..Default::default()
2802        }
2803    }
2804
2805    /// Set how unsupported target-dialect constructs should be handled.
2806    pub fn with_unsupported_level(mut self, level: UnsupportedLevel) -> Self {
2807        self.unsupported_level = level;
2808        self
2809    }
2810
2811    /// Set the maximum number of unsupported diagnostics to include in raised errors.
2812    pub fn with_max_unsupported(mut self, max: usize) -> Self {
2813        self.max_unsupported = max;
2814        self
2815    }
2816
2817    /// Set complexity guard limits for parse/transpile/generate recursion-heavy paths.
2818    pub fn with_complexity_guard(mut self, guard: ComplexityGuardOptions) -> Self {
2819        self.complexity_guard = guard;
2820        self
2821    }
2822}
2823
2824/// A value that can be used as the target dialect in [`Dialect::transpile`] /
2825/// [`Dialect::transpile_with`].
2826///
2827/// Implemented for [`DialectType`] (built-in dialect enum) and `&Dialect` (any
2828/// dialect handle, including custom ones). End users do not normally need to
2829/// implement this trait themselves.
2830#[cfg(feature = "transpile")]
2831pub trait TranspileTarget {
2832    /// Invoke `f` with a reference to the resolved target dialect.
2833    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R;
2834}
2835
2836#[cfg(feature = "transpile")]
2837impl TranspileTarget for DialectType {
2838    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R {
2839        f(&Dialect::get(self))
2840    }
2841}
2842
2843#[cfg(feature = "transpile")]
2844impl TranspileTarget for &Dialect {
2845    fn with_dialect<R>(self, f: impl FnOnce(&Dialect) -> R) -> R {
2846        f(self)
2847    }
2848}
2849
2850impl Dialect {
2851    /// Creates a fully configured [`Dialect`] instance for the given [`DialectType`].
2852    ///
2853    /// This is the primary constructor. It initializes the tokenizer, generator config,
2854    /// and expression transformer based on the dialect's [`DialectImpl`] implementation.
2855    /// For hybrid dialects like Athena, it also sets up expression-specific generator
2856    /// config routing.
2857    pub fn get(dialect_type: DialectType) -> Self {
2858        let configs = configs_for_dialect_type(dialect_type);
2859        let tokenizer_config = configs.tokenizer_config;
2860        #[cfg(feature = "generate")]
2861        let generator_config = configs.generator_config;
2862        #[cfg(feature = "transpile")]
2863        let transformer = configs.transformer;
2864
2865        // Set up expression-specific generator config for hybrid dialects
2866        #[cfg(feature = "generate")]
2867        let generator_config_for_expr: Option<
2868            Box<dyn Fn(&Expression) -> GeneratorConfig + Send + Sync>,
2869        > = match dialect_type {
2870            #[cfg(feature = "dialect-athena")]
2871            DialectType::Athena => Some(Box::new(|expr| {
2872                AthenaDialect.generator_config_for_expr(expr)
2873            })),
2874            _ => None,
2875        };
2876
2877        Self {
2878            dialect_type,
2879            tokenizer: Tokenizer::from_shared_config(tokenizer_config),
2880            #[cfg(feature = "generate")]
2881            generator_config,
2882            #[cfg(feature = "transpile")]
2883            transformer,
2884            #[cfg(feature = "generate")]
2885            generator_config_for_expr,
2886            #[cfg(feature = "transpile")]
2887            custom_preprocess: None,
2888        }
2889    }
2890
2891    /// Look up a dialect by string name.
2892    ///
2893    /// Checks built-in dialect names first (via [`DialectType::from_str`]), then
2894    /// falls back to the custom dialect registry. Returns `None` if no dialect
2895    /// with the given name exists.
2896    pub fn get_by_name(name: &str) -> Option<Self> {
2897        // Try built-in first
2898        if let Ok(dt) = DialectType::from_str(name) {
2899            return Some(Self::get(dt));
2900        }
2901
2902        // Try custom registry
2903        let config = get_custom_dialect_config(name)?;
2904        Some(Self::from_custom_config(&config))
2905    }
2906
2907    /// Construct a `Dialect` from a custom dialect configuration.
2908    fn from_custom_config(config: &CustomDialectConfig) -> Self {
2909        // Build the transformer: use custom if provided, else use base dialect's
2910        #[cfg(feature = "transpile")]
2911        let transformer: Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync> =
2912            if let Some(ref custom_transform) = config.transform {
2913                let t = Arc::clone(custom_transform);
2914                Box::new(move |e| t(e))
2915            } else {
2916                configs_for_dialect_type(config.base_dialect).transformer
2917            };
2918
2919        // Build the custom preprocess: use custom if provided
2920        #[cfg(feature = "transpile")]
2921        let custom_preprocess: Option<
2922            Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>,
2923        > = config.preprocess.as_ref().map(|p| {
2924            let p = Arc::clone(p);
2925            Box::new(move |e: Expression| p(e))
2926                as Box<dyn Fn(Expression) -> Result<Expression> + Send + Sync>
2927        });
2928
2929        Self {
2930            dialect_type: config.base_dialect,
2931            tokenizer: Tokenizer::from_shared_config(config.tokenizer_config.clone()),
2932            #[cfg(feature = "generate")]
2933            generator_config: Arc::new(config.generator_config.clone()),
2934            #[cfg(feature = "transpile")]
2935            transformer,
2936            #[cfg(feature = "generate")]
2937            generator_config_for_expr: None,
2938            #[cfg(feature = "transpile")]
2939            custom_preprocess,
2940        }
2941    }
2942
2943    /// Get the dialect type
2944    pub fn dialect_type(&self) -> DialectType {
2945        self.dialect_type
2946    }
2947
2948    /// Get the generator configuration
2949    #[cfg(feature = "generate")]
2950    pub fn generator_config(&self) -> &GeneratorConfig {
2951        &self.generator_config
2952    }
2953
2954    /// Parses a SQL string into a list of [`Expression`] AST nodes.
2955    ///
2956    /// The input may contain multiple semicolon-separated statements; each one
2957    /// produces a separate element in the returned vector. Tokenization uses
2958    /// this dialect's configured tokenizer, and parsing uses the dialect-aware parser.
2959    pub fn parse(&self, sql: &str) -> Result<Vec<Expression>> {
2960        self.parse_with_guard(sql, self.default_complexity_guard())
2961    }
2962
2963    /// Parse using per-call limits without changing this dialect's defaults.
2964    pub fn parse_with_options(&self, sql: &str, options: &ParseOptions) -> Result<Vec<Expression>> {
2965        self.parse_with_guard(
2966            sql,
2967            options
2968                .complexity_guard
2969                .unwrap_or_else(|| self.default_complexity_guard()),
2970        )
2971    }
2972
2973    fn parse_with_guard(
2974        &self,
2975        sql: &str,
2976        complexity_guard: ComplexityGuardOptions,
2977    ) -> Result<Vec<Expression>> {
2978        enforce_input(sql, &complexity_guard)?;
2979        let source: Arc<str> = Arc::from(sql);
2980        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
2981        let config = crate::parser::ParserConfig {
2982            dialect: Some(self.dialect_type),
2983            complexity_guard,
2984            ..Default::default()
2985        };
2986        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
2987        parser.parse()
2988    }
2989
2990    fn default_complexity_guard(&self) -> ComplexityGuardOptions {
2991        let mut guard = ComplexityGuardOptions::default();
2992        if matches!(self.dialect_type, DialectType::ClickHouse) {
2993            guard.max_ast_depth = Some(4_096);
2994            guard.max_function_call_depth = Some(512);
2995        }
2996        guard
2997    }
2998
2999    #[cfg(feature = "transpile")]
3000    fn default_transpile_complexity_guard(
3001        &self,
3002        target_dialect: &Dialect,
3003        guard: ComplexityGuardOptions,
3004    ) -> ComplexityGuardOptions {
3005        if guard != ComplexityGuardOptions::default() {
3006            return guard;
3007        }
3008
3009        if matches!(self.dialect_type, DialectType::ClickHouse)
3010            || matches!(target_dialect.dialect_type, DialectType::ClickHouse)
3011        {
3012            let mut guard = guard;
3013            guard.max_ast_depth = Some(4_096);
3014            guard.max_function_call_depth = Some(512);
3015            guard
3016        } else {
3017            guard
3018        }
3019    }
3020
3021    /// Parse a standalone SQL data type using this dialect's tokenizer and parser.
3022    ///
3023    /// This accepts type strings such as `DECIMAL(10, 2)`, `INT[]`, or
3024    /// `STRUCT(a INT, b VARCHAR)` without requiring a surrounding statement.
3025    pub fn parse_data_type(&self, sql: &str) -> Result<DataType> {
3026        self.parse_data_type_with_options(sql, &ParseOptions::default())
3027    }
3028
3029    /// Parse a standalone data type using per-call complexity limits.
3030    pub fn parse_data_type_with_options(
3031        &self,
3032        sql: &str,
3033        options: &ParseOptions,
3034    ) -> Result<DataType> {
3035        let complexity_guard = options
3036            .complexity_guard
3037            .unwrap_or_else(|| self.default_complexity_guard());
3038        enforce_input(sql, &complexity_guard)?;
3039        let source: Arc<str> = Arc::from(sql);
3040        let (tokens, token_guard_stats) = self.tokenizer.tokenize_for_parser(&source)?;
3041        let config = crate::parser::ParserConfig {
3042            dialect: Some(self.dialect_type),
3043            complexity_guard,
3044            ..Default::default()
3045        };
3046        let mut parser = Parser::with_parser_tokens(tokens, token_guard_stats, config, source);
3047        parser.parse_standalone_data_type()
3048    }
3049
3050    /// Tokenize SQL using this dialect's tokenizer configuration.
3051    pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
3052        self.tokenizer.tokenize(sql)
3053    }
3054
3055    /// Get the generator config for a specific expression (supports hybrid dialects).
3056    /// Returns an owned `GeneratorConfig` suitable for mutation before generation.
3057    #[cfg(feature = "generate")]
3058    fn get_config_for_expr(&self, expr: &Expression) -> GeneratorConfig {
3059        if let Some(ref config_fn) = self.generator_config_for_expr {
3060            config_fn(expr)
3061        } else {
3062            (*self.generator_config).clone()
3063        }
3064    }
3065
3066    /// Generates a SQL string from an [`Expression`] AST node.
3067    ///
3068    /// The output uses this dialect's generator configuration for identifier quoting,
3069    /// keyword casing, function name normalization, and syntax style. The result is
3070    /// a single-line (non-pretty) SQL string.
3071    #[cfg(feature = "generate")]
3072    pub fn generate(&self, expr: &Expression) -> Result<String> {
3073        // Fast path: when no per-expression config override, share the Arc cheaply.
3074        if self.generator_config_for_expr.is_none() {
3075            let mut generator = Generator::with_arc_config(self.generator_config.clone());
3076            return generator.generate(expr);
3077        }
3078        let config = self.get_config_for_expr(expr);
3079        let mut generator = Generator::with_config(config);
3080        generator.generate(expr)
3081    }
3082
3083    /// Generate SQL with pretty printing, retaining explicit null-ordering clauses.
3084    #[cfg(feature = "generate")]
3085    pub fn generate_pretty(&self, expr: &Expression) -> Result<String> {
3086        let mut config = self.get_config_for_expr(expr);
3087        config.pretty = true;
3088        let mut generator = Generator::with_config(config).with_preserved_null_ordering();
3089        generator.generate(expr)
3090    }
3091
3092    /// Render analysis facts with the same AST limits used to accept the input.
3093    #[cfg(all(feature = "semantic", feature = "generate"))]
3094    pub(crate) fn generate_with_guard(
3095        &self,
3096        expr: &Expression,
3097        guard: Option<ComplexityGuardOptions>,
3098    ) -> Result<String> {
3099        let Some(guard) = guard else {
3100            return self.generate(expr);
3101        };
3102        let mut config = self.get_config_for_expr(expr);
3103        config.complexity_guard = guard;
3104        Generator::with_config(config).generate(expr)
3105    }
3106
3107    /// Generate SQL from an expression with source dialect info (for transpilation)
3108    #[cfg(feature = "generate")]
3109    pub fn generate_with_source(&self, expr: &Expression, source: DialectType) -> Result<String> {
3110        let mut config = self.get_config_for_expr(expr);
3111        config.source_dialect = Some(source);
3112        let mut generator = Generator::with_config(config);
3113        generator.generate(expr)
3114    }
3115
3116    /// Generate SQL from an expression with pretty printing and source dialect info
3117    #[cfg(feature = "generate")]
3118    pub fn generate_pretty_with_source(
3119        &self,
3120        expr: &Expression,
3121        source: DialectType,
3122    ) -> Result<String> {
3123        let mut config = self.get_config_for_expr(expr);
3124        config.pretty = true;
3125        config.source_dialect = Some(source);
3126        let mut generator = Generator::with_config(config);
3127        generator.generate(expr)
3128    }
3129
3130    /// Generate SQL from an expression with source dialect and transpile options.
3131    #[cfg(all(feature = "generate", feature = "transpile"))]
3132    fn generate_with_transpile_options(
3133        &self,
3134        expr: &Expression,
3135        source: DialectType,
3136        opts: &TranspileOptions,
3137    ) -> Result<String> {
3138        let mut config = self.get_config_for_expr(expr);
3139        config.source_dialect = Some(source);
3140        config.pretty = opts.pretty;
3141        config.unsupported_level = opts.unsupported_level;
3142        config.max_unsupported = opts.max_unsupported.max(1);
3143        config.complexity_guard = opts.complexity_guard;
3144        let mut generator = Generator::with_config(config);
3145        generator.generate(expr)
3146    }
3147
3148    /// Generate SQL from an expression with forced identifier quoting (identify=True)
3149    #[cfg(feature = "generate")]
3150    pub fn generate_with_identify(&self, expr: &Expression) -> Result<String> {
3151        let mut config = self.get_config_for_expr(expr);
3152        config.always_quote_identifiers = true;
3153        let mut generator = Generator::with_config(config);
3154        generator.generate(expr)
3155    }
3156
3157    /// Generate SQL from an expression with pretty printing and forced identifier quoting
3158    #[cfg(feature = "generate")]
3159    pub fn generate_pretty_with_identify(&self, expr: &Expression) -> Result<String> {
3160        let mut config = (*self.generator_config).clone();
3161        config.pretty = true;
3162        config.always_quote_identifiers = true;
3163        let mut generator = Generator::with_config(config);
3164        generator.generate(expr)
3165    }
3166
3167    /// Generate SQL from an expression with caller-specified config overrides
3168    #[cfg(feature = "generate")]
3169    pub fn generate_with_overrides(
3170        &self,
3171        expr: &Expression,
3172        overrides: impl FnOnce(&mut GeneratorConfig),
3173    ) -> Result<String> {
3174        let mut config = self.get_config_for_expr(expr);
3175        overrides(&mut config);
3176        let mut generator = Generator::with_config(config);
3177        generator.generate(expr)
3178    }
3179
3180    /// Transforms an expression tree to conform to this dialect's syntax and semantics.
3181    ///
3182    /// The transformation proceeds in two phases:
3183    /// 1. **Preprocessing** -- whole-tree structural rewrites such as eliminating QUALIFY,
3184    ///    ensuring boolean predicates, or converting DISTINCT ON to a window-function pattern.
3185    /// 2. **Recursive per-node transform** -- a bottom-up pass via [`transform_recursive`]
3186    ///    that applies this dialect's [`DialectImpl::transform_expr`] to every node.
3187    ///
3188    /// This method is used both during transpilation (to rewrite an AST for a target dialect)
3189    /// and for identity transforms (normalizing SQL within the same dialect).
3190    #[cfg(feature = "transpile")]
3191    pub fn transform(&self, expr: Expression) -> Result<Expression> {
3192        self.transform_with_guard(expr, self.default_complexity_guard())
3193    }
3194
3195    #[cfg(feature = "transpile")]
3196    fn transform_with_guard(
3197        &self,
3198        expr: Expression,
3199        complexity_guard: ComplexityGuardOptions,
3200    ) -> Result<Expression> {
3201        enforce_generate_ast(&expr, &complexity_guard)?;
3202        // Apply preprocessing transforms based on dialect
3203        let preprocessed = self.preprocess(expr)?;
3204        // Then apply recursive transformation
3205        transform_recursive(preprocessed, &|node| {
3206            // Calls with retained source semantics are lowered by the generator;
3207            // ordinary AST visitors and optimizer passes still see their shared node kind.
3208            if node.source_dialect().is_some()
3209                || matches!(&node, Expression::Function(f) if !f.qualified_name.is_empty())
3210            {
3211                Ok(node)
3212            } else {
3213                (self.transformer)(node)
3214            }
3215        })
3216    }
3217
3218    /// Apply dialect-specific preprocessing transforms
3219    #[cfg(feature = "transpile")]
3220    fn preprocess(&self, expr: Expression) -> Result<Expression> {
3221        // If a custom preprocess function is set, use it instead of the built-in logic
3222        if let Some(ref custom_preprocess) = self.custom_preprocess {
3223            return custom_preprocess(expr);
3224        }
3225
3226        #[cfg(any(
3227            feature = "dialect-mysql",
3228            feature = "dialect-postgresql",
3229            feature = "dialect-bigquery",
3230            feature = "dialect-snowflake",
3231            feature = "dialect-tsql",
3232            feature = "dialect-spark",
3233            feature = "dialect-databricks",
3234            feature = "dialect-hive",
3235            feature = "dialect-sqlite",
3236            feature = "dialect-trino",
3237            feature = "dialect-presto",
3238            feature = "dialect-duckdb",
3239            feature = "dialect-redshift",
3240            feature = "dialect-starrocks",
3241            feature = "dialect-oracle",
3242            feature = "dialect-clickhouse",
3243            feature = "dialect-fabric",
3244            feature = "dialect-vertica",
3245        ))]
3246        use crate::transforms;
3247
3248        match self.dialect_type {
3249            // MySQL doesn't support QUALIFY, DISTINCT ON, FULL OUTER JOIN
3250            // MySQL doesn't natively support GENERATE_DATE_ARRAY (expand to recursive CTE)
3251            #[cfg(feature = "dialect-mysql")]
3252            DialectType::MySQL => {
3253                let expr = transforms::eliminate_qualify(expr)?;
3254                let expr = transforms::eliminate_full_outer_join(expr)?;
3255                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3256                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3257                Ok(expr)
3258            }
3259            // PostgreSQL doesn't support QUALIFY
3260            // PostgreSQL: UNNEST(GENERATE_SERIES) -> subquery wrapping
3261            // PostgreSQL: Normalize SET ... TO to SET ... = in CREATE FUNCTION
3262            #[cfg(feature = "dialect-postgresql")]
3263            DialectType::PostgreSQL => {
3264                let expr = transforms::eliminate_qualify(expr)?;
3265                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3266                let expr = transforms::unwrap_unnest_generate_series_for_postgres(expr)?;
3267                // Normalize SET ... TO to SET ... = in CREATE FUNCTION
3268                // Only normalize when sqlglot would fully parse (no body) —
3269                // sqlglot falls back to Command for complex function bodies,
3270                // preserving the original text including TO.
3271                let expr = if let Expression::CreateFunction(mut cf) = expr {
3272                    if cf.body.is_none() {
3273                        for opt in &mut cf.set_options {
3274                            if let crate::expressions::FunctionSetValue::Value { use_to, .. } =
3275                                &mut opt.value
3276                            {
3277                                *use_to = false;
3278                            }
3279                        }
3280                    }
3281                    Expression::CreateFunction(cf)
3282                } else {
3283                    expr
3284                };
3285                Ok(expr)
3286            }
3287            // BigQuery doesn't support DISTINCT ON or CTE column aliases
3288            #[cfg(feature = "dialect-bigquery")]
3289            DialectType::BigQuery => {
3290                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3291                let expr = transforms::pushdown_cte_column_names(expr)?;
3292                let expr = transforms::explode_projection_to_unnest(expr, DialectType::BigQuery)?;
3293                Ok(expr)
3294            }
3295            // Snowflake
3296            #[cfg(feature = "dialect-snowflake")]
3297            DialectType::Snowflake => {
3298                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3299                let expr = transforms::eliminate_window_clause(expr)?;
3300                let expr = transforms::snowflake_flatten_projection_to_unnest(expr)?;
3301                Ok(expr)
3302            }
3303            // TSQL doesn't support QUALIFY
3304            // TSQL requires boolean expressions in WHERE/HAVING (no implicit truthiness)
3305            // TSQL doesn't support CTEs in subqueries (hoist to top level)
3306            // NOTE: no_limit_order_by_union is handled in cross_dialect_normalize (not preprocess)
3307            // to avoid breaking TSQL identity tests where ORDER BY on UNION is valid
3308            #[cfg(feature = "dialect-tsql")]
3309            DialectType::TSQL => {
3310                let expr = transforms::eliminate_qualify(expr)?;
3311                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3312                let expr = transforms::normalize_grouping_sets_for_tsql(expr)?;
3313                let expr =
3314                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::TSQL)?;
3315                let expr = transforms::ensure_bools(expr)?;
3316                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3317                let expr = transforms::strip_cte_materialization(expr)?;
3318                let expr = transforms::move_ctes_to_top_level(expr)?;
3319                let expr = transforms::qualify_derived_table_outputs(expr)?;
3320                Ok(expr)
3321            }
3322            // Fabric shares T-SQL predicate rules and CTE placement restrictions,
3323            // but keeps Fabric-specific APPLY and derived-table behavior separate.
3324            #[cfg(feature = "dialect-fabric")]
3325            DialectType::Fabric => {
3326                let expr = transforms::normalize_grouping_sets_for_tsql(expr)?;
3327                let expr =
3328                    transforms::expand_distinct_grouping_sets_for_tsql(expr, DialectType::Fabric)?;
3329                let expr = transforms::ensure_bools(expr)?;
3330                let expr = transforms::strip_cte_materialization(expr)?;
3331                let expr = transforms::move_ctes_to_top_level(expr)?;
3332                Ok(expr)
3333            }
3334            // Spark doesn't support QUALIFY (but Databricks does)
3335            // Spark doesn't support CTEs in subqueries (hoist to top level)
3336            #[cfg(feature = "dialect-spark")]
3337            DialectType::Spark => {
3338                let expr = transforms::eliminate_qualify(expr)?;
3339                let expr = transforms::add_auto_table_alias(expr)?;
3340                let expr = transforms::simplify_nested_paren_values(expr)?;
3341                let expr = transforms::move_ctes_to_top_level(expr)?;
3342                Ok(expr)
3343            }
3344            // Databricks supports QUALIFY natively
3345            // Databricks doesn't support CTEs in subqueries (hoist to top level)
3346            #[cfg(feature = "dialect-databricks")]
3347            DialectType::Databricks => {
3348                let expr = transforms::add_auto_table_alias(expr)?;
3349                let expr = transforms::simplify_nested_paren_values(expr)?;
3350                let expr = transforms::move_ctes_to_top_level(expr)?;
3351                Ok(expr)
3352            }
3353            // Hive doesn't support QUALIFY or CTEs in subqueries
3354            #[cfg(feature = "dialect-hive")]
3355            DialectType::Hive => {
3356                let expr = transforms::eliminate_qualify(expr)?;
3357                let expr = transforms::move_ctes_to_top_level(expr)?;
3358                Ok(expr)
3359            }
3360            // SQLite doesn't support QUALIFY
3361            #[cfg(feature = "dialect-sqlite")]
3362            DialectType::SQLite => {
3363                let expr = transforms::eliminate_qualify(expr)?;
3364                Ok(expr)
3365            }
3366            // Trino doesn't support QUALIFY
3367            #[cfg(feature = "dialect-trino")]
3368            DialectType::Trino => {
3369                let expr = transforms::eliminate_qualify(expr)?;
3370                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Trino)?;
3371                Ok(expr)
3372            }
3373            // Presto doesn't support QUALIFY or WINDOW clause
3374            #[cfg(feature = "dialect-presto")]
3375            DialectType::Presto => {
3376                let expr = transforms::eliminate_qualify(expr)?;
3377                let expr = transforms::eliminate_window_clause(expr)?;
3378                let expr = transforms::explode_projection_to_unnest(expr, DialectType::Presto)?;
3379                Ok(expr)
3380            }
3381            // DuckDB supports QUALIFY - no elimination needed
3382            // Expand POSEXPLODE to GENERATE_SUBSCRIPTS + UNNEST
3383            // Expand LIKE ANY / ILIKE ANY to OR chains (DuckDB doesn't support quantifiers)
3384            #[cfg(feature = "dialect-duckdb")]
3385            DialectType::DuckDB => {
3386                let expr = transforms::expand_posexplode_duckdb(expr)?;
3387                let expr = transforms::expand_like_any(expr)?;
3388                Ok(expr)
3389            }
3390            // Redshift doesn't support QUALIFY, WINDOW clause, or GENERATE_DATE_ARRAY
3391            #[cfg(feature = "dialect-redshift")]
3392            DialectType::Redshift => {
3393                let expr = transforms::eliminate_qualify(expr)?;
3394                let expr = transforms::eliminate_window_clause(expr)?;
3395                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3396                Ok(expr)
3397            }
3398            // StarRocks doesn't support BETWEEN in DELETE statements or QUALIFY
3399            #[cfg(feature = "dialect-starrocks")]
3400            DialectType::StarRocks => {
3401                let expr = transforms::eliminate_qualify(expr)?;
3402                let expr = transforms::expand_between_in_delete(expr)?;
3403                let expr = transforms::eliminate_distinct_on_for_dialect(
3404                    expr,
3405                    Some(DialectType::StarRocks),
3406                    Some(DialectType::StarRocks),
3407                )?;
3408                let expr = transforms::unnest_generate_date_array_using_recursive_cte(expr)?;
3409                Ok(expr)
3410            }
3411            // DataFusion supports QUALIFY and semi/anti joins natively
3412            #[cfg(feature = "dialect-datafusion")]
3413            DialectType::DataFusion => Ok(expr),
3414            // Vertica doesn't support QUALIFY or semi/anti join syntax
3415            #[cfg(feature = "dialect-vertica")]
3416            DialectType::Vertica => {
3417                let expr = transforms::eliminate_qualify(expr)?;
3418                let expr = transforms::eliminate_semi_and_anti_joins(expr)?;
3419                Ok(expr)
3420            }
3421            // Oracle doesn't support QUALIFY
3422            #[cfg(feature = "dialect-oracle")]
3423            DialectType::Oracle => {
3424                let expr = transforms::eliminate_qualify(expr)?;
3425                Ok(expr)
3426            }
3427            // Drill - no special preprocessing needed
3428            #[cfg(feature = "dialect-drill")]
3429            DialectType::Drill => Ok(expr),
3430            // Teradata - no special preprocessing needed
3431            #[cfg(feature = "dialect-teradata")]
3432            DialectType::Teradata => Ok(expr),
3433            // ClickHouse doesn't support ORDER BY/LIMIT directly on UNION
3434            #[cfg(feature = "dialect-clickhouse")]
3435            DialectType::ClickHouse => {
3436                let expr = transforms::no_limit_order_by_union(expr)?;
3437                Ok(expr)
3438            }
3439            // Other dialects - no preprocessing
3440            _ => Ok(expr),
3441        }
3442    }
3443
3444    /// Transpile SQL from this dialect to the given target dialect.
3445    ///
3446    /// The target may be specified as either a built-in [`DialectType`] enum variant
3447    /// or as a reference to a [`Dialect`] handle (built-in or custom). Both work:
3448    ///
3449    /// ```rust,ignore
3450    /// let pg = Dialect::get(DialectType::PostgreSQL);
3451    /// pg.transpile("SELECT NOW()", DialectType::BigQuery)?;   // enum
3452    /// pg.transpile("SELECT NOW()", &custom_dialect)?;         // handle
3453    /// ```
3454    ///
3455    /// For pretty-printing or other options, use [`transpile_with`](Self::transpile_with).
3456    #[cfg(feature = "transpile")]
3457    pub fn transpile<T: TranspileTarget>(&self, sql: &str, target: T) -> Result<Vec<String>> {
3458        self.transpile_with(sql, target, TranspileOptions::default())
3459    }
3460
3461    /// Transpile SQL with configurable [`TranspileOptions`] (e.g. pretty-printing).
3462    #[cfg(feature = "transpile")]
3463    pub fn transpile_with<T: TranspileTarget>(
3464        &self,
3465        sql: &str,
3466        target: T,
3467        opts: TranspileOptions,
3468    ) -> Result<Vec<String>> {
3469        target.with_dialect(|td| self.transpile_inner(sql, td, &opts))
3470    }
3471
3472    #[cfg(feature = "transpile")]
3473    fn transpile_inner(
3474        &self,
3475        sql: &str,
3476        target_dialect: &Dialect,
3477        opts: &TranspileOptions,
3478    ) -> Result<Vec<String>> {
3479        let mut effective_opts = opts.clone();
3480        effective_opts.complexity_guard =
3481            self.default_transpile_complexity_guard(target_dialect, opts.complexity_guard);
3482        let opts = &effective_opts;
3483        let target = target_dialect.dialect_type;
3484        if matches!(self.dialect_type, DialectType::PostgreSQL)
3485            && matches!(target, DialectType::SQLite)
3486        {
3487            self.reject_pgvector_distance_operators_for_sqlite(sql)?;
3488        }
3489        let expressions = self.parse_with_guard(sql, opts.complexity_guard)?;
3490        let generic_identity =
3491            self.dialect_type == DialectType::Generic && target == DialectType::Generic;
3492
3493        if generic_identity {
3494            return expressions
3495                .into_iter()
3496                .map(|expr| {
3497                    Self::reject_strict_unsupported(&expr, self.dialect_type, target, opts)?;
3498                    target_dialect.generate_with_transpile_options(&expr, self.dialect_type, opts)
3499                })
3500                .collect();
3501        }
3502
3503        expressions
3504            .into_iter()
3505            .map(|expr| {
3506                // Reject unsupported source semantics before normalization can
3507                // erase them. This AST-only pass shares conversion decisions with
3508                // the generator and never renders throwaway SQL.
3509                Self::reject_hana_source_semantics(
3510                    &expr,
3511                    self.dialect_type,
3512                    target_dialect.generator_config.dialect.unwrap_or_default(),
3513                )?;
3514
3515                let expr =
3516                    normalization::vertica::prepare_conversion(expr, self.dialect_type, target)?;
3517                normalization::vertica::validate_conversion(&expr, self.dialect_type, target)?;
3518                // DuckDB source: normalize VARCHAR/CHAR to TEXT (DuckDB doesn't support
3519                // VARCHAR length constraints). This emulates Python sqlglot's DuckDB parser
3520                // where VARCHAR_LENGTH = None and VARCHAR maps to TEXT.
3521                let expr = if matches!(self.dialect_type, DialectType::DuckDB) {
3522                    use crate::expressions::DataType as DT;
3523                    transform_recursive(expr, &|e| match e {
3524                        Expression::DataType(DT::VarChar { .. }) => {
3525                            Ok(Expression::DataType(DT::Text))
3526                        }
3527                        Expression::DataType(DT::Char { .. }) => Ok(Expression::DataType(DT::Text)),
3528                        _ => Ok(e),
3529                    })?
3530                } else {
3531                    expr
3532                };
3533
3534                Self::reject_clickhouse_session_semantics(&expr, self.dialect_type, target, opts)?;
3535                Self::reject_postgres_tsql_strict_regex_predicates(
3536                    &expr,
3537                    self.dialect_type,
3538                    target,
3539                    opts,
3540                )?;
3541                Self::reject_tsql_strict_json_constructor_return_types(
3542                    &expr,
3543                    self.dialect_type,
3544                    target,
3545                    opts,
3546                )?;
3547                Self::reject_postgres_tsql_strict_json_aggregate_modifiers(
3548                    &expr,
3549                    self.dialect_type,
3550                    target,
3551                    opts,
3552                )?;
3553
3554                // When source and target differ, first normalize the source dialect's
3555                // AST constructs to standard SQL, so that the target dialect can handle them.
3556                // This handles cases like Snowflake's SQUARE -> POWER, DIV0 -> CASE, etc.
3557                let normalized =
3558                    if self.dialect_type != target && self.dialect_type != DialectType::Generic {
3559                        self.transform_with_guard(expr, opts.complexity_guard)?
3560                    } else {
3561                        expr
3562                    };
3563
3564                // For TSQL source targeting non-TSQL: unwrap ISNULL(JSON_QUERY(...), JSON_VALUE(...))
3565                // to just JSON_QUERY(...) so cross_dialect_normalize can convert it cleanly.
3566                // The TSQL read transform wraps JsonQuery in ISNULL for identity, but for
3567                // cross-dialect transpilation we need the unwrapped JSON_QUERY.
3568                let normalized =
3569                    if matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
3570                        && !matches!(target, DialectType::TSQL | DialectType::Fabric)
3571                    {
3572                        transform_recursive(normalized, &|e| {
3573                            if let Expression::Function(ref f) = e {
3574                                if f.name.eq_ignore_ascii_case("ISNULL") && f.args.len() == 2 {
3575                                    // Check if first arg is JSON_QUERY and second is JSON_VALUE
3576                                    if let (
3577                                        Expression::Function(ref jq),
3578                                        Expression::Function(ref jv),
3579                                    ) = (&f.args[0], &f.args[1])
3580                                    {
3581                                        if jq.name.eq_ignore_ascii_case("JSON_QUERY")
3582                                            && jv.name.eq_ignore_ascii_case("JSON_VALUE")
3583                                        {
3584                                            // Unwrap: return just JSON_QUERY(...)
3585                                            return Ok(f.args[0].clone());
3586                                        }
3587                                    }
3588                                }
3589                            }
3590                            Ok(e)
3591                        })?
3592                    } else {
3593                        normalized
3594                    };
3595
3596                // Snowflake source to non-Snowflake target: CURRENT_TIME -> LOCALTIME.
3597                // Preserve precision for ClickHouse's Time64 lowering; retain the existing
3598                // precision-less LOCALTIME compatibility output for other targets.
3599                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3600                    && !matches!(target, DialectType::Snowflake)
3601                {
3602                    transform_recursive(normalized, &|e| match e {
3603                        Expression::Function(ref f)
3604                            if f.name.eq_ignore_ascii_case("CURRENT_TIME") =>
3605                        {
3606                            let precision = if matches!(target, DialectType::ClickHouse) {
3607                                f.args.first().cloned().map(Box::new)
3608                            } else {
3609                                None
3610                            };
3611                            Ok(Expression::Localtime(Box::new(
3612                                crate::expressions::Localtime { this: precision },
3613                            )))
3614                        }
3615                        Expression::Localtime(ref localtime)
3616                            if !matches!(target, DialectType::ClickHouse)
3617                                && localtime.this.is_some() =>
3618                        {
3619                            Ok(Expression::Localtime(Box::new(
3620                                crate::expressions::Localtime { this: None },
3621                            )))
3622                        }
3623                        _ => Ok(e),
3624                    })?
3625                } else {
3626                    normalized
3627                };
3628
3629                // Snowflake source to DuckDB target: REPEAT(' ', n) -> REPEAT(' ', CAST(n AS BIGINT))
3630                // Snowflake's SPACE(n) is converted to REPEAT(' ', n) by the Snowflake source
3631                // transform. DuckDB requires the count argument to be BIGINT.
3632                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3633                    && matches!(target, DialectType::DuckDB)
3634                {
3635                    transform_recursive(normalized, &|e| {
3636                        if let Expression::Function(ref f) = e {
3637                            if f.name.eq_ignore_ascii_case("REPEAT") && f.args.len() == 2 {
3638                                // Check if first arg is space string literal
3639                                if let Expression::Literal(ref lit) = f.args[0] {
3640                                    if let crate::expressions::Literal::String(ref s) = lit.as_ref()
3641                                    {
3642                                        if s == " " {
3643                                            // Wrap second arg in CAST(... AS BIGINT) if not already
3644                                            if !matches!(f.args[1], Expression::Cast(_)) {
3645                                                let mut new_args = f.args.clone();
3646                                                new_args[1] = Expression::Cast(Box::new(
3647                                                    crate::expressions::Cast {
3648                                                        this: new_args[1].clone(),
3649                                                        to: crate::expressions::DataType::BigInt {
3650                                                            length: None,
3651                                                        },
3652                                                        trailing_comments: Vec::new(),
3653                                                        double_colon_syntax: false,
3654                                                        format: None,
3655                                                        default: None,
3656                                                        inferred_type: None,
3657                                                    },
3658                                                ));
3659                                                return Ok(Expression::Function(Box::new(
3660                                                    crate::expressions::Function {
3661                                                        on_error: None,
3662                                                        qualified_name: Vec::new(),
3663                                                        source_dialect: None,
3664
3665                                                        name: f.name.clone(),
3666                                                        args: new_args,
3667                                                        distinct: f.distinct,
3668                                                        trailing_comments: f
3669                                                            .trailing_comments
3670                                                            .clone(),
3671                                                        use_bracket_syntax: f.use_bracket_syntax,
3672                                                        no_parens: f.no_parens,
3673                                                        quoted: f.quoted,
3674                                                        span: None,
3675                                                        inferred_type: None,
3676                                                    },
3677                                                )));
3678                                            }
3679                                        }
3680                                    }
3681                                }
3682                            }
3683                        }
3684                        Ok(e)
3685                    })?
3686                } else {
3687                    normalized
3688                };
3689
3690                // Propagate struct field names in arrays (for BigQuery source to non-BigQuery target)
3691                // BigQuery->BigQuery should NOT propagate names (BigQuery handles implicit inheritance)
3692                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3693                    && !matches!(target, DialectType::BigQuery)
3694                {
3695                    crate::transforms::propagate_struct_field_names(normalized)?
3696                } else {
3697                    normalized
3698                };
3699
3700                // Snowflake source to DuckDB target: RANDOM()/RANDOM(seed) -> scaled RANDOM()
3701                // Snowflake RANDOM() returns integer in [-2^63, 2^63-1], DuckDB RANDOM() returns float [0, 1)
3702                // Skip RANDOM inside UNIFORM/NORMAL/ZIPF/RANDSTR generator args since those
3703                // functions handle their generator args differently (as float seeds).
3704                let normalized = if matches!(self.dialect_type, DialectType::Snowflake)
3705                    && matches!(target, DialectType::DuckDB)
3706                {
3707                    fn make_scaled_random() -> Expression {
3708                        let lower =
3709                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3710                                "-9.223372036854776E+18".to_string(),
3711                            )));
3712                        let upper =
3713                            Expression::Literal(Box::new(crate::expressions::Literal::Number(
3714                                "9.223372036854776e+18".to_string(),
3715                            )));
3716                        let random_call = Expression::Random(crate::expressions::Random);
3717                        let range_size = Expression::Paren(Box::new(crate::expressions::Paren {
3718                            this: Expression::Sub(Box::new(crate::expressions::BinaryOp {
3719                                left: upper,
3720                                right: lower.clone(),
3721                                left_comments: vec![],
3722                                operator_comments: vec![],
3723                                trailing_comments: vec![],
3724                                inferred_type: None,
3725                            })),
3726                            trailing_comments: vec![],
3727                        }));
3728                        let scaled = Expression::Mul(Box::new(crate::expressions::BinaryOp {
3729                            left: random_call,
3730                            right: range_size,
3731                            left_comments: vec![],
3732                            operator_comments: vec![],
3733                            trailing_comments: vec![],
3734                            inferred_type: None,
3735                        }));
3736                        let shifted = Expression::Add(Box::new(crate::expressions::BinaryOp {
3737                            left: lower,
3738                            right: scaled,
3739                            left_comments: vec![],
3740                            operator_comments: vec![],
3741                            trailing_comments: vec![],
3742                            inferred_type: None,
3743                        }));
3744                        Expression::Cast(Box::new(crate::expressions::Cast {
3745                            this: shifted,
3746                            to: crate::expressions::DataType::BigInt { length: None },
3747                            trailing_comments: vec![],
3748                            double_colon_syntax: false,
3749                            format: None,
3750                            default: None,
3751                            inferred_type: None,
3752                        }))
3753                    }
3754
3755                    // Pre-process: protect seeded RANDOM(seed) inside UNIFORM/NORMAL/ZIPF/RANDSTR
3756                    // by converting Rand{seed: Some(s)} to Function{name:"RANDOM", args:[s]}.
3757                    // This prevents transform_recursive (which is bottom-up) from expanding
3758                    // seeded RANDOM into make_scaled_random() and losing the seed value.
3759                    // Unseeded RANDOM()/Rand{seed:None} is left as-is so it gets expanded
3760                    // and then un-expanded back to Expression::Random by the code below.
3761                    let normalized = transform_recursive(normalized, &|e| {
3762                        if let Expression::Function(ref f) = e {
3763                            let n = f.name.to_ascii_uppercase();
3764                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" || n == "RANDSTR" {
3765                                if let Expression::Function(mut f) = e {
3766                                    for arg in f.args.iter_mut() {
3767                                        if let Expression::Rand(ref r) = arg {
3768                                            if r.lower.is_none() && r.upper.is_none() {
3769                                                if let Some(ref seed) = r.seed {
3770                                                    // Convert Rand{seed: Some(s)} to Function("RANDOM", [s])
3771                                                    // so it won't be expanded by the RANDOM expansion below
3772                                                    *arg = Expression::Function(Box::new(
3773                                                        crate::expressions::Function::new(
3774                                                            "RANDOM".to_string(),
3775                                                            vec![*seed.clone()],
3776                                                        ),
3777                                                    ));
3778                                                }
3779                                            }
3780                                        }
3781                                    }
3782                                    return Ok(Expression::Function(f));
3783                                }
3784                            }
3785                        }
3786                        Ok(e)
3787                    })?;
3788
3789                    // transform_recursive processes bottom-up, so RANDOM() (unseeded) inside
3790                    // generator functions (UNIFORM, NORMAL, ZIPF) gets expanded before
3791                    // we see the parent. We detect this and undo the expansion by replacing
3792                    // the expanded pattern back with Expression::Random.
3793                    // Seeded RANDOM(seed) was already protected above as Function("RANDOM", [seed]).
3794                    // Note: RANDSTR is NOT included here — it needs the expanded form for unseeded
3795                    // RANDOM() since the DuckDB handler uses the expanded SQL as-is in the hash.
3796                    transform_recursive(normalized, &|e| {
3797                        if let Expression::Function(ref f) = e {
3798                            let n = f.name.to_ascii_uppercase();
3799                            if n == "UNIFORM" || n == "NORMAL" || n == "ZIPF" {
3800                                if let Expression::Function(mut f) = e {
3801                                    for arg in f.args.iter_mut() {
3802                                        // Detect expanded RANDOM pattern: CAST(-9.22... + RANDOM() * (...) AS BIGINT)
3803                                        if let Expression::Cast(ref cast) = arg {
3804                                            if matches!(
3805                                                cast.to,
3806                                                crate::expressions::DataType::BigInt { .. }
3807                                            ) {
3808                                                if let Expression::Add(ref add) = cast.this {
3809                                                    if let Expression::Literal(ref lit) = add.left {
3810                                                        if let crate::expressions::Literal::Number(
3811                                                            ref num,
3812                                                        ) = lit.as_ref()
3813                                                        {
3814                                                            if num == "-9.223372036854776E+18" {
3815                                                                *arg = Expression::Random(
3816                                                                    crate::expressions::Random,
3817                                                                );
3818                                                            }
3819                                                        }
3820                                                    }
3821                                                }
3822                                            }
3823                                        }
3824                                    }
3825                                    return Ok(Expression::Function(f));
3826                                }
3827                                return Ok(e);
3828                            }
3829                        }
3830                        match e {
3831                            Expression::Random(_) => Ok(make_scaled_random()),
3832                            // Rand(seed) with no bounds: drop seed and expand
3833                            // (DuckDB RANDOM doesn't support seeds)
3834                            Expression::Rand(ref r) if r.lower.is_none() && r.upper.is_none() => {
3835                                Ok(make_scaled_random())
3836                            }
3837                            _ => Ok(e),
3838                        }
3839                    })?
3840                } else {
3841                    normalized
3842                };
3843
3844                // Apply cross-dialect semantic normalizations
3845                let normalized = normalization::normalize(
3846                    normalized,
3847                    self.dialect_type,
3848                    target,
3849                    matches!(
3850                        opts.unsupported_level,
3851                        UnsupportedLevel::Raise | UnsupportedLevel::Immediate
3852                    ),
3853                )?;
3854
3855                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
3856                    Self::normalize_tsql_fetch_overlaps_date_bin(normalized)?
3857                } else {
3858                    normalized
3859                };
3860
3861                let normalized =
3862                    if matches!(
3863                        self.dialect_type,
3864                        DialectType::PostgreSQL | DialectType::CockroachDB
3865                    ) && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
3866                    {
3867                        Self::normalize_postgres_type_function_casts(normalized, target)?
3868                    } else {
3869                        normalized
3870                    };
3871
3872                let normalized = if matches!(self.dialect_type, DialectType::SQLite)
3873                    && !matches!(target, DialectType::SQLite)
3874                {
3875                    Self::normalize_sqlite_double_quoted_defaults(normalized)?
3876                } else {
3877                    normalized
3878                };
3879
3880                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3881                    && matches!(target, DialectType::SQLite)
3882                {
3883                    Self::normalize_postgres_to_sqlite_types(normalized)?
3884                } else {
3885                    normalized
3886                };
3887
3888                let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
3889                    && matches!(target, DialectType::Fabric)
3890                {
3891                    Self::normalize_postgres_to_fabric_types(normalized)?
3892                } else {
3893                    normalized
3894                };
3895
3896                // For DuckDB target from BigQuery source: wrap UNNEST of struct arrays in
3897                // (SELECT UNNEST(..., max_depth => 2)) subquery
3898                // Must run BEFORE unnest_alias_to_column_alias since it changes alias structure
3899                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3900                    && matches!(target, DialectType::DuckDB)
3901                {
3902                    crate::transforms::wrap_duckdb_unnest_struct(normalized)?
3903                } else {
3904                    normalized
3905                };
3906
3907                // Convert BigQuery UNNEST aliases to column-alias format for DuckDB/Presto/Spark
3908                // UNNEST(arr) AS x -> UNNEST(arr) AS _t0(x)
3909                let normalized = if matches!(self.dialect_type, DialectType::BigQuery)
3910                    && matches!(
3911                        target,
3912                        DialectType::DuckDB
3913                            | DialectType::Presto
3914                            | DialectType::Trino
3915                            | DialectType::Athena
3916                            | DialectType::Spark
3917                            | DialectType::Databricks
3918                    ) {
3919                    crate::transforms::unnest_alias_to_column_alias(normalized)?
3920                } else if matches!(self.dialect_type, DialectType::BigQuery)
3921                    && matches!(target, DialectType::BigQuery | DialectType::Redshift)
3922                {
3923                    // For BigQuery/Redshift targets: move UNNEST FROM items to CROSS JOINs
3924                    // but don't convert alias format (no _t0 wrapper)
3925                    let result = crate::transforms::unnest_from_to_cross_join(normalized)?;
3926                    // For Redshift: strip UNNEST when arg is a column reference path
3927                    if matches!(target, DialectType::Redshift) {
3928                        crate::transforms::strip_unnest_column_refs(result)?
3929                    } else {
3930                        result
3931                    }
3932                } else {
3933                    normalized
3934                };
3935
3936                // For Presto/Trino targets from PostgreSQL/Redshift source:
3937                // Wrap UNNEST aliases from GENERATE_SERIES conversion: AS s -> AS _u(s)
3938                let normalized = if matches!(
3939                    self.dialect_type,
3940                    DialectType::PostgreSQL | DialectType::Redshift
3941                ) && matches!(
3942                    target,
3943                    DialectType::Presto | DialectType::Trino | DialectType::Athena
3944                ) {
3945                    crate::transforms::wrap_unnest_join_aliases(normalized)?
3946                } else {
3947                    normalized
3948                };
3949
3950                // Eliminate DISTINCT ON with target-dialect awareness
3951                // This must happen after source transform (which may produce DISTINCT ON)
3952                // and before target transform, with knowledge of the target dialect's NULL ordering behavior
3953                let normalized = crate::transforms::eliminate_distinct_on_for_dialect(
3954                    normalized,
3955                    Some(target),
3956                    Some(self.dialect_type),
3957                )?;
3958
3959                // GENERATE_DATE_ARRAY in UNNEST -> Snowflake ARRAY_GENERATE_RANGE + DATEADD
3960                let normalized = if matches!(target, DialectType::Snowflake) {
3961                    Self::transform_generate_date_array_snowflake(normalized)?
3962                } else {
3963                    normalized
3964                };
3965
3966                // CROSS JOIN UNNEST -> LATERAL VIEW EXPLODE/INLINE for Spark/Hive/Databricks
3967                let normalized = if matches!(
3968                    target,
3969                    DialectType::Spark | DialectType::Databricks | DialectType::Hive
3970                ) {
3971                    crate::transforms::unnest_to_explode_select(normalized)?
3972                } else {
3973                    normalized
3974                };
3975
3976                // Wrap UNION with ORDER BY/LIMIT in a subquery for dialects that require it
3977                let normalized = if matches!(target, DialectType::ClickHouse | DialectType::TSQL) {
3978                    crate::transforms::no_limit_order_by_union(normalized)?
3979                } else {
3980                    normalized
3981                };
3982
3983                let normalized = if matches!(
3984                    self.dialect_type,
3985                    DialectType::PostgreSQL | DialectType::CockroachDB
3986                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
3987                {
3988                    Self::normalize_postgres_boolean_semantics_for_tsql(normalized)?
3989                } else {
3990                    normalized
3991                };
3992
3993                let normalized = if self.dialect_type == DialectType::PostgreSQL
3994                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
3995                {
3996                    Self::normalize_postgres_bytea_literals_for_tsql(normalized)?
3997                } else {
3998                    normalized
3999                };
4000
4001                let normalized = if matches!(
4002                    self.dialect_type,
4003                    DialectType::PostgreSQL | DialectType::CockroachDB
4004                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4005                {
4006                    Self::normalize_postgres_string_semantics_for_tsql(normalized)?
4007                } else {
4008                    normalized
4009                };
4010
4011                // TSQL: Convert COUNT(*) -> COUNT_BIG(*) when source is not TSQL/Fabric
4012                // Python sqlglot does this in the TSQL generator, but we can't do it there
4013                // because it would break TSQL -> TSQL identity
4014                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
4015                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
4016                {
4017                    transform_recursive(normalized, &|e| {
4018                        if let Expression::Count(ref c) = e {
4019                            // Build COUNT_BIG(...) as an AggregateFunction
4020                            let args = if c.star {
4021                                vec![Expression::Star(crate::expressions::Star {
4022                                    table: None,
4023                                    except: None,
4024                                    replace: None,
4025                                    rename: None,
4026                                    trailing_comments: Vec::new(),
4027                                    span: None,
4028                                })]
4029                            } else if let Some(ref this) = c.this {
4030                                vec![this.clone()]
4031                            } else {
4032                                vec![]
4033                            };
4034                            Ok(Expression::AggregateFunction(Box::new(
4035                                crate::expressions::AggregateFunction {
4036                                    source_dialect: None,
4037
4038                                    name: "COUNT_BIG".to_string(),
4039                                    args,
4040                                    distinct: c.distinct,
4041                                    filter: c.filter.clone(),
4042                                    order_by: Vec::new(),
4043                                    limit: None,
4044                                    ignore_nulls: None,
4045                                    inferred_type: None,
4046                                },
4047                            )))
4048                        } else {
4049                            Ok(e)
4050                        }
4051                    })?
4052                } else {
4053                    normalized
4054                };
4055
4056                // T-SQL/Fabric do not have a scalar boolean type. Keep predicate
4057                // contexts intact, but materialize boolean-valued expressions used
4058                // as values before target transforms add ORDER BY null sort keys.
4059                let normalized = if matches!(target, DialectType::TSQL | DialectType::Fabric)
4060                    && !matches!(self.dialect_type, DialectType::TSQL | DialectType::Fabric)
4061                {
4062                    let normalized = if self.dialect_type == DialectType::PostgreSQL {
4063                        Self::rewrite_postgres_row_value_equality_for_tsql(normalized)?
4064                    } else {
4065                        normalized
4066                    };
4067                    Self::rewrite_boolean_values_for_tsql(normalized)?
4068                } else {
4069                    normalized
4070                };
4071
4072                let normalized = if matches!(
4073                    self.dialect_type,
4074                    DialectType::PostgreSQL | DialectType::CockroachDB
4075                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4076                {
4077                    Self::rewrite_postgres_format_for_tsql(normalized, target)?
4078                } else {
4079                    normalized
4080                };
4081
4082                let normalized = if self.dialect_type == DialectType::PostgreSQL
4083                    && matches!(target, DialectType::TSQL | DialectType::Fabric)
4084                {
4085                    Self::normalize_postgres_only_for_tsql(normalized)?
4086                } else {
4087                    normalized
4088                };
4089
4090                let transformed =
4091                    target_dialect.transform_with_guard(normalized, opts.complexity_guard)?;
4092
4093                // T-SQL and Fabric do not support aggregate FILTER clauses. Rewrite any
4094                // remaining filters after target transforms so special aggregate rewrites
4095                // (for example BOOL_OR/BOOL_AND) can consume their filters first.
4096                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4097                    Self::rewrite_aggregate_filters_for_tsql(transformed)?
4098                } else {
4099                    transformed
4100                };
4101
4102                let transformed = if matches!(
4103                    self.dialect_type,
4104                    DialectType::PostgreSQL | DialectType::CockroachDB
4105                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4106                {
4107                    crate::transforms::grouped_percentiles_to_tsql_windows(transformed)?
4108                } else {
4109                    transformed
4110                };
4111
4112                let transformed = if matches!(
4113                    self.dialect_type,
4114                    DialectType::PostgreSQL | DialectType::CockroachDB
4115                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4116                {
4117                    Self::normalize_postgres_trim_for_tsql(transformed)?
4118                } else {
4119                    transformed
4120                };
4121
4122                let transformed = if matches!(
4123                    self.dialect_type,
4124                    DialectType::PostgreSQL | DialectType::CockroachDB
4125                ) && matches!(target, DialectType::TSQL | DialectType::Fabric)
4126                {
4127                    Self::rewrite_postgres_json_array_elements_select_for_tsql(transformed)?
4128                } else {
4129                    transformed
4130                };
4131
4132                // DuckDB target: when FROM is RANGE(n), replace SEQ's ROW_NUMBER pattern with `range`
4133                let transformed = if matches!(target, DialectType::DuckDB) {
4134                    Self::seq_rownum_to_range(transformed)?
4135                } else {
4136                    transformed
4137                };
4138
4139                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4140                    Self::reject_tsql_interval_casts(&transformed, target, opts)?;
4141                }
4142
4143                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4144                    Self::rewrite_tsql_interval_casts_to_varchar(transformed)?
4145                } else {
4146                    transformed
4147                };
4148
4149                let transformed = if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4150                    Self::legalize_tsql_nested_order_by(transformed)?
4151                } else {
4152                    transformed
4153                };
4154
4155                Self::reject_strict_unsupported(&transformed, self.dialect_type, target, opts)?;
4156
4157                let mut sql = target_dialect.generate_with_transpile_options(
4158                    &transformed,
4159                    self.dialect_type,
4160                    opts,
4161                )?;
4162
4163                // Align a known Snowflake pretty-print edge case with Python sqlglot output.
4164                if opts.pretty && target == DialectType::Snowflake {
4165                    sql = Self::normalize_snowflake_pretty(sql);
4166                }
4167
4168                Ok(sql)
4169            })
4170            .collect()
4171    }
4172}
4173
4174// Transpile-only methods: cross-dialect normalization and helpers
4175#[cfg(feature = "transpile")]
4176impl Dialect {
4177    fn legalize_tsql_nested_order_by(expr: Expression) -> Result<Expression> {
4178        let preserve_root_order = matches!(&expr, Expression::Select(select) if Self::tsql_select_needs_order_offset(select));
4179
4180        let mut transformed = transform_recursive(expr, &|node| match node {
4181            Expression::Select(mut select) => {
4182                Self::legalize_tsql_select_offset(&mut select);
4183                if Self::tsql_select_needs_order_offset(&select) {
4184                    select.offset = Some(Offset {
4185                        this: Expression::Literal(Box::new(Literal::Number("0".to_string()))),
4186                        rows: Some(true),
4187                    });
4188                }
4189                Ok(Expression::Select(select))
4190            }
4191            Expression::Subquery(mut subquery) => {
4192                Self::legalize_tsql_offset(&mut subquery.order_by, &mut subquery.offset, false);
4193                Ok(Expression::Subquery(subquery))
4194            }
4195            Expression::Union(mut union) => {
4196                Self::legalize_tsql_set_offset(&mut union.order_by, &mut union.offset);
4197                Ok(Expression::Union(union))
4198            }
4199            Expression::Intersect(mut intersect) => {
4200                Self::legalize_tsql_set_offset(&mut intersect.order_by, &mut intersect.offset);
4201                Ok(Expression::Intersect(intersect))
4202            }
4203            Expression::Except(mut except) => {
4204                Self::legalize_tsql_set_offset(&mut except.order_by, &mut except.offset);
4205                Ok(Expression::Except(except))
4206            }
4207            other => Ok(other),
4208        })?;
4209
4210        if preserve_root_order {
4211            if let Expression::Select(select) = &mut transformed {
4212                select.offset = None;
4213            }
4214        }
4215
4216        Self::drop_tsql_unbounded_nested_set_order_by(transformed)
4217    }
4218
4219    fn drop_tsql_unbounded_nested_set_order_by(mut expr: Expression) -> Result<Expression> {
4220        let root_order_by = Self::take_tsql_root_set_order_by(&mut expr);
4221
4222        let mut transformed = transform_recursive(expr, &|node| match node {
4223            Expression::Union(mut union) => {
4224                if union.limit.is_none() && union.offset.is_none() {
4225                    union.order_by = None;
4226                }
4227                Ok(Expression::Union(union))
4228            }
4229            Expression::Intersect(mut intersect) => {
4230                if intersect.limit.is_none() && intersect.offset.is_none() {
4231                    intersect.order_by = None;
4232                }
4233                Ok(Expression::Intersect(intersect))
4234            }
4235            Expression::Except(mut except) => {
4236                if except.limit.is_none() && except.offset.is_none() {
4237                    except.order_by = None;
4238                }
4239                Ok(Expression::Except(except))
4240            }
4241            other => Ok(other),
4242        })?;
4243
4244        if let Some(order_by) = root_order_by {
4245            Self::restore_tsql_root_set_order_by(&mut transformed, order_by);
4246        }
4247
4248        Ok(transformed)
4249    }
4250
4251    fn take_tsql_root_set_order_by(expr: &mut Expression) -> Option<OrderBy> {
4252        match expr {
4253            Expression::Union(union) => union.order_by.take(),
4254            Expression::Intersect(intersect) => intersect.order_by.take(),
4255            Expression::Except(except) => except.order_by.take(),
4256            Expression::Subquery(subquery) if subquery.alias.is_none() => {
4257                Self::take_tsql_root_set_order_by(&mut subquery.this)
4258            }
4259            Expression::Paren(paren) => Self::take_tsql_root_set_order_by(&mut paren.this),
4260            _ => None,
4261        }
4262    }
4263
4264    fn restore_tsql_root_set_order_by(expr: &mut Expression, order_by: OrderBy) {
4265        match expr {
4266            Expression::Union(union) => union.order_by = Some(order_by),
4267            Expression::Intersect(intersect) => intersect.order_by = Some(order_by),
4268            Expression::Except(except) => except.order_by = Some(order_by),
4269            Expression::Subquery(subquery) if subquery.alias.is_none() => {
4270                Self::restore_tsql_root_set_order_by(&mut subquery.this, order_by);
4271            }
4272            Expression::Paren(paren) => {
4273                Self::restore_tsql_root_set_order_by(&mut paren.this, order_by);
4274            }
4275            _ => {}
4276        }
4277    }
4278
4279    fn legalize_tsql_select_offset(select: &mut crate::expressions::Select) {
4280        let has_fetch = select.fetch.is_some();
4281        Self::legalize_tsql_offset(&mut select.order_by, &mut select.offset, has_fetch);
4282    }
4283
4284    fn legalize_tsql_offset(
4285        order_by: &mut Option<OrderBy>,
4286        offset: &mut Option<Offset>,
4287        retain_inert_offset: bool,
4288    ) {
4289        if order_by.is_some() {
4290            return;
4291        }
4292
4293        if offset
4294            .as_ref()
4295            .is_some_and(|offset| Self::tsql_offset_is_inert(&offset.this))
4296            && !retain_inert_offset
4297        {
4298            *offset = None;
4299        } else if offset.is_some() {
4300            *order_by = Some(Generator::dummy_tsql_order_by());
4301        }
4302    }
4303
4304    fn legalize_tsql_set_offset(
4305        order_by: &mut Option<OrderBy>,
4306        offset: &mut Option<Box<Expression>>,
4307    ) {
4308        if order_by.is_some() {
4309            return;
4310        }
4311
4312        if offset.as_deref().is_some_and(Self::tsql_offset_is_inert) {
4313            *offset = None;
4314        } else if offset.is_some() {
4315            *order_by = Some(Generator::dummy_tsql_order_by());
4316        }
4317    }
4318
4319    fn tsql_offset_is_inert(expr: &Expression) -> bool {
4320        match expr {
4321            Expression::Null(_) => true,
4322            Expression::Literal(literal) => match literal.as_ref() {
4323                Literal::Number(value) => value.parse::<i128>().is_ok_and(|value| value == 0),
4324                _ => false,
4325            },
4326            _ => false,
4327        }
4328    }
4329
4330    fn tsql_select_needs_order_offset(select: &crate::expressions::Select) -> bool {
4331        select.order_by.is_some()
4332            && select.top.is_none()
4333            && select.limit.is_none()
4334            && select.offset.is_none()
4335            && select.fetch.is_none()
4336            && select.for_xml.is_empty()
4337            && select.for_json.is_empty()
4338    }
4339
4340    fn reject_hana_source_semantics(
4341        expression: &Expression,
4342        source: DialectType,
4343        target: DialectType,
4344    ) -> Result<()> {
4345        if source != DialectType::HANA || target == DialectType::HANA {
4346            return Ok(());
4347        }
4348        let mut pending = vec![expression];
4349        while let Some(node) = pending.pop() {
4350            if node.source_dialect() == Some(DialectType::HANA) {
4351                Generator::validate_hana_source_node(node, target)?;
4352            }
4353            // A supported outer call does not establish support for its arguments.
4354            // Types in column definitions, casts, routine signatures, etc. are
4355            // embedded syntax, not Expression children. Validate each type once,
4356            // before normalization can replace its enclosing ARRAY/STRUCT type.
4357            // A scalar native CAST already uses a value-aware conversion check
4358            // (e.g. a HANA TINYINT literal can fit a target signed integer).
4359            let cast_type_validated = matches!(node, Expression::Cast(c)
4360                if matches!(c.to, crate::expressions::DataType::Hana { .. }));
4361            let mut type_result = Ok(());
4362            crate::ast_children::for_each_child_and_type_untracked(
4363                node,
4364                |child| pending.push(child),
4365                |data_type| {
4366                    if !cast_type_validated && type_result.is_ok() {
4367                        type_result = Generator::validate_hana_source_type(data_type, target);
4368                    }
4369                },
4370            );
4371            type_result?;
4372        }
4373        Ok(())
4374    }
4375
4376    fn reject_clickhouse_session_semantics(
4377        expr: &Expression,
4378        source: DialectType,
4379        target: DialectType,
4380        opts: &TranspileOptions,
4381    ) -> Result<()> {
4382        if !matches!(
4383            opts.unsupported_level,
4384            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4385        ) || target != DialectType::ClickHouse
4386            || source == DialectType::ClickHouse
4387        {
4388            return Ok(());
4389        }
4390
4391        const JOIN_USE_NULLS_DIAGNOSTIC: &str = "ClickHouse outer joins require the target setting join_use_nulls = 1 to preserve unmatched-row NULL semantics";
4392        const AGGREGATE_NULL_FOR_EMPTY_DIAGNOSTIC: &str = "ClickHouse non-count aggregates require the target setting aggregate_functions_null_for_empty = 1 to preserve empty-input NULL semantics";
4393
4394        fn is_current_session_time(node: &Expression) -> bool {
4395            match node {
4396                Expression::CurrentDate(_)
4397                | Expression::CurrentTime(_)
4398                | Expression::CurrentTimestamp(_)
4399                | Expression::CurrentTimestampLTZ(_)
4400                | Expression::Localtime(_)
4401                | Expression::Localtimestamp(_)
4402                | Expression::Systimestamp(_) => true,
4403                Expression::Function(function) if !function.quoted => matches!(
4404                    function.name.to_ascii_uppercase().as_str(),
4405                    "CURRENT_DATE"
4406                        | "CURRENT_TIME"
4407                        | "LOCALTIME"
4408                        | "CURRENT_TIMESTAMP"
4409                        | "CURRENT_TIMESTAMP_LTZ"
4410                        | "LOCALTIMESTAMP"
4411                        | "NOW"
4412                        | "GETDATE"
4413                        | "SYSTIMESTAMP"
4414                ),
4415                _ => false,
4416            }
4417        }
4418
4419        fn is_session_week_part(expression: &Expression) -> bool {
4420            let part = match expression {
4421                Expression::Literal(literal) => match literal.as_ref() {
4422                    Literal::String(value) => Some(value.as_str()),
4423                    _ => None,
4424                },
4425                Expression::Identifier(identifier) => Some(identifier.name.as_str()),
4426                Expression::Var(var) => Some(var.this.as_str()),
4427                Expression::Column(column) if column.table.is_none() => {
4428                    Some(column.name.name.as_str())
4429                }
4430                _ => None,
4431            };
4432            part.is_some_and(|part| {
4433                matches!(
4434                    part.to_ascii_uppercase().as_str(),
4435                    "WEEK" | "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY"
4436                )
4437            })
4438        }
4439
4440        fn is_session_week_trunc(node: &Expression) -> bool {
4441            match node {
4442                Expression::DateTrunc(date_trunc) => {
4443                    matches!(date_trunc.unit, DateTimeField::Week)
4444                }
4445                Expression::Function(function)
4446                    if !function.quoted && function.name.eq_ignore_ascii_case("DATE_TRUNC") =>
4447                {
4448                    function.args.first().is_some_and(is_session_week_part)
4449                }
4450                _ => false,
4451            }
4452        }
4453
4454        fn is_null_extending_clickhouse_join(node: &Expression) -> bool {
4455            fn join_is_null_extending(join: &Join) -> bool {
4456                matches!(
4457                    join.kind,
4458                    JoinKind::Left
4459                        | JoinKind::Right
4460                        | JoinKind::Full
4461                        | JoinKind::NaturalLeft
4462                        | JoinKind::NaturalRight
4463                        | JoinKind::NaturalFull
4464                        | JoinKind::AsOfLeft
4465                        | JoinKind::AsOfRight
4466                )
4467            }
4468
4469            fn joins_are_null_extending(joins: &[Join]) -> bool {
4470                joins.iter().any(join_is_null_extending)
4471            }
4472
4473            match node {
4474                Expression::Join(join) => join_is_null_extending(join),
4475                Expression::Select(select) => joins_are_null_extending(&select.joins),
4476                Expression::JoinedTable(joined) => joins_are_null_extending(&joined.joins),
4477                Expression::Update(update) => {
4478                    joins_are_null_extending(&update.table_joins)
4479                        || joins_are_null_extending(&update.from_joins)
4480                }
4481                Expression::Delete(delete) => joins_are_null_extending(&delete.joins),
4482                _ => false,
4483            }
4484        }
4485
4486        fn is_count_like_aggregate(node: &Expression) -> bool {
4487            match node {
4488                Expression::Count(_)
4489                | Expression::CountIf(_)
4490                | Expression::ApproxDistinct(_)
4491                | Expression::ApproxCountDistinct(_) => true,
4492                Expression::AggregateFunction(function) => matches!(
4493                    function.name.to_ascii_uppercase().as_str(),
4494                    "COUNT" | "COUNT_IF" | "COUNTIF" | "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT"
4495                ),
4496                _ => false,
4497            }
4498        }
4499
4500        fn is_empty_input_sensitive_aggregate(node: &Expression, source: DialectType) -> bool {
4501            Dialect::node_is_aggregate_function(node)
4502                && !is_count_like_aggregate(node)
4503                && !matches!(node, Expression::BitwiseCount(_))
4504                && !matches!(node, Expression::Median(_) if source == DialectType::Snowflake)
4505                && !matches!(
4506                    node,
4507                    Expression::AggregateFunction(function)
4508                        if function.name.to_ascii_lowercase().ends_with("ornull")
4509                )
4510        }
4511
4512        fn setting_is_enabled(setting: &Expression, name: &str) -> bool {
4513            let Expression::Eq(equality) = setting else {
4514                return false;
4515            };
4516            let setting_name = match &equality.left {
4517                Expression::Identifier(identifier) => identifier.name.as_str(),
4518                Expression::Column(column) if column.table.is_none() => column.name.name.as_str(),
4519                Expression::Var(var) => var.this.as_str(),
4520                _ => return false,
4521            };
4522            let enabled = match &equality.right {
4523                Expression::Literal(literal) => {
4524                    matches!(literal.as_ref(), Literal::Number(value) if value.parse::<i128>().is_ok_and(|value| value == 1))
4525                }
4526                Expression::Boolean(boolean) => boolean.value,
4527                _ => false,
4528            };
4529            setting_name.eq_ignore_ascii_case(name) && enabled
4530        }
4531
4532        fn query_has_enabled_setting(expr: &Expression, name: &str) -> bool {
4533            expr.dfs().any(|node| {
4534                matches!(
4535                    node,
4536                    Expression::Select(select)
4537                        if select.settings.as_ref().is_some_and(|settings| {
4538                            settings.iter().any(|setting| setting_is_enabled(setting, name))
4539                        })
4540                )
4541            })
4542        }
4543
4544        let mut diagnostics = Vec::new();
4545        let join_use_nulls_enabled = query_has_enabled_setting(expr, "join_use_nulls");
4546        let aggregate_null_for_empty_enabled =
4547            query_has_enabled_setting(expr, "aggregate_functions_null_for_empty");
4548        for node in expr.dfs() {
4549            if source == DialectType::Snowflake && is_current_session_time(node) {
4550                Self::push_unsupported_diagnostic(
4551                    &mut diagnostics,
4552                    "Snowflake current date/time expressions depend on the session TIMEZONE, which cannot be preserved for ClickHouse",
4553                );
4554            }
4555            if source == DialectType::Snowflake && is_session_week_trunc(node) {
4556                Self::push_unsupported_diagnostic(
4557                    &mut diagnostics,
4558                    "Snowflake DATE_TRUNC with a week unit depends on the session WEEK_START, which cannot be preserved for ClickHouse",
4559                );
4560            }
4561            if !join_use_nulls_enabled && is_null_extending_clickhouse_join(node) {
4562                Self::push_unsupported_diagnostic(&mut diagnostics, JOIN_USE_NULLS_DIAGNOSTIC);
4563            }
4564            if !aggregate_null_for_empty_enabled && is_empty_input_sensitive_aggregate(node, source)
4565            {
4566                Self::push_unsupported_diagnostic(
4567                    &mut diagnostics,
4568                    AGGREGATE_NULL_FOR_EMPTY_DIAGNOSTIC,
4569                );
4570            }
4571            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4572                break;
4573            }
4574        }
4575
4576        if diagnostics.is_empty() {
4577            return Ok(());
4578        }
4579
4580        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4581            1
4582        } else {
4583            opts.max_unsupported.max(1)
4584        };
4585        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4586        if diagnostics.len() > limit {
4587            messages.push(format!("... and {} more", diagnostics.len() - limit));
4588        }
4589
4590        Err(crate::error::Error::unsupported(
4591            messages.join("; "),
4592            target.to_string(),
4593        ))
4594    }
4595
4596    fn reject_strict_unsupported(
4597        expr: &Expression,
4598        source: DialectType,
4599        target: DialectType,
4600        opts: &TranspileOptions,
4601    ) -> Result<()> {
4602        if !matches!(
4603            opts.unsupported_level,
4604            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4605        ) {
4606            return Ok(());
4607        }
4608
4609        let mut diagnostics = Vec::new();
4610        if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4611            && matches!(target, DialectType::TSQL | DialectType::Fabric)
4612            && Self::tsql_apply_has_invalid_outer_aggregate(expr)
4613        {
4614            Self::push_unsupported_diagnostic(
4615                &mut diagnostics,
4616                "APPLY aggregate expressions that combine an outer reference with another column reference",
4617            );
4618        }
4619        let structural_grouping_tuples =
4620            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4621                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4622            {
4623                Self::collect_tsql_grouping_tuple_nodes(expr)
4624            } else {
4625                HashSet::new()
4626            };
4627
4628        for node in expr.dfs() {
4629            if source == DialectType::Snowflake && target == DialectType::ClickHouse {
4630                if Self::node_is_function_named(node, "TO_CHAR") {
4631                    Self::push_unsupported_diagnostic(
4632                        &mut diagnostics,
4633                        "Snowflake TO_CHAR overload or dynamic format",
4634                    );
4635                }
4636                if Self::node_is_function_named(node, "TRY_TO_DOUBLE")
4637                    || matches!(node, Expression::ToDouble(to_double) if to_double.safe.is_some() && to_double.format.is_some())
4638                {
4639                    Self::push_unsupported_diagnostic(
4640                        &mut diagnostics,
4641                        "Snowflake TRY_TO_DOUBLE with a format model",
4642                    );
4643                }
4644                if Self::node_is_function_named(node, "TRY_TO_NUMBER")
4645                    || Self::node_is_function_named(node, "TRY_TO_NUMERIC")
4646                    || Self::node_is_function_named(node, "TRY_TO_DECIMAL")
4647                    || matches!(node, Expression::ToNumber(to_number) if to_number.safe.is_some())
4648                {
4649                    Self::push_unsupported_diagnostic(
4650                        &mut diagnostics,
4651                        "Snowflake TRY_TO_NUMBER decimal conversion semantics",
4652                    );
4653                }
4654                if Self::node_is_function_named(node, "FLATTEN") {
4655                    Self::push_unsupported_diagnostic(
4656                        &mut diagnostics,
4657                        "Snowflake FLATTEN table-function semantics",
4658                    );
4659                }
4660            }
4661
4662            if matches!(target, DialectType::Fabric | DialectType::Hive)
4663                && Self::node_has_recursive_with(node)
4664            {
4665                Self::push_unsupported_diagnostic(&mut diagnostics, "recursive CTEs");
4666            }
4667
4668            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4669                && Self::node_has_lateral(node)
4670            {
4671                Self::push_unsupported_diagnostic(&mut diagnostics, "LATERAL joins and subqueries");
4672            }
4673
4674            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4675                if Self::node_has_join_using(node) {
4676                    Self::push_unsupported_diagnostic(&mut diagnostics, "JOIN USING clauses");
4677                }
4678                if Self::node_has_natural_join(node) {
4679                    Self::push_unsupported_diagnostic(&mut diagnostics, "NATURAL JOIN");
4680                }
4681                if Self::node_has_unsupported_relation_column_aliases(node) {
4682                    Self::push_unsupported_diagnostic(
4683                        &mut diagnostics,
4684                        "column alias lists on base or joined table references",
4685                    );
4686                }
4687                if Self::node_has_qualified_whole_row_aggregate_argument(node) {
4688                    Self::push_unsupported_diagnostic(
4689                        &mut diagnostics,
4690                        "qualified whole-row aggregate arguments",
4691                    );
4692                }
4693                if Self::node_has_subquery_in_aggregate_argument(node) {
4694                    Self::push_unsupported_diagnostic(
4695                        &mut diagnostics,
4696                        "aggregate arguments containing subqueries",
4697                    );
4698                }
4699            }
4700
4701            if !Self::target_supports_distinct_on(target) && Self::node_has_distinct_on(node) {
4702                Self::push_unsupported_diagnostic(&mut diagnostics, "DISTINCT ON");
4703            }
4704
4705            if !Self::target_supports_remaining_unnest(target) && Self::node_is_unnest(node) {
4706                Self::push_unsupported_diagnostic(&mut diagnostics, "UNNEST");
4707            }
4708
4709            if !Self::target_supports_remaining_explode(target)
4710                && Self::node_is_explode(node)
4711                && !(source == DialectType::Vertica && target == DialectType::Vertica)
4712            {
4713                Self::push_unsupported_diagnostic(&mut diagnostics, "EXPLODE");
4714            }
4715
4716            if Self::target_lacks_array_agg(target) && Self::node_is_array_agg(node) {
4717                Self::push_unsupported_diagnostic(&mut diagnostics, "ARRAY_AGG");
4718            }
4719
4720            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4721                && Self::node_is_distinct_string_agg(node)
4722            {
4723                Self::push_unsupported_diagnostic(&mut diagnostics, "STRING_AGG with DISTINCT");
4724            }
4725
4726            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4727                && matches!(node, Expression::NthValue(_))
4728            {
4729                Self::push_unsupported_diagnostic(&mut diagnostics, "NTH_VALUE");
4730            }
4731
4732            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4733                if let Some(frame) = Self::node_window_frame(node) {
4734                    if matches!(frame.kind, WindowFrameKind::Groups) {
4735                        Self::push_unsupported_diagnostic(&mut diagnostics, "GROUPS window frames");
4736                    }
4737                    if matches!(frame.kind, WindowFrameKind::Range)
4738                        && (Self::window_frame_bound_has_value_offset(&frame.start)
4739                            || frame
4740                                .end
4741                                .as_ref()
4742                                .is_some_and(Self::window_frame_bound_has_value_offset))
4743                    {
4744                        Self::push_unsupported_diagnostic(
4745                            &mut diagnostics,
4746                            "value-offset RANGE window frames",
4747                        );
4748                    }
4749                    if frame.exclude.is_some() {
4750                        Self::push_unsupported_diagnostic(
4751                            &mut diagnostics,
4752                            "window frame EXCLUDE clauses",
4753                        );
4754                    }
4755                }
4756            }
4757
4758            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4759                && Self::node_is_regex_predicate(node)
4760            {
4761                Self::push_unsupported_diagnostic(
4762                    &mut diagnostics,
4763                    "regular expression predicates",
4764                );
4765            }
4766
4767            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4768                && Self::node_is_non_subquery_any(node)
4769            {
4770                Self::push_unsupported_diagnostic(
4771                    &mut diagnostics,
4772                    "ANY over non-subquery expressions",
4773                );
4774            }
4775
4776            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4777                && Self::node_is_row_value_subquery_comparison(node)
4778            {
4779                Self::push_unsupported_diagnostic(
4780                    &mut diagnostics,
4781                    "row-value subquery comparisons",
4782                );
4783            }
4784
4785            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4786                && Self::node_is_row_value_values_membership(node)
4787            {
4788                Self::push_unsupported_diagnostic(
4789                    &mut diagnostics,
4790                    "row-value VALUES membership comparisons",
4791                );
4792            }
4793
4794            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4795                && Self::node_has_fetch_with_ties(node)
4796            {
4797                Self::push_unsupported_diagnostic(&mut diagnostics, "FETCH WITH TIES without TOP");
4798            }
4799
4800            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4801                && Self::node_is_overlaps(node)
4802            {
4803                Self::push_unsupported_diagnostic(&mut diagnostics, "OVERLAPS");
4804            }
4805
4806            if matches!(target, DialectType::TSQL | DialectType::Fabric)
4807                && Self::node_is_date_bin(node)
4808            {
4809                Self::push_unsupported_diagnostic(&mut diagnostics, "DATE_BIN");
4810            }
4811
4812            if source == DialectType::PostgreSQL
4813                && matches!(target, DialectType::TSQL | DialectType::Fabric)
4814                && Self::node_is_unresolved_postgres_date_subtraction(node)
4815            {
4816                Self::push_unsupported_diagnostic(
4817                    &mut diagnostics,
4818                    "PostgreSQL date subtraction with an unresolved column type",
4819                );
4820            }
4821
4822            if matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4823                && !matches!(target, DialectType::PostgreSQL | DialectType::CockroachDB)
4824            {
4825                if Self::node_is_postgres_json_build_object(node)
4826                    && !(matches!(target, DialectType::TSQL | DialectType::Fabric)
4827                        && Self::postgres_json_build_object_can_lower_to_json_object(node))
4828                {
4829                    Self::push_unsupported_diagnostic(
4830                        &mut diagnostics,
4831                        "PostgreSQL JSON_BUILD_OBJECT",
4832                    );
4833                }
4834                if Self::node_is_function_named(node, "TO_TSVECTOR") {
4835                    Self::push_unsupported_diagnostic(&mut diagnostics, "PostgreSQL TO_TSVECTOR");
4836                }
4837                if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4838                    if let Some(composite_semantics) =
4839                        Self::postgres_tsql_unsupported_composite_semantics(
4840                            node,
4841                            structural_grouping_tuples.contains(&(node as *const Expression)),
4842                        )
4843                    {
4844                        Self::push_unsupported_diagnostic(
4845                            &mut diagnostics,
4846                            &format!("PostgreSQL {composite_semantics}"),
4847                        );
4848                    }
4849                    if Self::node_is_postgres_unknown_cast(node) {
4850                        Self::push_unsupported_diagnostic(
4851                            &mut diagnostics,
4852                            "PostgreSQL unresolved UNKNOWN casts",
4853                        );
4854                    }
4855                    if let Some(collation_name) =
4856                        Self::postgres_tsql_unsupported_collation_name(node)
4857                    {
4858                        Self::push_unsupported_diagnostic(
4859                            &mut diagnostics,
4860                            &format!("PostgreSQL collation \"{collation_name}\""),
4861                        );
4862                    }
4863                    if let Some(array_semantics) =
4864                        Self::postgres_tsql_unsupported_array_semantics(node)
4865                    {
4866                        Self::push_unsupported_diagnostic(
4867                            &mut diagnostics,
4868                            &format!("PostgreSQL {array_semantics}"),
4869                        );
4870                    }
4871                    if let Some(string_semantics) =
4872                        Self::postgres_tsql_unsupported_string_semantics(node)
4873                    {
4874                        Self::push_unsupported_diagnostic(
4875                            &mut diagnostics,
4876                            &format!("PostgreSQL {string_semantics}"),
4877                        );
4878                    }
4879                    if source == DialectType::PostgreSQL {
4880                        if let Some(binary_semantics) =
4881                            Self::postgres_tsql_unsupported_binary_semantics(node)
4882                        {
4883                            Self::push_unsupported_diagnostic(
4884                                &mut diagnostics,
4885                                &format!("PostgreSQL {binary_semantics}"),
4886                            );
4887                        }
4888                    }
4889                    if let Some(function_name) =
4890                        Self::postgres_tsql_unsupported_function_name(node, target)
4891                    {
4892                        Self::push_unsupported_diagnostic(
4893                            &mut diagnostics,
4894                            &format!("PostgreSQL {function_name}"),
4895                        );
4896                    }
4897                }
4898                if matches!(target, DialectType::TSQL | DialectType::Fabric)
4899                    && Self::node_is_postgres_type_function_cast(node)
4900                {
4901                    Self::push_unsupported_diagnostic(
4902                        &mut diagnostics,
4903                        "PostgreSQL type-name function casts",
4904                    );
4905                }
4906            }
4907
4908            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
4909                break;
4910            }
4911        }
4912
4913        if matches!(target, DialectType::TSQL | DialectType::Fabric) {
4914            Self::collect_tsql_unsupported_ordered_sets(expr, &mut diagnostics);
4915            Self::collect_tsql_windows_missing_order(expr, &HashMap::new(), &mut diagnostics);
4916        }
4917
4918        if diagnostics.is_empty() {
4919            return Ok(());
4920        }
4921
4922        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4923            1
4924        } else {
4925            opts.max_unsupported.max(1)
4926        };
4927        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
4928        if diagnostics.len() > limit {
4929            messages.push(format!("... and {} more", diagnostics.len() - limit));
4930        }
4931
4932        Err(crate::error::Error::unsupported(
4933            messages.join("; "),
4934            target.to_string(),
4935        ))
4936    }
4937
4938    fn reject_postgres_tsql_strict_regex_predicates(
4939        expr: &Expression,
4940        source: DialectType,
4941        target: DialectType,
4942        opts: &TranspileOptions,
4943    ) -> Result<()> {
4944        if !matches!(
4945            opts.unsupported_level,
4946            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4947        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
4948            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4949        {
4950            return Ok(());
4951        }
4952
4953        if expr.dfs().any(Self::node_is_regex_predicate) {
4954            return Err(crate::error::Error::unsupported(
4955                "regular expression predicates",
4956                target.to_string(),
4957            ));
4958        }
4959
4960        Ok(())
4961    }
4962
4963    fn reject_tsql_strict_json_constructor_return_types(
4964        expr: &Expression,
4965        source: DialectType,
4966        target: DialectType,
4967        opts: &TranspileOptions,
4968    ) -> Result<()> {
4969        if !matches!(
4970            opts.unsupported_level,
4971            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
4972        ) || source == target
4973            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
4974        {
4975            return Ok(());
4976        }
4977
4978        let mut diagnostics = Vec::new();
4979        for node in expr.dfs() {
4980            if let Some(return_type) =
4981                normalization::unsupported_tsql_json_constructor_return_type(node)
4982            {
4983                let message =
4984                    format!("SQL/JSON constructor RETURNING {return_type} cannot be preserved");
4985                Self::push_unsupported_diagnostic(&mut diagnostics, &message);
4986                if opts.unsupported_level == UnsupportedLevel::Immediate {
4987                    break;
4988                }
4989            }
4990        }
4991
4992        if diagnostics.is_empty() {
4993            return Ok(());
4994        }
4995
4996        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
4997            1
4998        } else {
4999            opts.max_unsupported.max(1)
5000        };
5001        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
5002        if diagnostics.len() > limit {
5003            messages.push(format!("... and {} more", diagnostics.len() - limit));
5004        }
5005
5006        Err(crate::error::Error::unsupported(
5007            messages.join("; "),
5008            target.to_string(),
5009        ))
5010    }
5011
5012    fn reject_postgres_tsql_strict_json_aggregate_modifiers(
5013        expr: &Expression,
5014        source: DialectType,
5015        target: DialectType,
5016        opts: &TranspileOptions,
5017    ) -> Result<()> {
5018        if !matches!(
5019            opts.unsupported_level,
5020            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
5021        ) || !matches!(source, DialectType::PostgreSQL | DialectType::CockroachDB)
5022            || !matches!(target, DialectType::TSQL | DialectType::Fabric)
5023        {
5024            return Ok(());
5025        }
5026
5027        let mut diagnostics = Vec::new();
5028        for node in expr.dfs() {
5029            match node {
5030                Expression::Function(function)
5031                    if !function.quoted
5032                        && matches!(
5033                            function.name.to_ascii_uppercase().as_str(),
5034                            "JSON_AGG" | "JSONB_AGG"
5035                        ) =>
5036                {
5037                    let name = function.name.to_ascii_uppercase();
5038                    if function.args.len() != 1 {
5039                        Self::push_unsupported_diagnostic(
5040                            &mut diagnostics,
5041                            &format!("PostgreSQL {name} with invalid argument count"),
5042                        );
5043                    }
5044                    if function.distinct {
5045                        Self::push_unsupported_diagnostic(
5046                            &mut diagnostics,
5047                            &format!("PostgreSQL {name} with DISTINCT"),
5048                        );
5049                    }
5050                }
5051                Expression::AggregateFunction(function)
5052                    if matches!(
5053                        function.name.to_ascii_uppercase().as_str(),
5054                        "JSON_AGG" | "JSONB_AGG"
5055                    ) =>
5056                {
5057                    let name = function.name.to_ascii_uppercase();
5058                    if function.args.len() != 1 {
5059                        Self::push_unsupported_diagnostic(
5060                            &mut diagnostics,
5061                            &format!("PostgreSQL {name} with invalid argument count"),
5062                        );
5063                    }
5064                    if function.distinct {
5065                        Self::push_unsupported_diagnostic(
5066                            &mut diagnostics,
5067                            &format!("PostgreSQL {name} with DISTINCT"),
5068                        );
5069                    }
5070                    if function.filter.is_some() {
5071                        Self::push_unsupported_diagnostic(
5072                            &mut diagnostics,
5073                            &format!("PostgreSQL {name} with FILTER"),
5074                        );
5075                    }
5076                    if function.limit.is_some() || function.ignore_nulls.is_some() {
5077                        Self::push_unsupported_diagnostic(
5078                            &mut diagnostics,
5079                            &format!("PostgreSQL {name} with unsupported aggregate modifiers"),
5080                        );
5081                    }
5082                }
5083                Expression::Filter(filter) => {
5084                    if let Some(name) = Self::postgres_json_aggregate_name(&filter.this) {
5085                        Self::push_unsupported_diagnostic(
5086                            &mut diagnostics,
5087                            &format!("PostgreSQL {name} with FILTER"),
5088                        );
5089                    }
5090                }
5091                _ => {}
5092            }
5093
5094            if opts.unsupported_level == UnsupportedLevel::Immediate && !diagnostics.is_empty() {
5095                break;
5096            }
5097        }
5098
5099        if diagnostics.is_empty() {
5100            return Ok(());
5101        }
5102
5103        let limit = if opts.unsupported_level == UnsupportedLevel::Immediate {
5104            1
5105        } else {
5106            opts.max_unsupported.max(1)
5107        };
5108        let mut messages = diagnostics.iter().take(limit).cloned().collect::<Vec<_>>();
5109        if diagnostics.len() > limit {
5110            messages.push(format!("... and {} more", diagnostics.len() - limit));
5111        }
5112
5113        Err(crate::error::Error::unsupported(
5114            messages.join("; "),
5115            target.to_string(),
5116        ))
5117    }
5118
5119    fn postgres_json_aggregate_name(expr: &Expression) -> Option<String> {
5120        let name = match expr {
5121            Expression::Function(function) if !function.quoted => &function.name,
5122            Expression::AggregateFunction(function) => &function.name,
5123            _ => return None,
5124        };
5125        let name = name.to_ascii_uppercase();
5126        matches!(name.as_str(), "JSON_AGG" | "JSONB_AGG").then_some(name)
5127    }
5128
5129    fn push_unsupported_diagnostic(diagnostics: &mut Vec<String>, message: &str) {
5130        if !diagnostics.iter().any(|existing| existing == message) {
5131            diagnostics.push(message.to_string());
5132        }
5133    }
5134
5135    fn node_is_unresolved_postgres_date_subtraction(expr: &Expression) -> bool {
5136        let Expression::Sub(op) = expr else {
5137            return false;
5138        };
5139
5140        (Self::is_explicit_date_expr(&op.left) && Self::is_column_expr(&op.right))
5141            || (Self::is_column_expr(&op.left) && Self::is_explicit_date_expr(&op.right))
5142    }
5143
5144    fn is_column_expr(expr: &Expression) -> bool {
5145        match expr {
5146            Expression::Column(_) => true,
5147            Expression::Paren(paren) => Self::is_column_expr(&paren.this),
5148            _ => false,
5149        }
5150    }
5151
5152    fn node_window_frame(expr: &Expression) -> Option<&WindowFrame> {
5153        match expr {
5154            Expression::WindowFunction(window) => window.over.frame.as_ref(),
5155            Expression::Window(window) | Expression::WindowSpec(window) => window.frame.as_ref(),
5156            _ => None,
5157        }
5158    }
5159
5160    fn window_frame_bound_has_value_offset(bound: &WindowFrameBound) -> bool {
5161        matches!(
5162            bound,
5163            WindowFrameBound::Preceding(_)
5164                | WindowFrameBound::Following(_)
5165                | WindowFrameBound::Value(_)
5166                | WindowFrameBound::BarePreceding
5167                | WindowFrameBound::BareFollowing
5168        )
5169    }
5170
5171    fn collect_tsql_windows_missing_order(
5172        expr: &Expression,
5173        active_windows: &HashMap<String, Over>,
5174        diagnostics: &mut Vec<String>,
5175    ) {
5176        if let Expression::Select(select) = expr {
5177            let local_windows = select
5178                .windows
5179                .as_ref()
5180                .map(|windows| {
5181                    windows
5182                        .iter()
5183                        .map(|window| (window.name.name.to_ascii_lowercase(), window.spec.clone()))
5184                        .collect()
5185                })
5186                .unwrap_or_default();
5187
5188            for child in expr.children() {
5189                Self::collect_tsql_windows_missing_order(child, &local_windows, diagnostics);
5190            }
5191            return;
5192        }
5193
5194        if let Expression::WindowFunction(window) = expr {
5195            let (has_order, has_frame) = Self::effective_window_order_and_frame(
5196                &window.over,
5197                active_windows,
5198                &mut Vec::new(),
5199            );
5200
5201            if !has_order {
5202                if has_frame {
5203                    Self::push_unsupported_diagnostic(
5204                        diagnostics,
5205                        "window frames without ORDER BY",
5206                    );
5207                }
5208                if let Some(function_name) =
5209                    Self::tsql_window_function_requiring_order(&window.this)
5210                {
5211                    Self::push_unsupported_diagnostic(
5212                        diagnostics,
5213                        &format!("{function_name} without ORDER BY"),
5214                    );
5215                }
5216            }
5217        }
5218
5219        for child in expr.children() {
5220            Self::collect_tsql_windows_missing_order(child, active_windows, diagnostics);
5221        }
5222    }
5223
5224    fn effective_window_order_and_frame(
5225        over: &Over,
5226        active_windows: &HashMap<String, Over>,
5227        seen: &mut Vec<String>,
5228    ) -> (bool, bool) {
5229        let inherited = over
5230            .window_name
5231            .as_ref()
5232            .and_then(|name| {
5233                let key = name.name.to_ascii_lowercase();
5234                if seen.iter().any(|seen_name| seen_name == &key) {
5235                    return None;
5236                }
5237                let named = active_windows.get(&key)?;
5238                seen.push(key);
5239                let properties =
5240                    Self::effective_window_order_and_frame(named, active_windows, seen);
5241                seen.pop();
5242                Some(properties)
5243            })
5244            .unwrap_or((false, false));
5245
5246        (
5247            !over.order_by.is_empty() || inherited.0,
5248            over.frame.is_some() || inherited.1,
5249        )
5250    }
5251
5252    fn tsql_window_function_requiring_order(expr: &Expression) -> Option<&'static str> {
5253        match expr {
5254            Expression::FirstValue(_) => Some("FIRST_VALUE"),
5255            Expression::LastValue(_) => Some("LAST_VALUE"),
5256            Expression::Function(function) if function.name.eq_ignore_ascii_case("FIRST_VALUE") => {
5257                Some("FIRST_VALUE")
5258            }
5259            Expression::Function(function) if function.name.eq_ignore_ascii_case("LAST_VALUE") => {
5260                Some("LAST_VALUE")
5261            }
5262            _ => None,
5263        }
5264    }
5265
5266    fn collect_tsql_unsupported_ordered_sets(expr: &Expression, diagnostics: &mut Vec<String>) {
5267        match expr {
5268            Expression::WindowFunction(window) => {
5269                if let Expression::WithinGroup(within_group) = &window.this {
5270                    if Self::within_group_is_hypothetical_set(within_group) {
5271                        Self::push_unsupported_diagnostic(
5272                            diagnostics,
5273                            "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
5274                        );
5275                        return;
5276                    }
5277
5278                    if Self::within_group_is_mode(within_group) {
5279                        Self::push_unsupported_diagnostic(
5280                            diagnostics,
5281                            "MODE ordered-set aggregates",
5282                        );
5283                        return;
5284                    }
5285
5286                    if Self::within_group_is_percentile(within_group) {
5287                        if !window.over.order_by.is_empty() || window.over.frame.is_some() {
5288                            Self::push_unsupported_diagnostic(
5289                                diagnostics,
5290                                "PERCENTILE_CONT/PERCENTILE_DISC window ORDER BY or frame clauses",
5291                            );
5292                        }
5293                        return;
5294                    }
5295                }
5296            }
5297            Expression::WithinGroup(within_group) => {
5298                if Self::within_group_is_hypothetical_set(within_group) {
5299                    Self::push_unsupported_diagnostic(
5300                        diagnostics,
5301                        "RANK/DENSE_RANK/CUME_DIST/PERCENT_RANK hypothetical-set aggregates",
5302                    );
5303                    return;
5304                }
5305
5306                if Self::within_group_is_mode(within_group) {
5307                    Self::push_unsupported_diagnostic(diagnostics, "MODE ordered-set aggregates");
5308                    return;
5309                }
5310
5311                if Self::within_group_is_percentile(within_group) {
5312                    Self::push_unsupported_diagnostic(
5313                        diagnostics,
5314                        "PERCENTILE_CONT/PERCENTILE_DISC ordered-set aggregates without OVER",
5315                    );
5316                    return;
5317                }
5318            }
5319            _ => {}
5320        }
5321
5322        for child in expr.children() {
5323            Self::collect_tsql_unsupported_ordered_sets(child, diagnostics);
5324        }
5325    }
5326
5327    fn within_group_is_hypothetical_set(within_group: &crate::expressions::WithinGroup) -> bool {
5328        match &within_group.this {
5329            Expression::Function(function) => Self::is_hypothetical_set_name(&function.name),
5330            Expression::AggregateFunction(function) => {
5331                Self::is_hypothetical_set_name(&function.name)
5332            }
5333            Expression::Rank(_)
5334            | Expression::DenseRank(_)
5335            | Expression::CumeDist(_)
5336            | Expression::PercentRank(_) => true,
5337            _ => false,
5338        }
5339    }
5340
5341    fn within_group_is_percentile(within_group: &crate::expressions::WithinGroup) -> bool {
5342        match &within_group.this {
5343            Expression::Function(function) => Self::is_percentile_ordered_set_name(&function.name),
5344            Expression::AggregateFunction(function) => {
5345                Self::is_percentile_ordered_set_name(&function.name)
5346            }
5347            Expression::PercentileCont(_) | Expression::PercentileDisc(_) => true,
5348            _ => false,
5349        }
5350    }
5351
5352    fn within_group_is_mode(within_group: &crate::expressions::WithinGroup) -> bool {
5353        match &within_group.this {
5354            Expression::Function(function) => function.name.eq_ignore_ascii_case("MODE"),
5355            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case("MODE"),
5356            Expression::Mode(_) => true,
5357            _ => false,
5358        }
5359    }
5360
5361    fn is_percentile_ordered_set_name(name: &str) -> bool {
5362        name.eq_ignore_ascii_case("PERCENTILE_CONT") || name.eq_ignore_ascii_case("PERCENTILE_DISC")
5363    }
5364
5365    fn is_hypothetical_set_name(name: &str) -> bool {
5366        name.eq_ignore_ascii_case("RANK")
5367            || name.eq_ignore_ascii_case("DENSE_RANK")
5368            || name.eq_ignore_ascii_case("CUME_DIST")
5369            || name.eq_ignore_ascii_case("PERCENT_RANK")
5370    }
5371
5372    fn target_supports_distinct_on(target: DialectType) -> bool {
5373        matches!(target, DialectType::PostgreSQL | DialectType::DuckDB)
5374    }
5375
5376    fn node_has_distinct_on(expr: &Expression) -> bool {
5377        matches!(
5378            expr,
5379            Expression::Select(select)
5380                if select
5381                    .distinct_on
5382                    .as_ref()
5383                    .is_some_and(|distinct_on| !distinct_on.is_empty())
5384        )
5385    }
5386
5387    fn node_has_recursive_with(expr: &Expression) -> bool {
5388        fn recursive(with: &Option<With>) -> bool {
5389            with.as_ref().is_some_and(|with| with.recursive)
5390        }
5391
5392        match expr {
5393            Expression::With(with) => with.recursive,
5394            Expression::Select(select) => recursive(&select.with),
5395            Expression::Union(union) => recursive(&union.with),
5396            Expression::Intersect(intersect) => recursive(&intersect.with),
5397            Expression::Except(except) => recursive(&except.with),
5398            Expression::Pivot(pivot) => recursive(&pivot.with),
5399            Expression::Insert(insert) => recursive(&insert.with),
5400            Expression::Update(update) => recursive(&update.with),
5401            Expression::Delete(delete) => recursive(&delete.with),
5402            _ => false,
5403        }
5404    }
5405
5406    fn node_has_lateral(expr: &Expression) -> bool {
5407        fn join_has_lateral(join: &Join) -> bool {
5408            matches!(
5409                join.kind,
5410                crate::expressions::JoinKind::Lateral | crate::expressions::JoinKind::LeftLateral
5411            ) || Dialect::node_has_lateral(&join.this)
5412                || join.on.as_ref().is_some_and(Dialect::node_has_lateral)
5413                || join
5414                    .match_condition
5415                    .as_ref()
5416                    .is_some_and(Dialect::node_has_lateral)
5417                || join.pivots.iter().any(Dialect::node_has_lateral)
5418        }
5419
5420        fn joins_have_lateral(joins: &[Join]) -> bool {
5421            joins.iter().any(join_has_lateral)
5422        }
5423
5424        match expr {
5425            Expression::Subquery(subquery) => {
5426                subquery.lateral || Dialect::node_has_lateral(&subquery.this)
5427            }
5428            Expression::Lateral(_) | Expression::LateralView(_) => true,
5429            Expression::Join(join) => join_has_lateral(join),
5430            Expression::Select(select) => {
5431                !select.lateral_views.is_empty()
5432                    || joins_have_lateral(&select.joins)
5433                    || select
5434                        .from
5435                        .as_ref()
5436                        .is_some_and(|from| from.expressions.iter().any(Dialect::node_has_lateral))
5437            }
5438            Expression::JoinedTable(joined) => {
5439                !joined.lateral_views.is_empty()
5440                    || Dialect::node_has_lateral(&joined.left)
5441                    || joins_have_lateral(&joined.joins)
5442            }
5443            Expression::Update(update) => {
5444                joins_have_lateral(&update.table_joins) || joins_have_lateral(&update.from_joins)
5445            }
5446            _ => false,
5447        }
5448    }
5449
5450    fn node_has_join_using(expr: &Expression) -> bool {
5451        fn has_using(joins: &[Join]) -> bool {
5452            joins.iter().any(|join| !join.using.is_empty())
5453        }
5454
5455        match expr {
5456            Expression::Join(join) => !join.using.is_empty(),
5457            Expression::Select(select) => has_using(&select.joins),
5458            Expression::JoinedTable(joined) => has_using(&joined.joins),
5459            Expression::Update(update) => {
5460                has_using(&update.table_joins) || has_using(&update.from_joins)
5461            }
5462            Expression::Delete(delete) => has_using(&delete.joins),
5463            _ => false,
5464        }
5465    }
5466
5467    fn node_has_natural_join(expr: &Expression) -> bool {
5468        fn is_natural(join: &Join) -> bool {
5469            matches!(
5470                join.kind,
5471                crate::expressions::JoinKind::Natural
5472                    | crate::expressions::JoinKind::NaturalLeft
5473                    | crate::expressions::JoinKind::NaturalRight
5474                    | crate::expressions::JoinKind::NaturalFull
5475            )
5476        }
5477
5478        fn has_natural(joins: &[Join]) -> bool {
5479            joins.iter().any(is_natural)
5480        }
5481
5482        match expr {
5483            Expression::Join(join) => is_natural(join),
5484            Expression::Select(select) => has_natural(&select.joins),
5485            Expression::JoinedTable(joined) => has_natural(&joined.joins),
5486            Expression::Update(update) => {
5487                has_natural(&update.table_joins) || has_natural(&update.from_joins)
5488            }
5489            Expression::Delete(delete) => has_natural(&delete.joins),
5490            _ => false,
5491        }
5492    }
5493
5494    fn node_has_unsupported_relation_column_aliases(expr: &Expression) -> bool {
5495        match expr {
5496            Expression::Table(table) => !table.column_aliases.is_empty(),
5497            Expression::Alias(alias) => {
5498                !alias.column_aliases.is_empty()
5499                    && matches!(
5500                        alias.this,
5501                        Expression::Table(_) | Expression::JoinedTable(_)
5502                    )
5503            }
5504            _ => false,
5505        }
5506    }
5507
5508    fn node_is_aggregate_function(expr: &Expression) -> bool {
5509        is_aggregate(expr)
5510    }
5511
5512    fn node_has_qualified_whole_row_aggregate_argument(expr: &Expression) -> bool {
5513        fn contains_qualified_star(expr: &Expression) -> bool {
5514            match expr {
5515                Expression::Star(star) => star.table.is_some(),
5516                // A star projected by an embedded query is not an argument of
5517                // the surrounding aggregate (for example, inside EXISTS).
5518                Expression::Select(_)
5519                | Expression::Subquery(_)
5520                | Expression::Union(_)
5521                | Expression::Intersect(_)
5522                | Expression::Except(_) => false,
5523                _ => expr.children().into_iter().any(contains_qualified_star),
5524            }
5525        }
5526
5527        Self::node_is_aggregate_function(expr)
5528            && expr.children().into_iter().any(contains_qualified_star)
5529    }
5530
5531    fn node_has_subquery_in_aggregate_argument(expr: &Expression) -> bool {
5532        fn contains_query(expr: &Expression) -> bool {
5533            matches!(
5534                expr,
5535                Expression::Select(_)
5536                    | Expression::Subquery(_)
5537                    | Expression::Union(_)
5538                    | Expression::Intersect(_)
5539                    | Expression::Except(_)
5540            ) || expr.children().into_iter().any(contains_query)
5541        }
5542
5543        Self::node_is_aggregate_function(expr) && expr.children().into_iter().any(contains_query)
5544    }
5545
5546    fn tsql_apply_has_invalid_outer_aggregate(expr: &Expression) -> bool {
5547        // SQL Server error 8124: if an aggregate expression contains an outer
5548        // reference, that reference must be the only column used by the expression.
5549        fn collect_source_names(expr: &Expression, names: &mut HashSet<String>) {
5550            let mut insert = |name: &Identifier| {
5551                if !name.name.is_empty() {
5552                    names.insert(name.name.to_ascii_lowercase());
5553                }
5554            };
5555
5556            match expr {
5557                Expression::Table(table) => {
5558                    insert(table.alias.as_ref().unwrap_or(&table.name));
5559                }
5560                Expression::Subquery(subquery) => {
5561                    if let Some(alias) = &subquery.alias {
5562                        insert(alias);
5563                    }
5564                }
5565                Expression::Alias(alias) => insert(&alias.alias),
5566                Expression::JoinedTable(joined) => {
5567                    if let Some(alias) = &joined.alias {
5568                        insert(alias);
5569                    } else {
5570                        collect_source_names(&joined.left, names);
5571                        for join in &joined.joins {
5572                            collect_source_names(&join.this, names);
5573                        }
5574                    }
5575                }
5576                Expression::Paren(paren) => collect_source_names(&paren.this, names),
5577                Expression::Pivot(pivot) => {
5578                    if let Some(alias) = &pivot.alias {
5579                        insert(alias);
5580                    } else {
5581                        collect_source_names(&pivot.this, names);
5582                    }
5583                }
5584                Expression::Unpivot(unpivot) => {
5585                    if let Some(alias) = &unpivot.alias {
5586                        insert(alias);
5587                    } else {
5588                        collect_source_names(&unpivot.this, names);
5589                    }
5590                }
5591                _ => {}
5592            }
5593        }
5594
5595        fn collect_columns<'a>(
5596            expr: &'a Expression,
5597            columns: &mut Vec<&'a crate::expressions::Column>,
5598        ) {
5599            match expr {
5600                Expression::Column(column) => columns.push(column),
5601                // Query expressions introduce a new name-resolution scope. Their
5602                // columns are checked when their own SELECT node is visited.
5603                Expression::Select(_)
5604                | Expression::Subquery(_)
5605                | Expression::Union(_)
5606                | Expression::Intersect(_)
5607                | Expression::Except(_) => {}
5608                _ => {
5609                    for child in expr.children() {
5610                        collect_columns(child, columns);
5611                    }
5612                }
5613            }
5614        }
5615
5616        fn same_column(
5617            left: &crate::expressions::Column,
5618            right: &crate::expressions::Column,
5619        ) -> bool {
5620            let same_identifier =
5621                |left: &Identifier, right: &Identifier| left.name.eq_ignore_ascii_case(&right.name);
5622
5623            same_identifier(&left.name, &right.name)
5624                && match (&left.table, &right.table) {
5625                    (Some(left), Some(right)) => same_identifier(left, right),
5626                    (None, None) => true,
5627                    _ => false,
5628                }
5629        }
5630
5631        fn aggregate_is_invalid(expr: &Expression, local_sources: &HashSet<String>) -> bool {
5632            let mut columns = Vec::new();
5633            collect_columns(expr, &mut columns);
5634
5635            let outer_column = columns.iter().copied().find(|column| match &column.table {
5636                Some(table) => !local_sources.contains(&table.name.to_ascii_lowercase()),
5637                // Without a local source, an unqualified reference in a lateral
5638                // query can only resolve against an outer scope.
5639                None => local_sources.is_empty(),
5640            });
5641
5642            outer_column
5643                .is_some_and(|outer| columns.iter().any(|column| !same_column(outer, column)))
5644        }
5645
5646        fn expression_has_invalid_aggregate(
5647            expr: &Expression,
5648            local_sources: &HashSet<String>,
5649        ) -> bool {
5650            if Dialect::node_is_aggregate_function(expr)
5651                && aggregate_is_invalid(expr, local_sources)
5652            {
5653                return true;
5654            }
5655
5656            match expr {
5657                Expression::Select(_)
5658                | Expression::Subquery(_)
5659                | Expression::Union(_)
5660                | Expression::Intersect(_)
5661                | Expression::Except(_) => false,
5662                _ => expr
5663                    .children()
5664                    .into_iter()
5665                    .any(|child| expression_has_invalid_aggregate(child, local_sources)),
5666            }
5667        }
5668
5669        fn select_has_invalid_aggregate(select: &Select) -> bool {
5670            let mut local_sources = HashSet::new();
5671            if let Some(from) = &select.from {
5672                for source in &from.expressions {
5673                    collect_source_names(source, &mut local_sources);
5674                }
5675            }
5676            for join in &select.joins {
5677                collect_source_names(&join.this, &mut local_sources);
5678            }
5679
5680            let invalid =
5681                |expr: &Expression| expression_has_invalid_aggregate(expr, &local_sources);
5682
5683            select.expressions.iter().any(invalid)
5684                || select.prewhere.as_ref().is_some_and(invalid)
5685                || select
5686                    .where_clause
5687                    .as_ref()
5688                    .is_some_and(|where_clause| invalid(&where_clause.this))
5689                || select
5690                    .group_by
5691                    .as_ref()
5692                    .is_some_and(|group_by| group_by.expressions.iter().any(invalid))
5693                || select
5694                    .having
5695                    .as_ref()
5696                    .is_some_and(|having| invalid(&having.this))
5697                || select
5698                    .qualify
5699                    .as_ref()
5700                    .is_some_and(|qualify| invalid(&qualify.this))
5701                || select.order_by.as_ref().is_some_and(|order_by| {
5702                    order_by
5703                        .expressions
5704                        .iter()
5705                        .any(|ordered| invalid(&ordered.this))
5706                })
5707        }
5708
5709        fn apply_rhs_is_invalid(rhs: &Expression) -> bool {
5710            rhs.dfs().any(|node| match node {
5711                Expression::Select(select) => select_has_invalid_aggregate(select),
5712                _ => false,
5713            })
5714        }
5715
5716        fn joins_have_invalid_aggregate(joins: &[Join]) -> bool {
5717            joins.iter().any(|join| {
5718                matches!(
5719                    join.kind,
5720                    crate::expressions::JoinKind::CrossApply
5721                        | crate::expressions::JoinKind::OuterApply
5722                ) && apply_rhs_is_invalid(&join.this)
5723            })
5724        }
5725
5726        expr.dfs().any(|node| match node {
5727            Expression::Select(select) => joins_have_invalid_aggregate(&select.joins),
5728            Expression::JoinedTable(joined) => joins_have_invalid_aggregate(&joined.joins),
5729            Expression::Update(update) => {
5730                joins_have_invalid_aggregate(&update.table_joins)
5731                    || joins_have_invalid_aggregate(&update.from_joins)
5732            }
5733            Expression::Delete(delete) => joins_have_invalid_aggregate(&delete.joins),
5734            _ => false,
5735        })
5736    }
5737
5738    fn target_supports_remaining_unnest(target: DialectType) -> bool {
5739        matches!(
5740            target,
5741            DialectType::PostgreSQL
5742                | DialectType::BigQuery
5743                | DialectType::DuckDB
5744                | DialectType::Presto
5745                | DialectType::Trino
5746                | DialectType::Athena
5747        )
5748    }
5749
5750    fn target_supports_remaining_explode(target: DialectType) -> bool {
5751        matches!(
5752            target,
5753            DialectType::Spark | DialectType::Databricks | DialectType::Hive
5754        )
5755    }
5756
5757    fn target_lacks_array_agg(target: DialectType) -> bool {
5758        matches!(
5759            target,
5760            DialectType::Fabric
5761                | DialectType::TSQL
5762                | DialectType::MySQL
5763                | DialectType::SQLite
5764                | DialectType::Oracle
5765        )
5766    }
5767
5768    fn node_is_unnest(expr: &Expression) -> bool {
5769        matches!(expr, Expression::Unnest(_)) || Self::node_is_function_named(expr, "UNNEST")
5770    }
5771
5772    fn node_is_explode(expr: &Expression) -> bool {
5773        matches!(expr, Expression::Explode(_) | Expression::ExplodeOuter(_))
5774            || Self::node_is_function_named(expr, "EXPLODE")
5775            || Self::node_is_function_named(expr, "EXPLODE_OUTER")
5776    }
5777
5778    fn node_is_array_agg(expr: &Expression) -> bool {
5779        matches!(expr, Expression::ArrayAgg(_)) || Self::node_is_function_named(expr, "ARRAY_AGG")
5780    }
5781
5782    fn node_is_distinct_string_agg(expr: &Expression) -> bool {
5783        match expr {
5784            Expression::StringAgg(agg) => agg.distinct,
5785            Expression::Function(function) => {
5786                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
5787            }
5788            Expression::AggregateFunction(function) => {
5789                function.distinct && function.name.eq_ignore_ascii_case("STRING_AGG")
5790            }
5791            _ => false,
5792        }
5793    }
5794
5795    fn postgres_tsql_unsupported_collation_name(expr: &Expression) -> Option<&'static str> {
5796        let Expression::Collation(collation) = expr else {
5797            return None;
5798        };
5799
5800        if collation.collation.eq_ignore_ascii_case("C") {
5801            Some("C")
5802        } else if collation.collation.eq_ignore_ascii_case("POSIX") {
5803            Some("POSIX")
5804        } else {
5805            None
5806        }
5807    }
5808
5809    fn collect_tsql_grouping_tuple_nodes(expr: &Expression) -> HashSet<*const Expression> {
5810        let mut tuples = HashSet::new();
5811
5812        for node in expr.dfs() {
5813            let Expression::Select(select) = node else {
5814                continue;
5815            };
5816            let Some(group_by) = &select.group_by else {
5817                continue;
5818            };
5819
5820            for expression in &group_by.expressions {
5821                Self::collect_tsql_grouping_element_tuples(expression, &mut tuples);
5822            }
5823        }
5824
5825        tuples
5826    }
5827
5828    fn collect_tsql_grouping_element_tuples(
5829        expr: &Expression,
5830        tuples: &mut HashSet<*const Expression>,
5831    ) {
5832        match expr {
5833            Expression::GroupingSets(grouping_sets) => {
5834                for expression in &grouping_sets.expressions {
5835                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5836                }
5837            }
5838            Expression::Rollup(rollup) => {
5839                for expression in &rollup.expressions {
5840                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5841                }
5842            }
5843            Expression::Cube(cube) => {
5844                for expression in &cube.expressions {
5845                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5846                }
5847            }
5848            Expression::Function(function)
5849                if !function.quoted
5850                    && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5851                        || function.name.eq_ignore_ascii_case("ROLLUP")
5852                        || function.name.eq_ignore_ascii_case("CUBE")) =>
5853            {
5854                for expression in &function.args {
5855                    Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5856                }
5857            }
5858            _ => {}
5859        }
5860    }
5861
5862    fn collect_tsql_grouping_unit_tuples(
5863        expr: &Expression,
5864        tuples: &mut HashSet<*const Expression>,
5865    ) {
5866        match expr {
5867            Expression::Tuple(tuple) => {
5868                tuples.insert(expr as *const Expression);
5869                for expression in &tuple.expressions {
5870                    match expression {
5871                        Expression::Tuple(_) | Expression::Paren(_) => {
5872                            Self::collect_tsql_grouping_unit_tuples(expression, tuples);
5873                        }
5874                        Expression::GroupingSets(_)
5875                        | Expression::Rollup(_)
5876                        | Expression::Cube(_) => {
5877                            Self::collect_tsql_grouping_element_tuples(expression, tuples);
5878                        }
5879                        Expression::Function(function)
5880                            if !function.quoted
5881                                && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5882                                    || function.name.eq_ignore_ascii_case("ROLLUP")
5883                                    || function.name.eq_ignore_ascii_case("CUBE")) =>
5884                        {
5885                            Self::collect_tsql_grouping_element_tuples(expression, tuples);
5886                        }
5887                        _ => {}
5888                    }
5889                }
5890            }
5891            Expression::Paren(paren) => {
5892                Self::collect_tsql_grouping_unit_tuples(&paren.this, tuples);
5893            }
5894            Expression::GroupingSets(_) | Expression::Rollup(_) | Expression::Cube(_) => {
5895                Self::collect_tsql_grouping_element_tuples(expr, tuples);
5896            }
5897            Expression::Function(function)
5898                if !function.quoted
5899                    && (function.name.eq_ignore_ascii_case("GROUPING SETS")
5900                        || function.name.eq_ignore_ascii_case("ROLLUP")
5901                        || function.name.eq_ignore_ascii_case("CUBE")) =>
5902            {
5903                Self::collect_tsql_grouping_element_tuples(expr, tuples);
5904            }
5905            _ => {}
5906        }
5907    }
5908
5909    fn postgres_tsql_unsupported_composite_semantics(
5910        expr: &Expression,
5911        structural_grouping_tuple: bool,
5912    ) -> Option<&'static str> {
5913        match expr {
5914            Expression::Tuple(_) if !structural_grouping_tuple => Some("row/composite values"),
5915            Expression::Struct(_) | Expression::StructFunc(_) => Some("row/composite values"),
5916            Expression::Function(function)
5917                if !function.quoted && function.name.eq_ignore_ascii_case("ROW") =>
5918            {
5919                Some("row/composite values")
5920            }
5921            Expression::StructExtract(_) => Some("row/composite field access"),
5922            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) if matches!(&cast.this, Expression::Star(star) if star.table.is_some()) => {
5923                Some("qualified whole-row casts")
5924            }
5925            _ => None,
5926        }
5927    }
5928
5929    fn node_is_postgres_unknown_cast(expr: &Expression) -> bool {
5930        match expr {
5931            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
5932                normalization::is_postgres_unknown_type(&cast.to)
5933            }
5934            _ => false,
5935        }
5936    }
5937
5938    fn postgres_tsql_unsupported_array_semantics(expr: &Expression) -> Option<&'static str> {
5939        match expr {
5940            Expression::Array(_) | Expression::ArrayFunc(_) => Some("array literals"),
5941            Expression::Subscript(_) => Some("array subscripts"),
5942            Expression::ArraySlice(_) => Some("array slices"),
5943            Expression::DataType(DataType::Array { .. }) => Some("array data types"),
5944            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
5945                if matches!(&cast.to, DataType::Array { .. }) =>
5946            {
5947                Some("array data types")
5948            }
5949            Expression::ArrayLength(_) | Expression::ArraySize(_) => Some("ARRAY_LENGTH"),
5950            Expression::Cardinality(_) => Some("CARDINALITY"),
5951            Expression::ArrayToString(_) | Expression::ArrayJoin(_) => Some("ARRAY_TO_STRING"),
5952            Expression::StringToArray(_) => Some("STRING_TO_ARRAY"),
5953            Expression::ArrayContains(_)
5954            | Expression::ArrayPosition(_)
5955            | Expression::ArrayAppend(_)
5956            | Expression::ArrayPrepend(_)
5957            | Expression::ArrayConcat(_)
5958            | Expression::ArraySort(_)
5959            | Expression::ArrayReverse(_)
5960            | Expression::ArrayDistinct(_)
5961            | Expression::ArrayFilter(_)
5962            | Expression::ArrayTransform(_)
5963            | Expression::ArrayFlatten(_)
5964            | Expression::ArrayCompact(_)
5965            | Expression::ArrayIntersect(_)
5966            | Expression::ArrayUnion(_)
5967            | Expression::ArrayExcept(_)
5968            | Expression::ArrayRemove(_)
5969            | Expression::ArrayZip(_)
5970            | Expression::ArrayAll(_)
5971            | Expression::ArrayAny(_)
5972            | Expression::ArrayConstructCompact(_)
5973            | Expression::ArraySum(_) => Some("array functions"),
5974            Expression::ArrayContainsAll(_)
5975            | Expression::ArrayContainedBy(_)
5976            | Expression::ArrayOverlaps(_) => Some("array operators"),
5977            Expression::Function(function) => {
5978                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5979            }
5980            Expression::AggregateFunction(function) => {
5981                Self::postgres_tsql_unsupported_array_function_name_str(&function.name)
5982            }
5983            _ => None,
5984        }
5985    }
5986
5987    fn postgres_tsql_unsupported_array_function_name_str(name: &str) -> Option<&'static str> {
5988        if name.eq_ignore_ascii_case("ARRAY") {
5989            Some("array literals")
5990        } else if name.eq_ignore_ascii_case("ARRAY_LENGTH")
5991            || name.eq_ignore_ascii_case("ARRAY_SIZE")
5992        {
5993            Some("ARRAY_LENGTH")
5994        } else if name.eq_ignore_ascii_case("CARDINALITY") {
5995            Some("CARDINALITY")
5996        } else if name.eq_ignore_ascii_case("ARRAY_TO_STRING")
5997            || name.eq_ignore_ascii_case("ARRAY_JOIN")
5998        {
5999            Some("ARRAY_TO_STRING")
6000        } else if name.eq_ignore_ascii_case("STRING_TO_ARRAY") {
6001            Some("STRING_TO_ARRAY")
6002        } else {
6003            None
6004        }
6005    }
6006
6007    fn node_is_regex_predicate(expr: &Expression) -> bool {
6008        matches!(
6009            expr,
6010            Expression::SimilarTo(_) | Expression::RegexpLike(_) | Expression::RegexpILike(_)
6011        ) || Self::node_is_function_named(expr, "REGEXP_LIKE")
6012            || Self::node_is_function_named(expr, "REGEXP_I_LIKE")
6013            || Self::node_is_function_named(expr, "REGEXP_ILIKE")
6014    }
6015
6016    fn node_is_non_subquery_any(expr: &Expression) -> bool {
6017        matches!(
6018            expr,
6019            Expression::Any(q) if !Self::quantified_rhs_is_subquery(&q.subquery)
6020        )
6021    }
6022
6023    fn quantified_rhs_is_subquery(expr: &Expression) -> bool {
6024        match expr {
6025            Expression::Select(_) | Expression::Subquery(_) => true,
6026            Expression::Paren(paren) => Self::quantified_rhs_is_subquery(&paren.this),
6027            _ => false,
6028        }
6029    }
6030
6031    fn node_is_row_value_subquery_comparison(expr: &Expression) -> bool {
6032        match expr {
6033            Expression::In(in_expr) => {
6034                Self::in_rhs_is_subquery_like(in_expr) && Self::expr_is_row_value(&in_expr.this)
6035            }
6036            Expression::Eq(op) | Expression::Neq(op) => {
6037                (Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right))
6038                    || (Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left))
6039            }
6040            _ => false,
6041        }
6042    }
6043
6044    fn node_is_row_value_values_membership(expr: &Expression) -> bool {
6045        matches!(
6046            expr,
6047            Expression::In(in_expr)
6048                if Self::expr_is_row_value(&in_expr.this)
6049                    && Self::in_rhs_is_values_like(in_expr)
6050        )
6051    }
6052
6053    fn expr_is_row_value(expr: &Expression) -> bool {
6054        match expr {
6055            Expression::Tuple(tuple) => tuple.expressions.len() > 1,
6056            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
6057                function.args.len() > 1
6058            }
6059            Expression::Paren(paren) => Self::expr_is_row_value(&paren.this),
6060            _ => false,
6061        }
6062    }
6063
6064    fn expr_is_subquery_like(expr: &Expression) -> bool {
6065        match expr {
6066            Expression::Select(_) | Expression::Subquery(_) => true,
6067            Expression::Paren(paren) => Self::expr_is_subquery_like(&paren.this),
6068            _ => false,
6069        }
6070    }
6071
6072    fn in_rhs_is_subquery_like(in_expr: &crate::expressions::In) -> bool {
6073        if in_expr
6074            .query
6075            .as_ref()
6076            .is_some_and(Self::expr_is_subquery_like)
6077        {
6078            return true;
6079        }
6080
6081        in_expr.expressions.len() == 1 && Self::expr_is_subquery_like(&in_expr.expressions[0])
6082    }
6083
6084    fn in_rhs_is_values_like(in_expr: &crate::expressions::In) -> bool {
6085        if in_expr
6086            .query
6087            .as_ref()
6088            .is_some_and(Self::expr_is_values_like)
6089        {
6090            return true;
6091        }
6092
6093        (in_expr.expressions.len() == 1
6094            && Self::expr_is_values_like(&in_expr.expressions[0]))
6095            || in_expr.expressions.first().is_some_and(|expr| {
6096                matches!(expr, Expression::Function(function) if function.name.eq_ignore_ascii_case("VALUES"))
6097            })
6098    }
6099
6100    fn expr_is_values_like(expr: &Expression) -> bool {
6101        match expr {
6102            Expression::Values(_) => true,
6103            Expression::Paren(paren) => Self::expr_is_values_like(&paren.this),
6104            Expression::Subquery(subquery) => Self::expr_is_values_like(&subquery.this),
6105            _ => false,
6106        }
6107    }
6108
6109    fn normalize_tsql_fetch_overlaps_date_bin(expr: Expression) -> Result<Expression> {
6110        transform_recursive(expr, &|e| match e {
6111            Expression::Select(mut select) => {
6112                if select.top.is_none() && select.offset.is_none() {
6113                    if let Some(fetch) = select.fetch.take() {
6114                        if let Some(top) = Self::fetch_with_ties_to_top(fetch.clone()) {
6115                            select.top = Some(top);
6116                        } else {
6117                            select.fetch = Some(fetch);
6118                        }
6119                    }
6120                }
6121                Self::rewrite_tsql_overlaps_in_select_predicates(&mut select)?;
6122                Ok(Expression::Select(select))
6123            }
6124            Expression::DateBin(date_bin) => {
6125                let date_bin = *date_bin;
6126                if let Some(rewritten) = Self::date_bin_to_date_bucket(date_bin.clone()) {
6127                    Ok(rewritten)
6128                } else {
6129                    Ok(Expression::DateBin(Box::new(date_bin)))
6130                }
6131            }
6132            Expression::Function(function) => {
6133                let function = *function;
6134                if function.name.eq_ignore_ascii_case("DATE_BIN") {
6135                    if let Some(rewritten) = Self::date_bin_function_to_date_bucket(&function) {
6136                        Ok(rewritten)
6137                    } else {
6138                        Ok(Expression::Function(Box::new(function)))
6139                    }
6140                } else {
6141                    Ok(Expression::Function(Box::new(function)))
6142                }
6143            }
6144            _ => Ok(e),
6145        })
6146    }
6147
6148    fn rewrite_tsql_overlaps_in_select_predicates(
6149        select: &mut crate::expressions::Select,
6150    ) -> Result<()> {
6151        if let Some(where_clause) = &mut select.where_clause {
6152            where_clause.this = Self::rewrite_tsql_overlaps_predicate(where_clause.this.clone())?;
6153        }
6154        if let Some(having) = &mut select.having {
6155            having.this = Self::rewrite_tsql_overlaps_predicate(having.this.clone())?;
6156        }
6157        if let Some(qualify) = &mut select.qualify {
6158            qualify.this = Self::rewrite_tsql_overlaps_predicate(qualify.this.clone())?;
6159        }
6160        for join in &mut select.joins {
6161            if let Some(on) = join.on.take() {
6162                join.on = Some(Self::rewrite_tsql_overlaps_predicate(on)?);
6163            }
6164            if let Some(match_condition) = join.match_condition.take() {
6165                join.match_condition =
6166                    Some(Self::rewrite_tsql_overlaps_predicate(match_condition)?);
6167            }
6168        }
6169        Ok(())
6170    }
6171
6172    fn rewrite_tsql_overlaps_predicate(expr: Expression) -> Result<Expression> {
6173        transform_recursive(expr, &|e| match e {
6174            Expression::Overlaps(overlaps) => {
6175                let overlaps = *overlaps;
6176                if let Some(rewritten) = Self::rewrite_full_overlaps_for_tsql(&overlaps) {
6177                    Ok(rewritten)
6178                } else {
6179                    Ok(Expression::Overlaps(Box::new(overlaps)))
6180                }
6181            }
6182            _ => Ok(e),
6183        })
6184    }
6185
6186    fn fetch_with_ties_to_top(fetch: Fetch) -> Option<Top> {
6187        if !fetch.with_ties {
6188            return None;
6189        }
6190
6191        fetch.count.map(|count| Top {
6192            this: count,
6193            percent: fetch.percent,
6194            with_ties: true,
6195            parenthesized: true,
6196        })
6197    }
6198
6199    fn rewrite_full_overlaps_for_tsql(
6200        overlaps: &crate::expressions::OverlapsExpr,
6201    ) -> Option<Expression> {
6202        let (left_start, left_end, right_start, right_end) =
6203            if let (Some(left_start), Some(left_end), Some(right_start), Some(right_end)) = (
6204                overlaps.left_start.as_ref(),
6205                overlaps.left_end.as_ref(),
6206                overlaps.right_start.as_ref(),
6207                overlaps.right_end.as_ref(),
6208            ) {
6209                (left_start, left_end, right_start, right_end)
6210            } else if let (
6211                Some(Expression::Tuple(left_tuple)),
6212                Some(Expression::Tuple(right_tuple)),
6213            ) = (&overlaps.this, &overlaps.expression)
6214            {
6215                if left_tuple.expressions.len() != 2 || right_tuple.expressions.len() != 2 {
6216                    return None;
6217                }
6218                (
6219                    &left_tuple.expressions[0],
6220                    &left_tuple.expressions[1],
6221                    &right_tuple.expressions[0],
6222                    &right_tuple.expressions[1],
6223                )
6224            } else {
6225                return None;
6226            };
6227
6228        let left_min = Self::case_min(left_start.clone(), left_end.clone());
6229        let left_max = Self::case_max(left_start.clone(), left_end.clone());
6230        let right_min = Self::case_min(right_start.clone(), right_end.clone());
6231        let right_max = Self::case_max(right_start.clone(), right_end.clone());
6232
6233        Some(Expression::And(Box::new(BinaryOp::new(
6234            Expression::Lte(Box::new(BinaryOp::new(left_min, right_max))),
6235            Expression::Lte(Box::new(BinaryOp::new(right_min, left_max))),
6236        ))))
6237    }
6238
6239    fn case_min(left: Expression, right: Expression) -> Expression {
6240        Expression::Case(Box::new(Case {
6241            operand: None,
6242            whens: vec![(
6243                Expression::Lte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
6244                left,
6245            )],
6246            else_: Some(right),
6247            comments: Vec::new(),
6248            inferred_type: None,
6249        }))
6250    }
6251
6252    fn case_max(left: Expression, right: Expression) -> Expression {
6253        Expression::Case(Box::new(Case {
6254            operand: None,
6255            whens: vec![(
6256                Expression::Gte(Box::new(BinaryOp::new(left.clone(), right.clone()))),
6257                left,
6258            )],
6259            else_: Some(right),
6260            comments: Vec::new(),
6261            inferred_type: None,
6262        }))
6263    }
6264
6265    fn date_bin_to_date_bucket(date_bin: DateBin) -> Option<Expression> {
6266        if date_bin.unit.is_some() || date_bin.zone.is_some() {
6267            return None;
6268        }
6269
6270        let (datepart, number) = Self::date_bucket_parts(&date_bin.this)?;
6271        let mut args = vec![
6272            Self::date_bucket_datepart(datepart),
6273            number,
6274            *date_bin.expression,
6275        ];
6276        if let Some(origin) = date_bin.origin {
6277            args.push(*origin);
6278        }
6279
6280        Some(Expression::Function(Box::new(Function::new(
6281            "DATE_BUCKET".to_string(),
6282            args,
6283        ))))
6284    }
6285
6286    fn date_bin_function_to_date_bucket(function: &Function) -> Option<Expression> {
6287        if !(2..=3).contains(&function.args.len()) {
6288            return None;
6289        }
6290
6291        let (datepart, number) = Self::date_bucket_parts(&function.args[0])?;
6292        let mut args = vec![
6293            Self::date_bucket_datepart(datepart),
6294            number,
6295            function.args[1].clone(),
6296        ];
6297        if let Some(origin) = function.args.get(2) {
6298            args.push(origin.clone());
6299        }
6300
6301        Some(Expression::Function(Box::new(Function::new(
6302            "DATE_BUCKET".to_string(),
6303            args,
6304        ))))
6305    }
6306
6307    fn date_bucket_parts(stride: &Expression) -> Option<(&'static str, Expression)> {
6308        match stride {
6309            Expression::Literal(lit) => match lit.as_ref() {
6310                Literal::String(value) => Self::date_bucket_parts_from_string(value),
6311                _ => None,
6312            },
6313            Expression::Interval(interval) => Self::date_bucket_parts_from_interval(interval),
6314            _ => None,
6315        }
6316    }
6317
6318    fn date_bucket_parts_from_interval(interval: &Interval) -> Option<(&'static str, Expression)> {
6319        match &interval.unit {
6320            Some(IntervalUnitSpec::Simple { unit, .. }) => {
6321                let datepart = Self::date_bucket_datepart_from_unit(*unit)?;
6322                let amount = interval
6323                    .this
6324                    .as_ref()
6325                    .and_then(Self::date_bucket_amount_expr)?;
6326                Some((datepart, amount))
6327            }
6328            None => interval.this.as_ref().and_then(|expr| match expr {
6329                Expression::Literal(lit) => match lit.as_ref() {
6330                    Literal::String(value) => Self::date_bucket_parts_from_string(value),
6331                    _ => None,
6332                },
6333                _ => None,
6334            }),
6335            _ => None,
6336        }
6337    }
6338
6339    fn date_bucket_parts_from_string(value: &str) -> Option<(&'static str, Expression)> {
6340        let mut parts = value.split_whitespace();
6341        let amount = parts.next()?;
6342        let unit = parts.next()?;
6343        if parts.next().is_some() {
6344            return None;
6345        }
6346
6347        Some((
6348            Self::date_bucket_datepart_from_name(unit)?,
6349            Self::positive_integer_expr(amount)?,
6350        ))
6351    }
6352
6353    fn date_bucket_amount_expr(expr: &Expression) -> Option<Expression> {
6354        match expr {
6355            Expression::Literal(lit) => match lit.as_ref() {
6356                Literal::Number(value) => Self::positive_integer_expr(value),
6357                Literal::String(value) => Self::positive_integer_expr(value),
6358                _ => None,
6359            },
6360            _ => Some(expr.clone()),
6361        }
6362    }
6363
6364    fn positive_integer_expr(value: &str) -> Option<Expression> {
6365        let parsed = value.trim().parse::<i64>().ok()?;
6366        (parsed > 0).then(|| Expression::number(parsed))
6367    }
6368
6369    fn date_bucket_datepart(datepart: &str) -> Expression {
6370        Expression::Var(Box::new(Var {
6371            this: datepart.to_string(),
6372        }))
6373    }
6374
6375    fn date_bucket_datepart_from_unit(unit: IntervalUnit) -> Option<&'static str> {
6376        match unit {
6377            IntervalUnit::Week => Some("WEEK"),
6378            IntervalUnit::Day => Some("DAY"),
6379            IntervalUnit::Hour => Some("HOUR"),
6380            IntervalUnit::Minute => Some("MINUTE"),
6381            IntervalUnit::Second => Some("SECOND"),
6382            IntervalUnit::Millisecond => Some("MILLISECOND"),
6383            _ => None,
6384        }
6385    }
6386
6387    fn date_bucket_datepart_from_name(unit: &str) -> Option<&'static str> {
6388        match unit.trim().to_ascii_uppercase().as_str() {
6389            "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" => Some("WEEK"),
6390            "DAY" | "DAYS" | "D" | "DD" => Some("DAY"),
6391            "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some("HOUR"),
6392            "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some("MINUTE"),
6393            "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some("SECOND"),
6394            "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MILLISEC" | "MILLISECS" => {
6395                Some("MILLISECOND")
6396            }
6397            _ => None,
6398        }
6399    }
6400
6401    fn node_has_fetch_with_ties(expr: &Expression) -> bool {
6402        matches!(
6403            expr,
6404            Expression::Select(select)
6405                if select
6406                    .fetch
6407                    .as_ref()
6408                    .is_some_and(|fetch| fetch.with_ties)
6409        )
6410    }
6411
6412    fn node_is_overlaps(expr: &Expression) -> bool {
6413        matches!(expr, Expression::Overlaps(_))
6414    }
6415
6416    fn node_is_date_bin(expr: &Expression) -> bool {
6417        matches!(expr, Expression::DateBin(_)) || Self::node_is_function_named(expr, "DATE_BIN")
6418    }
6419
6420    fn node_is_function_named(expr: &Expression, name: &str) -> bool {
6421        match expr {
6422            Expression::Function(function) => function.name.eq_ignore_ascii_case(name),
6423            Expression::AggregateFunction(function) => function.name.eq_ignore_ascii_case(name),
6424            _ => false,
6425        }
6426    }
6427
6428    fn node_is_postgres_json_build_object(expr: &Expression) -> bool {
6429        match expr {
6430            Expression::Function(function) => {
6431                function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
6432                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT")
6433            }
6434            _ => false,
6435        }
6436    }
6437
6438    fn postgres_json_build_object_can_lower_to_json_object(expr: &Expression) -> bool {
6439        matches!(
6440            expr,
6441            Expression::Function(function)
6442                if (function.name.eq_ignore_ascii_case("JSON_BUILD_OBJECT")
6443                    || function.name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT"))
6444                    && !function.distinct
6445                    && function.args.len() % 2 == 0
6446        )
6447    }
6448
6449    fn node_is_postgres_json_array_elements(expr: &Expression) -> bool {
6450        matches!(
6451            expr,
6452            Expression::Function(function)
6453                if function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS")
6454                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS")
6455                    || function.name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT")
6456                    || function.name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT")
6457        )
6458    }
6459
6460    fn postgres_tsql_unsupported_function_name(
6461        expr: &Expression,
6462        target: DialectType,
6463    ) -> Option<&'static str> {
6464        match expr {
6465            Expression::Lpad(_) => Some("LPAD"),
6466            Expression::Rpad(_) => Some("RPAD"),
6467            Expression::SplitPart(_) => Some("SPLIT_PART"),
6468            Expression::Initcap(_) => Some("INITCAP"),
6469            Expression::RegexpReplace(_) => Some("REGEXP_REPLACE"),
6470            Expression::RegexpInstr(_) => Some("REGEXP_INSTR"),
6471            Expression::RegexpCount(_) => Some("REGEXP_COUNT"),
6472            Expression::RegexpSplit(_) => Some("REGEXP_SPLIT"),
6473            Expression::DecodeCase(_) => Some("DECODE"),
6474            Expression::ToJson(_) => Some("TO_JSON"),
6475            Expression::JSONBObjectAgg(_) => Some("JSONB_OBJECT_AGG"),
6476            Expression::ToNumber(_) => Some("TO_NUMBER"),
6477            Expression::WidthBucket(_) => Some("WIDTH_BUCKET"),
6478            Expression::BitwiseAndAgg(_) => Some("BIT_AND"),
6479            Expression::BitwiseOrAgg(_) => Some("BIT_OR"),
6480            Expression::BitwiseXorAgg(_) => Some("BIT_XOR"),
6481            Expression::Corr(_) => Some("CORR"),
6482            Expression::CovarPop(_) => Some("COVAR_POP"),
6483            Expression::CovarSamp(_) => Some("COVAR_SAMP"),
6484            Expression::RegrAvgx(_) => Some("REGR_AVGX"),
6485            Expression::RegrAvgy(_) => Some("REGR_AVGY"),
6486            Expression::RegrCount(_) => Some("REGR_COUNT"),
6487            Expression::RegrIntercept(_) => Some("REGR_INTERCEPT"),
6488            Expression::RegrR2(_) => Some("REGR_R2"),
6489            Expression::RegrSlope(_) => Some("REGR_SLOPE"),
6490            Expression::RegrSxx(_) => Some("REGR_SXX"),
6491            Expression::RegrSxy(_) => Some("REGR_SXY"),
6492            Expression::RegrSyy(_) => Some("REGR_SYY"),
6493            Expression::Function(function) => {
6494                Self::postgres_tsql_unsupported_function_name_str(&function.name, target)
6495            }
6496            Expression::AggregateFunction(function) => {
6497                Self::postgres_tsql_unsupported_function_name_str(&function.name, target)
6498            }
6499            _ => None,
6500        }
6501    }
6502
6503    fn postgres_tsql_unsupported_function_name_str(
6504        name: &str,
6505        target: DialectType,
6506    ) -> Option<&'static str> {
6507        if name.eq_ignore_ascii_case("LPAD") {
6508            Some("LPAD")
6509        } else if name.eq_ignore_ascii_case("RPAD") {
6510            Some("RPAD")
6511        } else if name.eq_ignore_ascii_case("SPLIT_PART") {
6512            Some("SPLIT_PART")
6513        } else if name.eq_ignore_ascii_case("INITCAP") {
6514            Some("INITCAP")
6515        } else if name.eq_ignore_ascii_case("TO_JSON") {
6516            Some("TO_JSON")
6517        } else if name.eq_ignore_ascii_case("TO_JSONB") {
6518            Some("TO_JSONB")
6519        } else if name.eq_ignore_ascii_case("JSONB_OBJECT_AGG") {
6520            Some("JSONB_OBJECT_AGG")
6521        } else if name.eq_ignore_ascii_case("ROW_TO_JSON") {
6522            Some("ROW_TO_JSON")
6523        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS") {
6524            Some("JSON_ARRAY_ELEMENTS")
6525        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS") {
6526            Some("JSONB_ARRAY_ELEMENTS")
6527        } else if name.eq_ignore_ascii_case("JSON_ARRAY_ELEMENTS_TEXT") {
6528            Some("JSON_ARRAY_ELEMENTS_TEXT")
6529        } else if name.eq_ignore_ascii_case("JSONB_ARRAY_ELEMENTS_TEXT") {
6530            Some("JSONB_ARRAY_ELEMENTS_TEXT")
6531        } else if name.eq_ignore_ascii_case("ENCODE") {
6532            Some("ENCODE")
6533        } else if name.eq_ignore_ascii_case("DECODE") {
6534            Some("DECODE")
6535        } else if name.eq_ignore_ascii_case("REGEXP_REPLACE") {
6536            Some("REGEXP_REPLACE")
6537        } else if name.eq_ignore_ascii_case("REGEXP_COUNT") {
6538            Some("REGEXP_COUNT")
6539        } else if name.eq_ignore_ascii_case("REGEXP_INSTR") {
6540            Some("REGEXP_INSTR")
6541        } else if name.eq_ignore_ascii_case("REGEXP_SUBSTR") {
6542            Some("REGEXP_SUBSTR")
6543        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT") {
6544            Some("REGEXP_SPLIT")
6545        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT_TO_ARRAY") {
6546            Some("REGEXP_SPLIT_TO_ARRAY")
6547        } else if name.eq_ignore_ascii_case("REGEXP_SPLIT_TO_TABLE") {
6548            Some("REGEXP_SPLIT_TO_TABLE")
6549        } else if name.eq_ignore_ascii_case("SHA224") {
6550            Some("SHA224")
6551        } else if name.eq_ignore_ascii_case("SHA384") {
6552            Some("SHA384")
6553        } else if name.eq_ignore_ascii_case("TO_BIN") {
6554            Some("TO_BIN")
6555        } else if name.eq_ignore_ascii_case("TO_OCT") {
6556            Some("TO_OCT")
6557        } else if target == DialectType::TSQL && name.eq_ignore_ascii_case("UNISTR") {
6558            Some("UNISTR")
6559        } else if name.eq_ignore_ascii_case("AGE") {
6560            Some("AGE")
6561        } else if name.eq_ignore_ascii_case("ERF") {
6562            Some("ERF")
6563        } else if name.eq_ignore_ascii_case("SINH") {
6564            Some("SINH")
6565        } else if name.eq_ignore_ascii_case("COSH") {
6566            Some("COSH")
6567        } else if name.eq_ignore_ascii_case("TANH") {
6568            Some("TANH")
6569        } else if name.eq_ignore_ascii_case("ASINH") {
6570            Some("ASINH")
6571        } else if name.eq_ignore_ascii_case("ACOSH") {
6572            Some("ACOSH")
6573        } else if name.eq_ignore_ascii_case("ATANH") {
6574            Some("ATANH")
6575        } else if name.eq_ignore_ascii_case("GCD") {
6576            Some("GCD")
6577        } else if name.eq_ignore_ascii_case("LCM") {
6578            Some("LCM")
6579        } else if name.eq_ignore_ascii_case("QUOTE_LITERAL") {
6580            Some("QUOTE_LITERAL")
6581        } else if name.eq_ignore_ascii_case("WIDTH_BUCKET") {
6582            Some("WIDTH_BUCKET")
6583        } else if name.eq_ignore_ascii_case("SCALE") {
6584            Some("SCALE")
6585        } else if name.eq_ignore_ascii_case("TRIM_SCALE") {
6586            Some("TRIM_SCALE")
6587        } else if name.eq_ignore_ascii_case("MIN_SCALE") {
6588            Some("MIN_SCALE")
6589        } else if name.eq_ignore_ascii_case("FACTORIAL") {
6590            Some("FACTORIAL")
6591        } else if name.eq_ignore_ascii_case("PG_LSN") {
6592            Some("PG_LSN")
6593        } else if name.eq_ignore_ascii_case("TO_CHAR") {
6594            Some("TO_CHAR")
6595        } else if name.eq_ignore_ascii_case("PG_TYPEOF") {
6596            Some("PG_TYPEOF")
6597        } else if name.eq_ignore_ascii_case("BIT_AND") {
6598            Some("BIT_AND")
6599        } else if name.eq_ignore_ascii_case("BIT_OR") {
6600            Some("BIT_OR")
6601        } else if name.eq_ignore_ascii_case("BIT_XOR") {
6602            Some("BIT_XOR")
6603        } else if name.eq_ignore_ascii_case("CORR") {
6604            Some("CORR")
6605        } else if name.eq_ignore_ascii_case("COVAR_POP") {
6606            Some("COVAR_POP")
6607        } else if name.eq_ignore_ascii_case("COVAR_SAMP") {
6608            Some("COVAR_SAMP")
6609        } else if name.eq_ignore_ascii_case("REGR_AVGX") {
6610            Some("REGR_AVGX")
6611        } else if name.eq_ignore_ascii_case("REGR_AVGY") {
6612            Some("REGR_AVGY")
6613        } else if name.eq_ignore_ascii_case("REGR_COUNT") {
6614            Some("REGR_COUNT")
6615        } else if name.eq_ignore_ascii_case("REGR_INTERCEPT") {
6616            Some("REGR_INTERCEPT")
6617        } else if name.eq_ignore_ascii_case("REGR_R2") {
6618            Some("REGR_R2")
6619        } else if name.eq_ignore_ascii_case("REGR_SLOPE") {
6620            Some("REGR_SLOPE")
6621        } else if name.eq_ignore_ascii_case("REGR_SXX") {
6622            Some("REGR_SXX")
6623        } else if name.eq_ignore_ascii_case("REGR_SXY") {
6624            Some("REGR_SXY")
6625        } else if name.eq_ignore_ascii_case("REGR_SYY") {
6626            Some("REGR_SYY")
6627        } else if name.eq_ignore_ascii_case("FLOAT8_ACCUM") {
6628            Some("FLOAT8_ACCUM")
6629        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_ACCUM") {
6630            Some("FLOAT8_REGR_ACCUM")
6631        } else if name.eq_ignore_ascii_case("FLOAT8_COMBINE") {
6632            Some("FLOAT8_COMBINE")
6633        } else if name.eq_ignore_ascii_case("FLOAT8_REGR_COMBINE") {
6634            Some("FLOAT8_REGR_COMBINE")
6635        } else if name.eq_ignore_ascii_case("BOOLAND_STATEFUNC") {
6636            Some("BOOLAND_STATEFUNC")
6637        } else if name.eq_ignore_ascii_case("BOOLOR_STATEFUNC") {
6638            Some("BOOLOR_STATEFUNC")
6639        } else {
6640            None
6641        }
6642    }
6643
6644    fn normalize_postgres_trim_for_tsql(expr: Expression) -> Result<Expression> {
6645        transform_recursive(expr, &|e| match e {
6646            Expression::Trim(trim) => {
6647                let mut trim = *trim;
6648                trim.characters = trim.characters.map(Self::strip_postgres_text_literal_cast);
6649                match trim.position {
6650                    crate::expressions::TrimPosition::Both
6651                        if trim.position_explicit && trim.characters.is_some() =>
6652                    {
6653                        trim.position_explicit = false;
6654                        trim.sql_standard_syntax = true;
6655                        Ok(Expression::Trim(Box::new(trim)))
6656                    }
6657                    crate::expressions::TrimPosition::Leading if trim.characters.is_some() => {
6658                        let characters = trim.characters.take().expect("checked above");
6659                        Ok(Expression::Function(Box::new(Function::new(
6660                            "LTRIM",
6661                            vec![trim.this, characters],
6662                        ))))
6663                    }
6664                    crate::expressions::TrimPosition::Trailing if trim.characters.is_some() => {
6665                        let characters = trim.characters.take().expect("checked above");
6666                        Ok(Expression::Function(Box::new(Function::new(
6667                            "RTRIM",
6668                            vec![trim.this, characters],
6669                        ))))
6670                    }
6671                    _ => Ok(Expression::Trim(Box::new(trim))),
6672                }
6673            }
6674            other => Ok(other),
6675        })
6676    }
6677
6678    fn normalize_postgres_string_semantics_for_tsql(expr: Expression) -> Result<Expression> {
6679        transform_recursive(expr, &|e| match e {
6680            Expression::Like(mut op) => {
6681                Self::recover_postgres_like_escape(&mut op);
6682                Ok(Expression::Like(op))
6683            }
6684            Expression::ILike(mut op) => {
6685                Self::recover_postgres_like_escape(&mut op);
6686                Ok(Expression::ILike(op))
6687            }
6688            Expression::Substring(mut substring)
6689                if substring.length.is_none()
6690                    && Self::is_explicitly_numeric_expression(&substring.start) =>
6691            {
6692                substring.length = Some(Expression::number(i32::MAX as i64));
6693                Ok(Expression::Substring(substring))
6694            }
6695            Expression::Trim(mut trim) => {
6696                trim.characters = trim.characters.map(Self::strip_postgres_text_literal_cast);
6697                Ok(Expression::Trim(trim))
6698            }
6699            Expression::Function(mut function)
6700                if !function.quoted
6701                    && matches!(
6702                        function.name.to_ascii_uppercase().as_str(),
6703                        "BTRIM" | "LTRIM" | "RTRIM"
6704                    )
6705                    && function.args.len() == 2 =>
6706            {
6707                function.args[1] = Self::strip_postgres_text_literal_cast(function.args[1].clone());
6708                Ok(Expression::Function(function))
6709            }
6710            Expression::Translate(translate) => {
6711                Ok(Self::normalize_postgres_translate_for_tsql(*translate))
6712            }
6713            Expression::Function(function)
6714                if !function.quoted
6715                    && function.name.eq_ignore_ascii_case("TRANSLATE")
6716                    && function.args.len() == 3 =>
6717            {
6718                Ok(Self::normalize_postgres_translate_function_for_tsql(
6719                    *function,
6720                ))
6721            }
6722            other => Ok(other),
6723        })
6724    }
6725
6726    fn normalize_postgres_bytea_literals_for_tsql(expr: Expression) -> Result<Expression> {
6727        transform_recursive(expr, &|e| match e {
6728            Expression::Cast(cast) if Self::is_postgres_bytea_data_type(&cast.to) => {
6729                let Some(value) = Self::postgres_plain_string_literal_value(&cast.this) else {
6730                    return Ok(Expression::Cast(cast));
6731                };
6732                let Some(hex) = Self::postgres_bytea_hex_payload(value) else {
6733                    return Ok(Expression::Cast(cast));
6734                };
6735
6736                // Replace the complete BYTEA cast. Keeping a bare T-SQL
6737                // CAST(... AS VARBINARY) would apply SQL Server's default length
6738                // and could truncate payloads longer than 30 bytes.
6739                Ok(Expression::Literal(Box::new(Literal::HexString(hex))))
6740            }
6741            other => Ok(other),
6742        })
6743    }
6744
6745    fn postgres_plain_string_literal_value(expr: &Expression) -> Option<&str> {
6746        match expr {
6747            Expression::Literal(literal) => match literal.as_ref() {
6748                Literal::String(value) => Some(value),
6749                _ => None,
6750            },
6751            Expression::Paren(paren) => Self::postgres_plain_string_literal_value(&paren.this),
6752            _ => None,
6753        }
6754    }
6755
6756    fn postgres_bytea_hex_payload(value: &str) -> Option<String> {
6757        let payload = value.strip_prefix("\\x")?;
6758        if payload.is_empty() {
6759            return Some(String::new());
6760        }
6761
6762        let mut chars = payload.chars().peekable();
6763        let mut hex = String::with_capacity(payload.len());
6764        loop {
6765            let high = chars.next()?;
6766            let low = chars.next()?;
6767            if !high.is_ascii_hexdigit() || !low.is_ascii_hexdigit() {
6768                return None;
6769            }
6770            hex.push(high);
6771            hex.push(low);
6772
6773            let Some(next) = chars.peek().copied() else {
6774                return Some(hex);
6775            };
6776            if next.is_ascii_whitespace() {
6777                while chars
6778                    .peek()
6779                    .is_some_and(|character| character.is_ascii_whitespace())
6780                {
6781                    chars.next();
6782                }
6783                // PostgreSQL permits whitespace between byte pairs, not after
6784                // the prefix or after the final pair.
6785                chars.peek()?;
6786            }
6787        }
6788    }
6789
6790    fn is_postgres_bytea_data_type(data_type: &DataType) -> bool {
6791        match data_type {
6792            DataType::VarBinary { length: None } => true,
6793            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("BYTEA"),
6794            _ => false,
6795        }
6796    }
6797
6798    fn postgres_tsql_unsupported_binary_semantics(expr: &Expression) -> Option<&'static str> {
6799        let cast = match expr {
6800            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
6801                if Self::is_postgres_bytea_data_type(&cast.to) =>
6802            {
6803                cast
6804            }
6805            _ => return None,
6806        };
6807
6808        let literal = match &cast.this {
6809            Expression::Literal(literal) => literal.as_ref(),
6810            Expression::Paren(paren) => match &paren.this {
6811                Expression::Literal(literal) => literal.as_ref(),
6812                _ => return None,
6813            },
6814            _ => return None,
6815        };
6816        let value = match literal {
6817            Literal::String(value) | Literal::EscapeString(value) => value,
6818            _ => return None,
6819        };
6820
6821        if value.starts_with("\\x") {
6822            Some("bytea hex literals with invalid or unsupported formatting")
6823        } else if value.contains('\\') {
6824            Some("bytea escape-format literals")
6825        } else {
6826            None
6827        }
6828    }
6829
6830    fn recover_postgres_like_escape(op: &mut crate::expressions::LikeOp) {
6831        if op.escape.is_some() {
6832            return;
6833        }
6834
6835        let Expression::Function(function) = &op.right else {
6836            return;
6837        };
6838        if function.quoted
6839            || function.distinct
6840            || !function.name.eq_ignore_ascii_case("LIKE_ESCAPE")
6841            || function.args.len() != 2
6842        {
6843            return;
6844        }
6845
6846        let pattern = function.args[0].clone();
6847        let escape = function.args[1].clone();
6848        op.right = Self::strip_postgres_text_literal_cast(pattern);
6849        op.escape = Some(Self::strip_postgres_text_literal_cast(escape));
6850    }
6851
6852    fn normalize_postgres_translate_for_tsql(
6853        mut translate: crate::expressions::Translate,
6854    ) -> Expression {
6855        let (Some(from), Some(to)) = (&translate.from_, &translate.to) else {
6856            return Expression::Translate(Box::new(translate));
6857        };
6858
6859        let (Some(from_value), Some(to_value)) = (
6860            Self::postgres_text_literal_value(from),
6861            Self::postgres_text_literal_value(to),
6862        ) else {
6863            return Expression::Translate(Box::new(translate));
6864        };
6865        let from_value = from_value.to_string();
6866        let to_value = to_value.to_string();
6867
6868        if from_value.chars().count() > to_value.chars().count() {
6869            if let Some(input) = Self::postgres_text_literal_value(&translate.this) {
6870                return Expression::string(Self::translate_postgres_literal(
6871                    input,
6872                    &from_value,
6873                    &to_value,
6874                ));
6875            }
6876            return Expression::Translate(Box::new(translate));
6877        }
6878
6879        translate.from_ = Some(Box::new(Self::strip_postgres_text_literal_cast(
6880            *translate.from_.expect("checked above"),
6881        )));
6882        let normalized_to = if from_value.chars().count() < to_value.chars().count() {
6883            Expression::string(
6884                to_value
6885                    .chars()
6886                    .take(from_value.chars().count())
6887                    .collect::<String>(),
6888            )
6889        } else {
6890            Self::strip_postgres_text_literal_cast(*translate.to.expect("checked above"))
6891        };
6892        translate.to = Some(Box::new(normalized_to));
6893        Expression::Translate(Box::new(translate))
6894    }
6895
6896    fn normalize_postgres_translate_function_for_tsql(mut function: Function) -> Expression {
6897        let from = Self::postgres_text_literal_value(&function.args[1]);
6898        let to = Self::postgres_text_literal_value(&function.args[2]);
6899        let (Some(from), Some(to)) = (from, to) else {
6900            return Expression::Function(Box::new(function));
6901        };
6902        let from = from.to_string();
6903        let to = to.to_string();
6904
6905        if from.chars().count() > to.chars().count() {
6906            if let Some(input) = Self::postgres_text_literal_value(&function.args[0]) {
6907                return Expression::string(Self::translate_postgres_literal(input, &from, &to));
6908            }
6909            return Expression::Function(Box::new(function));
6910        }
6911
6912        function.args[1] = Self::strip_postgres_text_literal_cast(function.args[1].clone());
6913        function.args[2] = if from.chars().count() < to.chars().count() {
6914            Expression::string(to.chars().take(from.chars().count()).collect::<String>())
6915        } else {
6916            Self::strip_postgres_text_literal_cast(function.args[2].clone())
6917        };
6918        Expression::Function(Box::new(function))
6919    }
6920
6921    fn translate_postgres_literal(input: &str, from: &str, to: &str) -> String {
6922        let from = from.chars().collect::<Vec<_>>();
6923        let to = to.chars().collect::<Vec<_>>();
6924        let mut output = String::with_capacity(input.len());
6925
6926        for ch in input.chars() {
6927            match from.iter().position(|candidate| *candidate == ch) {
6928                Some(index) if index < to.len() => output.push(to[index]),
6929                Some(_) => {}
6930                None => output.push(ch),
6931            }
6932        }
6933
6934        output
6935    }
6936
6937    fn postgres_tsql_unsupported_string_semantics(expr: &Expression) -> Option<&'static str> {
6938        match expr {
6939            Expression::Substring(substring) if substring.length.is_none() => {
6940                if Self::postgres_text_literal_value(&substring.start).is_some() {
6941                    Some("regular-expression SUBSTRING")
6942                } else {
6943                    Some("SUBSTRING without a statically numeric start position")
6944                }
6945            }
6946            Expression::Translate(translate) => {
6947                let from = translate
6948                    .from_
6949                    .as_deref()
6950                    .and_then(Self::postgres_text_literal_value);
6951                let to = translate
6952                    .to
6953                    .as_deref()
6954                    .and_then(Self::postgres_text_literal_value);
6955                match (from, to) {
6956                    (Some(from), Some(to)) if from.chars().count() == to.chars().count() => None,
6957                    _ => Some("TRANSLATE with source and replacement lengths that differ or cannot be proven equal"),
6958                }
6959            }
6960            Expression::Function(function)
6961                if !function.quoted && function.name.eq_ignore_ascii_case("LIKE_ESCAPE") =>
6962            {
6963                Some("LIKE_ESCAPE helper outside a LIKE predicate")
6964            }
6965            Expression::Function(function)
6966                if !function.quoted
6967                    && function.name.eq_ignore_ascii_case("TRANSLATE")
6968                    && function.args.len() == 3 =>
6969            {
6970                let from = Self::postgres_text_literal_value(&function.args[1]);
6971                let to = Self::postgres_text_literal_value(&function.args[2]);
6972                match (from, to) {
6973                    (Some(from), Some(to)) if from.chars().count() == to.chars().count() => None,
6974                    _ => Some("TRANSLATE with source and replacement lengths that differ or cannot be proven equal"),
6975                }
6976            }
6977            Expression::Trim(trim)
6978                if trim
6979                    .characters
6980                    .as_ref()
6981                    .is_some_and(Self::is_unbounded_text_cast) =>
6982            {
6983                Some("TRIM character set cast to an unbounded text type")
6984            }
6985            Expression::Function(function)
6986                if !function.quoted
6987                    && matches!(
6988                        function.name.to_ascii_uppercase().as_str(),
6989                        "LTRIM" | "RTRIM"
6990                    )
6991                    && function.args.len() == 2
6992                    && Self::is_unbounded_text_cast(&function.args[1]) =>
6993            {
6994                Some("TRIM character set cast to an unbounded text type")
6995            }
6996            _ => None,
6997        }
6998    }
6999
7000    fn strip_postgres_text_literal_cast(expr: Expression) -> Expression {
7001        match expr {
7002            Expression::Cast(cast)
7003                if Self::is_text_data_type(&cast.to)
7004                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
7005            {
7006                Self::strip_postgres_text_literal_cast(cast.this)
7007            }
7008            Expression::TryCast(cast)
7009                if Self::is_text_data_type(&cast.to)
7010                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
7011            {
7012                Self::strip_postgres_text_literal_cast(cast.this)
7013            }
7014            Expression::SafeCast(cast)
7015                if Self::is_text_data_type(&cast.to)
7016                    && Self::postgres_text_literal_value(&cast.this).is_some() =>
7017            {
7018                Self::strip_postgres_text_literal_cast(cast.this)
7019            }
7020            Expression::Paren(mut paren)
7021                if Self::postgres_text_literal_value(&paren.this).is_some() =>
7022            {
7023                paren.this = Self::strip_postgres_text_literal_cast(paren.this);
7024                Expression::Paren(paren)
7025            }
7026            other => other,
7027        }
7028    }
7029
7030    fn postgres_text_literal_value(expr: &Expression) -> Option<&str> {
7031        match expr {
7032            Expression::Literal(literal) if literal.is_string() => Some(literal.value_str()),
7033            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
7034                if Self::is_text_data_type(&cast.to) =>
7035            {
7036                Self::postgres_text_literal_value(&cast.this)
7037            }
7038            Expression::Alias(alias) => Self::postgres_text_literal_value(&alias.this),
7039            Expression::Paren(paren) => Self::postgres_text_literal_value(&paren.this),
7040            _ => None,
7041        }
7042    }
7043
7044    fn is_text_data_type(data_type: &DataType) -> bool {
7045        match data_type {
7046            DataType::Char { .. }
7047            | DataType::VarChar { .. }
7048            | DataType::String { .. }
7049            | DataType::Text
7050            | DataType::TextWithLength { .. } => true,
7051            DataType::Custom { name } => {
7052                let base = name
7053                    .split_once('(')
7054                    .map_or(name.as_str(), |(base, _)| base)
7055                    .trim();
7056                matches!(
7057                    base.to_ascii_uppercase().as_str(),
7058                    "CHAR"
7059                        | "NCHAR"
7060                        | "VARCHAR"
7061                        | "NVARCHAR"
7062                        | "TEXT"
7063                        | "NTEXT"
7064                        | "STRING"
7065                        | "CHARACTER VARYING"
7066                )
7067            }
7068            _ => false,
7069        }
7070    }
7071
7072    fn is_unbounded_text_cast(expr: &Expression) -> bool {
7073        let data_type = match expr {
7074            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
7075                &cast.to
7076            }
7077            Expression::Paren(paren) => return Self::is_unbounded_text_cast(&paren.this),
7078            _ => return false,
7079        };
7080
7081        match data_type {
7082            DataType::Text => true,
7083            DataType::VarChar { length: None, .. } | DataType::String { length: None } => true,
7084            DataType::Custom { name } => name.to_ascii_uppercase().contains("(MAX)"),
7085            _ => false,
7086        }
7087    }
7088
7089    fn is_explicitly_numeric_expression(expr: &Expression) -> bool {
7090        if expr.inferred_type().is_some_and(Self::is_numeric_data_type) {
7091            return true;
7092        }
7093
7094        match expr {
7095            Expression::Literal(literal) => literal.is_number(),
7096            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
7097                Self::is_numeric_data_type(&cast.to)
7098            }
7099            Expression::Alias(alias) => Self::is_explicitly_numeric_expression(&alias.this),
7100            Expression::Paren(paren) => Self::is_explicitly_numeric_expression(&paren.this),
7101            Expression::Neg(unary) => Self::is_explicitly_numeric_expression(&unary.this),
7102            _ => false,
7103        }
7104    }
7105
7106    fn is_numeric_data_type(data_type: &DataType) -> bool {
7107        match data_type {
7108            DataType::TinyInt { .. }
7109            | DataType::SmallInt { .. }
7110            | DataType::Int { .. }
7111            | DataType::BigInt { .. }
7112            | DataType::Float { .. }
7113            | DataType::Double { .. }
7114            | DataType::Decimal { .. } => true,
7115            DataType::Custom { name } => {
7116                let base = name
7117                    .split_once('(')
7118                    .map_or(name.as_str(), |(base, _)| base)
7119                    .trim();
7120                matches!(
7121                    base.to_ascii_uppercase().as_str(),
7122                    "TINYINT"
7123                        | "SMALLINT"
7124                        | "INT"
7125                        | "INTEGER"
7126                        | "BIGINT"
7127                        | "DECIMAL"
7128                        | "NUMERIC"
7129                        | "REAL"
7130                        | "FLOAT"
7131                        | "MONEY"
7132                        | "SMALLMONEY"
7133                )
7134            }
7135            _ => false,
7136        }
7137    }
7138
7139    fn normalize_postgres_only_for_tsql(expr: Expression) -> Result<Expression> {
7140        transform_recursive(expr, &|e| match e {
7141            Expression::Table(mut table) if table.only => {
7142                table.only = false;
7143                Ok(Expression::Table(table))
7144            }
7145            other => Ok(other),
7146        })
7147    }
7148
7149    fn rewrite_postgres_json_array_elements_select_for_tsql(
7150        expr: Expression,
7151    ) -> Result<Expression> {
7152        let Expression::Select(select) = expr else {
7153            return Ok(expr);
7154        };
7155        let mut select = *select;
7156        if !Self::is_plain_single_projection_select(&select) {
7157            return Ok(Expression::Select(Box::new(select)));
7158        }
7159
7160        let Some(json_arg) =
7161            Self::postgres_json_array_elements_projection_arg(&select.expressions[0])
7162        else {
7163            return Ok(Expression::Select(Box::new(select)));
7164        };
7165
7166        select.expressions = vec![Expression::column("value")];
7167        select.from = Some(From {
7168            expressions: vec![Expression::OpenJSON(Box::new(
7169                crate::expressions::OpenJSON {
7170                    this: Box::new(json_arg),
7171                    path: None,
7172                    expressions: Vec::new(),
7173                },
7174            ))],
7175        });
7176
7177        Ok(Expression::Select(Box::new(select)))
7178    }
7179
7180    fn is_plain_single_projection_select(select: &crate::expressions::Select) -> bool {
7181        select.expressions.len() == 1
7182            && select.from.is_none()
7183            && select.joins.is_empty()
7184            && select.lateral_views.is_empty()
7185            && select.prewhere.is_none()
7186            && select.where_clause.is_none()
7187            && select.group_by.is_none()
7188            && select.having.is_none()
7189            && select.qualify.is_none()
7190            && select.order_by.is_none()
7191            && select.distribute_by.is_none()
7192            && select.cluster_by.is_none()
7193            && select.sort_by.is_none()
7194            && select.limit.is_none()
7195            && select.offset.is_none()
7196            && select.limit_by.is_none()
7197            && select.fetch.is_none()
7198            && !select.distinct
7199            && select.distinct_on.is_none()
7200            && select.top.is_none()
7201            && select.with.is_none()
7202            && select.sample.is_none()
7203            && select.into.is_none()
7204            && select.locks.is_empty()
7205            && select.for_xml.is_empty()
7206            && select.for_json.is_empty()
7207            && select.exclude.is_none()
7208    }
7209
7210    fn postgres_json_array_elements_projection_arg(expr: &Expression) -> Option<Expression> {
7211        match expr {
7212            Expression::Function(function)
7213                if Self::node_is_postgres_json_array_elements(expr) && function.args.len() == 1 =>
7214            {
7215                Some(function.args[0].clone())
7216            }
7217            Expression::Alias(alias) => {
7218                Self::postgres_json_array_elements_projection_arg(&alias.this)
7219            }
7220            _ => None,
7221        }
7222    }
7223
7224    fn normalize_postgres_type_function_casts(
7225        expr: Expression,
7226        target: DialectType,
7227    ) -> Result<Expression> {
7228        transform_recursive(expr, &|e| match e {
7229            Expression::Function(function) => {
7230                let mut function = *function;
7231                if function.args.len() == 1
7232                    && !function.distinct
7233                    && !function.quoted
7234                    && !function.use_bracket_syntax
7235                    && !function.name.contains('.')
7236                {
7237                    if let Some(to) = Self::postgres_type_function_data_type(&function.name) {
7238                        let this = function.args.remove(0);
7239                        let cast = Cast {
7240                            this,
7241                            to,
7242                            trailing_comments: function.trailing_comments,
7243                            double_colon_syntax: false,
7244                            format: None,
7245                            default: None,
7246                            inferred_type: function.inferred_type,
7247                        };
7248                        return Ok(
7249                            if matches!(target, DialectType::TSQL | DialectType::Fabric) {
7250                                normalization::rewrite_postgres_float_to_integer_cast(cast)
7251                            } else {
7252                                Expression::Cast(Box::new(cast))
7253                            },
7254                        );
7255                    }
7256                }
7257                Ok(Expression::Function(Box::new(function)))
7258            }
7259            _ => Ok(e),
7260        })
7261    }
7262
7263    fn node_is_postgres_type_function_cast(expr: &Expression) -> bool {
7264        matches!(
7265            expr,
7266            Expression::Function(function)
7267                if !function.quoted
7268                    && !function.use_bracket_syntax
7269                    && !function.name.contains('.')
7270                    && Self::postgres_type_function_data_type(&function.name).is_some()
7271        )
7272    }
7273
7274    fn postgres_type_function_data_type(name: &str) -> Option<DataType> {
7275        match name.to_ascii_uppercase().as_str() {
7276            "NUMERIC" | "DECIMAL" | "DEC" => Some(DataType::Decimal {
7277                precision: None,
7278                scale: None,
7279            }),
7280            "INT2" | "SMALLINT" => Some(DataType::SmallInt { length: None }),
7281            "INT4" | "INT" => Some(DataType::Int {
7282                length: None,
7283                integer_spelling: false,
7284            }),
7285            "INTEGER" => Some(DataType::Int {
7286                length: None,
7287                integer_spelling: true,
7288            }),
7289            "INT8" | "BIGINT" => Some(DataType::BigInt { length: None }),
7290            "FLOAT4" | "REAL" => Some(DataType::Float {
7291                precision: None,
7292                scale: None,
7293                real_spelling: true,
7294            }),
7295            "FLOAT8" => Some(DataType::Double {
7296                precision: None,
7297                scale: None,
7298            }),
7299            "BOOL" | "BOOLEAN" => Some(DataType::Boolean),
7300            "TEXT" => Some(DataType::Text),
7301            "VARCHAR" => Some(DataType::VarChar {
7302                length: None,
7303                parenthesized_length: false,
7304            }),
7305            "UUID" => Some(DataType::Uuid),
7306            _ => None,
7307        }
7308    }
7309
7310    fn rewrite_boolean_values_for_tsql(expr: Expression) -> Result<Expression> {
7311        match expr {
7312            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
7313            Expression::Subquery(mut subquery) => {
7314                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
7315                Ok(Expression::Subquery(subquery))
7316            }
7317            Expression::Union(mut union) => {
7318                let left = std::mem::replace(&mut union.left, Expression::null());
7319                let right = std::mem::replace(&mut union.right, Expression::null());
7320                union.left = Self::rewrite_boolean_values_for_tsql(left)?;
7321                union.right = Self::rewrite_boolean_values_for_tsql(right)?;
7322                if let Some(mut with) = union.with.take() {
7323                    with.ctes = with
7324                        .ctes
7325                        .into_iter()
7326                        .map(|mut cte| {
7327                            cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
7328                            Ok(cte)
7329                        })
7330                        .collect::<Result<Vec<_>>>()?;
7331                    union.with = Some(with);
7332                }
7333                Ok(Expression::Union(union))
7334            }
7335            Expression::Intersect(mut intersect) => {
7336                let left = std::mem::replace(&mut intersect.left, Expression::null());
7337                let right = std::mem::replace(&mut intersect.right, Expression::null());
7338                intersect.left = Self::rewrite_boolean_values_for_tsql(left)?;
7339                intersect.right = Self::rewrite_boolean_values_for_tsql(right)?;
7340                Ok(Expression::Intersect(intersect))
7341            }
7342            Expression::Except(mut except) => {
7343                let left = std::mem::replace(&mut except.left, Expression::null());
7344                let right = std::mem::replace(&mut except.right, Expression::null());
7345                except.left = Self::rewrite_boolean_values_for_tsql(left)?;
7346                except.right = Self::rewrite_boolean_values_for_tsql(right)?;
7347                Ok(Expression::Except(except))
7348            }
7349            other => Self::rewrite_tsql_boolean_nested_contexts(other),
7350        }
7351    }
7352
7353    fn rewrite_postgres_row_value_equality_for_tsql(expr: Expression) -> Result<Expression> {
7354        transform_recursive(expr, &|e| match e {
7355            Expression::Eq(op) => {
7356                let op = *op;
7357                Ok(Self::postgres_row_value_equality_to_tsql_scalar(&op)
7358                    .unwrap_or_else(|| Expression::Eq(Box::new(op))))
7359            }
7360            other => Ok(other),
7361        })
7362    }
7363
7364    fn postgres_row_value_equality_to_tsql_scalar(op: &BinaryOp) -> Option<Expression> {
7365        let (row, query) =
7366            if Self::expr_is_row_value(&op.left) && Self::expr_is_subquery_like(&op.right) {
7367                (&op.left, &op.right)
7368            } else if Self::expr_is_row_value(&op.right) && Self::expr_is_subquery_like(&op.left) {
7369                (&op.right, &op.left)
7370            } else {
7371                return None;
7372            };
7373
7374        let row_values = Self::row_value_expressions(row)?;
7375        let projection_count = Self::subquery_projection_count(query)?;
7376        if row_values.is_empty() || row_values.len() != projection_count {
7377            return None;
7378        }
7379
7380        // Keep the complete original query behind a derived table. The outer scalar
7381        // SELECT therefore returns the same number of rows as the PostgreSQL
7382        // single-row subquery: zero rows stay NULL and multiple rows still raise a
7383        // scalar-subquery cardinality error in T-SQL/Fabric.
7384        let mut taken_names = HashSet::new();
7385        Self::collect_generated_alias_conflicts(row, &mut taken_names);
7386        Self::collect_generated_alias_conflicts(query, &mut taken_names);
7387
7388        let source_alias = find_new_name(&taken_names, "_polyglot_row");
7389        taken_names.insert(source_alias.to_ascii_lowercase());
7390        let column_aliases = (1..=row_values.len())
7391            .map(|index| {
7392                let name = find_new_name(&taken_names, &format!("_polyglot_row_value_{index}"));
7393                taken_names.insert(name.to_ascii_lowercase());
7394                Identifier::new(name)
7395            })
7396            .collect::<Vec<_>>();
7397        let source = Self::subquery_as_derived_table(
7398            query,
7399            Identifier::new(&source_alias),
7400            column_aliases.clone(),
7401        )?;
7402
7403        let mut equal_components = Vec::with_capacity(row_values.len());
7404        let mut unequal_components = Vec::with_capacity(row_values.len());
7405        for (column, row_value) in column_aliases.into_iter().zip(row_values) {
7406            let projected = Expression::qualified_column(source_alias.clone(), column.name);
7407            equal_components.push(Expression::Eq(Box::new(BinaryOp::new(
7408                projected.clone(),
7409                row_value.clone(),
7410            ))));
7411            unequal_components.push(Expression::Neq(Box::new(BinaryOp::new(
7412                projected, row_value,
7413            ))));
7414        }
7415
7416        let all_equal = equal_components
7417            .into_iter()
7418            .reduce(|left, right| Expression::And(Box::new(BinaryOp::new(left, right))))?;
7419        let any_unequal = unequal_components
7420            .into_iter()
7421            .reduce(|left, right| Expression::Or(Box::new(BinaryOp::new(left, right))))?;
7422        let comparison = Expression::Case(Box::new(Case {
7423            operand: None,
7424            whens: vec![
7425                (all_equal, Expression::number(1)),
7426                (any_unequal, Expression::number(0)),
7427            ],
7428            else_: Some(Expression::null()),
7429            comments: Vec::new(),
7430            inferred_type: None,
7431        }));
7432
7433        let scalar_select = Select::new().column(comparison).from(source);
7434        let scalar_subquery = Expression::Subquery(Box::new(Subquery {
7435            this: Expression::Select(Box::new(scalar_select)),
7436            alias: None,
7437            column_aliases: Vec::new(),
7438            alias_explicit_as: false,
7439            alias_keyword: None,
7440            order_by: None,
7441            limit: None,
7442            offset: None,
7443            distribute_by: None,
7444            sort_by: None,
7445            cluster_by: None,
7446            lateral: false,
7447            modifiers_inside: false,
7448            trailing_comments: Vec::new(),
7449            inferred_type: Some(DataType::Boolean),
7450        }));
7451
7452        Some(Expression::Cast(Box::new(Cast {
7453            this: scalar_subquery,
7454            to: DataType::Boolean,
7455            trailing_comments: Vec::new(),
7456            double_colon_syntax: false,
7457            format: None,
7458            default: None,
7459            inferred_type: Some(DataType::Boolean),
7460        })))
7461    }
7462
7463    fn row_value_expressions(expr: &Expression) -> Option<Vec<Expression>> {
7464        match expr {
7465            Expression::Tuple(tuple) => Some(tuple.expressions.clone()),
7466            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
7467                Some(function.args.clone())
7468            }
7469            Expression::Paren(paren) => Self::row_value_expressions(&paren.this),
7470            _ => None,
7471        }
7472    }
7473
7474    fn subquery_projection_count(expr: &Expression) -> Option<usize> {
7475        match expr {
7476            Expression::Select(select) => Some(select.expressions.len()),
7477            Expression::Subquery(subquery) => Self::subquery_projection_count(&subquery.this),
7478            Expression::Paren(paren) => Self::subquery_projection_count(&paren.this),
7479            _ => None,
7480        }
7481    }
7482
7483    fn subquery_as_derived_table(
7484        expr: &Expression,
7485        alias: Identifier,
7486        column_aliases: Vec<Identifier>,
7487    ) -> Option<Expression> {
7488        match expr.clone() {
7489            Expression::Subquery(mut subquery) => {
7490                subquery.alias = Some(alias);
7491                subquery.column_aliases = column_aliases;
7492                subquery.alias_explicit_as = true;
7493                subquery.alias_keyword = None;
7494                Some(Expression::Subquery(subquery))
7495            }
7496            Expression::Select(_) | Expression::Paren(_) => {
7497                Some(Expression::Subquery(Box::new(Subquery {
7498                    this: expr.clone(),
7499                    alias: Some(alias),
7500                    column_aliases,
7501                    alias_explicit_as: true,
7502                    alias_keyword: None,
7503                    order_by: None,
7504                    limit: None,
7505                    offset: None,
7506                    distribute_by: None,
7507                    sort_by: None,
7508                    cluster_by: None,
7509                    lateral: false,
7510                    modifiers_inside: false,
7511                    trailing_comments: Vec::new(),
7512                    inferred_type: None,
7513                })))
7514            }
7515            _ => None,
7516        }
7517    }
7518
7519    fn collect_generated_alias_conflicts(expr: &Expression, names: &mut HashSet<String>) {
7520        fn insert(names: &mut HashSet<String>, identifier: &Identifier) {
7521            if !identifier.name.is_empty() {
7522                names.insert(identifier.name.to_ascii_lowercase());
7523            }
7524        }
7525
7526        for node in expr.dfs() {
7527            match node {
7528                Expression::Identifier(identifier) => insert(names, identifier),
7529                Expression::Column(column) => {
7530                    insert(names, &column.name);
7531                    if let Some(table) = &column.table {
7532                        insert(names, table);
7533                    }
7534                }
7535                Expression::Table(table) => {
7536                    insert(names, &table.name);
7537                    if let Some(schema) = &table.schema {
7538                        insert(names, schema);
7539                    }
7540                    if let Some(catalog) = &table.catalog {
7541                        insert(names, catalog);
7542                    }
7543                    if let Some(alias) = &table.alias {
7544                        insert(names, alias);
7545                    }
7546                    for alias in &table.column_aliases {
7547                        insert(names, alias);
7548                    }
7549                }
7550                Expression::Alias(alias) => {
7551                    insert(names, &alias.alias);
7552                    for column_alias in &alias.column_aliases {
7553                        insert(names, column_alias);
7554                    }
7555                }
7556                Expression::Subquery(subquery) => {
7557                    if let Some(alias) = &subquery.alias {
7558                        insert(names, alias);
7559                    }
7560                    for column_alias in &subquery.column_aliases {
7561                        insert(names, column_alias);
7562                    }
7563                }
7564                Expression::Cte(cte) => {
7565                    insert(names, &cte.alias);
7566                    for column in &cte.columns {
7567                        insert(names, column);
7568                    }
7569                    for key in &cte.key_expressions {
7570                        insert(names, key);
7571                    }
7572                }
7573                Expression::Values(values) => {
7574                    if let Some(alias) = &values.alias {
7575                        insert(names, alias);
7576                    }
7577                    for column_alias in &values.column_aliases {
7578                        insert(names, column_alias);
7579                    }
7580                }
7581                Expression::Unnest(unnest) => {
7582                    if let Some(alias) = &unnest.alias {
7583                        insert(names, alias);
7584                    }
7585                    if let Some(offset_alias) = &unnest.offset_alias {
7586                        insert(names, offset_alias);
7587                    }
7588                }
7589                _ => {}
7590            }
7591        }
7592    }
7593
7594    fn rewrite_postgres_format_for_tsql(
7595        expr: Expression,
7596        target: DialectType,
7597    ) -> Result<Expression> {
7598        transform_recursive(expr, &|e| match e {
7599            Expression::Function(f) if f.name.eq_ignore_ascii_case("FORMAT") => {
7600                Self::postgres_format_function_to_tsql(*f, target)
7601            }
7602            other => Ok(other),
7603        })
7604    }
7605
7606    fn postgres_format_function_to_tsql(f: Function, target: DialectType) -> Result<Expression> {
7607        let Some(format_expr) = f.args.first() else {
7608            return Err(Self::unsupported_postgres_format_for_tsql(
7609                target,
7610                "missing format string",
7611            ));
7612        };
7613
7614        let format = match format_expr {
7615            Expression::Literal(lit) if lit.is_string() => lit.value_str(),
7616            _ => {
7617                return Err(Self::unsupported_postgres_format_for_tsql(
7618                    target,
7619                    "dynamic format strings",
7620                ))
7621            }
7622        };
7623
7624        let value_args = &f.args[1..];
7625        let mut arg_index = 0usize;
7626        let mut literal = String::new();
7627        let mut segments = Vec::new();
7628        let mut chars = format.chars();
7629
7630        while let Some(ch) = chars.next() {
7631            if ch != '%' {
7632                literal.push(ch);
7633                continue;
7634            }
7635
7636            let Some(specifier) = chars.next() else {
7637                return Err(Self::unsupported_postgres_format_for_tsql(
7638                    target,
7639                    "unterminated format specifier",
7640                ));
7641            };
7642
7643            match specifier {
7644                '%' => literal.push('%'),
7645                's' => {
7646                    if !literal.is_empty() {
7647                        segments.push(Expression::string(std::mem::take(&mut literal)));
7648                    }
7649                    let Some(arg) = value_args.get(arg_index) else {
7650                        return Err(Self::unsupported_postgres_format_for_tsql(
7651                            target,
7652                            "not enough arguments",
7653                        ));
7654                    };
7655                    segments.push(arg.clone());
7656                    arg_index += 1;
7657                }
7658                other => {
7659                    return Err(Self::unsupported_postgres_format_for_tsql(
7660                        target,
7661                        format!("unsupported format specifier %{other}"),
7662                    ))
7663                }
7664            }
7665        }
7666
7667        if !literal.is_empty() {
7668            segments.push(Expression::string(literal));
7669        }
7670
7671        if arg_index != value_args.len() {
7672            return Err(Self::unsupported_postgres_format_for_tsql(
7673                target,
7674                "unused format arguments",
7675            ));
7676        }
7677
7678        Ok(Self::postgres_format_segments_to_tsql_concat(segments))
7679    }
7680
7681    fn postgres_format_segments_to_tsql_concat(mut segments: Vec<Expression>) -> Expression {
7682        if segments.is_empty() {
7683            return Expression::string("");
7684        }
7685
7686        if segments.len() == 1 {
7687            let only = segments.pop().expect("one segment");
7688            if matches!(&only, Expression::Literal(lit) if lit.is_string()) {
7689                return only;
7690            }
7691
7692            return Expression::Function(Box::new(Function::new(
7693                "CONCAT".to_string(),
7694                vec![only, Expression::string("")],
7695            )));
7696        }
7697
7698        Expression::Function(Box::new(Function::new("CONCAT".to_string(), segments)))
7699    }
7700
7701    fn unsupported_postgres_format_for_tsql(
7702        target: DialectType,
7703        reason: impl Into<String>,
7704    ) -> crate::error::Error {
7705        crate::error::Error::unsupported(
7706            format!("PostgreSQL format() ({})", reason.into()),
7707            target.to_string(),
7708        )
7709    }
7710
7711    fn rewrite_boolean_values_in_tsql_select(
7712        mut select: Box<crate::expressions::Select>,
7713    ) -> Result<Expression> {
7714        if let Some(mut with) = select.with.take() {
7715            with.ctes = with
7716                .ctes
7717                .into_iter()
7718                .map(|mut cte| {
7719                    cte.this = Self::rewrite_boolean_values_for_tsql(cte.this)?;
7720                    Ok(cte)
7721                })
7722                .collect::<Result<Vec<_>>>()?;
7723            select.with = Some(with);
7724        }
7725
7726        select.expressions = select
7727            .expressions
7728            .into_iter()
7729            .map(Self::rewrite_tsql_boolean_scalar_value)
7730            .collect::<Result<Vec<_>>>()?;
7731
7732        if let Some(mut from) = select.from.take() {
7733            from.expressions = from
7734                .expressions
7735                .into_iter()
7736                .map(Self::rewrite_tsql_boolean_nested_contexts)
7737                .collect::<Result<Vec<_>>>()?;
7738            select.from = Some(from);
7739        }
7740
7741        select.joins = select
7742            .joins
7743            .into_iter()
7744            .map(|mut join| {
7745                join.this = Self::rewrite_tsql_boolean_nested_contexts(join.this)?;
7746                if let Some(on) = join.on.take() {
7747                    join.on = Some(Self::rewrite_tsql_boolean_predicate_context(on)?);
7748                }
7749                if let Some(match_condition) = join.match_condition.take() {
7750                    join.match_condition = Some(Self::rewrite_tsql_boolean_predicate_context(
7751                        match_condition,
7752                    )?);
7753                }
7754                join.pivots = join
7755                    .pivots
7756                    .into_iter()
7757                    .map(Self::rewrite_tsql_boolean_nested_contexts)
7758                    .collect::<Result<Vec<_>>>()?;
7759                Ok(join)
7760            })
7761            .collect::<Result<Vec<_>>>()?;
7762
7763        select.lateral_views = select
7764            .lateral_views
7765            .into_iter()
7766            .map(|mut lateral_view| {
7767                lateral_view.this = Self::rewrite_tsql_boolean_nested_contexts(lateral_view.this)?;
7768                Ok(lateral_view)
7769            })
7770            .collect::<Result<Vec<_>>>()?;
7771
7772        if let Some(prewhere) = select.prewhere.take() {
7773            select.prewhere = Some(Self::rewrite_tsql_boolean_predicate_context(prewhere)?);
7774        }
7775
7776        if let Some(mut where_clause) = select.where_clause.take() {
7777            where_clause.this = Self::rewrite_tsql_boolean_predicate_context(where_clause.this)?;
7778            select.where_clause = Some(where_clause);
7779        }
7780
7781        if let Some(mut group_by) = select.group_by.take() {
7782            group_by.expressions = group_by
7783                .expressions
7784                .into_iter()
7785                .map(Self::rewrite_tsql_boolean_scalar_value)
7786                .collect::<Result<Vec<_>>>()?;
7787            select.group_by = Some(group_by);
7788        }
7789
7790        if let Some(mut having) = select.having.take() {
7791            having.this = Self::rewrite_tsql_boolean_predicate_context(having.this)?;
7792            select.having = Some(having);
7793        }
7794
7795        if let Some(mut qualify) = select.qualify.take() {
7796            qualify.this = Self::rewrite_tsql_boolean_predicate_context(qualify.this)?;
7797            select.qualify = Some(qualify);
7798        }
7799
7800        if let Some(mut order_by) = select.order_by.take() {
7801            order_by.expressions = Self::rewrite_tsql_boolean_ordered_values(order_by.expressions)?;
7802            select.order_by = Some(order_by);
7803        }
7804
7805        if let Some(mut distribute_by) = select.distribute_by.take() {
7806            distribute_by.expressions = distribute_by
7807                .expressions
7808                .into_iter()
7809                .map(Self::rewrite_tsql_boolean_scalar_value)
7810                .collect::<Result<Vec<_>>>()?;
7811            select.distribute_by = Some(distribute_by);
7812        }
7813
7814        if let Some(mut cluster_by) = select.cluster_by.take() {
7815            cluster_by.expressions =
7816                Self::rewrite_tsql_boolean_ordered_values(cluster_by.expressions)?;
7817            select.cluster_by = Some(cluster_by);
7818        }
7819
7820        if let Some(mut sort_by) = select.sort_by.take() {
7821            sort_by.expressions = Self::rewrite_tsql_boolean_ordered_values(sort_by.expressions)?;
7822            select.sort_by = Some(sort_by);
7823        }
7824
7825        if let Some(limit_by) = select.limit_by.take() {
7826            select.limit_by = Some(
7827                limit_by
7828                    .into_iter()
7829                    .map(Self::rewrite_tsql_boolean_scalar_value)
7830                    .collect::<Result<Vec<_>>>()?,
7831            );
7832        }
7833
7834        if let Some(distinct_on) = select.distinct_on.take() {
7835            select.distinct_on = Some(
7836                distinct_on
7837                    .into_iter()
7838                    .map(Self::rewrite_tsql_boolean_scalar_value)
7839                    .collect::<Result<Vec<_>>>()?,
7840            );
7841        }
7842
7843        if let Some(mut sample) = select.sample.take() {
7844            sample.size = Self::rewrite_tsql_boolean_nested_contexts(sample.size)?;
7845            if let Some(offset) = sample.offset.take() {
7846                sample.offset = Some(Self::rewrite_tsql_boolean_nested_contexts(offset)?);
7847            }
7848            if let Some(bucket_numerator) = sample.bucket_numerator.take() {
7849                sample.bucket_numerator = Some(Box::new(
7850                    Self::rewrite_tsql_boolean_nested_contexts(*bucket_numerator)?,
7851                ));
7852            }
7853            if let Some(bucket_denominator) = sample.bucket_denominator.take() {
7854                sample.bucket_denominator = Some(Box::new(
7855                    Self::rewrite_tsql_boolean_nested_contexts(*bucket_denominator)?,
7856                ));
7857            }
7858            if let Some(bucket_field) = sample.bucket_field.take() {
7859                sample.bucket_field = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7860                    *bucket_field,
7861                )?));
7862            }
7863            select.sample = Some(sample);
7864        }
7865
7866        if let Some(settings) = select.settings.take() {
7867            select.settings = Some(
7868                settings
7869                    .into_iter()
7870                    .map(Self::rewrite_tsql_boolean_nested_contexts)
7871                    .collect::<Result<Vec<_>>>()?,
7872            );
7873        }
7874
7875        if let Some(format) = select.format.take() {
7876            select.format = Some(Self::rewrite_tsql_boolean_nested_contexts(format)?);
7877        }
7878
7879        if let Some(mut windows) = select.windows.take() {
7880            for window in windows.iter_mut() {
7881                Self::rewrite_tsql_boolean_over_values(&mut window.spec)?;
7882            }
7883            select.windows = Some(windows);
7884        }
7885
7886        Ok(Expression::Select(select))
7887    }
7888
7889    fn normalize_postgres_boolean_semantics_for_tsql(expr: Expression) -> Result<Expression> {
7890        transform_recursive(expr, &|e| match e {
7891            Expression::Function(function)
7892                if function.args.len() == 2
7893                    && (function.name.eq_ignore_ascii_case("BOOLEQ")
7894                        || function.name.eq_ignore_ascii_case("BOOLNE")) =>
7895            {
7896                let is_equal = function.name.eq_ignore_ascii_case("BOOLEQ");
7897                let mut args = function.args.into_iter();
7898                let op = BinaryOp {
7899                    left: args.next().expect("checked boolean operator arity"),
7900                    right: args.next().expect("checked boolean operator arity"),
7901                    left_comments: Vec::new(),
7902                    operator_comments: Vec::new(),
7903                    trailing_comments: function.trailing_comments,
7904                    inferred_type: None,
7905                };
7906                if is_equal {
7907                    Ok(Expression::Eq(Box::new(op)))
7908                } else {
7909                    Ok(Expression::Neq(Box::new(op)))
7910                }
7911            }
7912            Expression::Cast(cast)
7913                if matches!(cast.to, DataType::Text)
7914                    && Self::is_known_postgres_boolean_expression(&cast.this) =>
7915            {
7916                Ok(Self::postgres_boolean_text_value(cast.this))
7917            }
7918            other => Ok(other),
7919        })
7920    }
7921
7922    fn is_known_postgres_boolean_expression(expr: &Expression) -> bool {
7923        match expr {
7924            Expression::Boolean(_) => true,
7925            Expression::Cast(cast) => matches!(cast.to, DataType::Boolean),
7926            Expression::Paren(paren) => Self::is_known_postgres_boolean_expression(&paren.this),
7927            other => Self::is_tsql_boolean_value_expression(other),
7928        }
7929    }
7930
7931    fn postgres_boolean_text_value(predicate: Expression) -> Expression {
7932        if let Expression::Boolean(boolean) = predicate {
7933            return Expression::string(if boolean.value { "true" } else { "false" });
7934        }
7935
7936        Self::three_valued_boolean_case(
7937            predicate,
7938            Expression::string("true"),
7939            Expression::string("false"),
7940        )
7941    }
7942
7943    fn rewrite_tsql_boolean_scalar_value(expr: Expression) -> Result<Expression> {
7944        if let Expression::Boolean(boolean) = expr {
7945            return Ok(Expression::Cast(Box::new(Cast {
7946                this: Expression::Boolean(boolean),
7947                to: DataType::Boolean,
7948                trailing_comments: Vec::new(),
7949                double_colon_syntax: false,
7950                format: None,
7951                default: None,
7952                inferred_type: None,
7953            })));
7954        }
7955
7956        if Self::is_tsql_boolean_value_expression(&expr) {
7957            // Tuple/subquery equality currently lowers only its positive branch to EXISTS.
7958            // Keep its established two-way scalar fallback until that rewrite models UNKNOWN.
7959            let can_be_unknown = Self::tsql_boolean_expression_can_be_unknown(&expr)
7960                && !Self::node_is_row_value_subquery_comparison(&expr);
7961            let predicate = Self::rewrite_tsql_boolean_predicate_context(expr)?;
7962            return Ok(Self::tsql_boolean_value_case(predicate, can_be_unknown));
7963        }
7964
7965        match expr {
7966            Expression::Alias(mut alias) => {
7967                alias.this = Self::rewrite_tsql_boolean_scalar_value(alias.this)?;
7968                Ok(Expression::Alias(alias))
7969            }
7970            Expression::Paren(mut paren) => {
7971                paren.this = Self::rewrite_tsql_boolean_scalar_value(paren.this)?;
7972                Ok(Expression::Paren(paren))
7973            }
7974            Expression::Cast(mut cast) => {
7975                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7976                if let Some(format) = cast.format.take() {
7977                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7978                        *format,
7979                    )?));
7980                }
7981                if let Some(default) = cast.default.take() {
7982                    cast.default =
7983                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
7984                }
7985                Ok(Expression::Cast(cast))
7986            }
7987            Expression::TryCast(mut cast) => {
7988                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
7989                if let Some(format) = cast.format.take() {
7990                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
7991                        *format,
7992                    )?));
7993                }
7994                if let Some(default) = cast.default.take() {
7995                    cast.default =
7996                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
7997                }
7998                Ok(Expression::TryCast(cast))
7999            }
8000            Expression::SafeCast(mut cast) => {
8001                cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
8002                if let Some(format) = cast.format.take() {
8003                    cast.format = Some(Box::new(Self::rewrite_tsql_boolean_nested_contexts(
8004                        *format,
8005                    )?));
8006                }
8007                if let Some(default) = cast.default.take() {
8008                    cast.default =
8009                        Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*default)?));
8010                }
8011                Ok(Expression::SafeCast(cast))
8012            }
8013            Expression::Case(mut case) => {
8014                let is_simple_case = case.operand.is_some();
8015                if let Some(operand) = case.operand.take() {
8016                    case.operand = Some(Self::rewrite_tsql_boolean_scalar_value(operand)?);
8017                }
8018                case.whens = case
8019                    .whens
8020                    .into_iter()
8021                    .map(|(condition, result)| {
8022                        let condition = if is_simple_case {
8023                            Self::rewrite_tsql_boolean_scalar_value(condition)?
8024                        } else {
8025                            Self::rewrite_tsql_boolean_predicate_context(condition)?
8026                        };
8027                        Ok((condition, Self::rewrite_tsql_boolean_scalar_value(result)?))
8028                    })
8029                    .collect::<Result<Vec<_>>>()?;
8030                if let Some(else_) = case.else_.take() {
8031                    case.else_ = Some(Self::rewrite_tsql_boolean_scalar_value(else_)?);
8032                }
8033                Ok(Expression::Case(case))
8034            }
8035            Expression::IfFunc(mut if_func) => {
8036                if_func.condition =
8037                    Self::rewrite_tsql_boolean_predicate_context(if_func.condition)?;
8038                if_func.true_value = Self::rewrite_tsql_boolean_scalar_value(if_func.true_value)?;
8039                if let Some(false_value) = if_func.false_value.take() {
8040                    if_func.false_value =
8041                        Some(Self::rewrite_tsql_boolean_scalar_value(false_value)?);
8042                }
8043                Ok(Expression::IfFunc(if_func))
8044            }
8045            Expression::WindowFunction(mut window_function) => {
8046                window_function.this =
8047                    Self::rewrite_tsql_boolean_nested_contexts(window_function.this)?;
8048                Self::rewrite_tsql_boolean_over_values(&mut window_function.over)?;
8049                if let Some(mut keep) = window_function.keep.take() {
8050                    keep.order_by = Self::rewrite_tsql_boolean_ordered_values(keep.order_by)?;
8051                    window_function.keep = Some(keep);
8052                }
8053                Ok(Expression::WindowFunction(window_function))
8054            }
8055            Expression::WithinGroup(mut within_group) => {
8056                within_group.this = Self::rewrite_tsql_boolean_nested_contexts(within_group.this)?;
8057                within_group.order_by =
8058                    Self::rewrite_tsql_boolean_ordered_values(within_group.order_by)?;
8059                Ok(Expression::WithinGroup(within_group))
8060            }
8061            Expression::Subquery(mut subquery) => {
8062                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
8063                Ok(Expression::Subquery(subquery))
8064            }
8065            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
8066            other => Self::rewrite_tsql_boolean_nested_contexts(other),
8067        }
8068    }
8069
8070    fn rewrite_tsql_boolean_predicate_context(expr: Expression) -> Result<Expression> {
8071        let expr = Self::rewrite_tsql_boolean_nested_contexts(expr)?;
8072        Ok(crate::transforms::ensure_bool_condition(expr))
8073    }
8074
8075    fn rewrite_tsql_boolean_nested_contexts(expr: Expression) -> Result<Expression> {
8076        transform_recursive(expr, &|e| match e {
8077            Expression::Select(select) => Self::rewrite_boolean_values_in_tsql_select(select),
8078            Expression::Subquery(mut subquery) => {
8079                subquery.this = Self::rewrite_boolean_values_for_tsql(subquery.this)?;
8080                Ok(Expression::Subquery(subquery))
8081            }
8082            Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => {
8083                Self::rewrite_boolean_values_for_tsql(e)
8084            }
8085            other => Self::rewrite_tsql_boolean_cast_operand(other),
8086        })
8087    }
8088
8089    fn rewrite_tsql_boolean_cast_operand(expr: Expression) -> Result<Expression> {
8090        macro_rules! rewrite_cast_operand {
8091            ($variant:ident, $cast:expr) => {{
8092                let mut cast = $cast;
8093                if Self::is_tsql_boolean_value_expression(&cast.this) {
8094                    cast.this = Self::rewrite_tsql_boolean_scalar_value(cast.this)?;
8095                }
8096                Ok(Expression::$variant(cast))
8097            }};
8098        }
8099
8100        match expr {
8101            Expression::Cast(cast) => rewrite_cast_operand!(Cast, cast),
8102            Expression::TryCast(cast) => rewrite_cast_operand!(TryCast, cast),
8103            Expression::SafeCast(cast) => rewrite_cast_operand!(SafeCast, cast),
8104            other => Ok(other),
8105        }
8106    }
8107
8108    fn rewrite_tsql_boolean_ordered_values(
8109        ordered: Vec<crate::expressions::Ordered>,
8110    ) -> Result<Vec<crate::expressions::Ordered>> {
8111        ordered
8112            .into_iter()
8113            .map(|mut ordered| {
8114                ordered.this = Self::rewrite_tsql_boolean_scalar_value(ordered.this)?;
8115                if let Some(with_fill) = ordered.with_fill.take() {
8116                    ordered.with_fill = Some(Box::new(
8117                        Self::rewrite_tsql_boolean_with_fill_values(*with_fill)?,
8118                    ));
8119                }
8120                Ok(ordered)
8121            })
8122            .collect()
8123    }
8124
8125    fn rewrite_tsql_boolean_with_fill_values(
8126        mut with_fill: crate::expressions::WithFill,
8127    ) -> Result<crate::expressions::WithFill> {
8128        if let Some(from) = with_fill.from_.take() {
8129            with_fill.from_ = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*from)?));
8130        }
8131        if let Some(to) = with_fill.to.take() {
8132            with_fill.to = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*to)?));
8133        }
8134        if let Some(step) = with_fill.step.take() {
8135            with_fill.step = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(*step)?));
8136        }
8137        if let Some(staleness) = with_fill.staleness.take() {
8138            with_fill.staleness = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
8139                *staleness,
8140            )?));
8141        }
8142        if let Some(interpolate) = with_fill.interpolate.take() {
8143            with_fill.interpolate = Some(Box::new(Self::rewrite_tsql_boolean_scalar_value(
8144                *interpolate,
8145            )?));
8146        }
8147        Ok(with_fill)
8148    }
8149
8150    fn rewrite_tsql_boolean_over_values(over: &mut crate::expressions::Over) -> Result<()> {
8151        over.partition_by = std::mem::take(&mut over.partition_by)
8152            .into_iter()
8153            .map(Self::rewrite_tsql_boolean_scalar_value)
8154            .collect::<Result<Vec<_>>>()?;
8155        over.order_by =
8156            Self::rewrite_tsql_boolean_ordered_values(std::mem::take(&mut over.order_by))?;
8157        Ok(())
8158    }
8159
8160    fn is_tsql_boolean_value_expression(expr: &Expression) -> bool {
8161        match expr {
8162            Expression::Paren(paren) => Self::is_tsql_boolean_value_expression(&paren.this),
8163            Expression::Eq(_)
8164            | Expression::Neq(_)
8165            | Expression::Lt(_)
8166            | Expression::Lte(_)
8167            | Expression::Gt(_)
8168            | Expression::Gte(_)
8169            | Expression::Is(_)
8170            | Expression::IsNull(_)
8171            | Expression::IsTrue(_)
8172            | Expression::IsFalse(_)
8173            | Expression::Like(_)
8174            | Expression::ILike(_)
8175            | Expression::StartsWith(_)
8176            | Expression::SimilarTo(_)
8177            | Expression::Glob(_)
8178            | Expression::RegexpLike(_)
8179            | Expression::In(_)
8180            | Expression::Between(_)
8181            | Expression::Exists(_)
8182            | Expression::And(_)
8183            | Expression::Or(_)
8184            | Expression::Not(_)
8185            | Expression::Any(_)
8186            | Expression::All(_)
8187            | Expression::NullSafeEq(_)
8188            | Expression::NullSafeNeq(_)
8189            | Expression::EqualNull(_) => true,
8190            _ => false,
8191        }
8192    }
8193
8194    fn tsql_boolean_expression_can_be_unknown(expr: &Expression) -> bool {
8195        match expr {
8196            Expression::Boolean(_)
8197            | Expression::IsNull(_)
8198            | Expression::IsTrue(_)
8199            | Expression::IsFalse(_)
8200            | Expression::Exists(_)
8201            | Expression::NullSafeEq(_)
8202            | Expression::NullSafeNeq(_)
8203            | Expression::EqualNull(_) => false,
8204            Expression::Paren(paren) => Self::tsql_boolean_expression_can_be_unknown(&paren.this),
8205            Expression::Not(op) => Self::tsql_boolean_expression_can_be_unknown(&op.this),
8206            Expression::And(op) | Expression::Or(op) => {
8207                Self::tsql_boolean_expression_can_be_unknown(&op.left)
8208                    || Self::tsql_boolean_expression_can_be_unknown(&op.right)
8209            }
8210            _ => true,
8211        }
8212    }
8213
8214    fn tsql_boolean_value_case(predicate: Expression, can_be_unknown: bool) -> Expression {
8215        let case = if can_be_unknown {
8216            Self::three_valued_boolean_case(predicate, Expression::number(1), Expression::number(0))
8217        } else {
8218            Expression::Case(Box::new(crate::expressions::Case {
8219                operand: None,
8220                whens: vec![(predicate, Expression::number(1))],
8221                else_: Some(Expression::number(0)),
8222                comments: Vec::new(),
8223                inferred_type: None,
8224            }))
8225        };
8226
8227        Expression::Cast(Box::new(Cast {
8228            this: case,
8229            to: DataType::Boolean,
8230            trailing_comments: Vec::new(),
8231            double_colon_syntax: false,
8232            format: None,
8233            default: None,
8234            inferred_type: None,
8235        }))
8236    }
8237
8238    fn three_valued_boolean_case(
8239        predicate: Expression,
8240        true_value: Expression,
8241        false_value: Expression,
8242    ) -> Expression {
8243        let false_operand = if matches!(predicate, Expression::And(_) | Expression::Or(_)) {
8244            Expression::Paren(Box::new(crate::expressions::Paren {
8245                this: predicate.clone(),
8246                trailing_comments: Vec::new(),
8247            }))
8248        } else {
8249            predicate.clone()
8250        };
8251        let false_predicate = Expression::Not(Box::new(crate::expressions::UnaryOp {
8252            this: false_operand,
8253            inferred_type: None,
8254        }));
8255
8256        Expression::Case(Box::new(crate::expressions::Case {
8257            operand: None,
8258            whens: vec![(predicate, true_value), (false_predicate, false_value)],
8259            else_: Some(Expression::null()),
8260            comments: Vec::new(),
8261            inferred_type: None,
8262        }))
8263    }
8264
8265    fn rewrite_aggregate_filters_for_tsql(expr: Expression) -> Result<Expression> {
8266        transform_recursive(expr, &|e| Self::rewrite_aggregate_filter_for_tsql(e))
8267    }
8268
8269    fn rewrite_aggregate_filter_for_tsql(expr: Expression) -> Result<Expression> {
8270        macro_rules! rewrite_agg_filter {
8271            ($variant:ident, $agg:expr) => {{
8272                let mut agg = $agg;
8273                if let Some(filter) = agg.filter.take() {
8274                    let this = std::mem::replace(&mut agg.this, Expression::null());
8275                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8276                }
8277                Ok(Expression::$variant(agg))
8278            }};
8279        }
8280
8281        match expr {
8282            Expression::Filter(filter) => {
8283                let condition = match *filter.expression {
8284                    Expression::Where(where_) => where_.this,
8285                    other => other,
8286                };
8287                Ok(Self::push_filter_into_tsql_aggregate(
8288                    *filter.this,
8289                    condition,
8290                ))
8291            }
8292            Expression::AggregateFunction(mut agg) => {
8293                if let Some(filter) = agg.filter.take() {
8294                    Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
8295                }
8296                Ok(Expression::AggregateFunction(agg))
8297            }
8298            Expression::Count(mut count) => {
8299                if let Some(filter) = count.filter.take() {
8300                    let value = if count.star {
8301                        Expression::number(1)
8302                    } else {
8303                        count.this.take().unwrap_or_else(|| Expression::number(1))
8304                    };
8305                    count.star = false;
8306                    count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
8307                }
8308                Ok(Expression::Count(count))
8309            }
8310            Expression::Sum(agg) => rewrite_agg_filter!(Sum, agg),
8311            Expression::Avg(agg) => rewrite_agg_filter!(Avg, agg),
8312            Expression::Min(agg) => rewrite_agg_filter!(Min, agg),
8313            Expression::Max(agg) => rewrite_agg_filter!(Max, agg),
8314            Expression::ArrayAgg(agg) => rewrite_agg_filter!(ArrayAgg, agg),
8315            Expression::CountIf(agg) => Ok(Expression::CountIf(agg)),
8316            Expression::Stddev(agg) => rewrite_agg_filter!(Stddev, agg),
8317            Expression::StddevPop(agg) => rewrite_agg_filter!(StddevPop, agg),
8318            Expression::StddevSamp(agg) => rewrite_agg_filter!(StddevSamp, agg),
8319            Expression::Variance(agg) => rewrite_agg_filter!(Variance, agg),
8320            Expression::VarPop(agg) => rewrite_agg_filter!(VarPop, agg),
8321            Expression::VarSamp(agg) => rewrite_agg_filter!(VarSamp, agg),
8322            Expression::Median(agg) => rewrite_agg_filter!(Median, agg),
8323            Expression::Mode(agg) => rewrite_agg_filter!(Mode, agg),
8324            Expression::First(agg) => rewrite_agg_filter!(First, agg),
8325            Expression::Last(agg) => rewrite_agg_filter!(Last, agg),
8326            Expression::AnyValue(agg) => rewrite_agg_filter!(AnyValue, agg),
8327            Expression::ApproxDistinct(agg) => rewrite_agg_filter!(ApproxDistinct, agg),
8328            Expression::ApproxCountDistinct(agg) => {
8329                rewrite_agg_filter!(ApproxCountDistinct, agg)
8330            }
8331            Expression::LogicalAnd(agg) => rewrite_agg_filter!(LogicalAnd, agg),
8332            Expression::LogicalOr(agg) => rewrite_agg_filter!(LogicalOr, agg),
8333            Expression::Skewness(agg) => rewrite_agg_filter!(Skewness, agg),
8334            Expression::ArrayConcatAgg(agg) => rewrite_agg_filter!(ArrayConcatAgg, agg),
8335            Expression::ArrayUniqueAgg(agg) => rewrite_agg_filter!(ArrayUniqueAgg, agg),
8336            Expression::BoolXorAgg(agg) => rewrite_agg_filter!(BoolXorAgg, agg),
8337            Expression::BitwiseAndAgg(agg) => rewrite_agg_filter!(BitwiseAndAgg, agg),
8338            Expression::BitwiseOrAgg(agg) => rewrite_agg_filter!(BitwiseOrAgg, agg),
8339            Expression::BitwiseXorAgg(agg) => rewrite_agg_filter!(BitwiseXorAgg, agg),
8340            Expression::StringAgg(mut agg) => {
8341                if let Some(filter) = agg.filter.take() {
8342                    let this = std::mem::replace(&mut agg.this, Expression::null());
8343                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8344                }
8345                Ok(Expression::StringAgg(agg))
8346            }
8347            Expression::GroupConcat(mut agg) => {
8348                if let Some(filter) = agg.filter.take() {
8349                    let this = std::mem::replace(&mut agg.this, Expression::null());
8350                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8351                }
8352                Ok(Expression::GroupConcat(agg))
8353            }
8354            Expression::ListAgg(mut agg) => {
8355                if let Some(filter) = agg.filter.take() {
8356                    let this = std::mem::replace(&mut agg.this, Expression::null());
8357                    agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8358                }
8359                Ok(Expression::ListAgg(agg))
8360            }
8361            Expression::WithinGroup(mut within_group) => {
8362                within_group.this = Self::rewrite_aggregate_filters_for_tsql(within_group.this)?;
8363                Ok(Expression::WithinGroup(within_group))
8364            }
8365            other => Ok(other),
8366        }
8367    }
8368
8369    fn push_filter_into_tsql_aggregate(expr: Expression, filter: Expression) -> Expression {
8370        macro_rules! push_agg_filter {
8371            ($variant:ident, $agg:expr) => {{
8372                let mut agg = $agg;
8373                let this = std::mem::replace(&mut agg.this, Expression::null());
8374                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8375                agg.filter = None;
8376                Expression::$variant(agg)
8377            }};
8378        }
8379
8380        match expr {
8381            Expression::AggregateFunction(mut agg) => {
8382                Self::rewrite_generic_aggregate_filter_for_tsql(&mut agg, filter);
8383                Expression::AggregateFunction(agg)
8384            }
8385            Expression::Count(mut count) => {
8386                let value = if count.star {
8387                    Expression::number(1)
8388                } else {
8389                    count.this.take().unwrap_or_else(|| Expression::number(1))
8390                };
8391                count.star = false;
8392                count.filter = None;
8393                count.this = Some(Self::conditional_aggregate_value_for_tsql(filter, value));
8394                Expression::Count(count)
8395            }
8396            Expression::Sum(agg) => push_agg_filter!(Sum, agg),
8397            Expression::Avg(agg) => push_agg_filter!(Avg, agg),
8398            Expression::Min(agg) => push_agg_filter!(Min, agg),
8399            Expression::Max(agg) => push_agg_filter!(Max, agg),
8400            Expression::ArrayAgg(agg) => push_agg_filter!(ArrayAgg, agg),
8401            Expression::CountIf(mut agg) => {
8402                agg.filter = Some(filter);
8403                Expression::CountIf(agg)
8404            }
8405            Expression::Stddev(agg) => push_agg_filter!(Stddev, agg),
8406            Expression::StddevPop(agg) => push_agg_filter!(StddevPop, agg),
8407            Expression::StddevSamp(agg) => push_agg_filter!(StddevSamp, agg),
8408            Expression::Variance(agg) => push_agg_filter!(Variance, agg),
8409            Expression::VarPop(agg) => push_agg_filter!(VarPop, agg),
8410            Expression::VarSamp(agg) => push_agg_filter!(VarSamp, agg),
8411            Expression::Median(agg) => push_agg_filter!(Median, agg),
8412            Expression::Mode(agg) => push_agg_filter!(Mode, agg),
8413            Expression::First(agg) => push_agg_filter!(First, agg),
8414            Expression::Last(agg) => push_agg_filter!(Last, agg),
8415            Expression::AnyValue(agg) => push_agg_filter!(AnyValue, agg),
8416            Expression::ApproxDistinct(agg) => push_agg_filter!(ApproxDistinct, agg),
8417            Expression::ApproxCountDistinct(agg) => {
8418                push_agg_filter!(ApproxCountDistinct, agg)
8419            }
8420            Expression::LogicalAnd(agg) => push_agg_filter!(LogicalAnd, agg),
8421            Expression::LogicalOr(agg) => push_agg_filter!(LogicalOr, agg),
8422            Expression::Skewness(agg) => push_agg_filter!(Skewness, agg),
8423            Expression::ArrayConcatAgg(agg) => push_agg_filter!(ArrayConcatAgg, agg),
8424            Expression::ArrayUniqueAgg(agg) => push_agg_filter!(ArrayUniqueAgg, agg),
8425            Expression::BoolXorAgg(agg) => push_agg_filter!(BoolXorAgg, agg),
8426            Expression::BitwiseAndAgg(agg) => push_agg_filter!(BitwiseAndAgg, agg),
8427            Expression::BitwiseOrAgg(agg) => push_agg_filter!(BitwiseOrAgg, agg),
8428            Expression::BitwiseXorAgg(agg) => push_agg_filter!(BitwiseXorAgg, agg),
8429            Expression::StringAgg(mut agg) => {
8430                let this = std::mem::replace(&mut agg.this, Expression::null());
8431                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8432                agg.filter = None;
8433                Expression::StringAgg(agg)
8434            }
8435            Expression::GroupConcat(mut agg) => {
8436                let this = std::mem::replace(&mut agg.this, Expression::null());
8437                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8438                agg.filter = None;
8439                Expression::GroupConcat(agg)
8440            }
8441            Expression::ListAgg(mut agg) => {
8442                let this = std::mem::replace(&mut agg.this, Expression::null());
8443                agg.this = Self::conditional_aggregate_value_for_tsql(filter, this);
8444                agg.filter = None;
8445                Expression::ListAgg(agg)
8446            }
8447            Expression::WithinGroup(mut within_group) => {
8448                within_group.this =
8449                    Self::push_filter_into_tsql_aggregate(within_group.this, filter);
8450                Expression::WithinGroup(within_group)
8451            }
8452            other => Expression::Filter(Box::new(crate::expressions::Filter {
8453                this: Box::new(other),
8454                expression: Box::new(filter),
8455            })),
8456        }
8457    }
8458
8459    fn rewrite_generic_aggregate_filter_for_tsql(
8460        agg: &mut crate::expressions::AggregateFunction,
8461        filter: Expression,
8462    ) {
8463        let is_count =
8464            agg.name.eq_ignore_ascii_case("COUNT") || agg.name.eq_ignore_ascii_case("COUNT_BIG");
8465        let is_count_star = is_count
8466            && (agg.args.is_empty()
8467                || (agg.args.len() == 1 && matches!(agg.args[0], Expression::Star(_))));
8468
8469        if is_count_star {
8470            agg.args = vec![Self::conditional_aggregate_value_for_tsql(
8471                filter,
8472                Expression::number(1),
8473            )];
8474        } else if !agg.args.is_empty() {
8475            agg.args = agg
8476                .args
8477                .drain(..)
8478                .map(|arg| Self::conditional_aggregate_value_for_tsql(filter.clone(), arg))
8479                .collect();
8480        } else {
8481            agg.filter = Some(filter);
8482        }
8483    }
8484
8485    fn conditional_aggregate_value_for_tsql(filter: Expression, value: Expression) -> Expression {
8486        let filter = crate::transforms::ensure_bool_condition(filter);
8487        Expression::Case(Box::new(crate::expressions::Case {
8488            operand: None,
8489            whens: vec![(filter, value)],
8490            else_: None,
8491            comments: Vec::new(),
8492            inferred_type: None,
8493        }))
8494    }
8495
8496    fn reject_pgvector_distance_operators_for_sqlite(&self, sql: &str) -> Result<()> {
8497        let tokens = self.tokenize(sql)?;
8498        for (i, token) in tokens.iter().enumerate() {
8499            if token.token_type == TokenType::NullsafeEq {
8500                return Err(crate::error::Error::unsupported(
8501                    "PostgreSQL pgvector cosine distance operator <=>",
8502                    "SQLite",
8503                ));
8504            }
8505            if token.token_type == TokenType::Lt
8506                && tokens
8507                    .get(i + 1)
8508                    .is_some_and(|token| token.token_type == TokenType::Tilde)
8509                && tokens
8510                    .get(i + 2)
8511                    .is_some_and(|token| token.token_type == TokenType::Gt)
8512            {
8513                return Err(crate::error::Error::unsupported(
8514                    "PostgreSQL pgvector Hamming distance operator <~>",
8515                    "SQLite",
8516                ));
8517            }
8518        }
8519        Ok(())
8520    }
8521
8522    fn normalize_sqlite_double_quoted_defaults(expr: Expression) -> Result<Expression> {
8523        fn normalize_default_expr(expr: Expression) -> Result<Expression> {
8524            transform_recursive(expr, &|e| match e {
8525                Expression::Column(col)
8526                    if col.table.is_none() && col.name.quoted && !col.join_mark =>
8527                {
8528                    Ok(Expression::Literal(Box::new(Literal::String(
8529                        col.name.name,
8530                    ))))
8531                }
8532                Expression::Identifier(id) if id.quoted => {
8533                    Ok(Expression::Literal(Box::new(Literal::String(id.name))))
8534                }
8535                _ => Ok(e),
8536            })
8537        }
8538
8539        fn normalize_column_default(col: &mut crate::expressions::ColumnDef) -> Result<()> {
8540            if let Some(default) = col.default.take() {
8541                col.default = Some(normalize_default_expr(default)?);
8542            }
8543
8544            for constraint in &mut col.constraints {
8545                if let ColumnConstraint::Default(default) = constraint {
8546                    *default = normalize_default_expr(default.clone())?;
8547                }
8548            }
8549
8550            Ok(())
8551        }
8552
8553        transform_recursive(expr, &|e| match e {
8554            Expression::CreateTable(mut ct) => {
8555                for column in &mut ct.columns {
8556                    normalize_column_default(column)?;
8557                }
8558                Ok(Expression::CreateTable(ct))
8559            }
8560            Expression::ColumnDef(mut col) => {
8561                normalize_column_default(&mut col)?;
8562                Ok(Expression::ColumnDef(col))
8563            }
8564            _ => Ok(e),
8565        })
8566    }
8567
8568    fn normalize_postgres_to_sqlite_types(expr: Expression) -> Result<Expression> {
8569        fn sqlite_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
8570            use crate::expressions::DataType;
8571
8572            match dt {
8573                DataType::Bit { .. } => DataType::Int {
8574                    length: None,
8575                    integer_spelling: true,
8576                },
8577                DataType::TextWithLength { .. } => DataType::Text,
8578                DataType::VarChar { .. } => DataType::Text,
8579                DataType::Char { .. } => DataType::Text,
8580                DataType::Timestamp { timezone: true, .. } => DataType::Text,
8581                DataType::Custom { name } => {
8582                    let base = name
8583                        .split_once('(')
8584                        .map_or(name.as_str(), |(base, _)| base)
8585                        .trim();
8586                    if base.eq_ignore_ascii_case("TSVECTOR")
8587                        || base.eq_ignore_ascii_case("TIMESTAMPTZ")
8588                        || base.eq_ignore_ascii_case("TIMESTAMP WITH TIME ZONE")
8589                        || base.eq_ignore_ascii_case("NVARCHAR")
8590                        || base.eq_ignore_ascii_case("NCHAR")
8591                    {
8592                        DataType::Text
8593                    } else {
8594                        DataType::Custom { name }
8595                    }
8596                }
8597                _ => dt,
8598            }
8599        }
8600
8601        transform_recursive(expr, &|e| match e {
8602            Expression::DataType(dt) => Ok(Expression::DataType(sqlite_type(dt))),
8603            Expression::CreateTable(mut ct) => {
8604                for column in &mut ct.columns {
8605                    column.data_type = sqlite_type(column.data_type.clone());
8606                }
8607                Ok(Expression::CreateTable(ct))
8608            }
8609            _ => Ok(e),
8610        })
8611    }
8612
8613    fn normalize_postgres_to_fabric_types(expr: Expression) -> Result<Expression> {
8614        fn fabric_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
8615            use crate::expressions::DataType;
8616
8617            match dt {
8618                DataType::Decimal {
8619                    precision: None,
8620                    scale: None,
8621                } => DataType::Decimal {
8622                    precision: Some(38),
8623                    scale: Some(10),
8624                },
8625                DataType::Json | DataType::JsonB => DataType::Custom {
8626                    name: "VARCHAR(MAX)".to_string(),
8627                },
8628                _ => dt,
8629            }
8630        }
8631
8632        transform_recursive(expr, &|e| match e {
8633            Expression::DataType(dt) => Ok(Expression::DataType(fabric_type(dt))),
8634            Expression::CreateTable(mut ct) => {
8635                for column in &mut ct.columns {
8636                    column.data_type = fabric_type(column.data_type.clone());
8637                }
8638                Ok(Expression::CreateTable(ct))
8639            }
8640            Expression::ColumnDef(mut col) => {
8641                col.data_type = fabric_type(col.data_type);
8642                Ok(Expression::ColumnDef(col))
8643            }
8644            _ => Ok(e),
8645        })
8646    }
8647
8648    /// For DuckDB target: when FROM clause contains RANGE(n), replace
8649    /// `(ROW_NUMBER() OVER (ORDER BY 1 NULLS FIRST) - 1)` with `range` in select expressions.
8650    /// This handles SEQ1/2/4/8 → RANGE transpilation from Snowflake.
8651    fn seq_rownum_to_range(expr: Expression) -> Result<Expression> {
8652        if let Expression::Select(mut select) = expr {
8653            // Check if FROM contains a RANGE function
8654            let has_range_from = if let Some(ref from) = select.from {
8655                from.expressions.iter().any(|e| {
8656                    // Check for direct RANGE(...) or aliased RANGE(...)
8657                    match e {
8658                        Expression::Function(f) => f.name.eq_ignore_ascii_case("RANGE"),
8659                        Expression::Alias(a) => {
8660                            matches!(&a.this, Expression::Function(f) if f.name.eq_ignore_ascii_case("RANGE"))
8661                        }
8662                        _ => false,
8663                    }
8664                })
8665            } else {
8666                false
8667            };
8668
8669            if has_range_from {
8670                // Replace the ROW_NUMBER pattern in select expressions
8671                select.expressions = select
8672                    .expressions
8673                    .into_iter()
8674                    .map(|e| Self::replace_rownum_with_range(e))
8675                    .collect();
8676            }
8677
8678            Ok(Expression::Select(select))
8679        } else {
8680            Ok(expr)
8681        }
8682    }
8683
8684    /// Replace `(ROW_NUMBER() OVER (...) - 1)` with `range` column reference
8685    fn replace_rownum_with_range(expr: Expression) -> Expression {
8686        match expr {
8687            // Match: (ROW_NUMBER() OVER (...) - 1) % N → range % N
8688            Expression::Mod(op) => {
8689                let new_left = Self::try_replace_rownum_paren(&op.left);
8690                Expression::Mod(Box::new(crate::expressions::BinaryOp {
8691                    left: new_left,
8692                    right: op.right,
8693                    left_comments: op.left_comments,
8694                    operator_comments: op.operator_comments,
8695                    trailing_comments: op.trailing_comments,
8696                    inferred_type: op.inferred_type,
8697                }))
8698            }
8699            // Match: (CASE WHEN (ROW...) % N >= ... THEN ... ELSE ... END)
8700            Expression::Paren(p) => {
8701                let inner = Self::replace_rownum_with_range(p.this);
8702                Expression::Paren(Box::new(crate::expressions::Paren {
8703                    this: inner,
8704                    trailing_comments: p.trailing_comments,
8705                }))
8706            }
8707            Expression::Case(mut c) => {
8708                // Replace ROW_NUMBER in WHEN conditions and THEN expressions
8709                c.whens = c
8710                    .whens
8711                    .into_iter()
8712                    .map(|(cond, then)| {
8713                        (
8714                            Self::replace_rownum_with_range(cond),
8715                            Self::replace_rownum_with_range(then),
8716                        )
8717                    })
8718                    .collect();
8719                if let Some(else_) = c.else_ {
8720                    c.else_ = Some(Self::replace_rownum_with_range(else_));
8721                }
8722                Expression::Case(c)
8723            }
8724            Expression::Gte(op) => Expression::Gte(Box::new(crate::expressions::BinaryOp {
8725                left: Self::replace_rownum_with_range(op.left),
8726                right: op.right,
8727                left_comments: op.left_comments,
8728                operator_comments: op.operator_comments,
8729                trailing_comments: op.trailing_comments,
8730                inferred_type: op.inferred_type,
8731            })),
8732            Expression::Sub(op) => Expression::Sub(Box::new(crate::expressions::BinaryOp {
8733                left: Self::replace_rownum_with_range(op.left),
8734                right: op.right,
8735                left_comments: op.left_comments,
8736                operator_comments: op.operator_comments,
8737                trailing_comments: op.trailing_comments,
8738                inferred_type: op.inferred_type,
8739            })),
8740            Expression::Alias(mut a) => {
8741                a.this = Self::replace_rownum_with_range(a.this);
8742                Expression::Alias(a)
8743            }
8744            other => other,
8745        }
8746    }
8747
8748    /// Check if an expression is `(ROW_NUMBER() OVER (...) - 1)` and replace with `range`
8749    fn try_replace_rownum_paren(expr: &Expression) -> Expression {
8750        if let Expression::Paren(ref p) = expr {
8751            if let Expression::Sub(ref sub) = p.this {
8752                if let Expression::WindowFunction(ref wf) = sub.left {
8753                    if let Expression::Function(ref f) = wf.this {
8754                        if f.name.eq_ignore_ascii_case("ROW_NUMBER") {
8755                            if let Expression::Literal(ref lit) = sub.right {
8756                                if let crate::expressions::Literal::Number(ref n) = lit.as_ref() {
8757                                    if n == "1" {
8758                                        return Expression::column("range");
8759                                    }
8760                                }
8761                            }
8762                        }
8763                    }
8764                }
8765            }
8766        }
8767        expr.clone()
8768    }
8769
8770    /// Transform BigQuery GENERATE_DATE_ARRAY in UNNEST for Snowflake target.
8771    /// Converts:
8772    ///   SELECT ..., alias, ... FROM t CROSS JOIN UNNEST(GENERATE_DATE_ARRAY(start, end, INTERVAL '1' unit)) AS alias
8773    /// To:
8774    ///   SELECT ..., DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE)) AS alias, ...
8775    ///   FROM t, LATERAL FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)) AS _t0(seq, key, path, index, alias, this)
8776    fn transform_generate_date_array_snowflake(expr: Expression) -> Result<Expression> {
8777        use crate::expressions::*;
8778        transform_recursive(expr, &|e| {
8779            // Handle ARRAY_SIZE(GENERATE_DATE_ARRAY(...)) -> ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM subquery))
8780            if let Expression::ArraySize(ref af) = e {
8781                if let Expression::Function(ref f) = af.this {
8782                    if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
8783                        let result = Self::convert_array_size_gda_snowflake(f)?;
8784                        return Ok(result);
8785                    }
8786                }
8787            }
8788
8789            let Expression::Select(mut sel) = e else {
8790                return Ok(e);
8791            };
8792
8793            // Find joins with UNNEST containing GenerateSeries (from GENERATE_DATE_ARRAY conversion)
8794            let mut gda_info: Option<(String, Expression, Expression, String)> = None; // (alias_name, start_expr, end_expr, unit)
8795            let mut gda_join_idx: Option<usize> = None;
8796
8797            for (idx, join) in sel.joins.iter().enumerate() {
8798                // The join.this may be:
8799                // 1. Unnest(UnnestFunc { alias: Some("mnth"), ... })
8800                // 2. Alias(Alias { this: Unnest(UnnestFunc { alias: None, ... }), alias: "mnth", ... })
8801                let (unnest_ref, alias_name) = match &join.this {
8802                    Expression::Unnest(ref unnest) => {
8803                        let alias = unnest.alias.as_ref().map(|id| id.name.clone());
8804                        (Some(unnest.as_ref()), alias)
8805                    }
8806                    Expression::Alias(ref a) => {
8807                        if let Expression::Unnest(ref unnest) = a.this {
8808                            (Some(unnest.as_ref()), Some(a.alias.name.clone()))
8809                        } else {
8810                            (None, None)
8811                        }
8812                    }
8813                    _ => (None, None),
8814                };
8815
8816                if let (Some(unnest), Some(alias)) = (unnest_ref, alias_name) {
8817                    // Check the main expression (this) of the UNNEST for GENERATE_DATE_ARRAY function
8818                    if let Expression::Function(ref f) = unnest.this {
8819                        if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY") && f.args.len() >= 2 {
8820                            let start_expr = f.args[0].clone();
8821                            let end_expr = f.args[1].clone();
8822                            let step = f.args.get(2).cloned();
8823
8824                            // Extract unit from step interval
8825                            let unit = if let Some(Expression::Interval(ref iv)) = step {
8826                                if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
8827                                    Some(format!("{:?}", unit).to_ascii_uppercase())
8828                                } else if let Some(ref this) = iv.this {
8829                                    // The interval may be stored as a string like "1 MONTH"
8830                                    if let Expression::Literal(lit) = this {
8831                                        if let Literal::String(ref s) = lit.as_ref() {
8832                                            let parts: Vec<&str> = s.split_whitespace().collect();
8833                                            if parts.len() == 2 {
8834                                                Some(parts[1].to_ascii_uppercase())
8835                                            } else if parts.len() == 1 {
8836                                                // Single word like "MONTH" or just "1"
8837                                                let upper = parts[0].to_ascii_uppercase();
8838                                                if matches!(
8839                                                    upper.as_str(),
8840                                                    "YEAR"
8841                                                        | "QUARTER"
8842                                                        | "MONTH"
8843                                                        | "WEEK"
8844                                                        | "DAY"
8845                                                        | "HOUR"
8846                                                        | "MINUTE"
8847                                                        | "SECOND"
8848                                                ) {
8849                                                    Some(upper)
8850                                                } else {
8851                                                    None
8852                                                }
8853                                            } else {
8854                                                None
8855                                            }
8856                                        } else {
8857                                            None
8858                                        }
8859                                    } else {
8860                                        None
8861                                    }
8862                                } else {
8863                                    None
8864                                }
8865                            } else {
8866                                None
8867                            };
8868
8869                            if let Some(unit_str) = unit {
8870                                gda_info = Some((alias, start_expr, end_expr, unit_str));
8871                                gda_join_idx = Some(idx);
8872                            }
8873                        }
8874                    }
8875                }
8876                if gda_info.is_some() {
8877                    break;
8878                }
8879            }
8880
8881            let Some((alias_name, start_expr, end_expr, unit_str)) = gda_info else {
8882                // Also check FROM clause for UNNEST(GENERATE_DATE_ARRAY(...)) patterns
8883                // This handles Generic->Snowflake where GENERATE_DATE_ARRAY is in FROM, not in JOIN
8884                let result = Self::try_transform_from_gda_snowflake(sel);
8885                return result;
8886            };
8887            let join_idx = gda_join_idx.unwrap();
8888
8889            // Build ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1)
8890            // ARRAY_GENERATE_RANGE uses exclusive end, and we need DATEDIFF + 1 values
8891            // (inclusive date range), so the exclusive end is DATEDIFF + 1.
8892            let datediff = Expression::Function(Box::new(Function::new(
8893                "DATEDIFF".to_string(),
8894                vec![
8895                    Expression::boxed_column(Column {
8896                        name: Identifier::new(&unit_str),
8897                        table: None,
8898                        join_mark: false,
8899                        trailing_comments: vec![],
8900                        span: None,
8901                        inferred_type: None,
8902                    }),
8903                    start_expr.clone(),
8904                    end_expr.clone(),
8905                ],
8906            )));
8907            let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
8908                left: datediff,
8909                right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
8910                left_comments: vec![],
8911                operator_comments: vec![],
8912                trailing_comments: vec![],
8913                inferred_type: None,
8914            }));
8915
8916            let array_gen_range = Expression::Function(Box::new(Function::new(
8917                "ARRAY_GENERATE_RANGE".to_string(),
8918                vec![
8919                    Expression::Literal(Box::new(Literal::Number("0".to_string()))),
8920                    datediff_plus_one,
8921                ],
8922            )));
8923
8924            // Build FLATTEN(INPUT => ARRAY_GENERATE_RANGE(...))
8925            let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
8926                name: Identifier::new("INPUT"),
8927                value: array_gen_range,
8928                separator: crate::expressions::NamedArgSeparator::DArrow,
8929            }));
8930            let flatten = Expression::Function(Box::new(Function::new(
8931                "FLATTEN".to_string(),
8932                vec![flatten_input],
8933            )));
8934
8935            // Build LATERAL FLATTEN(...) AS _t0(seq, key, path, index, alias, this)
8936            let alias_table = Alias {
8937                this: flatten,
8938                alias: Identifier::new("_t0"),
8939                column_aliases: vec![
8940                    Identifier::new("seq"),
8941                    Identifier::new("key"),
8942                    Identifier::new("path"),
8943                    Identifier::new("index"),
8944                    Identifier::new(&alias_name),
8945                    Identifier::new("this"),
8946                ],
8947                alias_explicit_as: false,
8948                alias_keyword: None,
8949                pre_alias_comments: vec![],
8950                trailing_comments: vec![],
8951                inferred_type: None,
8952            };
8953            let lateral_expr = Expression::Lateral(Box::new(Lateral {
8954                this: Box::new(Expression::Alias(Box::new(alias_table))),
8955                view: None,
8956                outer: None,
8957                alias: None,
8958                alias_quoted: false,
8959                cross_apply: None,
8960                ordinality: None,
8961                column_aliases: vec![],
8962            }));
8963
8964            // Remove the original join and add to FROM expressions
8965            sel.joins.remove(join_idx);
8966            if let Some(ref mut from) = sel.from {
8967                from.expressions.push(lateral_expr);
8968            }
8969
8970            // Build DATEADD(unit, CAST(alias AS INT), CAST(start AS DATE))
8971            let dateadd_expr = Expression::Function(Box::new(Function::new(
8972                "DATEADD".to_string(),
8973                vec![
8974                    Expression::boxed_column(Column {
8975                        name: Identifier::new(&unit_str),
8976                        table: None,
8977                        join_mark: false,
8978                        trailing_comments: vec![],
8979                        span: None,
8980                        inferred_type: None,
8981                    }),
8982                    Expression::Cast(Box::new(Cast {
8983                        this: Expression::boxed_column(Column {
8984                            name: Identifier::new(&alias_name),
8985                            table: None,
8986                            join_mark: false,
8987                            trailing_comments: vec![],
8988                            span: None,
8989                            inferred_type: None,
8990                        }),
8991                        to: DataType::Int {
8992                            length: None,
8993                            integer_spelling: false,
8994                        },
8995                        trailing_comments: vec![],
8996                        double_colon_syntax: false,
8997                        format: None,
8998                        default: None,
8999                        inferred_type: None,
9000                    })),
9001                    Expression::Cast(Box::new(Cast {
9002                        this: start_expr.clone(),
9003                        to: DataType::Date,
9004                        trailing_comments: vec![],
9005                        double_colon_syntax: false,
9006                        format: None,
9007                        default: None,
9008                        inferred_type: None,
9009                    })),
9010                ],
9011            )));
9012
9013            // Replace references to the alias in the SELECT list
9014            let new_exprs: Vec<Expression> = sel
9015                .expressions
9016                .iter()
9017                .map(|expr| Self::replace_column_ref_with_dateadd(expr, &alias_name, &dateadd_expr))
9018                .collect();
9019            sel.expressions = new_exprs;
9020
9021            Ok(Expression::Select(sel))
9022        })
9023    }
9024
9025    /// Helper: replace column references to `alias_name` with dateadd expression
9026    fn replace_column_ref_with_dateadd(
9027        expr: &Expression,
9028        alias_name: &str,
9029        dateadd: &Expression,
9030    ) -> Expression {
9031        use crate::expressions::*;
9032        match expr {
9033            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
9034                // Plain column reference -> DATEADD(...) AS alias_name
9035                Expression::Alias(Box::new(Alias {
9036                    this: dateadd.clone(),
9037                    alias: Identifier::new(alias_name),
9038                    column_aliases: vec![],
9039                    alias_explicit_as: false,
9040                    alias_keyword: None,
9041                    pre_alias_comments: vec![],
9042                    trailing_comments: vec![],
9043                    inferred_type: None,
9044                }))
9045            }
9046            Expression::Alias(a) => {
9047                // Check if the inner expression references the alias
9048                let new_this = Self::replace_column_ref_inner(&a.this, alias_name, dateadd);
9049                Expression::Alias(Box::new(Alias {
9050                    this: new_this,
9051                    alias: a.alias.clone(),
9052                    column_aliases: a.column_aliases.clone(),
9053                    alias_explicit_as: false,
9054                    alias_keyword: None,
9055                    pre_alias_comments: a.pre_alias_comments.clone(),
9056                    trailing_comments: a.trailing_comments.clone(),
9057                    inferred_type: None,
9058                }))
9059            }
9060            _ => expr.clone(),
9061        }
9062    }
9063
9064    /// Helper: replace column references in inner expression (not top-level)
9065    fn replace_column_ref_inner(
9066        expr: &Expression,
9067        alias_name: &str,
9068        dateadd: &Expression,
9069    ) -> Expression {
9070        use crate::expressions::*;
9071        match expr {
9072            Expression::Column(c) if c.name.name == alias_name && c.table.is_none() => {
9073                dateadd.clone()
9074            }
9075            Expression::Add(op) => {
9076                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
9077                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
9078                Expression::Add(Box::new(BinaryOp {
9079                    left,
9080                    right,
9081                    left_comments: op.left_comments.clone(),
9082                    operator_comments: op.operator_comments.clone(),
9083                    trailing_comments: op.trailing_comments.clone(),
9084                    inferred_type: None,
9085                }))
9086            }
9087            Expression::Sub(op) => {
9088                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
9089                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
9090                Expression::Sub(Box::new(BinaryOp {
9091                    left,
9092                    right,
9093                    left_comments: op.left_comments.clone(),
9094                    operator_comments: op.operator_comments.clone(),
9095                    trailing_comments: op.trailing_comments.clone(),
9096                    inferred_type: None,
9097                }))
9098            }
9099            Expression::Mul(op) => {
9100                let left = Self::replace_column_ref_inner(&op.left, alias_name, dateadd);
9101                let right = Self::replace_column_ref_inner(&op.right, alias_name, dateadd);
9102                Expression::Mul(Box::new(BinaryOp {
9103                    left,
9104                    right,
9105                    left_comments: op.left_comments.clone(),
9106                    operator_comments: op.operator_comments.clone(),
9107                    trailing_comments: op.trailing_comments.clone(),
9108                    inferred_type: None,
9109                }))
9110            }
9111            _ => expr.clone(),
9112        }
9113    }
9114
9115    /// Handle UNNEST(GENERATE_DATE_ARRAY(...)) in FROM clause for Snowflake target.
9116    /// Converts to a subquery with DATEADD + TABLE(FLATTEN(ARRAY_GENERATE_RANGE(...))).
9117    fn try_transform_from_gda_snowflake(
9118        mut sel: Box<crate::expressions::Select>,
9119    ) -> Result<Expression> {
9120        use crate::expressions::*;
9121
9122        // Extract GDA info from FROM clause
9123        let mut gda_info: Option<(
9124            usize,
9125            String,
9126            Expression,
9127            Expression,
9128            String,
9129            Option<(String, Vec<Identifier>)>,
9130        )> = None; // (from_idx, col_name, start, end, unit, outer_alias)
9131
9132        if let Some(ref from) = sel.from {
9133            for (idx, table_expr) in from.expressions.iter().enumerate() {
9134                // Pattern 1: UNNEST(GENERATE_DATE_ARRAY(...))
9135                // Pattern 2: Alias(UNNEST(GENERATE_DATE_ARRAY(...))) AS _q(date_week)
9136                let (unnest_opt, outer_alias_info) = match table_expr {
9137                    Expression::Unnest(ref unnest) => (Some(unnest.as_ref()), None),
9138                    Expression::Alias(ref a) => {
9139                        if let Expression::Unnest(ref unnest) = a.this {
9140                            let alias_info = (a.alias.name.clone(), a.column_aliases.clone());
9141                            (Some(unnest.as_ref()), Some(alias_info))
9142                        } else {
9143                            (None, None)
9144                        }
9145                    }
9146                    _ => (None, None),
9147                };
9148
9149                if let Some(unnest) = unnest_opt {
9150                    // Check for GENERATE_DATE_ARRAY function
9151                    let func_opt = match &unnest.this {
9152                        Expression::Function(ref f)
9153                            if f.name.eq_ignore_ascii_case("GENERATE_DATE_ARRAY")
9154                                && f.args.len() >= 2 =>
9155                        {
9156                            Some(f)
9157                        }
9158                        // Also check for GenerateSeries (from earlier normalization)
9159                        _ => None,
9160                    };
9161
9162                    if let Some(f) = func_opt {
9163                        let start_expr = f.args[0].clone();
9164                        let end_expr = f.args[1].clone();
9165                        let step = f.args.get(2).cloned();
9166
9167                        // Extract unit and column name
9168                        let unit = Self::extract_interval_unit_str(&step);
9169                        let col_name = outer_alias_info
9170                            .as_ref()
9171                            .and_then(|(_, cols)| cols.first().map(|id| id.name.clone()))
9172                            .unwrap_or_else(|| "value".to_string());
9173
9174                        if let Some(unit_str) = unit {
9175                            gda_info = Some((
9176                                idx,
9177                                col_name,
9178                                start_expr,
9179                                end_expr,
9180                                unit_str,
9181                                outer_alias_info,
9182                            ));
9183                            break;
9184                        }
9185                    }
9186                }
9187            }
9188        }
9189
9190        let Some((from_idx, col_name, start_expr, end_expr, unit_str, outer_alias_info)) = gda_info
9191        else {
9192            return Ok(Expression::Select(sel));
9193        };
9194
9195        // Build the Snowflake subquery:
9196        // (SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
9197        //  FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(seq, key, path, index, col_name, this))
9198
9199        // DATEDIFF(unit, start, end)
9200        let datediff = Expression::Function(Box::new(Function::new(
9201            "DATEDIFF".to_string(),
9202            vec![
9203                Expression::boxed_column(Column {
9204                    name: Identifier::new(&unit_str),
9205                    table: None,
9206                    join_mark: false,
9207                    trailing_comments: vec![],
9208                    span: None,
9209                    inferred_type: None,
9210                }),
9211                start_expr.clone(),
9212                end_expr.clone(),
9213            ],
9214        )));
9215        // DATEDIFF(...) + 1
9216        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
9217            left: datediff,
9218            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
9219            left_comments: vec![],
9220            operator_comments: vec![],
9221            trailing_comments: vec![],
9222            inferred_type: None,
9223        }));
9224
9225        let array_gen_range = Expression::Function(Box::new(Function::new(
9226            "ARRAY_GENERATE_RANGE".to_string(),
9227            vec![
9228                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
9229                datediff_plus_one,
9230            ],
9231        )));
9232
9233        // TABLE(FLATTEN(INPUT => ...))
9234        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
9235            name: Identifier::new("INPUT"),
9236            value: array_gen_range,
9237            separator: crate::expressions::NamedArgSeparator::DArrow,
9238        }));
9239        let flatten = Expression::Function(Box::new(Function::new(
9240            "FLATTEN".to_string(),
9241            vec![flatten_input],
9242        )));
9243
9244        // Determine alias name for the table: use outer alias or _t0
9245        let table_alias_name = outer_alias_info
9246            .as_ref()
9247            .map(|(name, _)| name.clone())
9248            .unwrap_or_else(|| "_t0".to_string());
9249
9250        // TABLE(FLATTEN(...)) AS _t0(seq, key, path, index, col_name, this)
9251        let table_func =
9252            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
9253        let flatten_aliased = Expression::Alias(Box::new(Alias {
9254            this: table_func,
9255            alias: Identifier::new(&table_alias_name),
9256            column_aliases: vec![
9257                Identifier::new("seq"),
9258                Identifier::new("key"),
9259                Identifier::new("path"),
9260                Identifier::new("index"),
9261                Identifier::new(&col_name),
9262                Identifier::new("this"),
9263            ],
9264            alias_explicit_as: false,
9265            alias_keyword: None,
9266            pre_alias_comments: vec![],
9267            trailing_comments: vec![],
9268            inferred_type: None,
9269        }));
9270
9271        // SELECT DATEADD(unit, CAST(col_name AS INT), CAST(start AS DATE)) AS col_name
9272        let dateadd_expr = Expression::Function(Box::new(Function::new(
9273            "DATEADD".to_string(),
9274            vec![
9275                Expression::boxed_column(Column {
9276                    name: Identifier::new(&unit_str),
9277                    table: None,
9278                    join_mark: false,
9279                    trailing_comments: vec![],
9280                    span: None,
9281                    inferred_type: None,
9282                }),
9283                Expression::Cast(Box::new(Cast {
9284                    this: Expression::boxed_column(Column {
9285                        name: Identifier::new(&col_name),
9286                        table: None,
9287                        join_mark: false,
9288                        trailing_comments: vec![],
9289                        span: None,
9290                        inferred_type: None,
9291                    }),
9292                    to: DataType::Int {
9293                        length: None,
9294                        integer_spelling: false,
9295                    },
9296                    trailing_comments: vec![],
9297                    double_colon_syntax: false,
9298                    format: None,
9299                    default: None,
9300                    inferred_type: None,
9301                })),
9302                // Use start_expr directly - it's already been normalized (DATE literal -> CAST)
9303                start_expr.clone(),
9304            ],
9305        )));
9306        let dateadd_aliased = Expression::Alias(Box::new(Alias {
9307            this: dateadd_expr,
9308            alias: Identifier::new(&col_name),
9309            column_aliases: vec![],
9310            alias_explicit_as: false,
9311            alias_keyword: None,
9312            pre_alias_comments: vec![],
9313            trailing_comments: vec![],
9314            inferred_type: None,
9315        }));
9316
9317        // Build inner SELECT
9318        let mut inner_select = Select::new();
9319        inner_select.expressions = vec![dateadd_aliased];
9320        inner_select.from = Some(From {
9321            expressions: vec![flatten_aliased],
9322        });
9323
9324        let inner_select_expr = Expression::Select(Box::new(inner_select));
9325        let subquery = Expression::Subquery(Box::new(Subquery {
9326            this: inner_select_expr,
9327            alias: None,
9328            column_aliases: vec![],
9329            alias_explicit_as: false,
9330            alias_keyword: None,
9331            order_by: None,
9332            limit: None,
9333            offset: None,
9334            distribute_by: None,
9335            sort_by: None,
9336            cluster_by: None,
9337            lateral: false,
9338            modifiers_inside: false,
9339            trailing_comments: vec![],
9340            inferred_type: None,
9341        }));
9342
9343        // If there was an outer alias (e.g., AS _q(date_week)), wrap with alias
9344        let replacement = if let Some((alias_name, col_aliases)) = outer_alias_info {
9345            Expression::Alias(Box::new(Alias {
9346                this: subquery,
9347                alias: Identifier::new(&alias_name),
9348                column_aliases: col_aliases,
9349                alias_explicit_as: false,
9350                alias_keyword: None,
9351                pre_alias_comments: vec![],
9352                trailing_comments: vec![],
9353                inferred_type: None,
9354            }))
9355        } else {
9356            subquery
9357        };
9358
9359        // Replace the FROM expression
9360        if let Some(ref mut from) = sel.from {
9361            from.expressions[from_idx] = replacement;
9362        }
9363
9364        Ok(Expression::Select(sel))
9365    }
9366
9367    /// Convert ARRAY_SIZE(GENERATE_DATE_ARRAY(start, end, step)) for Snowflake.
9368    /// Produces: ARRAY_SIZE((SELECT ARRAY_AGG(*) FROM (SELECT DATEADD(unit, CAST(value AS INT), start) AS value
9369    ///   FROM TABLE(FLATTEN(INPUT => ARRAY_GENERATE_RANGE(0, DATEDIFF(unit, start, end) + 1))) AS _t0(...))))
9370    fn convert_array_size_gda_snowflake(f: &crate::expressions::Function) -> Result<Expression> {
9371        use crate::expressions::*;
9372
9373        let start_expr = f.args[0].clone();
9374        let end_expr = f.args[1].clone();
9375        let step = f.args.get(2).cloned();
9376        let unit_str = Self::extract_interval_unit_str(&step).unwrap_or_else(|| "DAY".to_string());
9377        let col_name = "value";
9378
9379        // Build the inner subquery: same as try_transform_from_gda_snowflake
9380        let datediff = Expression::Function(Box::new(Function::new(
9381            "DATEDIFF".to_string(),
9382            vec![
9383                Expression::boxed_column(Column {
9384                    name: Identifier::new(&unit_str),
9385                    table: None,
9386                    join_mark: false,
9387                    trailing_comments: vec![],
9388                    span: None,
9389                    inferred_type: None,
9390                }),
9391                start_expr.clone(),
9392                end_expr.clone(),
9393            ],
9394        )));
9395        // DATEDIFF(...) + 1
9396        let datediff_plus_one = Expression::Add(Box::new(BinaryOp {
9397            left: datediff,
9398            right: Expression::Literal(Box::new(Literal::Number("1".to_string()))),
9399            left_comments: vec![],
9400            operator_comments: vec![],
9401            trailing_comments: vec![],
9402            inferred_type: None,
9403        }));
9404
9405        let array_gen_range = Expression::Function(Box::new(Function::new(
9406            "ARRAY_GENERATE_RANGE".to_string(),
9407            vec![
9408                Expression::Literal(Box::new(Literal::Number("0".to_string()))),
9409                datediff_plus_one,
9410            ],
9411        )));
9412
9413        let flatten_input = Expression::NamedArgument(Box::new(NamedArgument {
9414            name: Identifier::new("INPUT"),
9415            value: array_gen_range,
9416            separator: crate::expressions::NamedArgSeparator::DArrow,
9417        }));
9418        let flatten = Expression::Function(Box::new(Function::new(
9419            "FLATTEN".to_string(),
9420            vec![flatten_input],
9421        )));
9422
9423        let table_func =
9424            Expression::Function(Box::new(Function::new("TABLE".to_string(), vec![flatten])));
9425        let flatten_aliased = Expression::Alias(Box::new(Alias {
9426            this: table_func,
9427            alias: Identifier::new("_t0"),
9428            column_aliases: vec![
9429                Identifier::new("seq"),
9430                Identifier::new("key"),
9431                Identifier::new("path"),
9432                Identifier::new("index"),
9433                Identifier::new(col_name),
9434                Identifier::new("this"),
9435            ],
9436            alias_explicit_as: false,
9437            alias_keyword: None,
9438            pre_alias_comments: vec![],
9439            trailing_comments: vec![],
9440            inferred_type: None,
9441        }));
9442
9443        let dateadd_expr = Expression::Function(Box::new(Function::new(
9444            "DATEADD".to_string(),
9445            vec![
9446                Expression::boxed_column(Column {
9447                    name: Identifier::new(&unit_str),
9448                    table: None,
9449                    join_mark: false,
9450                    trailing_comments: vec![],
9451                    span: None,
9452                    inferred_type: None,
9453                }),
9454                Expression::Cast(Box::new(Cast {
9455                    this: Expression::boxed_column(Column {
9456                        name: Identifier::new(col_name),
9457                        table: None,
9458                        join_mark: false,
9459                        trailing_comments: vec![],
9460                        span: None,
9461                        inferred_type: None,
9462                    }),
9463                    to: DataType::Int {
9464                        length: None,
9465                        integer_spelling: false,
9466                    },
9467                    trailing_comments: vec![],
9468                    double_colon_syntax: false,
9469                    format: None,
9470                    default: None,
9471                    inferred_type: None,
9472                })),
9473                start_expr.clone(),
9474            ],
9475        )));
9476        let dateadd_aliased = Expression::Alias(Box::new(Alias {
9477            this: dateadd_expr,
9478            alias: Identifier::new(col_name),
9479            column_aliases: vec![],
9480            alias_explicit_as: false,
9481            alias_keyword: None,
9482            pre_alias_comments: vec![],
9483            trailing_comments: vec![],
9484            inferred_type: None,
9485        }));
9486
9487        // Inner SELECT: SELECT DATEADD(...) AS value FROM TABLE(FLATTEN(...)) AS _t0(...)
9488        let mut inner_select = Select::new();
9489        inner_select.expressions = vec![dateadd_aliased];
9490        inner_select.from = Some(From {
9491            expressions: vec![flatten_aliased],
9492        });
9493
9494        // Wrap in subquery for the inner part
9495        let inner_subquery = Expression::Subquery(Box::new(Subquery {
9496            this: Expression::Select(Box::new(inner_select)),
9497            alias: None,
9498            column_aliases: vec![],
9499            alias_explicit_as: false,
9500            alias_keyword: None,
9501            order_by: None,
9502            limit: None,
9503            offset: None,
9504            distribute_by: None,
9505            sort_by: None,
9506            cluster_by: None,
9507            lateral: false,
9508            modifiers_inside: false,
9509            trailing_comments: vec![],
9510            inferred_type: None,
9511        }));
9512
9513        // Outer: SELECT ARRAY_AGG(*) FROM (inner_subquery)
9514        let star = Expression::Star(Star {
9515            table: None,
9516            except: None,
9517            replace: None,
9518            rename: None,
9519            trailing_comments: vec![],
9520            span: None,
9521        });
9522        let array_agg = Expression::ArrayAgg(Box::new(AggFunc {
9523            this: star,
9524            distinct: false,
9525            filter: None,
9526            order_by: vec![],
9527            name: Some("ARRAY_AGG".to_string()),
9528            ignore_nulls: None,
9529            having_max: None,
9530            limit: None,
9531            inferred_type: None,
9532        }));
9533
9534        let mut outer_select = Select::new();
9535        outer_select.expressions = vec![array_agg];
9536        outer_select.from = Some(From {
9537            expressions: vec![inner_subquery],
9538        });
9539
9540        // Wrap in a subquery
9541        let outer_subquery = Expression::Subquery(Box::new(Subquery {
9542            this: Expression::Select(Box::new(outer_select)),
9543            alias: None,
9544            column_aliases: vec![],
9545            alias_explicit_as: false,
9546            alias_keyword: None,
9547            order_by: None,
9548            limit: None,
9549            offset: None,
9550            distribute_by: None,
9551            sort_by: None,
9552            cluster_by: None,
9553            lateral: false,
9554            modifiers_inside: false,
9555            trailing_comments: vec![],
9556            inferred_type: None,
9557        }));
9558
9559        // ARRAY_SIZE(subquery)
9560        Ok(Expression::ArraySize(Box::new(UnaryFunc::new(
9561            outer_subquery,
9562        ))))
9563    }
9564
9565    /// Extract interval unit string from an optional step expression.
9566    fn extract_interval_unit_str(step: &Option<Expression>) -> Option<String> {
9567        use crate::expressions::*;
9568        if let Some(Expression::Interval(ref iv)) = step {
9569            if let Some(IntervalUnitSpec::Simple { ref unit, .. }) = iv.unit {
9570                return Some(format!("{:?}", unit).to_ascii_uppercase());
9571            }
9572            if let Some(ref this) = iv.this {
9573                if let Expression::Literal(lit) = this {
9574                    if let Literal::String(ref s) = lit.as_ref() {
9575                        let parts: Vec<&str> = s.split_whitespace().collect();
9576                        if parts.len() == 2 {
9577                            return Some(parts[1].to_ascii_uppercase());
9578                        } else if parts.len() == 1 {
9579                            let upper = parts[0].to_ascii_uppercase();
9580                            if matches!(
9581                                upper.as_str(),
9582                                "YEAR"
9583                                    | "QUARTER"
9584                                    | "MONTH"
9585                                    | "WEEK"
9586                                    | "DAY"
9587                                    | "HOUR"
9588                                    | "MINUTE"
9589                                    | "SECOND"
9590                            ) {
9591                                return Some(upper);
9592                            }
9593                        }
9594                    }
9595                }
9596            }
9597        }
9598        // Default to DAY if no step or no interval
9599        if step.is_none() {
9600            return Some("DAY".to_string());
9601        }
9602        None
9603    }
9604
9605    fn normalize_snowflake_pretty(mut sql: String) -> String {
9606        if sql.contains("LATERAL IFF(_u.pos = _u_2.pos_2, _u_2.entity, NULL) AS datasource(SEQ, KEY, PATH, INDEX, VALUE, THIS)")
9607            && sql.contains("ARRAY_GENERATE_RANGE(0, (GREATEST(ARRAY_SIZE(INPUT => PARSE_JSON(flags))) - 1) + 1)")
9608        {
9609            sql = sql.replace(
9610                "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')",
9611                "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    )",
9612            );
9613
9614            sql = sql.replace(
9615                "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)",
9616                "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)",
9617            );
9618
9619            sql = sql.replace(
9620                "OR (_u.pos > (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1)\n  AND _u_2.pos_2 = (ARRAY_SIZE(INPUT => PARSE_JSON(flags)) - 1))",
9621                "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  )",
9622            );
9623        }
9624
9625        sql
9626    }
9627
9628    #[cfg(feature = "transpile")]
9629    fn wrap_tsql_top_level_values(expr: Expression) -> Expression {
9630        match expr {
9631            Expression::Values(values) => Self::tsql_values_as_select(*values),
9632            Expression::Union(mut union) => {
9633                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
9634                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
9635                union.left = Self::wrap_tsql_values_set_operand(left);
9636                union.right = Self::wrap_tsql_values_set_operand(right);
9637                Expression::Union(union)
9638            }
9639            Expression::Intersect(mut intersect) => {
9640                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
9641                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
9642                intersect.left = Self::wrap_tsql_values_set_operand(left);
9643                intersect.right = Self::wrap_tsql_values_set_operand(right);
9644                Expression::Intersect(intersect)
9645            }
9646            Expression::Except(mut except) => {
9647                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
9648                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
9649                except.left = Self::wrap_tsql_values_set_operand(left);
9650                except.right = Self::wrap_tsql_values_set_operand(right);
9651                Expression::Except(except)
9652            }
9653            other => other,
9654        }
9655    }
9656
9657    #[cfg(feature = "transpile")]
9658    fn wrap_tsql_values_set_operand(expr: Expression) -> Expression {
9659        match expr {
9660            Expression::Values(values) => Self::tsql_values_as_select(*values),
9661            Expression::Select(mut select)
9662                if Self::is_parser_wrapped_values_set_operand(&select) =>
9663            {
9664                let mut from = select.from.take().expect("checked as present");
9665                let Expression::Values(values) = from
9666                    .expressions
9667                    .pop()
9668                    .expect("checked as a single VALUES source")
9669                else {
9670                    unreachable!("checked as a VALUES source");
9671                };
9672                Self::tsql_values_as_select(*values)
9673            }
9674            Expression::Annotated(mut annotated) => {
9675                annotated.this = Self::wrap_tsql_values_set_operand(annotated.this);
9676                Expression::Annotated(annotated)
9677            }
9678            Expression::Union(mut union) => {
9679                let left = std::mem::replace(&mut union.left, Expression::Null(Null));
9680                let right = std::mem::replace(&mut union.right, Expression::Null(Null));
9681                union.left = Self::wrap_tsql_values_set_operand(left);
9682                union.right = Self::wrap_tsql_values_set_operand(right);
9683                Expression::Union(union)
9684            }
9685            Expression::Intersect(mut intersect) => {
9686                let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
9687                let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
9688                intersect.left = Self::wrap_tsql_values_set_operand(left);
9689                intersect.right = Self::wrap_tsql_values_set_operand(right);
9690                Expression::Intersect(intersect)
9691            }
9692            Expression::Except(mut except) => {
9693                let left = std::mem::replace(&mut except.left, Expression::Null(Null));
9694                let right = std::mem::replace(&mut except.right, Expression::Null(Null));
9695                except.left = Self::wrap_tsql_values_set_operand(left);
9696                except.right = Self::wrap_tsql_values_set_operand(right);
9697                Expression::Except(except)
9698            }
9699            other => other,
9700        }
9701    }
9702
9703    #[cfg(feature = "transpile")]
9704    fn is_parser_wrapped_values_set_operand(select: &Select) -> bool {
9705        let Some(from) = &select.from else {
9706            return false;
9707        };
9708        let [Expression::Values(values)] = from.expressions.as_slice() else {
9709            return false;
9710        };
9711        if !values
9712            .alias
9713            .as_ref()
9714            .is_some_and(|alias| alias.name == "_values")
9715        {
9716            return false;
9717        }
9718
9719        let mut parser_wrapper = Select::new();
9720        parser_wrapper.expressions = vec![Expression::star()];
9721        parser_wrapper.from = Some(from.clone());
9722        select == &parser_wrapper
9723    }
9724
9725    #[cfg(feature = "transpile")]
9726    fn tsql_values_as_select(mut values: crate::expressions::Values) -> Expression {
9727        let column_aliases = if values.column_aliases.is_empty() {
9728            let column_count = values
9729                .expressions
9730                .first()
9731                .map(|row| row.expressions.len())
9732                .unwrap_or(0);
9733            (1..=column_count)
9734                .map(|index| Identifier::new(format!("column{index}")))
9735                .collect()
9736        } else {
9737            std::mem::take(&mut values.column_aliases)
9738        };
9739
9740        values.alias = None;
9741
9742        let values_subquery = Expression::Subquery(Box::new(crate::expressions::Subquery {
9743            this: Expression::Values(Box::new(values)),
9744            alias: Some(Identifier::new("_v")),
9745            column_aliases,
9746            alias_explicit_as: false,
9747            alias_keyword: None,
9748            order_by: None,
9749            limit: None,
9750            offset: None,
9751            distribute_by: None,
9752            sort_by: None,
9753            cluster_by: None,
9754            lateral: false,
9755            modifiers_inside: false,
9756            trailing_comments: Vec::new(),
9757            inferred_type: None,
9758        }));
9759
9760        let mut select = crate::expressions::Select::new();
9761        select.expressions = vec![Expression::star()];
9762        select.from = Some(From {
9763            expressions: vec![values_subquery],
9764        });
9765
9766        Expression::Select(Box::new(select))
9767    }
9768
9769    fn extract_interval_parts(
9770        interval_expr: &Expression,
9771    ) -> Option<(Expression, crate::expressions::IntervalUnit)> {
9772        use crate::expressions::{DataType, IntervalUnit, IntervalUnitSpec, Literal};
9773
9774        fn unit_from_str(unit: &str) -> Option<IntervalUnit> {
9775            match unit.trim().to_ascii_uppercase().as_str() {
9776                "YEAR" | "YEARS" | "Y" | "YR" | "YRS" | "YY" | "YYYY" => Some(IntervalUnit::Year),
9777                "QUARTER" | "QUARTERS" | "Q" | "QTR" | "QTRS" | "QQ" => Some(IntervalUnit::Quarter),
9778                "MONTH" | "MONTHS" | "MON" | "MONS" | "MM" => Some(IntervalUnit::Month),
9779                "WEEK" | "WEEKS" | "W" | "WK" | "WKS" | "WW" | "ISOWEEK" => {
9780                    Some(IntervalUnit::Week)
9781                }
9782                "DAY" | "DAYS" | "D" | "DD" => Some(IntervalUnit::Day),
9783                "HOUR" | "HOURS" | "H" | "HH" | "HR" | "HRS" => Some(IntervalUnit::Hour),
9784                "MINUTE" | "MINUTES" | "MI" | "MIN" | "MINS" | "N" => Some(IntervalUnit::Minute),
9785                "SECOND" | "SECONDS" | "S" | "SEC" | "SECS" | "SS" => Some(IntervalUnit::Second),
9786                "MILLISECOND" | "MILLISECONDS" | "MS" | "MSEC" | "MSECS" | "MSECOND"
9787                | "MSECONDS" | "MILLISEC" | "MILLISECS" | "MILLISECON" => {
9788                    Some(IntervalUnit::Millisecond)
9789                }
9790                "MICROSECOND" | "MICROSECONDS" | "US" | "USEC" | "USECS" | "USECOND"
9791                | "USECONDS" | "MICROSEC" | "MICROSECS" | "MCS" => Some(IntervalUnit::Microsecond),
9792                "NANOSECOND" | "NANOSECONDS" | "NS" | "NSEC" | "NSECS" | "NSECOND" | "NSECONDS"
9793                | "NANOSEC" | "NANOSECS" => Some(IntervalUnit::Nanosecond),
9794                _ => None,
9795            }
9796        }
9797
9798        fn parts_from_literal_string(s: &str) -> Option<(Expression, IntervalUnit)> {
9799            let mut parts = s.split_whitespace();
9800            let value = parts.next()?;
9801            let unit = unit_from_str(parts.next()?)?;
9802            Some((
9803                Expression::Literal(Box::new(Literal::String(value.to_string()))),
9804                unit,
9805            ))
9806        }
9807
9808        fn unit_from_spec(unit: &IntervalUnitSpec) -> Option<IntervalUnit> {
9809            match unit {
9810                IntervalUnitSpec::Simple { unit, .. } => Some(*unit),
9811                IntervalUnitSpec::Expr(expr) => match expr.as_ref() {
9812                    Expression::Day(_) => Some(IntervalUnit::Day),
9813                    Expression::Month(_) => Some(IntervalUnit::Month),
9814                    Expression::Year(_) => Some(IntervalUnit::Year),
9815                    Expression::Identifier(id) => unit_from_str(&id.name),
9816                    Expression::Var(v) => unit_from_str(&v.this),
9817                    Expression::Column(col) => unit_from_str(&col.name.name),
9818                    _ => None,
9819                },
9820                _ => None,
9821            }
9822        }
9823
9824        match interval_expr {
9825            Expression::Interval(iv) => {
9826                let val = iv.this.clone().unwrap_or(Expression::number(0));
9827                if let Expression::Literal(lit) = &val {
9828                    if let Literal::String(s) = lit.as_ref() {
9829                        if let Some(parts) = parts_from_literal_string(s) {
9830                            return Some(parts);
9831                        }
9832                    }
9833                }
9834                let unit = iv
9835                    .unit
9836                    .as_ref()
9837                    .and_then(unit_from_spec)
9838                    .unwrap_or(IntervalUnit::Day);
9839                Some((val, unit))
9840            }
9841            Expression::Cast(cast) if matches!(cast.to, DataType::Interval { .. }) => {
9842                if let Expression::Literal(lit) = &cast.this {
9843                    if let Literal::String(s) = lit.as_ref() {
9844                        if let Some(parts) = parts_from_literal_string(s) {
9845                            return Some(parts);
9846                        }
9847                    }
9848                }
9849                let unit = match &cast.to {
9850                    DataType::Interval {
9851                        unit: Some(unit), ..
9852                    } => unit_from_str(unit).unwrap_or(IntervalUnit::Day),
9853                    _ => IntervalUnit::Day,
9854                };
9855                Some((cast.this.clone(), unit))
9856            }
9857            _ => None,
9858        }
9859    }
9860
9861    fn data_type_is_interval(dt: &DataType) -> bool {
9862        match dt {
9863            DataType::Interval { .. } => true,
9864            DataType::Custom { name } => name.trim().eq_ignore_ascii_case("INTERVAL"),
9865            _ => false,
9866        }
9867    }
9868
9869    fn node_is_interval_cast(node: &Expression) -> bool {
9870        match node {
9871            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
9872                Self::data_type_is_interval(&c.to)
9873            }
9874            _ => false,
9875        }
9876    }
9877
9878    fn reject_tsql_interval_casts(
9879        expr: &Expression,
9880        target: DialectType,
9881        opts: &TranspileOptions,
9882    ) -> Result<()> {
9883        if !matches!(
9884            opts.unsupported_level,
9885            UnsupportedLevel::Raise | UnsupportedLevel::Immediate
9886        ) {
9887            return Ok(());
9888        }
9889
9890        if expr.dfs().any(Self::node_is_interval_cast) {
9891            return Err(crate::error::Error::unsupported(
9892                "INTERVAL casts",
9893                target.to_string(),
9894            ));
9895        }
9896
9897        Ok(())
9898    }
9899
9900    fn tsql_varchar_max_type() -> DataType {
9901        DataType::Custom {
9902            name: "VARCHAR(MAX)".to_string(),
9903        }
9904    }
9905
9906    fn rewrite_tsql_interval_casts_to_varchar(expr: Expression) -> Result<Expression> {
9907        transform_recursive(expr, &|e| match e {
9908            Expression::Cast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9909                cast.to = Self::tsql_varchar_max_type();
9910                cast.double_colon_syntax = false;
9911                Ok(Expression::Cast(cast))
9912            }
9913            Expression::TryCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9914                cast.to = Self::tsql_varchar_max_type();
9915                cast.double_colon_syntax = false;
9916                Ok(Expression::TryCast(cast))
9917            }
9918            Expression::SafeCast(mut cast) if Self::data_type_is_interval(&cast.to) => {
9919                cast.to = Self::tsql_varchar_max_type();
9920                cast.double_colon_syntax = false;
9921                Ok(Expression::SafeCast(cast))
9922            }
9923            _ => Ok(e),
9924        })
9925    }
9926
9927    fn rewrite_tsql_interval_arithmetic_legacy(
9928        expr: &Expression,
9929        source: DialectType,
9930    ) -> Option<Expression> {
9931        match expr {
9932            Expression::Add(op) => {
9933                if Self::extract_interval_parts(&op.right).is_some() {
9934                    return Some(Self::build_tsql_dateadd_from_interval(
9935                        op.left.clone(),
9936                        &op.right,
9937                        false,
9938                    ));
9939                }
9940
9941                if Self::is_postgres_family_source(source) {
9942                    if Self::is_explicit_date_expr(&op.left)
9943                        && Self::is_integer_day_offset_expr(&op.right)
9944                    {
9945                        return Some(Self::build_tsql_dateadd_days(
9946                            op.left.clone(),
9947                            op.right.clone(),
9948                            false,
9949                        ));
9950                    }
9951
9952                    if Self::is_integer_day_offset_expr(&op.left)
9953                        && Self::is_explicit_date_expr(&op.right)
9954                    {
9955                        return Some(Self::build_tsql_dateadd_days(
9956                            op.right.clone(),
9957                            op.left.clone(),
9958                            false,
9959                        ));
9960                    }
9961                }
9962
9963                None
9964            }
9965            Expression::Sub(op) => {
9966                if Self::extract_interval_parts(&op.right).is_some() {
9967                    return Some(Self::build_tsql_dateadd_from_interval(
9968                        op.left.clone(),
9969                        &op.right,
9970                        true,
9971                    ));
9972                }
9973
9974                if Self::is_postgres_family_source(source) {
9975                    if Self::is_explicit_date_expr(&op.left)
9976                        && Self::is_explicit_date_expr(&op.right)
9977                    {
9978                        return Some(Self::build_tsql_datediff_days(
9979                            op.right.clone(),
9980                            op.left.clone(),
9981                        ));
9982                    }
9983
9984                    if Self::is_explicit_date_expr(&op.left)
9985                        && Self::is_integer_day_offset_expr(&op.right)
9986                    {
9987                        return Some(Self::build_tsql_dateadd_days(
9988                            op.left.clone(),
9989                            op.right.clone(),
9990                            true,
9991                        ));
9992                    }
9993                }
9994
9995                None
9996            }
9997            _ => None,
9998        }
9999    }
10000
10001    fn is_postgres_family_source(source: DialectType) -> bool {
10002        matches!(
10003            source,
10004            DialectType::PostgreSQL
10005                | DialectType::Redshift
10006                | DialectType::Materialize
10007                | DialectType::RisingWave
10008                | DialectType::CockroachDB
10009        )
10010    }
10011
10012    fn is_explicit_date_expr(expr: &Expression) -> bool {
10013        use crate::expressions::Literal;
10014
10015        match expr {
10016            Expression::Literal(lit) => matches!(lit.as_ref(), Literal::Date(_)),
10017            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
10018                matches!(c.to, crate::expressions::DataType::Date)
10019            }
10020            Expression::Paren(p) => Self::is_explicit_date_expr(&p.this),
10021            Expression::CurrentDate(_)
10022            | Expression::Date(_)
10023            | Expression::MakeDate(_)
10024            | Expression::ToDate(_)
10025            | Expression::DateStrToDate(_) => true,
10026            _ => false,
10027        }
10028    }
10029
10030    fn is_integer_day_offset_expr(expr: &Expression) -> bool {
10031        use crate::expressions::Literal;
10032
10033        match expr {
10034            Expression::Literal(lit) => match lit.as_ref() {
10035                Literal::Number(n) => n.parse::<i64>().is_ok(),
10036                _ => false,
10037            },
10038            Expression::Parameter(_) | Expression::Placeholder(_) => true,
10039            Expression::Neg(op) => Self::is_integer_day_offset_expr(&op.this),
10040            Expression::Paren(p) => Self::is_integer_day_offset_expr(&p.this),
10041            _ => false,
10042        }
10043    }
10044
10045    fn build_tsql_datediff_days(start: Expression, end: Expression) -> Expression {
10046        Expression::Function(Box::new(Function::new(
10047            "DATEDIFF".to_string(),
10048            vec![Expression::Identifier(Identifier::new("DAY")), start, end],
10049        )))
10050    }
10051
10052    fn build_tsql_dateadd_days(date: Expression, amount: Expression, subtract: bool) -> Expression {
10053        Expression::Function(Box::new(Function::new(
10054            "DATEADD".to_string(),
10055            vec![
10056                Expression::Identifier(Identifier::new("DAY")),
10057                Self::tsql_dateadd_amount(amount, subtract),
10058                date,
10059            ],
10060        )))
10061    }
10062
10063    fn build_tsql_dateadd_from_interval(
10064        date: Expression,
10065        interval: &Expression,
10066        subtract: bool,
10067    ) -> Expression {
10068        let (value, unit) = Self::extract_interval_parts(interval)
10069            .unwrap_or_else(|| (interval.clone(), crate::expressions::IntervalUnit::Day));
10070        let unit = normalization::temporal::interval_unit_to_string(&unit);
10071        let amount = Self::tsql_dateadd_amount(value, subtract);
10072
10073        Expression::Function(Box::new(Function::new(
10074            "DATEADD".to_string(),
10075            vec![Expression::Identifier(Identifier::new(unit)), amount, date],
10076        )))
10077    }
10078
10079    fn tsql_dateadd_amount(value: Expression, negate: bool) -> Expression {
10080        use crate::expressions::{Parameter, ParameterStyle, UnaryOp};
10081
10082        fn numeric_literal_value(value: &Expression) -> Option<&str> {
10083            match value {
10084                Expression::Literal(lit) => match lit.as_ref() {
10085                    crate::expressions::Literal::Number(n)
10086                    | crate::expressions::Literal::String(n) => Some(n.as_str()),
10087                    _ => None,
10088                },
10089                _ => None,
10090            }
10091        }
10092
10093        fn colon_parameter(value: &Expression) -> Option<Expression> {
10094            let Expression::Literal(lit) = value else {
10095                return None;
10096            };
10097            let crate::expressions::Literal::String(s) = lit.as_ref() else {
10098                return None;
10099            };
10100            let name = s.strip_prefix(':')?;
10101            if name.is_empty()
10102                || !name
10103                    .chars()
10104                    .all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
10105            {
10106                return None;
10107            }
10108
10109            Some(Expression::Parameter(Box::new(Parameter {
10110                name: if name.chars().all(|ch| ch.is_ascii_digit()) {
10111                    None
10112                } else {
10113                    Some(name.to_string())
10114                },
10115                index: name.parse::<u32>().ok(),
10116                style: ParameterStyle::Colon,
10117                quoted: false,
10118                string_quoted: false,
10119                expression: None,
10120            })))
10121        }
10122
10123        let value = colon_parameter(&value).unwrap_or(value);
10124
10125        if let Some(n) = numeric_literal_value(&value) {
10126            if let Ok(parsed) = n.parse::<f64>() {
10127                let normalized = if negate { -parsed } else { parsed };
10128                let rendered = if normalized.fract() == 0.0 {
10129                    format!("{}", normalized as i64)
10130                } else {
10131                    normalized.to_string()
10132                };
10133                return Expression::Literal(Box::new(crate::expressions::Literal::Number(
10134                    rendered,
10135                )));
10136            }
10137        }
10138
10139        if !negate {
10140            return value;
10141        }
10142
10143        match value {
10144            Expression::Neg(op) => op.this,
10145            other => Expression::Neg(Box::new(UnaryOp {
10146                this: other,
10147                inferred_type: None,
10148            })),
10149        }
10150    }
10151
10152    /// Internal TO_DATE function that won't be converted to CAST by the Snowflake handler.
10153    /// Uses the name `_POLYGLOT_TO_DATE` which is not recognized by the TO_DATE -> CAST logic.
10154    /// The Snowflake DATEDIFF handler converts these back to TO_DATE.
10155    const PRESERVED_TO_DATE: &'static str = "_POLYGLOT_TO_DATE";
10156}
10157
10158#[cfg(test)]
10159mod tests {
10160    use super::*;
10161
10162    #[cfg(all(
10163        feature = "transpile",
10164        feature = "dialect-hana",
10165        feature = "dialect-duckdb",
10166        feature = "dialect-trino"
10167    ))]
10168    #[test]
10169    fn hana_validation_and_generation_agree_after_ast_serialization() {
10170        let hana = Dialect::get(DialectType::HANA);
10171        for (sql, duckdb, trino) in [
10172            ("SELECT COALESCE(IFNULL(x, 0), 1) FROM t", true, true),
10173            ("SELECT LOCATE('abcabc', 'bc', 2)", true, true),
10174            ("SELECT LOCATE('abcabc', 'bc', 1, 2)", false, true),
10175            ("SELECT SUBSTRING('abc', -1, -2)", true, true),
10176            ("SELECT TO_DECIMAL(12.345, 5, 2)", true, true),
10177            (
10178                "SELECT CAST(12.345 AS DECIMAL(5)), CAST(-12.9 AS INT)",
10179                true,
10180                true,
10181            ),
10182            ("SELECT TO_DATE('2024-01-15', 'YYYY-MM-DD')", true, true),
10183            (
10184                "SELECT TO_TIMESTAMP('2024-01-15', 'YYYY-MM-DD')",
10185                false,
10186                true,
10187            ),
10188            (
10189                "SELECT TO_VARCHAR(DATE '2024-01-15', 'YYYY-MM-DD')",
10190                true,
10191                true,
10192            ),
10193            ("SELECT CURRENT_UTCDATE", true, true),
10194            (
10195                "SELECT CURRENT_UTCTIMESTAMP(7), CURRENT_UTCTIME",
10196                false,
10197                true,
10198            ),
10199            ("CREATE TABLE t (n INT, xs INT ARRAY)", true, true),
10200            ("CREATE TABLE t (n SMALLDECIMAL ARRAY)", false, false),
10201            ("ALTER TABLE t ADD (n SMALLDECIMAL ARRAY)", false, false),
10202            ("ALTER TABLE t ADD (n INT ARRAY)", true, true),
10203            ("SELECT COALESCE(ADD_DAYS(d, 1), d) FROM t", false, false),
10204            ("SELECT COALESCE(CAST(x AS INT), 0) FROM t", false, false),
10205            (
10206                "SELECT COALESCE(JSON_VALUE(j, '$.a'), 'x') FROM t",
10207                false,
10208                false,
10209            ),
10210            (
10211                "SELECT COALESCE(SUBSTR_REGEXPR('a' IN s), 'x') FROM t",
10212                false,
10213                false,
10214            ),
10215            (
10216                "SELECT COALESCE(STRING_AGG(x, ','), '') FROM t",
10217                false,
10218                false,
10219            ),
10220            (
10221                "CREATE TABLE t (n INT DEFAULT ADD_DAYS(d, 1))",
10222                false,
10223                false,
10224            ),
10225            ("SELECT * FROM t FOR JSON", false, false),
10226            ("CREATE COLUMN TABLE t (n INT)", false, false),
10227            ("SELECT * FROM t WITH HINT (NO_INLINE)", false, false),
10228        ] {
10229            let ast = hana.parse(sql).unwrap().remove(0);
10230            let json = serde_json::to_string(&ast).unwrap();
10231            let ast: Expression = serde_json::from_str(&json).unwrap();
10232            for (target, supported) in [(DialectType::DuckDB, duckdb), (DialectType::Trino, trino)]
10233            {
10234                assert_eq!(
10235                    Dialect::reject_hana_source_semantics(&ast, DialectType::HANA, target).is_ok(),
10236                    supported,
10237                    "validation: {sql} -> {target}"
10238                );
10239                assert_eq!(
10240                    Dialect::get(target).generate(&ast).is_ok(),
10241                    supported,
10242                    "generation: {sql} -> {target}"
10243                );
10244            }
10245            assert!(Dialect::reject_hana_source_semantics(
10246                &ast,
10247                DialectType::HANA,
10248                DialectType::HANA
10249            )
10250            .is_ok());
10251        }
10252    }
10253
10254    #[cfg(all(
10255        feature = "transpile",
10256        feature = "dialect-hana",
10257        feature = "dialect-duckdb"
10258    ))]
10259    #[test]
10260    fn hana_rejects_source_semantics_before_preprocessing_can_erase_them() {
10261        use std::sync::atomic::{AtomicBool, Ordering};
10262        let called = Arc::new(AtomicBool::new(false));
10263        let seen = Arc::clone(&called);
10264        let mut hana = Dialect::get(DialectType::HANA);
10265        hana.custom_preprocess = Some(Box::new(move |_| {
10266            seen.store(true, Ordering::Relaxed);
10267            Ok(Expression::number(1))
10268        }));
10269        for level in [
10270            UnsupportedLevel::Ignore,
10271            UnsupportedLevel::Warn,
10272            UnsupportedLevel::Raise,
10273            UnsupportedLevel::Immediate,
10274        ] {
10275            for sql in [
10276                "SELECT COALESCE(ADD_DAYS(d, 1), d) FROM t",
10277                "SELECT COALESCE(CAST(x AS INT), 0) FROM t",
10278                "SELECT COALESCE(JSON_VALUE(j, '$.a'), 'x') FROM t",
10279                "CREATE TABLE t (x SMALLDECIMAL ARRAY)",
10280                "ALTER TABLE t ADD (x SMALLDECIMAL ARRAY)",
10281                "SELECT CAST(xs AS SMALLDECIMAL ARRAY) FROM t",
10282                "CREATE FUNCTION f(x SMALLDECIMAL ARRAY) RETURNS INT AS 1",
10283                "SELECT * FROM t FOR JSON",
10284                "SELECT * FROM t WITH HINT (NO_INLINE)",
10285            ] {
10286                assert!(
10287                    hana.transpile_with(
10288                        sql,
10289                        DialectType::DuckDB,
10290                        TranspileOptions::default().with_unsupported_level(level)
10291                    )
10292                    .is_err(),
10293                    "{sql}"
10294                );
10295                assert!(
10296                    !called.load(Ordering::Relaxed),
10297                    "preprocessing must not erase unsupported input: {sql}"
10298                );
10299            }
10300        }
10301        let mut target = Dialect::get(DialectType::DuckDB);
10302        Arc::make_mut(&mut target.generator_config).dialect = Some(DialectType::Generic);
10303        assert!(hana
10304            .transpile("SELECT LOCATE('abc', 'b')", &target)
10305            .is_err());
10306        assert!(!called.load(Ordering::Relaxed));
10307        assert!(hana
10308            .transpile("SELECT COALESCE(x, 0) FROM t", DialectType::DuckDB)
10309            .is_ok());
10310        assert!(called.load(Ordering::Relaxed));
10311    }
10312
10313    #[test]
10314    fn built_in_dialect_instances_share_tokenizer_config() {
10315        let first = Dialect::get(DialectType::PostgreSQL);
10316        let second = Dialect::get(DialectType::PostgreSQL);
10317
10318        assert!(first.tokenizer.shares_config_with(&second.tokenizer));
10319    }
10320
10321    #[test]
10322    fn test_dialect_type_from_str() {
10323        assert_eq!(
10324            "postgres".parse::<DialectType>().unwrap(),
10325            DialectType::PostgreSQL
10326        );
10327        assert_eq!(
10328            "postgresql".parse::<DialectType>().unwrap(),
10329            DialectType::PostgreSQL
10330        );
10331        assert_eq!("mysql".parse::<DialectType>().unwrap(), DialectType::MySQL);
10332        assert_eq!(
10333            "bigquery".parse::<DialectType>().unwrap(),
10334            DialectType::BigQuery
10335        );
10336    }
10337
10338    #[test]
10339    fn test_basic_transpile() {
10340        let dialect = Dialect::get(DialectType::Generic);
10341        let result = dialect
10342            .transpile("SELECT 1", DialectType::PostgreSQL)
10343            .unwrap();
10344        assert_eq!(result.len(), 1);
10345        assert_eq!(result[0], "SELECT 1");
10346    }
10347
10348    #[test]
10349    fn test_sqlite_double_quoted_column_defaults_to_postgres_strings() {
10350        let sqlite = Dialect::get(DialectType::SQLite);
10351        let result = sqlite
10352            .transpile(
10353                r#"CREATE TABLE "_collections" (
10354                    "type" TEXT DEFAULT "base" NOT NULL,
10355                    "fields" JSON DEFAULT "[]" NOT NULL,
10356                    "options" JSON DEFAULT "{}" NOT NULL
10357                )"#,
10358                DialectType::PostgreSQL,
10359            )
10360            .unwrap();
10361
10362        assert!(result[0].contains(r#""type" TEXT DEFAULT 'base' NOT NULL"#));
10363        assert!(result[0].contains(r#""fields" JSON DEFAULT '[]' NOT NULL"#));
10364        assert!(result[0].contains(r#""options" JSON DEFAULT '{}' NOT NULL"#));
10365    }
10366
10367    #[test]
10368    fn test_sqlite_identity_preserves_double_quoted_column_defaults() {
10369        let sqlite = Dialect::get(DialectType::SQLite);
10370        let result = sqlite
10371            .transpile(
10372                r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#,
10373                DialectType::SQLite,
10374            )
10375            .unwrap();
10376
10377        assert_eq!(
10378            result[0],
10379            r#"CREATE TABLE "_collections" ("type" TEXT DEFAULT "base" NOT NULL)"#
10380        );
10381    }
10382
10383    #[test]
10384    fn test_function_transformation_mysql() {
10385        // NVL should be transformed to IFNULL in MySQL
10386        let dialect = Dialect::get(DialectType::Generic);
10387        let result = dialect
10388            .transpile("SELECT NVL(a, b)", DialectType::MySQL)
10389            .unwrap();
10390        assert_eq!(result[0], "SELECT IFNULL(a, b)");
10391    }
10392
10393    #[test]
10394    fn test_get_path_duckdb() {
10395        // Test: step by step
10396        let snowflake = Dialect::get(DialectType::Snowflake);
10397
10398        // Step 1: Parse and check what Snowflake produces as intermediate
10399        let result_sf_sf = snowflake
10400            .transpile(
10401                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
10402                DialectType::Snowflake,
10403            )
10404            .unwrap();
10405        eprintln!("Snowflake->Snowflake colon: {}", result_sf_sf[0]);
10406
10407        // Step 2: DuckDB target
10408        let result_sf_dk = snowflake
10409            .transpile(
10410                "SELECT PARSE_JSON('{\"fruit\":\"banana\"}'):fruit",
10411                DialectType::DuckDB,
10412            )
10413            .unwrap();
10414        eprintln!("Snowflake->DuckDB colon: {}", result_sf_dk[0]);
10415
10416        // Step 3: GET_PATH directly
10417        let result_gp = snowflake
10418            .transpile(
10419                "SELECT GET_PATH(PARSE_JSON('{\"fruit\":\"banana\"}'), 'fruit')",
10420                DialectType::DuckDB,
10421            )
10422            .unwrap();
10423        eprintln!("Snowflake->DuckDB explicit GET_PATH: {}", result_gp[0]);
10424    }
10425
10426    #[test]
10427    fn test_function_transformation_postgres() {
10428        // IFNULL should be transformed to COALESCE in PostgreSQL
10429        let dialect = Dialect::get(DialectType::Generic);
10430        let result = dialect
10431            .transpile("SELECT IFNULL(a, b)", DialectType::PostgreSQL)
10432            .unwrap();
10433        assert_eq!(result[0], "SELECT COALESCE(a, b)");
10434
10435        // NVL should also be transformed to COALESCE
10436        let result = dialect
10437            .transpile("SELECT NVL(a, b)", DialectType::PostgreSQL)
10438            .unwrap();
10439        assert_eq!(result[0], "SELECT COALESCE(a, b)");
10440    }
10441
10442    #[test]
10443    fn test_hive_cast_to_trycast() {
10444        // Hive CAST should become TRY_CAST for targets that support it
10445        let hive = Dialect::get(DialectType::Hive);
10446        let result = hive
10447            .transpile("CAST(1 AS INT)", DialectType::DuckDB)
10448            .unwrap();
10449        assert_eq!(result[0], "TRY_CAST(1 AS INT)");
10450
10451        let result = hive
10452            .transpile("CAST(1 AS INT)", DialectType::Presto)
10453            .unwrap();
10454        assert_eq!(result[0], "TRY_CAST(1 AS INTEGER)");
10455    }
10456
10457    #[test]
10458    fn test_hive_array_identity() {
10459        // Hive ARRAY<DATE> should preserve angle bracket syntax
10460        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')";
10461        let hive = Dialect::get(DialectType::Hive);
10462
10463        // Test via transpile (this works)
10464        let result = hive.transpile(sql, DialectType::Hive).unwrap();
10465        eprintln!("Hive ARRAY via transpile: {}", result[0]);
10466        assert!(
10467            result[0].contains("ARRAY<DATE>"),
10468            "transpile: Expected ARRAY<DATE>, got: {}",
10469            result[0]
10470        );
10471
10472        // Test via parse -> transform -> generate (identity test path)
10473        let ast = hive.parse(sql).unwrap();
10474        let transformed = hive.transform(ast[0].clone()).unwrap();
10475        let output = hive.generate(&transformed).unwrap();
10476        eprintln!("Hive ARRAY via identity path: {}", output);
10477        assert!(
10478            output.contains("ARRAY<DATE>"),
10479            "identity path: Expected ARRAY<DATE>, got: {}",
10480            output
10481        );
10482    }
10483
10484    #[test]
10485    fn test_starrocks_delete_between_expansion() {
10486        // StarRocks doesn't support BETWEEN in DELETE statements
10487        let dialect = Dialect::get(DialectType::Generic);
10488
10489        // BETWEEN should be expanded to >= AND <= in DELETE
10490        let result = dialect
10491            .transpile(
10492                "DELETE FROM t WHERE a BETWEEN b AND c",
10493                DialectType::StarRocks,
10494            )
10495            .unwrap();
10496        assert_eq!(result[0], "DELETE FROM t WHERE a >= b AND a <= c");
10497
10498        // NOT BETWEEN should be expanded to < OR > in DELETE
10499        let result = dialect
10500            .transpile(
10501                "DELETE FROM t WHERE a NOT BETWEEN b AND c",
10502                DialectType::StarRocks,
10503            )
10504            .unwrap();
10505        assert_eq!(result[0], "DELETE FROM t WHERE a < b OR a > c");
10506
10507        // BETWEEN in SELECT should NOT be expanded (StarRocks supports it there)
10508        let result = dialect
10509            .transpile(
10510                "SELECT * FROM t WHERE a BETWEEN b AND c",
10511                DialectType::StarRocks,
10512            )
10513            .unwrap();
10514        assert!(
10515            result[0].contains("BETWEEN"),
10516            "BETWEEN should be preserved in SELECT"
10517        );
10518    }
10519
10520    #[test]
10521    fn test_snowflake_ltrim_rtrim_parse() {
10522        let sf = Dialect::get(DialectType::Snowflake);
10523        let sql = "SELECT LTRIM(RTRIM(col)) FROM t1";
10524        let result = sf.transpile(sql, DialectType::DuckDB);
10525        match &result {
10526            Ok(r) => eprintln!("LTRIM/RTRIM result: {}", r[0]),
10527            Err(e) => eprintln!("LTRIM/RTRIM error: {}", e),
10528        }
10529        assert!(
10530            result.is_ok(),
10531            "Expected successful parse of LTRIM(RTRIM(col)), got error: {:?}",
10532            result.err()
10533        );
10534    }
10535
10536    #[test]
10537    fn test_duckdb_count_if_parse() {
10538        let duck = Dialect::get(DialectType::DuckDB);
10539        let sql = "COUNT_IF(x)";
10540        let result = duck.transpile(sql, DialectType::DuckDB);
10541        match &result {
10542            Ok(r) => eprintln!("COUNT_IF result: {}", r[0]),
10543            Err(e) => eprintln!("COUNT_IF error: {}", e),
10544        }
10545        assert!(
10546            result.is_ok(),
10547            "Expected successful parse of COUNT_IF(x), got error: {:?}",
10548            result.err()
10549        );
10550    }
10551
10552    #[test]
10553    fn test_tsql_cast_tinyint_parse() {
10554        let tsql = Dialect::get(DialectType::TSQL);
10555        let sql = "CAST(X AS TINYINT)";
10556        let result = tsql.transpile(sql, DialectType::DuckDB);
10557        match &result {
10558            Ok(r) => eprintln!("TSQL CAST TINYINT result: {}", r[0]),
10559            Err(e) => eprintln!("TSQL CAST TINYINT error: {}", e),
10560        }
10561        assert!(
10562            result.is_ok(),
10563            "Expected successful transpile, got error: {:?}",
10564            result.err()
10565        );
10566    }
10567
10568    #[test]
10569    fn test_pg_hash_bitwise_xor() {
10570        let dialect = Dialect::get(DialectType::PostgreSQL);
10571        let result = dialect.transpile("x # y", DialectType::PostgreSQL).unwrap();
10572        assert_eq!(result[0], "x # y");
10573    }
10574
10575    #[test]
10576    fn test_pg_array_to_duckdb() {
10577        let dialect = Dialect::get(DialectType::PostgreSQL);
10578        let result = dialect
10579            .transpile("SELECT ARRAY[1, 2, 3] @> ARRAY[1, 2]", DialectType::DuckDB)
10580            .unwrap();
10581        assert_eq!(result[0], "SELECT [1, 2, 3] @> [1, 2]");
10582    }
10583
10584    #[test]
10585    fn test_array_remove_bigquery() {
10586        let dialect = Dialect::get(DialectType::Generic);
10587        let result = dialect
10588            .transpile("ARRAY_REMOVE(the_array, target)", DialectType::BigQuery)
10589            .unwrap();
10590        assert_eq!(
10591            result[0],
10592            "ARRAY(SELECT _u FROM UNNEST(the_array) AS _u WHERE _u <> target)"
10593        );
10594    }
10595
10596    #[test]
10597    fn test_map_clickhouse_case() {
10598        let dialect = Dialect::get(DialectType::Generic);
10599        let parsed = dialect
10600            .parse("CAST(MAP('a', '1') AS MAP(TEXT, TEXT))")
10601            .unwrap();
10602        eprintln!("MAP parsed: {:?}", parsed);
10603        let result = dialect
10604            .transpile(
10605                "CAST(MAP('a', '1') AS MAP(TEXT, TEXT))",
10606                DialectType::ClickHouse,
10607            )
10608            .unwrap();
10609        eprintln!("MAP result: {}", result[0]);
10610    }
10611
10612    #[test]
10613    fn test_generate_date_array_presto() {
10614        let dialect = Dialect::get(DialectType::Generic);
10615        let result = dialect.transpile(
10616            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10617            DialectType::Presto,
10618        ).unwrap();
10619        eprintln!("GDA -> Presto: {}", result[0]);
10620        assert_eq!(result[0], "SELECT * FROM UNNEST(SEQUENCE(CAST('2020-01-01' AS DATE), CAST('2020-02-01' AS DATE), (1 * INTERVAL '7' DAY)))");
10621    }
10622
10623    #[test]
10624    fn test_generate_date_array_postgres() {
10625        let dialect = Dialect::get(DialectType::Generic);
10626        let result = dialect.transpile(
10627            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10628            DialectType::PostgreSQL,
10629        ).unwrap();
10630        eprintln!("GDA -> PostgreSQL: {}", result[0]);
10631    }
10632
10633    #[test]
10634    fn test_generate_date_array_snowflake() {
10635        let dialect = Dialect::get(DialectType::Generic);
10636        let result = dialect
10637            .transpile(
10638                "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10639                DialectType::Snowflake,
10640            )
10641            .unwrap();
10642        eprintln!("GDA -> Snowflake: {}", result[0]);
10643    }
10644
10645    #[test]
10646    fn test_array_length_generate_date_array_snowflake() {
10647        let dialect = Dialect::get(DialectType::Generic);
10648        let result = dialect.transpile(
10649            "SELECT ARRAY_LENGTH(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10650            DialectType::Snowflake,
10651        ).unwrap();
10652        eprintln!("ARRAY_LENGTH(GDA) -> Snowflake: {}", result[0]);
10653    }
10654
10655    #[test]
10656    fn test_generate_date_array_mysql() {
10657        let dialect = Dialect::get(DialectType::Generic);
10658        let result = dialect.transpile(
10659            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10660            DialectType::MySQL,
10661        ).unwrap();
10662        eprintln!("GDA -> MySQL: {}", result[0]);
10663    }
10664
10665    #[test]
10666    fn test_generate_date_array_redshift() {
10667        let dialect = Dialect::get(DialectType::Generic);
10668        let result = dialect.transpile(
10669            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10670            DialectType::Redshift,
10671        ).unwrap();
10672        eprintln!("GDA -> Redshift: {}", result[0]);
10673    }
10674
10675    #[test]
10676    fn test_generate_date_array_tsql() {
10677        let dialect = Dialect::get(DialectType::Generic);
10678        let result = dialect.transpile(
10679            "SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))",
10680            DialectType::TSQL,
10681        ).unwrap();
10682        eprintln!("GDA -> TSQL: {}", result[0]);
10683    }
10684
10685    #[test]
10686    fn test_struct_colon_syntax() {
10687        let dialect = Dialect::get(DialectType::Generic);
10688        // Test without colon first
10689        let result = dialect.transpile(
10690            "CAST((1, 2, 3, 4) AS STRUCT<a TINYINT, b SMALLINT, c INT, d BIGINT>)",
10691            DialectType::ClickHouse,
10692        );
10693        match result {
10694            Ok(r) => eprintln!("STRUCT no colon -> ClickHouse: {}", r[0]),
10695            Err(e) => eprintln!("STRUCT no colon error: {}", e),
10696        }
10697        // Now test with colon
10698        let result = dialect.transpile(
10699            "CAST((1, 2, 3, 4) AS STRUCT<a: TINYINT, b: SMALLINT, c: INT, d: BIGINT>)",
10700            DialectType::ClickHouse,
10701        );
10702        match result {
10703            Ok(r) => eprintln!("STRUCT colon -> ClickHouse: {}", r[0]),
10704            Err(e) => eprintln!("STRUCT colon error: {}", e),
10705        }
10706    }
10707
10708    #[test]
10709    fn test_generate_date_array_cte_wrapped_mysql() {
10710        let dialect = Dialect::get(DialectType::Generic);
10711        let result = dialect.transpile(
10712            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
10713            DialectType::MySQL,
10714        ).unwrap();
10715        eprintln!("GDA CTE -> MySQL: {}", result[0]);
10716    }
10717
10718    #[test]
10719    fn test_generate_date_array_cte_wrapped_tsql() {
10720        let dialect = Dialect::get(DialectType::Generic);
10721        let result = dialect.transpile(
10722            "WITH dates AS (SELECT * FROM UNNEST(GENERATE_DATE_ARRAY(DATE '2020-01-01', DATE '2020-02-01', INTERVAL 1 WEEK))) SELECT * FROM dates",
10723            DialectType::TSQL,
10724        ).unwrap();
10725        eprintln!("GDA CTE -> TSQL: {}", result[0]);
10726    }
10727
10728    #[test]
10729    fn test_decode_literal_no_null_check() {
10730        // Oracle DECODE with all literals should produce simple equality, no IS NULL
10731        let dialect = Dialect::get(DialectType::Oracle);
10732        let result = dialect
10733            .transpile("SELECT decode(1,2,3,4)", DialectType::DuckDB)
10734            .unwrap();
10735        assert_eq!(
10736            result[0], "SELECT CASE WHEN 1 = 2 THEN 3 ELSE 4 END",
10737            "Literal DECODE should not have IS NULL checks"
10738        );
10739    }
10740
10741    #[test]
10742    fn test_decode_column_vs_literal_no_null_check() {
10743        // Oracle DECODE with column vs literal should use simple equality (like sqlglot)
10744        let dialect = Dialect::get(DialectType::Oracle);
10745        let result = dialect
10746            .transpile("SELECT decode(col, 2, 3, 4) FROM t", DialectType::DuckDB)
10747            .unwrap();
10748        assert_eq!(
10749            result[0], "SELECT CASE WHEN col = 2 THEN 3 ELSE 4 END FROM t",
10750            "Column vs literal DECODE should not have IS NULL checks"
10751        );
10752    }
10753
10754    #[test]
10755    fn test_decode_column_vs_column_keeps_null_check() {
10756        // Oracle DECODE with column vs column should keep null-safe comparison
10757        let dialect = Dialect::get(DialectType::Oracle);
10758        let result = dialect
10759            .transpile("SELECT decode(col, col2, 3, 4) FROM t", DialectType::DuckDB)
10760            .unwrap();
10761        assert!(
10762            result[0].contains("IS NULL"),
10763            "Column vs column DECODE should have IS NULL checks, got: {}",
10764            result[0]
10765        );
10766    }
10767
10768    #[test]
10769    fn test_decode_null_search() {
10770        // Oracle DECODE with NULL search should use IS NULL
10771        let dialect = Dialect::get(DialectType::Oracle);
10772        let result = dialect
10773            .transpile("SELECT decode(col, NULL, 3, 4) FROM t", DialectType::DuckDB)
10774            .unwrap();
10775        assert_eq!(
10776            result[0],
10777            "SELECT CASE WHEN col IS NULL THEN 3 ELSE 4 END FROM t",
10778        );
10779    }
10780
10781    // =========================================================================
10782    // REGEXP function transpilation tests
10783    // =========================================================================
10784
10785    #[test]
10786    fn test_regexp_substr_snowflake_to_duckdb_2arg() {
10787        let dialect = Dialect::get(DialectType::Snowflake);
10788        let result = dialect
10789            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern')", DialectType::DuckDB)
10790            .unwrap();
10791        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10792    }
10793
10794    #[test]
10795    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos1() {
10796        let dialect = Dialect::get(DialectType::Snowflake);
10797        let result = dialect
10798            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 1)", DialectType::DuckDB)
10799            .unwrap();
10800        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10801    }
10802
10803    #[test]
10804    fn test_regexp_substr_snowflake_to_duckdb_3arg_pos_gt1() {
10805        let dialect = Dialect::get(DialectType::Snowflake);
10806        let result = dialect
10807            .transpile("SELECT REGEXP_SUBSTR(s, 'pattern', 3)", DialectType::DuckDB)
10808            .unwrap();
10809        assert_eq!(
10810            result[0],
10811            "SELECT REGEXP_EXTRACT(NULLIF(SUBSTRING(s, 3), ''), 'pattern')"
10812        );
10813    }
10814
10815    #[test]
10816    fn test_regexp_substr_snowflake_to_duckdb_4arg_occ_gt1() {
10817        let dialect = Dialect::get(DialectType::Snowflake);
10818        let result = dialect
10819            .transpile(
10820                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 3)",
10821                DialectType::DuckDB,
10822            )
10823            .unwrap();
10824        assert_eq!(
10825            result[0],
10826            "SELECT ARRAY_EXTRACT(REGEXP_EXTRACT_ALL(s, 'pattern'), 3)"
10827        );
10828    }
10829
10830    #[test]
10831    fn test_regexp_substr_snowflake_to_duckdb_5arg_e_flag() {
10832        let dialect = Dialect::get(DialectType::Snowflake);
10833        let result = dialect
10834            .transpile(
10835                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')",
10836                DialectType::DuckDB,
10837            )
10838            .unwrap();
10839        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10840    }
10841
10842    #[test]
10843    fn test_regexp_substr_snowflake_to_duckdb_6arg_group0() {
10844        let dialect = Dialect::get(DialectType::Snowflake);
10845        let result = dialect
10846            .transpile(
10847                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
10848                DialectType::DuckDB,
10849            )
10850            .unwrap();
10851        assert_eq!(result[0], "SELECT REGEXP_EXTRACT(s, 'pattern')");
10852    }
10853
10854    #[test]
10855    fn test_regexp_substr_snowflake_identity_strip_group0() {
10856        let dialect = Dialect::get(DialectType::Snowflake);
10857        let result = dialect
10858            .transpile(
10859                "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e', 0)",
10860                DialectType::Snowflake,
10861            )
10862            .unwrap();
10863        assert_eq!(result[0], "SELECT REGEXP_SUBSTR(s, 'pattern', 1, 1, 'e')");
10864    }
10865
10866    #[test]
10867    fn test_regexp_substr_all_snowflake_to_duckdb_2arg() {
10868        let dialect = Dialect::get(DialectType::Snowflake);
10869        let result = dialect
10870            .transpile(
10871                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')",
10872                DialectType::DuckDB,
10873            )
10874            .unwrap();
10875        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10876    }
10877
10878    #[test]
10879    fn test_regexp_substr_all_snowflake_to_duckdb_3arg_pos_gt1() {
10880        let dialect = Dialect::get(DialectType::Snowflake);
10881        let result = dialect
10882            .transpile(
10883                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 3)",
10884                DialectType::DuckDB,
10885            )
10886            .unwrap();
10887        assert_eq!(
10888            result[0],
10889            "SELECT REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')"
10890        );
10891    }
10892
10893    #[test]
10894    fn test_regexp_substr_all_snowflake_to_duckdb_5arg_e_flag() {
10895        let dialect = Dialect::get(DialectType::Snowflake);
10896        let result = dialect
10897            .transpile(
10898                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')",
10899                DialectType::DuckDB,
10900            )
10901            .unwrap();
10902        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10903    }
10904
10905    #[test]
10906    fn test_regexp_substr_all_snowflake_to_duckdb_6arg_group0() {
10907        let dialect = Dialect::get(DialectType::Snowflake);
10908        let result = dialect
10909            .transpile(
10910                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
10911                DialectType::DuckDB,
10912            )
10913            .unwrap();
10914        assert_eq!(result[0], "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')");
10915    }
10916
10917    #[test]
10918    fn test_regexp_substr_all_snowflake_identity_strip_group0() {
10919        let dialect = Dialect::get(DialectType::Snowflake);
10920        let result = dialect
10921            .transpile(
10922                "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e', 0)",
10923                DialectType::Snowflake,
10924            )
10925            .unwrap();
10926        assert_eq!(
10927            result[0],
10928            "SELECT REGEXP_SUBSTR_ALL(s, 'pattern', 1, 1, 'e')"
10929        );
10930    }
10931
10932    #[test]
10933    fn test_regexp_count_snowflake_to_duckdb_2arg() {
10934        let dialect = Dialect::get(DialectType::Snowflake);
10935        let result = dialect
10936            .transpile("SELECT REGEXP_COUNT(s, 'pattern')", DialectType::DuckDB)
10937            .unwrap();
10938        assert_eq!(
10939            result[0],
10940            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(s, 'pattern')) END"
10941        );
10942    }
10943
10944    #[test]
10945    fn test_regexp_count_snowflake_to_duckdb_3arg() {
10946        let dialect = Dialect::get(DialectType::Snowflake);
10947        let result = dialect
10948            .transpile("SELECT REGEXP_COUNT(s, 'pattern', 3)", DialectType::DuckDB)
10949            .unwrap();
10950        assert_eq!(
10951            result[0],
10952            "SELECT CASE WHEN 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 3), 'pattern')) END"
10953        );
10954    }
10955
10956    #[test]
10957    fn test_regexp_count_snowflake_to_duckdb_4arg_flags() {
10958        let dialect = Dialect::get(DialectType::Snowflake);
10959        let result = dialect
10960            .transpile(
10961                "SELECT REGEXP_COUNT(s, 'pattern', 1, 'i')",
10962                DialectType::DuckDB,
10963            )
10964            .unwrap();
10965        assert_eq!(
10966            result[0],
10967            "SELECT CASE WHEN '(?i)' || 'pattern' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING(s, 1), '(?i)' || 'pattern')) END"
10968        );
10969    }
10970
10971    #[test]
10972    fn test_regexp_count_snowflake_to_duckdb_4arg_flags_literal_string() {
10973        let dialect = Dialect::get(DialectType::Snowflake);
10974        let result = dialect
10975            .transpile(
10976                "SELECT REGEXP_COUNT('Hello World', 'L', 1, 'im')",
10977                DialectType::DuckDB,
10978            )
10979            .unwrap();
10980        assert_eq!(
10981            result[0],
10982            "SELECT CASE WHEN '(?im)' || 'L' = '' THEN 0 ELSE LENGTH(REGEXP_EXTRACT_ALL(SUBSTRING('Hello World', 1), '(?im)' || 'L')) END"
10983        );
10984    }
10985
10986    #[test]
10987    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos1_occ1() {
10988        let dialect = Dialect::get(DialectType::Snowflake);
10989        let result = dialect
10990            .transpile(
10991                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 1, 1)",
10992                DialectType::DuckDB,
10993            )
10994            .unwrap();
10995        assert_eq!(result[0], "SELECT REGEXP_REPLACE(s, 'pattern', 'repl')");
10996    }
10997
10998    #[test]
10999    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ0() {
11000        let dialect = Dialect::get(DialectType::Snowflake);
11001        let result = dialect
11002            .transpile(
11003                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 0)",
11004                DialectType::DuckDB,
11005            )
11006            .unwrap();
11007        assert_eq!(
11008            result[0],
11009            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl', 'g')"
11010        );
11011    }
11012
11013    #[test]
11014    fn test_regexp_replace_snowflake_to_duckdb_5arg_pos_gt1_occ1() {
11015        let dialect = Dialect::get(DialectType::Snowflake);
11016        let result = dialect
11017            .transpile(
11018                "SELECT REGEXP_REPLACE(s, 'pattern', 'repl', 3, 1)",
11019                DialectType::DuckDB,
11020            )
11021            .unwrap();
11022        assert_eq!(
11023            result[0],
11024            "SELECT SUBSTRING(s, 1, 2) || REGEXP_REPLACE(SUBSTRING(s, 3), 'pattern', 'repl')"
11025        );
11026    }
11027
11028    #[test]
11029    fn test_rlike_snowflake_to_duckdb_2arg() {
11030        let dialect = Dialect::get(DialectType::Snowflake);
11031        let result = dialect
11032            .transpile("SELECT RLIKE(a, b)", DialectType::DuckDB)
11033            .unwrap();
11034        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b)");
11035    }
11036
11037    #[test]
11038    fn test_rlike_snowflake_to_duckdb_3arg_flags() {
11039        let dialect = Dialect::get(DialectType::Snowflake);
11040        let result = dialect
11041            .transpile("SELECT RLIKE(a, b, 'i')", DialectType::DuckDB)
11042            .unwrap();
11043        assert_eq!(result[0], "SELECT REGEXP_FULL_MATCH(a, b, 'i')");
11044    }
11045
11046    #[test]
11047    fn test_regexp_extract_all_bigquery_to_snowflake_no_capture() {
11048        let dialect = Dialect::get(DialectType::BigQuery);
11049        let result = dialect
11050            .transpile(
11051                "SELECT REGEXP_EXTRACT_ALL(s, 'pattern')",
11052                DialectType::Snowflake,
11053            )
11054            .unwrap();
11055        assert_eq!(result[0], "SELECT REGEXP_SUBSTR_ALL(s, 'pattern')");
11056    }
11057
11058    #[test]
11059    fn test_regexp_extract_all_bigquery_to_snowflake_with_capture() {
11060        let dialect = Dialect::get(DialectType::BigQuery);
11061        let result = dialect
11062            .transpile(
11063                "SELECT REGEXP_EXTRACT_ALL(s, '(a)[0-9]')",
11064                DialectType::Snowflake,
11065            )
11066            .unwrap();
11067        assert_eq!(
11068            result[0],
11069            "SELECT REGEXP_SUBSTR_ALL(s, '(a)[0-9]', 1, 1, 'c', 1)"
11070        );
11071    }
11072
11073    #[test]
11074    fn test_regexp_instr_snowflake_to_duckdb_2arg() {
11075        let dialect = Dialect::get(DialectType::Snowflake);
11076        let result = dialect
11077            .transpile("SELECT REGEXP_INSTR(s, 'pattern')", DialectType::DuckDB)
11078            .unwrap();
11079        assert!(
11080            result[0].contains("CASE WHEN"),
11081            "Expected CASE WHEN in result: {}",
11082            result[0]
11083        );
11084        assert!(
11085            result[0].contains("LIST_SUM"),
11086            "Expected LIST_SUM in result: {}",
11087            result[0]
11088        );
11089    }
11090
11091    #[test]
11092    fn test_array_except_generic_to_duckdb() {
11093        let dialect = Dialect::get(DialectType::Generic);
11094        let result = dialect
11095            .transpile(
11096                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
11097                DialectType::DuckDB,
11098            )
11099            .unwrap();
11100        eprintln!("ARRAY_EXCEPT Generic->DuckDB: {}", result[0]);
11101        assert!(
11102            result[0].contains("CASE WHEN"),
11103            "Expected CASE WHEN: {}",
11104            result[0]
11105        );
11106        assert!(
11107            result[0].contains("LIST_FILTER"),
11108            "Expected LIST_FILTER: {}",
11109            result[0]
11110        );
11111        assert!(
11112            result[0].contains("LIST_DISTINCT"),
11113            "Expected LIST_DISTINCT: {}",
11114            result[0]
11115        );
11116        assert!(
11117            result[0].contains("IS NOT DISTINCT FROM"),
11118            "Expected IS NOT DISTINCT FROM: {}",
11119            result[0]
11120        );
11121        assert!(
11122            result[0].contains("= 0"),
11123            "Expected = 0 filter: {}",
11124            result[0]
11125        );
11126    }
11127
11128    #[test]
11129    fn test_array_except_generic_to_snowflake() {
11130        let dialect = Dialect::get(DialectType::Generic);
11131        let result = dialect
11132            .transpile(
11133                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
11134                DialectType::Snowflake,
11135            )
11136            .unwrap();
11137        eprintln!("ARRAY_EXCEPT Generic->Snowflake: {}", result[0]);
11138        assert_eq!(result[0], "SELECT ARRAY_EXCEPT([1, 2, 3], [2])");
11139    }
11140
11141    #[test]
11142    fn test_array_except_generic_to_presto() {
11143        let dialect = Dialect::get(DialectType::Generic);
11144        let result = dialect
11145            .transpile(
11146                "SELECT ARRAY_EXCEPT(ARRAY(1, 2, 3), ARRAY(2))",
11147                DialectType::Presto,
11148            )
11149            .unwrap();
11150        eprintln!("ARRAY_EXCEPT Generic->Presto: {}", result[0]);
11151        assert_eq!(result[0], "SELECT ARRAY_EXCEPT(ARRAY[1, 2, 3], ARRAY[2])");
11152    }
11153
11154    #[test]
11155    fn test_array_except_snowflake_to_duckdb() {
11156        let dialect = Dialect::get(DialectType::Snowflake);
11157        let result = dialect
11158            .transpile("SELECT ARRAY_EXCEPT([1, 2, 3], [2])", DialectType::DuckDB)
11159            .unwrap();
11160        eprintln!("ARRAY_EXCEPT Snowflake->DuckDB: {}", result[0]);
11161        assert!(
11162            result[0].contains("CASE WHEN"),
11163            "Expected CASE WHEN: {}",
11164            result[0]
11165        );
11166        assert!(
11167            result[0].contains("LIST_TRANSFORM"),
11168            "Expected LIST_TRANSFORM: {}",
11169            result[0]
11170        );
11171    }
11172
11173    #[test]
11174    fn test_array_contains_snowflake_to_snowflake() {
11175        let dialect = Dialect::get(DialectType::Snowflake);
11176        let result = dialect
11177            .transpile(
11178                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
11179                DialectType::Snowflake,
11180            )
11181            .unwrap();
11182        eprintln!("ARRAY_CONTAINS Snowflake->Snowflake: {}", result[0]);
11183        assert_eq!(result[0], "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])");
11184    }
11185
11186    #[test]
11187    fn test_array_contains_snowflake_to_duckdb() {
11188        let dialect = Dialect::get(DialectType::Snowflake);
11189        let result = dialect
11190            .transpile(
11191                "SELECT ARRAY_CONTAINS(x, [1, NULL, 3])",
11192                DialectType::DuckDB,
11193            )
11194            .unwrap();
11195        eprintln!("ARRAY_CONTAINS Snowflake->DuckDB: {}", result[0]);
11196        assert!(
11197            result[0].contains("CASE WHEN"),
11198            "Expected CASE WHEN: {}",
11199            result[0]
11200        );
11201        assert!(
11202            result[0].contains("NULLIF"),
11203            "Expected NULLIF: {}",
11204            result[0]
11205        );
11206        assert!(
11207            result[0].contains("ARRAY_CONTAINS"),
11208            "Expected ARRAY_CONTAINS: {}",
11209            result[0]
11210        );
11211    }
11212
11213    #[test]
11214    fn test_array_distinct_snowflake_to_duckdb() {
11215        let dialect = Dialect::get(DialectType::Snowflake);
11216        let result = dialect
11217            .transpile(
11218                "SELECT ARRAY_DISTINCT([1, 2, 2, 3, 1])",
11219                DialectType::DuckDB,
11220            )
11221            .unwrap();
11222        eprintln!("ARRAY_DISTINCT Snowflake->DuckDB: {}", result[0]);
11223        assert!(
11224            result[0].contains("CASE WHEN"),
11225            "Expected CASE WHEN: {}",
11226            result[0]
11227        );
11228        assert!(
11229            result[0].contains("LIST_DISTINCT"),
11230            "Expected LIST_DISTINCT: {}",
11231            result[0]
11232        );
11233        assert!(
11234            result[0].contains("LIST_APPEND"),
11235            "Expected LIST_APPEND: {}",
11236            result[0]
11237        );
11238        assert!(
11239            result[0].contains("LIST_FILTER"),
11240            "Expected LIST_FILTER: {}",
11241            result[0]
11242        );
11243    }
11244}