Skip to main content

polyglot_sql/dialects/
tsql.rs

1//! T-SQL (SQL Server) Dialect
2//!
3//! SQL Server-specific transformations based on sqlglot patterns.
4//! Key differences:
5//! - TOP instead of LIMIT
6//! - ISNULL instead of COALESCE (though COALESCE also works)
7//! - Square brackets for identifiers
8//! - + for string concatenation
9//! - CONVERT vs CAST
10//! - CROSS APPLY / OUTER APPLY for lateral joins
11//! - Different date functions (GETDATE, DATEADD, DATEDIFF, DATENAME)
12
13use super::{DialectImpl, DialectType};
14use crate::error::Result;
15use crate::expressions::{
16    Alias, BinaryOp, Cast, Column, Cte, DataType, Exists, Expression, From, Function, Identifier,
17    In, Join, JoinKind, LikeOp, Literal, Null, Over, Paren, QuantifiedExpr, QuantifiedOp, Select,
18    Star, StringAggFunc, Subquery, TrimFunc, TrimPosition, Tuple, UnaryFunc, Values, Where,
19    WindowFunction,
20};
21#[cfg(feature = "generate")]
22use crate::generator::GeneratorConfig;
23use crate::tokens::TokenizerConfig;
24use std::collections::HashMap;
25
26/// T-SQL (SQL Server) dialect
27pub struct TSQLDialect;
28
29enum PostgresToCharFormat {
30    Numeric,
31    Temporal {
32        strftime: String,
33        requires_english_culture: bool,
34    },
35    Unsupported,
36}
37
38impl DialectImpl for TSQLDialect {
39    fn dialect_type(&self) -> DialectType {
40        DialectType::TSQL
41    }
42
43    fn tokenizer_config(&self) -> TokenizerConfig {
44        let mut config = TokenizerConfig::default();
45        // SQL Server uses square brackets for identifiers
46        config.identifiers.insert('[', ']');
47        // SQL Server also supports double quotes (when QUOTED_IDENTIFIER is ON)
48        config.identifiers.insert('"', '"');
49        // SQL Server uses 0x-prefixed binary/varbinary hex literals.
50        config.hex_number_strings = true;
51        config.allow_empty_hex_string = true;
52        config
53    }
54
55    #[cfg(feature = "generate")]
56
57    fn generator_config(&self) -> GeneratorConfig {
58        use crate::generator::IdentifierQuoteStyle;
59        GeneratorConfig {
60            // Use square brackets by default for SQL Server
61            identifier_quote: '[',
62            identifier_quote_style: IdentifierQuoteStyle::BRACKET,
63            dialect: Some(DialectType::TSQL),
64            // T-SQL specific settings from Python sqlglot
65            // SQL Server uses TOP (or OFFSET/FETCH, handled by the generator) instead of LIMIT
66            limit_fetch_style: crate::generator::LimitFetchStyle::Top,
67            // NULLS FIRST/LAST not supported in SQL Server
68            null_ordering_supported: false,
69            // SQL Server does not support SQL:2003 aggregate FILTER clauses.
70            aggregate_filter_supported: false,
71            // SQL Server supports SELECT INTO
72            supports_select_into: true,
73            // ALTER TABLE doesn't require COLUMN keyword
74            alter_table_include_column_keyword: false,
75            // Computed columns don't need type declaration
76            computed_column_with_type: false,
77            // RECURSIVE keyword not required in CTEs
78            cte_recursive_keyword_required: false,
79            // Ensure boolean expressions
80            ensure_bools: true,
81            // CONCAT requires at least 2 args
82            supports_single_arg_concat: false,
83            // TABLESAMPLE REPEATABLE
84            tablesample_seed_keyword: "REPEATABLE",
85            // JSON path without brackets
86            json_path_bracketed_key_supported: false,
87            // No TO_NUMBER function
88            supports_to_number: false,
89            // SET operation modifiers not supported
90            set_op_modifiers: false,
91            // COPY params need equals sign
92            copy_params_eq_required: true,
93            // No ALL clause for EXCEPT/INTERSECT
94            except_intersect_support_all_clause: false,
95            // ALTER SET is wrapped
96            alter_set_wrapped: true,
97            // T-SQL supports TRY_CAST
98            try_supported: true,
99            // No NVL2 support
100            nvl2_supported: false,
101            // TSQL uses = instead of DEFAULT for parameter defaults
102            parameter_default_equals: true,
103            // No window EXCLUDE support
104            supports_window_exclude: false,
105            // No DISTINCT with multiple args
106            multi_arg_distinct: false,
107            // TSQL doesn't support FOR UPDATE/SHARE
108            locking_reads_supported: false,
109            ..Default::default()
110        }
111    }
112
113    #[cfg(feature = "transpile")]
114
115    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
116        // Transform column data types in DDL (transform_recursive skips them by design).
117        if let Expression::CreateTable(mut ct) = expr {
118            for col in &mut ct.columns {
119                if let Ok(Expression::DataType(new_dt)) =
120                    self.transform_data_type(col.data_type.clone())
121                {
122                    col.data_type = new_dt;
123                }
124            }
125            return Ok(Expression::CreateTable(ct));
126        }
127
128        match expr {
129            // ===== SELECT a = 1 → SELECT 1 AS a =====
130            // In T-SQL, `SELECT a = expr` is equivalent to `SELECT expr AS a`
131            // BUT: `SELECT @a = expr` is a variable assignment, not an alias!
132            // Python sqlglot handles this at parser level via _parse_projections()
133            Expression::Select(mut select) => {
134                select.expressions = select
135                    .expressions
136                    .into_iter()
137                    .map(|e| {
138                        match e {
139                            Expression::Eq(op) => {
140                                // Check if left side is an identifier (column name)
141                                // Don't transform if it's a variable (starts with @)
142                                match &op.left {
143                                    Expression::Column(col)
144                                        if col.table.is_none()
145                                            && !col.name.name.starts_with('@') =>
146                                    {
147                                        Expression::Alias(Box::new(Alias {
148                                            this: op.right,
149                                            alias: col.name.clone(),
150                                            column_aliases: Vec::new(),
151                                            alias_explicit_as: false,
152                                            alias_keyword: None,
153                                            pre_alias_comments: Vec::new(),
154                                            trailing_comments: Vec::new(),
155                                            inferred_type: None,
156                                        }))
157                                    }
158                                    Expression::Identifier(ident)
159                                        if !ident.name.starts_with('@') =>
160                                    {
161                                        Expression::Alias(Box::new(Alias {
162                                            this: op.right,
163                                            alias: ident.clone(),
164                                            column_aliases: Vec::new(),
165                                            alias_explicit_as: false,
166                                            alias_keyword: None,
167                                            pre_alias_comments: Vec::new(),
168                                            trailing_comments: Vec::new(),
169                                            inferred_type: None,
170                                        }))
171                                    }
172                                    _ => Expression::Eq(op),
173                                }
174                            }
175                            other => other,
176                        }
177                    })
178                    .collect();
179
180                Self::normalize_frame_incompatible_window_functions(&mut select);
181
182                let outer_qualifier = Self::single_select_source_qualifier(&select);
183
184                select.expressions = select
185                    .expressions
186                    .into_iter()
187                    .map(|expression| {
188                        Self::rewrite_tuple_in_subquery_predicates(
189                            expression,
190                            outer_qualifier.as_ref(),
191                            false,
192                        )
193                    })
194                    .collect();
195
196                for join in &mut select.joins {
197                    if let Some(on) = join.on.take() {
198                        join.on = Some(Self::rewrite_tuple_in_subquery_predicates(
199                            on,
200                            outer_qualifier.as_ref(),
201                            false,
202                        ));
203                    }
204                    if let Some(match_condition) = join.match_condition.take() {
205                        join.match_condition = Some(Self::rewrite_tuple_in_subquery_predicates(
206                            match_condition,
207                            outer_qualifier.as_ref(),
208                            false,
209                        ));
210                    }
211                }
212
213                if let Some(ref mut prewhere) = select.prewhere {
214                    *prewhere = Self::rewrite_tuple_in_subquery_predicates(
215                        std::mem::replace(prewhere, Expression::Null(Null)),
216                        outer_qualifier.as_ref(),
217                        false,
218                    );
219                }
220
221                if let Some(ref mut where_clause) = select.where_clause {
222                    where_clause.this = Self::rewrite_tuple_in_subquery_predicates(
223                        std::mem::replace(&mut where_clause.this, Expression::Null(Null)),
224                        outer_qualifier.as_ref(),
225                        false,
226                    );
227                }
228
229                if let Some(ref mut having) = select.having {
230                    having.this = Self::rewrite_tuple_in_subquery_predicates(
231                        std::mem::replace(&mut having.this, Expression::Null(Null)),
232                        outer_qualifier.as_ref(),
233                        false,
234                    );
235                }
236
237                if let Some(ref mut qualify) = select.qualify {
238                    qualify.this = Self::rewrite_tuple_in_subquery_predicates(
239                        std::mem::replace(&mut qualify.this, Expression::Null(Null)),
240                        outer_qualifier.as_ref(),
241                        false,
242                    );
243                }
244
245                // Transform CTEs in the WITH clause to add auto-aliases
246                if let Some(ref mut with) = select.with {
247                    with.ctes = with
248                        .ctes
249                        .drain(..)
250                        .map(|cte| self.transform_cte_inner(cte))
251                        .collect();
252                }
253
254                Self::rewrite_comma_lateral_sources_to_joins(&mut select);
255
256                Ok(Expression::Select(select))
257            }
258
259            // ===== Data Type Mappings =====
260            Expression::DataType(dt) => self.transform_data_type(dt),
261
262            // ===== Boolean IS TRUE/FALSE -> T-SQL 3VL truth table =====
263            // T-SQL doesn't have IS TRUE/IS FALSE syntax. Negated forms must
264            // explicitly preserve UNKNOWN/NULL rows instead of using NOT (x = n).
265            Expression::IsTrue(it) => Ok(Self::boolean_test_predicate(it.this, true, it.not)),
266            Expression::IsFalse(it) => Ok(Self::boolean_test_predicate(it.this, false, it.not)),
267
268            // Note: CASE WHEN boolean conditions are handled in ensure_bools preprocessing
269
270            // NOT IN -> NOT ... IN for TSQL (TSQL prefers NOT prefix)
271            Expression::In(mut in_expr) if in_expr.not => {
272                in_expr.not = false;
273                Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
274                    this: Expression::In(in_expr),
275                    inferred_type: None,
276                })))
277            }
278
279            // COALESCE with 2 args -> ISNULL in SQL Server (optimization)
280            // Note: COALESCE works in SQL Server, ISNULL is just more idiomatic
281            Expression::Coalesce(f) if f.expressions.len() == 2 => Ok(Expression::Function(
282                Box::new(Function::new("ISNULL".to_string(), f.expressions)),
283            )),
284
285            // NVL -> ISNULL in SQL Server
286            Expression::Nvl(f) => Ok(Expression::Function(Box::new(Function::new(
287                "ISNULL".to_string(),
288                vec![f.this, f.expression],
289            )))),
290
291            // GROUP_CONCAT -> STRING_AGG in SQL Server (SQL Server 2017+)
292            Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
293                this: f.this,
294                separator: f.separator,
295                order_by: f.order_by,
296                distinct: f.distinct,
297                filter: f.filter,
298                limit: None,
299                inferred_type: None,
300            }))),
301
302            // LISTAGG -> STRING_AGG in SQL Server (SQL Server 2017+)
303            Expression::ListAgg(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
304                this: f.this,
305                separator: f.separator,
306                order_by: f.order_by,
307                distinct: f.distinct,
308                filter: f.filter,
309                limit: None,
310                inferred_type: None,
311            }))),
312
313            // PostgreSQL accepts inline ORDER BY for every aggregate, even when
314            // input order cannot affect the result. T-SQL only accepts ordering
315            // for these functions in an analytic OVER clause.
316            Expression::Sum(f) => Ok(Expression::Sum(Self::without_inert_ordering(f))),
317            Expression::Avg(f) => Ok(Expression::Avg(Self::without_inert_ordering(f))),
318            Expression::Min(f) => Ok(Expression::Min(Self::without_inert_ordering(f))),
319            Expression::Max(f) => Ok(Expression::Max(Self::without_inert_ordering(f))),
320            Expression::AnyValue(f) => Ok(Expression::Max(Self::without_inert_ordering(f))),
321            Expression::ApproxCountDistinct(f) => Ok(Expression::ApproxCountDistinct(
322                Self::without_inert_ordering(f),
323            )),
324
325            // T-SQL/Fabric do not have boolean aggregates. Preserve PostgreSQL NULL
326            // semantics by returning NULL for unknown input predicates.
327            Expression::LogicalAnd(f) => Self::transform_logical_aggregate(f.this, f.filter, "MIN"),
328            Expression::LogicalOr(f) => Self::transform_logical_aggregate(f.this, f.filter, "MAX"),
329
330            // The bottom-up transform turns a windowed boolean aggregate into
331            // CAST(MIN|MAX(CASE ...) AS BIT) OVER (...). OVER belongs to the
332            // aggregate in T-SQL, so keep the result cast outside the window.
333            Expression::WindowFunction(f) => Ok(Self::reassociate_logical_aggregate_window(*f)),
334
335            // TryCast -> TRY_CAST (SQL Server supports TRY_CAST starting from 2012)
336            Expression::TryCast(c) => Ok(Expression::TryCast(c)),
337
338            // SafeCast -> TRY_CAST
339            Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
340
341            // ILIKE -> LOWER() LIKE LOWER() in SQL Server (no ILIKE support)
342            Expression::ILike(op) => {
343                // SQL Server is case-insensitive by default based on collation
344                // But for explicit case-insensitive matching, use LOWER
345                let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
346                let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
347                Ok(Expression::Like(Box::new(LikeOp {
348                    left: lower_left,
349                    right: lower_right,
350                    escape: op.escape,
351                    quantifier: op.quantifier,
352                    inferred_type: None,
353                })))
354            }
355
356            // || (Concat operator) -> + in SQL Server
357            // SQL Server uses + for string concatenation
358            Expression::Concat(op) => {
359                // Convert || to + operator (Add)
360                Ok(Expression::Add(op))
361            }
362
363            // RANDOM -> RAND in SQL Server
364            Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
365                seed: None,
366                lower: None,
367                upper: None,
368            }))),
369
370            // UNNEST -> Not directly supported, use CROSS APPLY with STRING_SPLIT or OPENJSON
371            Expression::Unnest(f) => {
372                // For basic cases, we'll use a placeholder
373                // Full support would require context-specific transformation
374                Ok(Expression::Function(Box::new(Function::new(
375                    "OPENJSON".to_string(),
376                    vec![f.this],
377                ))))
378            }
379
380            // EXPLODE -> Similar to UNNEST, use CROSS APPLY
381            Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
382                "OPENJSON".to_string(),
383                vec![f.this],
384            )))),
385
386            // PostgreSQL LATERAL join forms -> SQL Server APPLY.
387            Expression::Join(join) => Ok(Expression::Join(Box::new(
388                Self::transform_lateral_join_to_apply(*join)?,
389            ))),
390
391            // LENGTH -> LEN in SQL Server
392            Expression::Length(f) => Ok(Expression::Function(Box::new(Function::new(
393                "LEN".to_string(),
394                vec![f.this],
395            )))),
396
397            // STDDEV -> STDEV in SQL Server
398            Expression::Stddev(f) => Ok(Expression::Function(Box::new(Function::new(
399                "STDEV".to_string(),
400                vec![f.this],
401            )))),
402            Expression::StddevSamp(f) => Ok(Expression::Function(Box::new(Function::new(
403                "STDEV".to_string(),
404                vec![f.this],
405            )))),
406            Expression::StddevPop(f) => Ok(Expression::Function(Box::new(Function::new(
407                "STDEVP".to_string(),
408                vec![f.this],
409            )))),
410
411            // Boolean literals TRUE/FALSE -> 1/0 in SQL Server
412            Expression::Boolean(b) => {
413                let value = if b.value { 1 } else { 0 };
414                Ok(Expression::Literal(Box::new(
415                    crate::expressions::Literal::Number(value.to_string()),
416                )))
417            }
418
419            // LN -> LOG in SQL Server
420            Expression::Ln(f) => Ok(Expression::Function(Box::new(Function::new(
421                "LOG".to_string(),
422                vec![f.this],
423            )))),
424
425            // ===== Date/time =====
426            // CurrentDate -> CAST(GETDATE() AS DATE) in SQL Server
427            Expression::CurrentDate(_) => Ok(Self::cast_getdate_to(DataType::Date)),
428
429            // CurrentTime -> CAST(GETDATE() AS TIME) in SQL Server
430            Expression::CurrentTime(_) => Ok(Self::cast_getdate_to(DataType::Time {
431                precision: None,
432                timezone: false,
433            })),
434
435            // CurrentTimestamp -> GETDATE() in SQL Server
436            Expression::CurrentTimestamp(_) => Ok(Self::getdate()),
437
438            // Localtimestamp -> GETDATE() in SQL Server
439            Expression::Localtimestamp(_) => Ok(Self::getdate()),
440
441            // PostgreSQL MAKE_DATE(y, m, d) -> SQL Server DATEFROMPARTS(y, m, d)
442            Expression::MakeDate(f) => Ok(Self::function(
443                "DATEFROMPARTS",
444                vec![f.year, f.month, f.day],
445            )),
446
447            // DateDiff -> DATEDIFF
448            Expression::DateDiff(f) => {
449                // TSQL: DATEDIFF(unit, start, end)
450                let unit_str = match f.unit {
451                    Some(crate::expressions::IntervalUnit::Year) => "YEAR",
452                    Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
453                    Some(crate::expressions::IntervalUnit::Month) => "MONTH",
454                    Some(crate::expressions::IntervalUnit::Week) => "WEEK",
455                    Some(crate::expressions::IntervalUnit::Day) => "DAY",
456                    Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
457                    Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
458                    Some(crate::expressions::IntervalUnit::Second) => "SECOND",
459                    Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
460                    Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
461                    Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
462                    None => "DAY",
463                };
464                let unit = Expression::Identifier(crate::expressions::Identifier {
465                    name: unit_str.to_string(),
466                    quoted: false,
467                    trailing_comments: Vec::new(),
468                    span: None,
469                });
470                Ok(Expression::Function(Box::new(Function::new(
471                    "DATEDIFF".to_string(),
472                    vec![unit, f.expression, f.this], // Note: order is different in TSQL
473                ))))
474            }
475
476            // DateAdd -> DATEADD
477            Expression::DateAdd(f) => {
478                let unit_str = match f.unit {
479                    crate::expressions::IntervalUnit::Year => "YEAR",
480                    crate::expressions::IntervalUnit::Quarter => "QUARTER",
481                    crate::expressions::IntervalUnit::Month => "MONTH",
482                    crate::expressions::IntervalUnit::Week => "WEEK",
483                    crate::expressions::IntervalUnit::Day => "DAY",
484                    crate::expressions::IntervalUnit::Hour => "HOUR",
485                    crate::expressions::IntervalUnit::Minute => "MINUTE",
486                    crate::expressions::IntervalUnit::Second => "SECOND",
487                    crate::expressions::IntervalUnit::Millisecond => "MILLISECOND",
488                    crate::expressions::IntervalUnit::Microsecond => "MICROSECOND",
489                    crate::expressions::IntervalUnit::Nanosecond => "NANOSECOND",
490                };
491                let unit = Expression::Identifier(crate::expressions::Identifier {
492                    name: unit_str.to_string(),
493                    quoted: false,
494                    trailing_comments: Vec::new(),
495                    span: None,
496                });
497                Ok(Expression::Function(Box::new(Function::new(
498                    "DATEADD".to_string(),
499                    vec![unit, f.interval, f.this],
500                ))))
501            }
502
503            // ===== UUID =====
504            // Uuid -> NEWID in SQL Server
505            Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
506                "NEWID".to_string(),
507                vec![],
508            )))),
509
510            // ===== Conditional =====
511            // IfFunc -> IIF in SQL Server
512            Expression::IfFunc(f) => {
513                let false_val = f
514                    .false_value
515                    .unwrap_or(Expression::Null(crate::expressions::Null));
516                Ok(Expression::Function(Box::new(Function::new(
517                    "IIF".to_string(),
518                    vec![f.condition, f.true_value, false_val],
519                ))))
520            }
521
522            // ===== String functions =====
523            // StringAgg -> STRING_AGG in SQL Server 2017+ - keep as-is to preserve ORDER BY
524            Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
525
526            // LastDay -> EOMONTH (note: TSQL doesn't support date part argument)
527            Expression::LastDay(f) => Ok(Expression::Function(Box::new(Function::new(
528                "EOMONTH".to_string(),
529                vec![f.this.clone()],
530            )))),
531
532            // Ceil -> CEILING
533            Expression::Ceil(f) => Ok(Expression::Function(Box::new(Function::new(
534                "CEILING".to_string(),
535                vec![f.this],
536            )))),
537
538            // Repeat -> REPLICATE in SQL Server
539            Expression::Repeat(f) => Ok(Expression::Function(Box::new(Function::new(
540                "REPLICATE".to_string(),
541                vec![f.this, f.times],
542            )))),
543
544            // Chr -> CHAR in SQL Server
545            Expression::Chr(f) => Ok(Expression::Function(Box::new(Function::new(
546                "CHAR".to_string(),
547                vec![f.this],
548            )))),
549
550            // SQL standard OVERLAY(...) -> T-SQL STUFF(...)
551            Expression::Overlay(f) => Ok(Self::overlay_to_stuff(*f)),
552
553            // PostgreSQL starts_with(text, prefix) -> T-SQL prefix predicate.
554            // Scalar SELECT positions are wrapped by the shared T-SQL boolean materializer.
555            Expression::StartsWith(f) => Ok(Self::starts_with_predicate(f.this, f.expression)),
556
557            // PostgreSQL decode(text, 'hex') -> T-SQL hexadecimal binary conversion.
558            Expression::DecodeCase(mut f)
559                if f.expressions.len() == 2
560                    && Self::literal_string(&f.expressions[1])
561                        .is_some_and(|format| format.eq_ignore_ascii_case("hex")) =>
562            {
563                Ok(Self::tsql_convert(
564                    DataType::Custom {
565                        name: "VARBINARY(MAX)".to_string(),
566                    },
567                    f.expressions.remove(0),
568                    Some(2),
569                ))
570            }
571
572            // PostgreSQL TO_NUMBER with simple literal masks can be represented as TRY_CONVERT.
573            // More complex masks intentionally remain as TO_NUMBER so strict mode rejects them.
574            Expression::ToNumber(f) => Ok(Self::to_number_or_fallback(*f)),
575
576            // ===== Variance =====
577            // VarPop -> VARP
578            Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
579                "VARP".to_string(),
580                vec![f.this],
581            )))),
582
583            // Variance -> VAR
584            Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
585                "VAR".to_string(),
586                vec![f.this],
587            )))),
588            Expression::VarSamp(f) => Ok(Expression::Function(Box::new(Function::new(
589                "VAR".to_string(),
590                vec![f.this],
591            )))),
592
593            // ===== Hash functions =====
594            // MD5Digest -> HASHBYTES('MD5', ...)
595            Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
596                "HASHBYTES".to_string(),
597                vec![Expression::string("MD5"), *f.this],
598            )))),
599
600            // SHA -> HASHBYTES('SHA1', ...)
601            Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
602                "HASHBYTES".to_string(),
603                vec![Expression::string("SHA1"), f.this],
604            )))),
605
606            // SHA1Digest -> HASHBYTES('SHA1', ...)
607            Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
608                "HASHBYTES".to_string(),
609                vec![Expression::string("SHA1"), f.this],
610            )))),
611
612            // ===== Array functions =====
613            // ArrayToString -> STRING_AGG
614            Expression::ArrayToString(f) => Ok(Expression::Function(Box::new(Function::new(
615                "STRING_AGG".to_string(),
616                vec![f.this],
617            )))),
618
619            // ===== DDL Column Constraints =====
620            // AutoIncrementColumnConstraint -> IDENTITY in SQL Server
621            Expression::AutoIncrementColumnConstraint(_) => Ok(Expression::Function(Box::new(
622                Function::new("IDENTITY".to_string(), vec![]),
623            ))),
624
625            // ===== DDL three-part name stripping =====
626            // TSQL strips database (catalog) prefix from 3-part names for CREATE VIEW/DROP VIEW
627            // Python sqlglot: expression.this.set("catalog", None)
628            Expression::CreateView(mut view) => {
629                // Strip catalog from three-part name (a.b.c -> b.c)
630                view.name.catalog = None;
631                Ok(Expression::CreateView(view))
632            }
633
634            Expression::DropView(mut view) => {
635                // Strip catalog from three-part name (a.b.c -> b.c)
636                view.name.catalog = None;
637                Ok(Expression::DropView(view))
638            }
639
640            // ParseJson: handled by generator (emits just the string literal for TSQL)
641
642            // JSONExtract with variant_extract (Snowflake colon syntax) -> ISNULL(JSON_QUERY, JSON_VALUE)
643            Expression::JSONExtract(e) if e.variant_extract.is_some() => {
644                let path = match *e.expression {
645                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
646                        let Literal::String(s) = lit.as_ref() else {
647                            unreachable!()
648                        };
649                        let normalized = if s.starts_with('$') {
650                            s.clone()
651                        } else if s.starts_with('[') {
652                            format!("${}", s)
653                        } else {
654                            format!("$.{}", s)
655                        };
656                        Expression::Literal(Box::new(Literal::String(normalized)))
657                    }
658                    other => other,
659                };
660                let json_query = Expression::Function(Box::new(Function::new(
661                    "JSON_QUERY".to_string(),
662                    vec![(*e.this).clone(), path.clone()],
663                )));
664                let json_value = Expression::Function(Box::new(Function::new(
665                    "JSON_VALUE".to_string(),
666                    vec![*e.this, path],
667                )));
668                Ok(Expression::Function(Box::new(Function::new(
669                    "ISNULL".to_string(),
670                    vec![json_query, json_value],
671                ))))
672            }
673
674            // Generic function transformations
675            Expression::Function(f) => self.transform_function(*f),
676
677            // Generic aggregate function transformations
678            Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
679
680            // ===== CTEs need auto-aliased outputs =====
681            // In TSQL, bare expressions in CTEs need explicit aliases
682            Expression::Cte(cte) => self.transform_cte(*cte),
683
684            // ===== Subqueries need auto-aliased outputs =====
685            // In TSQL, bare expressions in aliased subqueries need explicit aliases
686            Expression::Subquery(subquery) => self.transform_subquery(*subquery),
687
688            // Convert JsonQuery struct to ISNULL(JSON_QUERY(..., path), JSON_VALUE(..., path))
689            Expression::JsonQuery(f) => {
690                let json_query = Expression::Function(Box::new(Function::new(
691                    "JSON_QUERY".to_string(),
692                    vec![f.this.clone(), f.path.clone()],
693                )));
694                let json_value = Expression::Function(Box::new(Function::new(
695                    "JSON_VALUE".to_string(),
696                    vec![f.this, f.path],
697                )));
698                Ok(Expression::Function(Box::new(Function::new(
699                    "ISNULL".to_string(),
700                    vec![json_query, json_value],
701                ))))
702            }
703            // Convert JsonValue struct to Function("JSON_VALUE", ...) for uniform handling
704            Expression::JsonValue(f) => Ok(Expression::Function(Box::new(Function::new(
705                "JSON_VALUE".to_string(),
706                vec![f.this, f.path],
707            )))),
708
709            // PostgreSQL pg_get_querydef can emit scalar array comparisons for
710            // literal arrays/tuples. T-SQL/Fabric require scalar predicates for
711            // these shapes because quantified comparisons only accept subqueries.
712            Expression::Any(q) => {
713                Ok(Self::lower_scalar_array_quantifier(&q, true).unwrap_or(Expression::Any(q)))
714            }
715            Expression::All(q) => {
716                Ok(Self::lower_scalar_array_quantifier(&q, false).unwrap_or(Expression::All(q)))
717            }
718
719            // Pass through everything else
720            _ => Ok(expr),
721        }
722    }
723}
724
725#[cfg(feature = "transpile")]
726impl TSQLDialect {
727    fn getdate() -> Expression {
728        Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])))
729    }
730
731    fn cast_getdate_to(to: DataType) -> Expression {
732        Expression::Cast(Box::new(Cast {
733            this: Self::getdate(),
734            to,
735            trailing_comments: Vec::new(),
736            double_colon_syntax: false,
737            format: None,
738            default: None,
739            inferred_type: None,
740        }))
741    }
742
743    fn cast(this: Expression, to: DataType) -> Expression {
744        Expression::Cast(Box::new(Cast {
745            this,
746            to,
747            trailing_comments: Vec::new(),
748            double_colon_syntax: false,
749            format: None,
750            default: None,
751            inferred_type: None,
752        }))
753    }
754
755    fn function(name: impl Into<String>, args: Vec<Expression>) -> Expression {
756        Expression::Function(Box::new(Function::new(name, args)))
757    }
758
759    fn make_time(mut args: Vec<Expression>) -> Expression {
760        let seconds = args.pop().expect("MAKE_TIME has three arguments");
761        let minute = args.pop().expect("MAKE_TIME has three arguments");
762        let hour = args.pop().expect("MAKE_TIME has three arguments");
763
764        if let Some((whole_seconds, microseconds)) = Self::literal_time_parts(&seconds) {
765            let (fractions, precision) = if microseconds == 0 {
766                (Expression::number(0), Expression::number(0))
767            } else {
768                (Expression::number(microseconds), Expression::number(6))
769            };
770
771            return Self::function(
772                "TIMEFROMPARTS",
773                vec![
774                    hour,
775                    minute,
776                    Expression::number(whole_seconds),
777                    fractions,
778                    precision,
779                ],
780            );
781        }
782
783        // TIMEFROMPARTS requires integral seconds and fractions. Round the
784        // PostgreSQL double-precision seconds argument to microseconds before
785        // splitting the integer value, without dropping fractional seconds.
786        let rounded_microseconds = Self::cast(
787            Self::function(
788                "ROUND",
789                vec![
790                    Expression::Mul(Box::new(BinaryOp::new(
791                        seconds,
792                        Expression::number(1_000_000),
793                    ))),
794                    Expression::number(0),
795                ],
796            ),
797            DataType::BigInt { length: None },
798        );
799
800        Self::function(
801            "TIMEFROMPARTS",
802            vec![
803                hour,
804                minute,
805                Expression::Div(Box::new(BinaryOp::new(
806                    rounded_microseconds.clone(),
807                    Expression::number(1_000_000),
808                ))),
809                Expression::Mod(Box::new(BinaryOp::new(
810                    rounded_microseconds,
811                    Expression::number(1_000_000),
812                ))),
813                Expression::number(6),
814            ],
815        )
816    }
817
818    fn literal_time_parts(expr: &Expression) -> Option<(i64, i64)> {
819        let value = match expr {
820            Expression::Literal(lit) => match lit.as_ref() {
821                Literal::Number(value) => value.parse::<f64>().ok()?,
822                _ => return None,
823            },
824            Expression::Paren(paren) => return Self::literal_time_parts(&paren.this),
825            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
826                if Self::is_numeric_data_type(&cast.to) =>
827            {
828                let parts = Self::literal_time_parts(&cast.this);
829                return match (&cast.to, parts) {
830                    // Zero is unchanged by every numeric cast. Other literal
831                    // casts are folded only when their floating-point
832                    // semantics cannot truncate the seconds value.
833                    (_, Some((0, 0))) => Some((0, 0)),
834                    (DataType::Float { .. } | DataType::Double { .. }, parts) => parts,
835                    _ => None,
836                };
837            }
838            _ => return None,
839        };
840
841        if !value.is_finite() || value < 0.0 || value > i64::MAX as f64 / 1_000_000.0 {
842            return None;
843        }
844
845        let total_microseconds = (value * 1_000_000.0).round() as i64;
846        Some((
847            total_microseconds / 1_000_000,
848            total_microseconds % 1_000_000,
849        ))
850    }
851
852    fn lower(this: Expression) -> Expression {
853        Expression::Lower(Box::new(UnaryFunc::new(this)))
854    }
855
856    fn tsql_convert(to: DataType, expression: Expression, style: Option<i64>) -> Expression {
857        let mut args = vec![Expression::DataType(to), expression];
858        if let Some(style) = style {
859            args.push(Expression::number(style));
860        }
861        Self::function("CONVERT", args)
862    }
863
864    fn tsql_hex_text(expression: Expression, varchar_type: DataType) -> Expression {
865        Self::lower(Self::tsql_convert(varchar_type, expression, Some(2)))
866    }
867
868    fn tsql_hex_from_varbinary(expression: Expression) -> Expression {
869        Self::tsql_hex_text(
870            Self::cast(
871                expression,
872                DataType::Custom {
873                    name: "VARBINARY(MAX)".to_string(),
874                },
875            ),
876            DataType::Text,
877        )
878    }
879
880    fn tsql_postgres_to_hex(expression: Expression) -> Expression {
881        let hex = Self::tsql_hex_from_varbinary(expression);
882        let without_leading_zeroes = Self::function("LTRIM", vec![hex, Expression::string("0")]);
883        let non_empty = Self::function(
884            "NULLIF",
885            vec![without_leading_zeroes, Expression::string("")],
886        );
887        Self::function("ISNULL", vec![non_empty, Expression::string("0")])
888    }
889
890    fn tsql_md5_hex(expression: Expression) -> Expression {
891        let hashbytes = Self::function("HASHBYTES", vec![Expression::string("MD5"), expression]);
892        Self::tsql_hex_text(
893            hashbytes,
894            DataType::VarChar {
895                length: Some(32),
896                parenthesized_length: false,
897            },
898        )
899    }
900
901    fn overlay_to_stuff(f: crate::expressions::OverlayFunc) -> Expression {
902        let length = f
903            .length
904            .unwrap_or_else(|| Self::function("LEN", vec![f.replacement.clone()]));
905        Self::function("STUFF", vec![f.this, f.from, length, f.replacement])
906    }
907
908    fn starts_with_predicate(this: Expression, prefix: Expression) -> Expression {
909        let prefix_len = Self::function("LEN", vec![prefix.clone()]);
910        let left_prefix = Self::function("LEFT", vec![this, prefix_len]);
911        Self::eq(left_prefix, prefix)
912    }
913
914    fn to_number_or_fallback(f: crate::expressions::ToNumber) -> Expression {
915        let crate::expressions::ToNumber {
916            this,
917            format,
918            nlsparam,
919            precision,
920            scale,
921            safe,
922            safe_name,
923        } = f;
924
925        if nlsparam.is_none()
926            && precision.is_none()
927            && scale.is_none()
928            && safe.is_none()
929            && safe_name.is_none()
930        {
931            if let Some(format) = format.as_deref() {
932                if let Some(scale) = Self::simple_to_number_scale(format) {
933                    return Self::function(
934                        "TRY_CONVERT",
935                        vec![
936                            Expression::DataType(DataType::Decimal {
937                                precision: Some(18),
938                                scale: Some(scale),
939                            }),
940                            *this,
941                        ],
942                    );
943                }
944            }
945        }
946
947        Expression::ToNumber(Box::new(crate::expressions::ToNumber {
948            this,
949            format,
950            nlsparam,
951            precision,
952            scale,
953            safe,
954            safe_name,
955        }))
956    }
957
958    fn simple_to_number_scale(format: &Expression) -> Option<u32> {
959        let format = Self::literal_string(format)?;
960        let format = format.strip_prefix("FM").unwrap_or(format);
961        let mut saw_digit = false;
962        let mut saw_decimal = false;
963        let mut scale = 0u32;
964
965        for ch in format.chars() {
966            match ch {
967                '9' | '0' => {
968                    saw_digit = true;
969                    if saw_decimal {
970                        scale = scale.checked_add(1)?;
971                    }
972                }
973                '.' if !saw_decimal => saw_decimal = true,
974                // Group separators and positional whitespace have format-model
975                // semantics that a raw TRY_CONVERT cannot reproduce.
976                ',' | ' ' => return None,
977                _ => return None,
978            }
979        }
980
981        saw_digit.then_some(scale)
982    }
983
984    fn binary(
985        left: Expression,
986        right: Expression,
987        op: fn(Box<BinaryOp>) -> Expression,
988    ) -> Expression {
989        op(Box::new(BinaryOp {
990            left,
991            right,
992            left_comments: Vec::new(),
993            operator_comments: Vec::new(),
994            trailing_comments: Vec::new(),
995            inferred_type: None,
996        }))
997    }
998
999    fn eq(left: Expression, right: Expression) -> Expression {
1000        Self::binary(left, right, Expression::Eq)
1001    }
1002
1003    fn or(left: Expression, right: Expression) -> Expression {
1004        Self::binary(left, right, Expression::Or)
1005    }
1006
1007    fn not(this: Expression) -> Expression {
1008        Expression::Not(Box::new(crate::expressions::UnaryOp {
1009            this,
1010            inferred_type: None,
1011        }))
1012    }
1013
1014    fn is_null(this: Expression) -> Expression {
1015        Expression::IsNull(Box::new(crate::expressions::IsNull {
1016            this,
1017            not: false,
1018            postfix_form: false,
1019        }))
1020    }
1021
1022    fn paren(this: Expression) -> Expression {
1023        Expression::Paren(Box::new(Paren {
1024            this,
1025            trailing_comments: Vec::new(),
1026        }))
1027    }
1028
1029    fn boolean_test_case_for_predicate(
1030        predicate: Expression,
1031        test_true: bool,
1032        negated: bool,
1033    ) -> Expression {
1034        let condition = match (test_true, negated) {
1035            (true, false) => predicate,
1036            (false, false) => Self::not(predicate),
1037            (true, true) => {
1038                return Expression::Case(Box::new(crate::expressions::Case {
1039                    operand: None,
1040                    whens: vec![(predicate, Expression::number(0))],
1041                    else_: Some(Expression::number(1)),
1042                    comments: Vec::new(),
1043                    inferred_type: None,
1044                }))
1045            }
1046            (false, true) => {
1047                return Expression::Case(Box::new(crate::expressions::Case {
1048                    operand: None,
1049                    whens: vec![(Self::not(predicate), Expression::number(0))],
1050                    else_: Some(Expression::number(1)),
1051                    comments: Vec::new(),
1052                    inferred_type: None,
1053                }))
1054            }
1055        };
1056
1057        Expression::Case(Box::new(crate::expressions::Case {
1058            operand: None,
1059            whens: vec![(condition, Expression::number(1))],
1060            else_: Some(Expression::number(0)),
1061            comments: Vec::new(),
1062            inferred_type: None,
1063        }))
1064    }
1065
1066    fn boolean_test_predicate(operand: Expression, test_true: bool, negated: bool) -> Expression {
1067        if Self::is_boolean_predicate_operand(&operand) {
1068            return match (test_true, negated) {
1069                (true, false) => operand,
1070                (false, false) => Self::not(operand),
1071                _ => Self::eq(
1072                    Self::boolean_test_case_for_predicate(operand, test_true, negated),
1073                    Expression::number(1),
1074                ),
1075            };
1076        }
1077
1078        match (test_true, negated) {
1079            (true, false) => Self::eq(operand, Expression::number(1)),
1080            (false, false) => Self::eq(operand, Expression::number(0)),
1081            (true, true) => Self::or(
1082                Self::eq(operand.clone(), Expression::number(0)),
1083                Self::is_null(operand),
1084            ),
1085            (false, true) => Self::or(
1086                Self::eq(operand.clone(), Expression::number(1)),
1087                Self::is_null(operand),
1088            ),
1089        }
1090    }
1091
1092    fn is_boolean_predicate_operand(expr: &Expression) -> bool {
1093        match expr {
1094            Expression::Paren(paren) => Self::is_boolean_predicate_operand(&paren.this),
1095            Expression::Eq(_)
1096            | Expression::Neq(_)
1097            | Expression::Lt(_)
1098            | Expression::Lte(_)
1099            | Expression::Gt(_)
1100            | Expression::Gte(_)
1101            | Expression::Is(_)
1102            | Expression::IsNull(_)
1103            | Expression::IsTrue(_)
1104            | Expression::IsFalse(_)
1105            | Expression::Like(_)
1106            | Expression::ILike(_)
1107            | Expression::SimilarTo(_)
1108            | Expression::Glob(_)
1109            | Expression::RegexpLike(_)
1110            | Expression::In(_)
1111            | Expression::Between(_)
1112            | Expression::Exists(_)
1113            | Expression::And(_)
1114            | Expression::Or(_)
1115            | Expression::Not(_)
1116            | Expression::Any(_)
1117            | Expression::All(_)
1118            | Expression::EqualNull(_) => true,
1119            _ => false,
1120        }
1121    }
1122
1123    fn scalar_array_comparison_values(expr: &Expression) -> Option<Vec<Expression>> {
1124        let (mut values, element_type) = Self::scalar_array_comparison_values_inner(expr)?;
1125        if let Some(to) = element_type {
1126            values = values
1127                .into_iter()
1128                .map(|value| Self::cast_scalar_array_comparison_value(value, to.clone()))
1129                .collect();
1130        }
1131        Some(values)
1132    }
1133
1134    fn lower_scalar_array_quantifier(
1135        quantified: &QuantifiedExpr,
1136        is_any: bool,
1137    ) -> Option<Expression> {
1138        let op = quantified.op.as_ref()?;
1139        let expressions = Self::scalar_array_comparison_values(&quantified.subquery)?;
1140
1141        if expressions.is_empty() {
1142            return Some(Self::eq(
1143                Expression::number(1),
1144                Expression::number(if is_any { 0 } else { 1 }),
1145            ));
1146        }
1147
1148        if is_any && matches!(op, QuantifiedOp::Eq) {
1149            return Some(Self::in_list(quantified.this.clone(), expressions, false));
1150        }
1151
1152        if !is_any && matches!(op, QuantifiedOp::Neq) {
1153            return Some(Self::in_list(quantified.this.clone(), expressions, true));
1154        }
1155
1156        let mut comparisons = expressions
1157            .into_iter()
1158            .map(|expression| Self::quantified_comparison(quantified.this.clone(), expression, op));
1159        let first = comparisons.next()?;
1160        let combined = comparisons.fold(first, |left, right| {
1161            if is_any {
1162                Expression::Or(Box::new(BinaryOp::new(left, right)))
1163            } else {
1164                Expression::And(Box::new(BinaryOp::new(left, right)))
1165            }
1166        });
1167
1168        Some(Self::paren(combined))
1169    }
1170
1171    fn in_list(this: Expression, expressions: Vec<Expression>, not: bool) -> Expression {
1172        Expression::In(Box::new(In {
1173            this,
1174            expressions,
1175            query: None,
1176            not,
1177            global: false,
1178            unnest: None,
1179            is_field: false,
1180        }))
1181    }
1182
1183    fn quantified_comparison(left: Expression, right: Expression, op: &QuantifiedOp) -> Expression {
1184        let binary = Box::new(BinaryOp::new(left, right));
1185        match op {
1186            QuantifiedOp::Eq => Expression::Eq(binary),
1187            QuantifiedOp::Neq => Expression::Neq(binary),
1188            QuantifiedOp::Lt => Expression::Lt(binary),
1189            QuantifiedOp::Lte => Expression::Lte(binary),
1190            QuantifiedOp::Gt => Expression::Gt(binary),
1191            QuantifiedOp::Gte => Expression::Gte(binary),
1192        }
1193    }
1194
1195    fn scalar_array_comparison_values_inner(
1196        expr: &Expression,
1197    ) -> Option<(Vec<Expression>, Option<DataType>)> {
1198        match expr {
1199            Expression::ArrayFunc(a) => Some((a.expressions.clone(), None)),
1200            Expression::Array(a) => Some((a.expressions.clone(), None)),
1201            Expression::Tuple(t) => Some((t.expressions.clone(), None)),
1202            Expression::Paren(p) => Self::scalar_array_comparison_values_inner(&p.this),
1203            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
1204                let DataType::Array { element_type, .. } = &c.to else {
1205                    return None;
1206                };
1207                let (values, _) = Self::scalar_array_comparison_values_inner(&c.this)?;
1208                Some((values, Some((**element_type).clone())))
1209            }
1210            _ => None,
1211        }
1212    }
1213
1214    fn cast_scalar_array_comparison_value(value: Expression, to: DataType) -> Expression {
1215        if matches!(&value, Expression::Cast(c) if c.to == to) {
1216            return value;
1217        }
1218
1219        Expression::Cast(Box::new(Cast {
1220            this: value,
1221            to,
1222            trailing_comments: Vec::new(),
1223            double_colon_syntax: false,
1224            format: None,
1225            default: None,
1226            inferred_type: None,
1227        }))
1228    }
1229
1230    fn normalize_frame_incompatible_window_functions(select: &mut Select) {
1231        let window_map: HashMap<String, Over> = select
1232            .windows
1233            .as_ref()
1234            .map(|windows| {
1235                windows
1236                    .iter()
1237                    .map(|window| (window.name.name.to_lowercase(), window.spec.clone()))
1238                    .collect()
1239            })
1240            .unwrap_or_default();
1241
1242        for expr in &mut select.expressions {
1243            Self::normalize_frame_incompatible_window_expr(expr, &window_map);
1244        }
1245
1246        if let Some(order_by) = &mut select.order_by {
1247            for ordered in &mut order_by.expressions {
1248                Self::normalize_frame_incompatible_window_expr(&mut ordered.this, &window_map);
1249            }
1250        }
1251
1252        if let Some(qualify) = &mut select.qualify {
1253            Self::normalize_frame_incompatible_window_expr(&mut qualify.this, &window_map);
1254        }
1255    }
1256
1257    fn normalize_frame_incompatible_window_expr(
1258        expr: &mut Expression,
1259        window_map: &HashMap<String, Over>,
1260    ) {
1261        match expr {
1262            Expression::WindowFunction(wf) => {
1263                Self::normalize_frame_incompatible_window_expr(&mut wf.this, window_map);
1264
1265                if !Self::is_tsql_frame_incompatible_window_function(&wf.this) {
1266                    return;
1267                }
1268
1269                wf.over.frame = None;
1270
1271                let Some(window_name) = wf.over.window_name.clone() else {
1272                    return;
1273                };
1274                let Some(named_spec) =
1275                    Self::resolve_named_window_spec(&window_name.name, window_map, &mut Vec::new())
1276                else {
1277                    return;
1278                };
1279
1280                if named_spec.frame.is_none() {
1281                    return;
1282                }
1283
1284                if wf.over.partition_by.is_empty() {
1285                    wf.over.partition_by = named_spec.partition_by;
1286                }
1287                if wf.over.order_by.is_empty() {
1288                    wf.over.order_by = named_spec.order_by;
1289                }
1290                wf.over.window_name = None;
1291                wf.over.frame = None;
1292            }
1293            Expression::Alias(alias) => {
1294                Self::normalize_frame_incompatible_window_expr(&mut alias.this, window_map);
1295            }
1296            Expression::Paren(paren) => {
1297                Self::normalize_frame_incompatible_window_expr(&mut paren.this, window_map);
1298            }
1299            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
1300                Self::normalize_frame_incompatible_window_expr(&mut cast.this, window_map);
1301            }
1302            Expression::Function(function) => {
1303                for arg in &mut function.args {
1304                    Self::normalize_frame_incompatible_window_expr(arg, window_map);
1305                }
1306            }
1307            Expression::Case(case) => {
1308                if let Some(operand) = &mut case.operand {
1309                    Self::normalize_frame_incompatible_window_expr(operand, window_map);
1310                }
1311                for (condition, result) in &mut case.whens {
1312                    Self::normalize_frame_incompatible_window_expr(condition, window_map);
1313                    Self::normalize_frame_incompatible_window_expr(result, window_map);
1314                }
1315                if let Some(else_expr) = &mut case.else_ {
1316                    Self::normalize_frame_incompatible_window_expr(else_expr, window_map);
1317                }
1318            }
1319            Expression::And(op)
1320            | Expression::Or(op)
1321            | Expression::Add(op)
1322            | Expression::Sub(op)
1323            | Expression::Mul(op)
1324            | Expression::Div(op)
1325            | Expression::Mod(op)
1326            | Expression::Eq(op)
1327            | Expression::Neq(op)
1328            | Expression::Lt(op)
1329            | Expression::Lte(op)
1330            | Expression::Gt(op)
1331            | Expression::Gte(op)
1332            | Expression::Match(op)
1333            | Expression::BitwiseAnd(op)
1334            | Expression::BitwiseOr(op)
1335            | Expression::BitwiseXor(op)
1336            | Expression::Concat(op)
1337            | Expression::Adjacent(op)
1338            | Expression::TsMatch(op)
1339            | Expression::PropertyEQ(op)
1340            | Expression::ArrayContainsAll(op)
1341            | Expression::ArrayContainedBy(op)
1342            | Expression::ArrayOverlaps(op)
1343            | Expression::JSONBContainsAllTopKeys(op)
1344            | Expression::JSONBContainsAnyTopKeys(op)
1345            | Expression::JSONBDeleteAtPath(op)
1346            | Expression::ExtendsLeft(op)
1347            | Expression::ExtendsRight(op)
1348            | Expression::Is(op)
1349            | Expression::MemberOf(op) => {
1350                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
1351                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
1352            }
1353            Expression::Like(op) | Expression::ILike(op) => {
1354                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
1355                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
1356                if let Some(escape) = &mut op.escape {
1357                    Self::normalize_frame_incompatible_window_expr(escape, window_map);
1358                }
1359            }
1360            Expression::Not(op) | Expression::Neg(op) | Expression::BitwiseNot(op) => {
1361                Self::normalize_frame_incompatible_window_expr(&mut op.this, window_map);
1362            }
1363            Expression::In(in_expr) => {
1364                Self::normalize_frame_incompatible_window_expr(&mut in_expr.this, window_map);
1365                for value in &mut in_expr.expressions {
1366                    Self::normalize_frame_incompatible_window_expr(value, window_map);
1367                }
1368            }
1369            Expression::Between(between) => {
1370                Self::normalize_frame_incompatible_window_expr(&mut between.this, window_map);
1371                Self::normalize_frame_incompatible_window_expr(&mut between.low, window_map);
1372                Self::normalize_frame_incompatible_window_expr(&mut between.high, window_map);
1373            }
1374            Expression::IsNull(is_null) => {
1375                Self::normalize_frame_incompatible_window_expr(&mut is_null.this, window_map);
1376            }
1377            Expression::IsTrue(is_true) | Expression::IsFalse(is_true) => {
1378                Self::normalize_frame_incompatible_window_expr(&mut is_true.this, window_map);
1379            }
1380            _ => {}
1381        }
1382    }
1383
1384    fn is_tsql_frame_incompatible_window_function(expr: &Expression) -> bool {
1385        matches!(
1386            expr,
1387            Expression::RowNumber(_)
1388                | Expression::Rank(_)
1389                | Expression::DenseRank(_)
1390                | Expression::NTile(_)
1391                | Expression::Ntile(_)
1392                | Expression::Lead(_)
1393                | Expression::Lag(_)
1394                | Expression::PercentRank(_)
1395                | Expression::CumeDist(_)
1396        )
1397    }
1398
1399    fn resolve_named_window_spec(
1400        name: &str,
1401        window_map: &HashMap<String, Over>,
1402        seen: &mut Vec<String>,
1403    ) -> Option<Over> {
1404        let key = name.to_lowercase();
1405        if seen.iter().any(|seen_name| seen_name == &key) {
1406            return None;
1407        }
1408
1409        let named_spec = window_map.get(&key)?.clone();
1410        seen.push(key);
1411
1412        let mut resolved = if let Some(base_window) = &named_spec.window_name {
1413            Self::resolve_named_window_spec(&base_window.name, window_map, seen)
1414                .unwrap_or_else(Self::empty_over)
1415        } else {
1416            Self::empty_over()
1417        };
1418
1419        if !named_spec.partition_by.is_empty() {
1420            resolved.partition_by = named_spec.partition_by;
1421        }
1422        if !named_spec.order_by.is_empty() {
1423            resolved.order_by = named_spec.order_by;
1424        }
1425        if named_spec.frame.is_some() {
1426            resolved.frame = named_spec.frame;
1427        }
1428
1429        Some(resolved)
1430    }
1431
1432    fn empty_over() -> Over {
1433        Over {
1434            window_name: None,
1435            partition_by: Vec::new(),
1436            order_by: Vec::new(),
1437            frame: None,
1438            alias: None,
1439        }
1440    }
1441
1442    const LATERAL_WRAPPER_SOURCE_ALIAS: &'static str = "_polyglot_lateral_source";
1443    const LATERAL_WRAPPER_OUTPUT_ALIAS: &'static str = "_polyglot_lateral";
1444
1445    fn transform_lateral_join_to_apply(mut join: Join) -> Result<Join> {
1446        let Some(apply_kind) = Self::lateral_apply_kind(&join) else {
1447            return Ok(join);
1448        };
1449
1450        let original_alias = Self::table_expression_alias(&join.this);
1451        let on = join.on.take();
1452        let rhs = Self::remove_lateral_marker(join.this);
1453        join.this = if on
1454            .as_ref()
1455            .is_some_and(|expr| !Self::is_true_condition(expr))
1456        {
1457            Self::wrap_lateral_apply_rhs(rhs, on.expect("checked as Some"), original_alias)?
1458        } else {
1459            rhs
1460        };
1461        join.using.clear();
1462        join.kind = apply_kind;
1463        join.use_inner_keyword = false;
1464        join.use_outer_keyword = false;
1465        join.deferred_condition = false;
1466        join.join_hint = None;
1467        join.match_condition = None;
1468        join.directed = false;
1469        Ok(join)
1470    }
1471
1472    fn rewrite_comma_lateral_sources_to_joins(select: &mut Select) {
1473        let Some(from) = select.from.as_mut() else {
1474            return;
1475        };
1476        let has_comma_lateral = from
1477            .expressions
1478            .iter()
1479            .skip(1)
1480            .any(Self::is_lateral_table_expression);
1481        let has_apply_join = select
1482            .joins
1483            .iter()
1484            .any(|join| matches!(join.kind, JoinKind::CrossApply | JoinKind::OuterApply));
1485
1486        if from.expressions.len() < 2 || (!has_comma_lateral && !has_apply_join) {
1487            return;
1488        }
1489
1490        let mut expressions = std::mem::take(&mut from.expressions).into_iter();
1491        let Some(first) = expressions.next() else {
1492            return;
1493        };
1494        from.expressions = vec![first];
1495
1496        let mut joins = expressions
1497            .map(|source| {
1498                if Self::is_lateral_table_expression(&source) {
1499                    Self::new_join(Self::remove_lateral_marker(source), JoinKind::CrossApply)
1500                } else {
1501                    Self::new_join(source, JoinKind::Cross)
1502                }
1503            })
1504            .collect::<Vec<_>>();
1505        joins.append(&mut select.joins);
1506        select.joins = joins;
1507    }
1508
1509    fn new_join(this: Expression, kind: JoinKind) -> Join {
1510        Join {
1511            this,
1512            on: None,
1513            using: Vec::new(),
1514            kind,
1515            use_inner_keyword: false,
1516            use_outer_keyword: false,
1517            deferred_condition: false,
1518            join_hint: None,
1519            match_condition: None,
1520            pivots: Vec::new(),
1521            comments: Vec::new(),
1522            nesting_group: 0,
1523            directed: false,
1524        }
1525    }
1526
1527    fn lateral_apply_kind(join: &Join) -> Option<JoinKind> {
1528        if !join.using.is_empty() {
1529            return None;
1530        }
1531
1532        match join.kind {
1533            JoinKind::Lateral => Some(JoinKind::CrossApply),
1534            JoinKind::LeftLateral => Some(JoinKind::OuterApply),
1535            JoinKind::Cross | JoinKind::Inner | JoinKind::Implicit
1536                if Self::is_lateral_table_expression(&join.this) =>
1537            {
1538                Some(JoinKind::CrossApply)
1539            }
1540            JoinKind::Left if Self::is_lateral_table_expression(&join.this) => {
1541                Some(JoinKind::OuterApply)
1542            }
1543            _ => None,
1544        }
1545    }
1546
1547    fn is_true_condition(expr: &Expression) -> bool {
1548        match expr {
1549            Expression::Boolean(boolean) => boolean.value,
1550            Expression::Literal(lit) => {
1551                matches!(lit.as_ref(), Literal::Number(value) if value.trim() == "1")
1552            }
1553            Expression::Eq(op) => {
1554                Self::is_true_condition(&op.left) && Self::is_true_condition(&op.right)
1555            }
1556            Expression::Paren(paren) => Self::is_true_condition(&paren.this),
1557            _ => false,
1558        }
1559    }
1560
1561    fn table_expression_alias(expr: &Expression) -> Option<(Identifier, Vec<Identifier>)> {
1562        match expr {
1563            Expression::Subquery(subquery) => subquery
1564                .alias
1565                .clone()
1566                .map(|alias| (alias, subquery.column_aliases.clone())),
1567            Expression::Alias(alias) if !alias.alias.is_empty() => {
1568                Some((alias.alias.clone(), alias.column_aliases.clone()))
1569            }
1570            Expression::Lateral(lateral) => lateral.alias.as_ref().map(|alias| {
1571                (
1572                    if lateral.alias_quoted {
1573                        Identifier::quoted(alias)
1574                    } else {
1575                        Identifier::new(alias)
1576                    },
1577                    lateral
1578                        .column_aliases
1579                        .iter()
1580                        .map(|column| Identifier::new(column.clone()))
1581                        .collect(),
1582                )
1583            }),
1584            _ => None,
1585        }
1586    }
1587
1588    fn wrap_lateral_apply_rhs(
1589        rhs: Expression,
1590        predicate: Expression,
1591        original_alias: Option<(Identifier, Vec<Identifier>)>,
1592    ) -> Result<Expression> {
1593        let (outer_alias, column_aliases) = original_alias.unwrap_or_else(|| {
1594            (
1595                Identifier::new(Self::LATERAL_WRAPPER_OUTPUT_ALIAS),
1596                Vec::new(),
1597            )
1598        });
1599        let inner_alias = Identifier::new(Self::LATERAL_WRAPPER_SOURCE_ALIAS);
1600        let source =
1601            Self::with_table_expression_alias(rhs, inner_alias.clone(), column_aliases.clone());
1602        let predicate = Self::rewrite_column_qualifier(predicate, &outer_alias, &inner_alias)?;
1603
1604        let mut select = Select::new();
1605        select.expressions = vec![Expression::Star(Star {
1606            table: None,
1607            except: None,
1608            replace: None,
1609            rename: None,
1610            trailing_comments: Vec::new(),
1611            span: None,
1612        })];
1613        select.from = Some(crate::expressions::From {
1614            expressions: vec![source],
1615        });
1616        select.where_clause = Some(Where { this: predicate });
1617
1618        Ok(Expression::Subquery(Box::new(Subquery {
1619            this: Expression::Select(Box::new(select)),
1620            alias: Some(outer_alias),
1621            column_aliases,
1622            alias_explicit_as: true,
1623            alias_keyword: None,
1624            order_by: None,
1625            limit: None,
1626            offset: None,
1627            distribute_by: None,
1628            sort_by: None,
1629            cluster_by: None,
1630            lateral: false,
1631            modifiers_inside: false,
1632            trailing_comments: Vec::new(),
1633            inferred_type: None,
1634        })))
1635    }
1636
1637    fn with_table_expression_alias(
1638        expr: Expression,
1639        alias: Identifier,
1640        column_aliases: Vec<Identifier>,
1641    ) -> Expression {
1642        match expr {
1643            Expression::Subquery(mut subquery) => {
1644                subquery.alias = Some(alias);
1645                subquery.column_aliases = column_aliases;
1646                subquery.alias_explicit_as = true;
1647                subquery.alias_keyword = None;
1648                Expression::Subquery(subquery)
1649            }
1650            Expression::Alias(mut aliased) => {
1651                aliased.alias = alias;
1652                aliased.column_aliases = column_aliases;
1653                aliased.alias_explicit_as = true;
1654                aliased.alias_keyword = None;
1655                Expression::Alias(aliased)
1656            }
1657            Expression::Table(mut table) => {
1658                table.alias = Some(alias);
1659                table.alias_explicit_as = true;
1660                table.column_aliases = column_aliases;
1661                Expression::Table(table)
1662            }
1663            other => Expression::Alias(Box::new(Alias {
1664                this: other,
1665                alias,
1666                column_aliases,
1667                alias_explicit_as: true,
1668                alias_keyword: None,
1669                pre_alias_comments: Vec::new(),
1670                trailing_comments: Vec::new(),
1671                inferred_type: None,
1672            })),
1673        }
1674    }
1675
1676    fn rewrite_column_qualifier(
1677        expr: Expression,
1678        from: &Identifier,
1679        to: &Identifier,
1680    ) -> Result<Expression> {
1681        super::transform_recursive(expr, &|expr| {
1682            Ok(match expr {
1683                Expression::Column(mut column)
1684                    if column
1685                        .table
1686                        .as_ref()
1687                        .is_some_and(|table| Self::same_identifier(table, from)) =>
1688                {
1689                    column.table = Some(to.clone());
1690                    Expression::Column(column)
1691                }
1692                other => other,
1693            })
1694        })
1695    }
1696
1697    fn same_identifier(left: &Identifier, right: &Identifier) -> bool {
1698        if left.quoted || right.quoted {
1699            left.quoted == right.quoted && left.name == right.name
1700        } else {
1701            left.name.eq_ignore_ascii_case(&right.name)
1702        }
1703    }
1704
1705    fn is_lateral_table_expression(expr: &Expression) -> bool {
1706        match expr {
1707            Expression::Subquery(subquery) => subquery.lateral,
1708            Expression::Lateral(_) => true,
1709            Expression::Alias(alias) => Self::is_lateral_table_expression(&alias.this),
1710            _ => false,
1711        }
1712    }
1713
1714    fn remove_lateral_marker(expr: Expression) -> Expression {
1715        match expr {
1716            Expression::Subquery(mut subquery) => {
1717                subquery.lateral = false;
1718                Expression::Subquery(subquery)
1719            }
1720            Expression::Lateral(lateral) => Self::lateral_to_table_expression(*lateral),
1721            Expression::Alias(mut alias) => {
1722                alias.this = Self::remove_lateral_marker(alias.this);
1723                Expression::Alias(alias)
1724            }
1725            other => other,
1726        }
1727    }
1728
1729    fn lateral_to_table_expression(lateral: crate::expressions::Lateral) -> Expression {
1730        let expr = *lateral.this;
1731        let Some(alias) = lateral.alias else {
1732            return expr;
1733        };
1734
1735        Expression::Alias(Box::new(Alias {
1736            this: expr,
1737            alias: if lateral.alias_quoted {
1738                Identifier::quoted(alias)
1739            } else {
1740                Identifier::new(alias)
1741            },
1742            column_aliases: lateral
1743                .column_aliases
1744                .into_iter()
1745                .map(Identifier::new)
1746                .collect(),
1747            alias_explicit_as: true,
1748            alias_keyword: None,
1749            pre_alias_comments: Vec::new(),
1750            trailing_comments: Vec::new(),
1751            inferred_type: None,
1752        }))
1753    }
1754
1755    fn rewrite_tuple_in_subquery_predicates(
1756        expr: Expression,
1757        outer_qualifier: Option<&Identifier>,
1758        under_not: bool,
1759    ) -> Expression {
1760        match expr {
1761            Expression::In(in_expr) if !under_not => {
1762                let in_expr = *in_expr;
1763                Self::tuple_in_subquery_to_exists(&in_expr, outer_qualifier, in_expr.not)
1764                    .unwrap_or_else(|| Expression::In(Box::new(in_expr)))
1765            }
1766            Expression::Eq(op) if !under_not => {
1767                let op = *op;
1768                Self::tuple_subquery_eq_to_exists(&op, outer_qualifier)
1769                    .unwrap_or_else(|| Expression::Eq(Box::new(op)))
1770            }
1771            Expression::And(mut op) => {
1772                op.left =
1773                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1774                op.right = Self::rewrite_tuple_in_subquery_predicates(
1775                    op.right,
1776                    outer_qualifier,
1777                    under_not,
1778                );
1779                Expression::And(op)
1780            }
1781            Expression::Or(mut op) => {
1782                op.left =
1783                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1784                op.right = Self::rewrite_tuple_in_subquery_predicates(
1785                    op.right,
1786                    outer_qualifier,
1787                    under_not,
1788                );
1789                Expression::Or(op)
1790            }
1791            Expression::Paren(mut paren) => {
1792                paren.this = Self::rewrite_tuple_in_subquery_predicates(
1793                    paren.this,
1794                    outer_qualifier,
1795                    under_not,
1796                );
1797                Expression::Paren(paren)
1798            }
1799            Expression::Not(mut not) => {
1800                if let Some(rewritten) = Self::direct_tuple_subquery_predicate_to_exists(
1801                    &not.this,
1802                    outer_qualifier,
1803                    true,
1804                ) {
1805                    rewritten
1806                } else {
1807                    not.this =
1808                        Self::rewrite_tuple_in_subquery_predicates(not.this, outer_qualifier, true);
1809                    Expression::Not(not)
1810                }
1811            }
1812            Expression::Alias(mut alias) => {
1813                alias.this = Self::rewrite_tuple_in_subquery_predicates(
1814                    alias.this,
1815                    outer_qualifier,
1816                    under_not,
1817                );
1818                Expression::Alias(alias)
1819            }
1820            Expression::Cast(mut cast) => {
1821                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1822                    cast.this,
1823                    outer_qualifier,
1824                    under_not,
1825                );
1826                if let Some(format) = cast.format.take() {
1827                    cast.format = Some(Box::new(Self::rewrite_tuple_in_subquery_predicates(
1828                        *format,
1829                        outer_qualifier,
1830                        under_not,
1831                    )));
1832                }
1833                if let Some(default) = cast.default.take() {
1834                    cast.default = Some(Box::new(Self::rewrite_tuple_in_subquery_predicates(
1835                        *default,
1836                        outer_qualifier,
1837                        under_not,
1838                    )));
1839                }
1840                Expression::Cast(cast)
1841            }
1842            Expression::TryCast(mut cast) => {
1843                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1844                    cast.this,
1845                    outer_qualifier,
1846                    under_not,
1847                );
1848                Expression::TryCast(cast)
1849            }
1850            Expression::SafeCast(mut cast) => {
1851                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1852                    cast.this,
1853                    outer_qualifier,
1854                    under_not,
1855                );
1856                Expression::SafeCast(cast)
1857            }
1858            Expression::Case(mut case) => {
1859                if let Some(operand) = case.operand.take() {
1860                    case.operand = Some(Self::rewrite_tuple_in_subquery_predicates(
1861                        operand,
1862                        outer_qualifier,
1863                        under_not,
1864                    ));
1865                }
1866                case.whens = case
1867                    .whens
1868                    .into_iter()
1869                    .map(|(condition, result)| {
1870                        (
1871                            Self::rewrite_tuple_in_subquery_predicates(
1872                                condition,
1873                                outer_qualifier,
1874                                false,
1875                            ),
1876                            Self::rewrite_tuple_in_subquery_predicates(
1877                                result,
1878                                outer_qualifier,
1879                                under_not,
1880                            ),
1881                        )
1882                    })
1883                    .collect();
1884                if let Some(else_) = case.else_.take() {
1885                    case.else_ = Some(Self::rewrite_tuple_in_subquery_predicates(
1886                        else_,
1887                        outer_qualifier,
1888                        under_not,
1889                    ));
1890                }
1891                Expression::Case(case)
1892            }
1893            Expression::IfFunc(mut if_func) => {
1894                if_func.condition = Self::rewrite_tuple_in_subquery_predicates(
1895                    if_func.condition,
1896                    outer_qualifier,
1897                    false,
1898                );
1899                if_func.true_value = Self::rewrite_tuple_in_subquery_predicates(
1900                    if_func.true_value,
1901                    outer_qualifier,
1902                    under_not,
1903                );
1904                if let Some(false_value) = if_func.false_value.take() {
1905                    if_func.false_value = Some(Self::rewrite_tuple_in_subquery_predicates(
1906                        false_value,
1907                        outer_qualifier,
1908                        under_not,
1909                    ));
1910                }
1911                Expression::IfFunc(if_func)
1912            }
1913            other => other,
1914        }
1915    }
1916
1917    fn direct_tuple_subquery_predicate_to_exists(
1918        expr: &Expression,
1919        outer_qualifier: Option<&Identifier>,
1920        negated: bool,
1921    ) -> Option<Expression> {
1922        match expr {
1923            Expression::In(in_expr) => {
1924                Self::tuple_in_subquery_to_exists(in_expr, outer_qualifier, negated ^ in_expr.not)
1925            }
1926            Expression::Paren(paren) => Self::direct_tuple_subquery_predicate_to_exists(
1927                &paren.this,
1928                outer_qualifier,
1929                negated,
1930            ),
1931            _ => None,
1932        }
1933    }
1934
1935    fn tuple_in_subquery_to_exists(
1936        in_expr: &In,
1937        outer_qualifier: Option<&Identifier>,
1938        negated: bool,
1939    ) -> Option<Expression> {
1940        if in_expr.unnest.is_some() {
1941            return None;
1942        }
1943
1944        let left_expressions = Self::tuple_expressions(&in_expr.this)?;
1945        let mut select = Self::select_from_in_rhs(in_expr)?;
1946
1947        if left_expressions.len() != select.expressions.len() || left_expressions.is_empty() {
1948            return None;
1949        }
1950
1951        let inner_qualifier = Self::single_select_source_qualifier(&select);
1952        let mut predicates = Vec::with_capacity(left_expressions.len() + 1);
1953        for (projection, left) in select
1954            .expressions
1955            .iter()
1956            .cloned()
1957            .zip(left_expressions.iter().cloned())
1958        {
1959            let inner = Self::tuple_in_projection_expr(projection, inner_qualifier.as_ref())?;
1960            let outer = Self::qualify_tuple_operand(left, outer_qualifier);
1961            predicates.push(if negated {
1962                Self::tuple_component_may_match(inner, outer)
1963            } else {
1964                Expression::Eq(Box::new(BinaryOp::new(inner, outer)))
1965            });
1966        }
1967
1968        if let Some(where_clause) = select.where_clause.take() {
1969            predicates.push(where_clause.this);
1970        }
1971
1972        select.expressions = vec![Expression::number(1)];
1973        select.where_clause = Some(Where {
1974            this: Self::and_all(predicates)?,
1975        });
1976
1977        Some(Expression::Exists(Box::new(Exists {
1978            this: Expression::Select(Box::new(select)),
1979            not: negated,
1980        })))
1981    }
1982
1983    fn tuple_subquery_eq_to_exists(
1984        op: &BinaryOp,
1985        outer_qualifier: Option<&Identifier>,
1986    ) -> Option<Expression> {
1987        if let Some((tuple_expr, query_expr)) = Self::tuple_and_query_operands(&op.left, &op.right)
1988        {
1989            return Self::tuple_subquery_eq_to_exists_inner(
1990                tuple_expr,
1991                query_expr,
1992                outer_qualifier,
1993            );
1994        }
1995
1996        if let Some((tuple_expr, query_expr)) = Self::tuple_and_query_operands(&op.right, &op.left)
1997        {
1998            return Self::tuple_subquery_eq_to_exists_inner(
1999                tuple_expr,
2000                query_expr,
2001                outer_qualifier,
2002            );
2003        }
2004
2005        None
2006    }
2007
2008    fn tuple_subquery_eq_to_exists_inner(
2009        tuple_expr: &Expression,
2010        query_expr: &Expression,
2011        outer_qualifier: Option<&Identifier>,
2012    ) -> Option<Expression> {
2013        let tuple_expressions = Self::tuple_expressions(tuple_expr)?;
2014        let mut select = Self::select_from_query_expression(query_expr)?;
2015
2016        if tuple_expressions.len() != select.expressions.len() || tuple_expressions.is_empty() {
2017            return None;
2018        }
2019
2020        let inner_qualifier = Self::single_select_source_qualifier(&select);
2021        let mut predicates = Vec::with_capacity(tuple_expressions.len() + 1);
2022        for (projection, tuple_operand) in select
2023            .expressions
2024            .iter()
2025            .cloned()
2026            .zip(tuple_expressions.iter().cloned())
2027        {
2028            let inner = Self::tuple_in_projection_expr(projection, inner_qualifier.as_ref())?;
2029            let outer = Self::qualify_tuple_operand(tuple_operand, outer_qualifier);
2030            predicates.push(Expression::Eq(Box::new(BinaryOp::new(inner, outer))));
2031        }
2032
2033        if let Some(where_clause) = select.where_clause.take() {
2034            predicates.push(where_clause.this);
2035        }
2036
2037        select.expressions = vec![Expression::number(1)];
2038        select.where_clause = Some(Where {
2039            this: Self::and_all(predicates)?,
2040        });
2041
2042        Some(Expression::Exists(Box::new(Exists {
2043            this: Expression::Select(Box::new(select)),
2044            not: false,
2045        })))
2046    }
2047
2048    fn tuple_and_query_operands<'a>(
2049        tuple_candidate: &'a Expression,
2050        query_candidate: &'a Expression,
2051    ) -> Option<(&'a Expression, &'a Expression)> {
2052        if Self::tuple_expressions(tuple_candidate).is_some()
2053            && Self::select_from_query_expression(query_candidate).is_some()
2054        {
2055            Some((tuple_candidate, query_candidate))
2056        } else {
2057            None
2058        }
2059    }
2060
2061    fn select_from_query_expression(expr: &Expression) -> Option<Select> {
2062        match expr {
2063            Expression::Select(select) => Some((**select).clone()),
2064            Expression::Subquery(subquery) => Self::select_from_query_expression(&subquery.this),
2065            Expression::Paren(paren) => Self::select_from_query_expression(&paren.this),
2066            _ => None,
2067        }
2068    }
2069
2070    fn select_from_in_rhs(in_expr: &In) -> Option<Select> {
2071        if let Some(values) = Self::values_from_in_rhs(in_expr) {
2072            return Self::select_from_values(&values);
2073        }
2074
2075        if let Some(query) = &in_expr.query {
2076            return if in_expr.expressions.is_empty() {
2077                Self::select_from_query_expression(query)
2078            } else {
2079                None
2080            };
2081        }
2082
2083        if in_expr.expressions.len() == 1 {
2084            Self::select_from_query_expression(&in_expr.expressions[0])
2085        } else {
2086            None
2087        }
2088    }
2089
2090    fn values_from_in_rhs(in_expr: &In) -> Option<Values> {
2091        if let Some(query) = &in_expr.query {
2092            return if in_expr.expressions.is_empty() {
2093                Self::values_from_expression(query)
2094            } else {
2095                None
2096            };
2097        }
2098
2099        if in_expr.expressions.len() == 1 {
2100            if let Some(values) = Self::values_from_expression(&in_expr.expressions[0]) {
2101                return Some(values);
2102            }
2103        }
2104
2105        // IN (VALUES ...) currently parses as VALUES(first_row), followed by tuple rows.
2106        let Expression::Function(first_row) = in_expr.expressions.first()? else {
2107            return None;
2108        };
2109        if !first_row.name.eq_ignore_ascii_case("VALUES") {
2110            return None;
2111        }
2112
2113        let mut rows = Vec::with_capacity(in_expr.expressions.len());
2114        rows.push(Tuple {
2115            expressions: first_row.args.clone(),
2116        });
2117        for row in &in_expr.expressions[1..] {
2118            rows.push(Self::tuple_from_values_row(row)?);
2119        }
2120
2121        Some(Values {
2122            expressions: rows,
2123            alias: None,
2124            column_aliases: Vec::new(),
2125        })
2126    }
2127
2128    fn values_from_expression(expr: &Expression) -> Option<Values> {
2129        match expr {
2130            Expression::Values(values) => Some((**values).clone()),
2131            Expression::Paren(paren) => Self::values_from_expression(&paren.this),
2132            Expression::Subquery(subquery) => Self::values_from_expression(&subquery.this),
2133            _ => None,
2134        }
2135    }
2136
2137    fn tuple_from_values_row(expr: &Expression) -> Option<Tuple> {
2138        match expr {
2139            Expression::Tuple(tuple) => Some((**tuple).clone()),
2140            Expression::Paren(paren) => match &paren.this {
2141                Expression::Tuple(tuple) => Some((**tuple).clone()),
2142                other => Some(Tuple {
2143                    expressions: vec![other.clone()],
2144                }),
2145            },
2146            _ => None,
2147        }
2148    }
2149
2150    fn select_from_values(values: &Values) -> Option<Select> {
2151        let column_count = values.expressions.first()?.expressions.len();
2152        if column_count == 0
2153            || values
2154                .expressions
2155                .iter()
2156                .any(|row| row.expressions.len() != column_count)
2157        {
2158            return None;
2159        }
2160
2161        let source_alias = Identifier::new("_polyglot_values");
2162        let column_aliases = (1..=column_count)
2163            .map(|index| Identifier::new(format!("_polyglot_value_{index}")))
2164            .collect::<Vec<_>>();
2165        let projections = column_aliases
2166            .iter()
2167            .cloned()
2168            .map(|column| Self::column_from_identifier(column, Some(source_alias.clone())))
2169            .collect();
2170
2171        let mut source_values = values.clone();
2172        source_values.alias = None;
2173        source_values.column_aliases.clear();
2174
2175        let source = Expression::Subquery(Box::new(Subquery {
2176            this: Expression::Values(Box::new(source_values)),
2177            alias: Some(source_alias),
2178            column_aliases,
2179            alias_explicit_as: true,
2180            alias_keyword: None,
2181            order_by: None,
2182            limit: None,
2183            offset: None,
2184            distribute_by: None,
2185            sort_by: None,
2186            cluster_by: None,
2187            lateral: false,
2188            modifiers_inside: false,
2189            trailing_comments: Vec::new(),
2190            inferred_type: None,
2191        }));
2192
2193        let mut select = Select::new();
2194        select.expressions = projections;
2195        select.from = Some(From {
2196            expressions: vec![source],
2197        });
2198        Some(select)
2199    }
2200
2201    fn tuple_expressions(expr: &Expression) -> Option<&[Expression]> {
2202        match expr {
2203            Expression::Tuple(tuple) => Some(&tuple.expressions),
2204            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
2205                Some(&function.args)
2206            }
2207            Expression::Paren(paren) => Self::tuple_expressions(&paren.this),
2208            _ => None,
2209        }
2210    }
2211
2212    fn tuple_in_projection_expr(
2213        expr: Expression,
2214        qualifier: Option<&Identifier>,
2215    ) -> Option<Expression> {
2216        match expr {
2217            Expression::Alias(alias) => Self::tuple_in_projection_expr(alias.this, qualifier),
2218            Expression::Column(mut column) => {
2219                if column.table.is_none() {
2220                    column.table = qualifier.cloned();
2221                }
2222                Some(Expression::Column(column))
2223            }
2224            Expression::Identifier(identifier) => {
2225                Some(Self::column_from_identifier(identifier, qualifier.cloned()))
2226            }
2227            Expression::Dot(_) => Some(expr),
2228            other => Some(Self::qualify_tuple_expression(other, qualifier)),
2229        }
2230    }
2231
2232    fn qualify_tuple_operand(expr: Expression, qualifier: Option<&Identifier>) -> Expression {
2233        Self::qualify_tuple_expression(expr, qualifier)
2234    }
2235
2236    fn qualify_tuple_expression(expr: Expression, qualifier: Option<&Identifier>) -> Expression {
2237        match expr {
2238            Expression::Column(mut column) => {
2239                if column.table.is_none() {
2240                    column.table = qualifier.cloned();
2241                }
2242                Expression::Column(column)
2243            }
2244            Expression::Identifier(identifier) => {
2245                Self::column_from_identifier(identifier, qualifier.cloned())
2246            }
2247            Expression::Alias(mut alias) => {
2248                alias.this = Self::qualify_tuple_expression(alias.this, qualifier);
2249                Expression::Alias(alias)
2250            }
2251            Expression::Paren(mut paren) => {
2252                paren.this = Self::qualify_tuple_expression(paren.this, qualifier);
2253                Expression::Paren(paren)
2254            }
2255            Expression::Cast(mut cast) => {
2256                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2257                if let Some(format) = cast.format.take() {
2258                    cast.format =
2259                        Some(Box::new(Self::qualify_tuple_expression(*format, qualifier)));
2260                }
2261                if let Some(default) = cast.default.take() {
2262                    cast.default = Some(Box::new(Self::qualify_tuple_expression(
2263                        *default, qualifier,
2264                    )));
2265                }
2266                Expression::Cast(cast)
2267            }
2268            Expression::TryCast(mut cast) => {
2269                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2270                Expression::TryCast(cast)
2271            }
2272            Expression::SafeCast(mut cast) => {
2273                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2274                Expression::SafeCast(cast)
2275            }
2276            Expression::Function(mut function) => {
2277                function.args = function
2278                    .args
2279                    .into_iter()
2280                    .map(|arg| Self::qualify_tuple_expression(arg, qualifier))
2281                    .collect();
2282                Expression::Function(function)
2283            }
2284            Expression::Add(mut op) => {
2285                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2286                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2287                Expression::Add(op)
2288            }
2289            Expression::Sub(mut op) => {
2290                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2291                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2292                Expression::Sub(op)
2293            }
2294            Expression::Mul(mut op) => {
2295                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2296                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2297                Expression::Mul(op)
2298            }
2299            Expression::Div(mut op) => {
2300                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2301                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2302                Expression::Div(op)
2303            }
2304            Expression::Mod(mut op) => {
2305                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2306                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2307                Expression::Mod(op)
2308            }
2309            other => other,
2310        }
2311    }
2312
2313    fn tuple_component_may_match(inner: Expression, outer: Expression) -> Expression {
2314        Self::paren(
2315            Self::or_all(vec![
2316                Expression::Eq(Box::new(BinaryOp::new(inner.clone(), outer.clone()))),
2317                Self::is_null(inner),
2318                Self::is_null(outer),
2319            ])
2320            .expect("tuple component match condition is non-empty"),
2321        )
2322    }
2323
2324    fn column_from_identifier(identifier: Identifier, table: Option<Identifier>) -> Expression {
2325        Expression::Column(Box::new(Column {
2326            name: identifier,
2327            table,
2328            join_mark: false,
2329            trailing_comments: Vec::new(),
2330            span: None,
2331            inferred_type: None,
2332        }))
2333    }
2334
2335    fn single_select_source_qualifier(select: &Select) -> Option<Identifier> {
2336        if !select.joins.is_empty() {
2337            return None;
2338        }
2339
2340        let from = select.from.as_ref()?;
2341        if from.expressions.len() != 1 {
2342            return None;
2343        }
2344
2345        Self::source_qualifier(&from.expressions[0])
2346    }
2347
2348    fn source_qualifier(source: &Expression) -> Option<Identifier> {
2349        match source {
2350            Expression::Table(table) => table.alias.clone().or_else(|| Some(table.name.clone())),
2351            Expression::Subquery(subquery) => subquery.alias.clone(),
2352            _ => None,
2353        }
2354    }
2355
2356    fn and_all(mut predicates: Vec<Expression>) -> Option<Expression> {
2357        if predicates.is_empty() {
2358            return None;
2359        }
2360
2361        let first = predicates.remove(0);
2362        Some(predicates.into_iter().fold(first, |left, right| {
2363            Expression::And(Box::new(BinaryOp::new(left, right)))
2364        }))
2365    }
2366
2367    fn or_all(mut predicates: Vec<Expression>) -> Option<Expression> {
2368        if predicates.is_empty() {
2369            return None;
2370        }
2371
2372        let first = predicates.remove(0);
2373        Some(predicates.into_iter().fold(first, |left, right| {
2374            Expression::Or(Box::new(BinaryOp::new(left, right)))
2375        }))
2376    }
2377
2378    /// Transform data types according to T-SQL TYPE_MAPPING
2379    pub(super) fn transform_data_type(
2380        &self,
2381        dt: crate::expressions::DataType,
2382    ) -> Result<Expression> {
2383        use crate::expressions::DataType;
2384        let transformed = match dt {
2385            // BOOLEAN -> BIT
2386            DataType::Boolean => DataType::Custom {
2387                name: "BIT".to_string(),
2388            },
2389            // INT stays as INT in TSQL (native type)
2390            DataType::Int { .. } => dt,
2391            // DOUBLE stays as Double internally (TSQL generator outputs FLOAT for it)
2392            // DECIMAL -> NUMERIC
2393            DataType::Decimal { precision, scale } => DataType::Custom {
2394                name: if let (Some(p), Some(s)) = (&precision, &scale) {
2395                    format!("NUMERIC({}, {})", p, s)
2396                } else if let Some(p) = &precision {
2397                    format!("NUMERIC({})", p)
2398                } else {
2399                    "NUMERIC".to_string()
2400                },
2401            },
2402            // TEXT -> VARCHAR(MAX)
2403            DataType::Text => DataType::Custom {
2404                name: "VARCHAR(MAX)".to_string(),
2405            },
2406            // TIMESTAMP -> DATETIME2
2407            DataType::Timestamp { .. } => DataType::Custom {
2408                name: "DATETIME2".to_string(),
2409            },
2410            // UUID -> UNIQUEIDENTIFIER
2411            DataType::Uuid => DataType::Custom {
2412                name: "UNIQUEIDENTIFIER".to_string(),
2413            },
2414            // Normalise custom type names that have PostgreSQL aliases
2415            DataType::Custom { ref name } => {
2416                let upper = name.trim().to_uppercase();
2417                let (base_name, precision, _scale) = Self::parse_type_precision_and_scale(&upper);
2418                match base_name.as_str() {
2419                    // PostgreSQL DOUBLE PRECISION is SQL Server FLOAT.
2420                    "DOUBLE PRECISION" => DataType::Custom {
2421                        name: "FLOAT".to_string(),
2422                    },
2423                    // BPCHAR is PostgreSQL's blank-padded CHAR alias — map to CHAR
2424                    "BPCHAR" => {
2425                        if let Some(len) = precision {
2426                            DataType::Char { length: Some(len) }
2427                        } else {
2428                            DataType::Char { length: None }
2429                        }
2430                    }
2431                    _ => dt,
2432                }
2433            }
2434            // Keep all other types as-is
2435            other => other,
2436        };
2437        Ok(Expression::DataType(transformed))
2438    }
2439
2440    /// Parse a type name that may embed precision/scale: `"TYPENAME(n, m)"` → `("TYPENAME", Some(n), Some(m))`.
2441    pub(super) fn parse_type_precision_and_scale(name: &str) -> (String, Option<u32>, Option<u32>) {
2442        if let Some(paren_pos) = name.find('(') {
2443            let base = name[..paren_pos].to_string();
2444            let rest = &name[paren_pos + 1..];
2445            if let Some(close_pos) = rest.find(')') {
2446                let args = &rest[..close_pos];
2447                let parts: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
2448                let precision = parts.first().and_then(|s| s.parse::<u32>().ok());
2449                let scale = parts.get(1).and_then(|s| s.parse::<u32>().ok());
2450                return (base, precision, scale);
2451            }
2452            (base, None, None)
2453        } else {
2454            (name.to_string(), None, None)
2455        }
2456    }
2457
2458    fn transform_logical_aggregate(
2459        condition: Expression,
2460        filter: Option<Expression>,
2461        aggregate_name: &str,
2462    ) -> Result<Expression> {
2463        let false_condition = Expression::Not(Box::new(crate::expressions::UnaryOp {
2464            this: condition.clone(),
2465            inferred_type: None,
2466        }));
2467        let true_condition = Self::apply_aggregate_filter(condition, filter.clone());
2468        let false_condition = Self::apply_aggregate_filter(false_condition, filter);
2469
2470        let case_expr = Expression::Case(Box::new(crate::expressions::Case {
2471            operand: None,
2472            whens: vec![
2473                (true_condition, Expression::number(1)),
2474                (false_condition, Expression::number(0)),
2475            ],
2476            else_: Some(Expression::null()),
2477            comments: Vec::new(),
2478            inferred_type: None,
2479        }));
2480
2481        let case_expr = crate::transforms::ensure_bools(case_expr)?;
2482        let aggregate = Expression::Function(Box::new(Function::new(
2483            aggregate_name.to_string(),
2484            vec![case_expr],
2485        )));
2486
2487        Ok(Expression::Cast(Box::new(Cast {
2488            this: aggregate,
2489            to: DataType::Custom {
2490                name: "BIT".to_string(),
2491            },
2492            trailing_comments: Vec::new(),
2493            double_colon_syntax: false,
2494            format: None,
2495            default: None,
2496            inferred_type: None,
2497        })))
2498    }
2499
2500    fn reassociate_logical_aggregate_window(mut window: WindowFunction) -> Expression {
2501        let Expression::Cast(mut cast) = window.this else {
2502            return Expression::WindowFunction(Box::new(window));
2503        };
2504
2505        if !Self::is_transformed_logical_aggregate_cast(&cast) {
2506            window.this = Expression::Cast(cast);
2507            return Expression::WindowFunction(Box::new(window));
2508        }
2509
2510        window.this = cast.this;
2511        cast.this = Expression::WindowFunction(Box::new(window));
2512        Expression::Cast(cast)
2513    }
2514
2515    fn is_transformed_logical_aggregate_cast(cast: &Cast) -> bool {
2516        if !matches!(
2517            &cast.to,
2518            DataType::Custom { name } if name.eq_ignore_ascii_case("BIT")
2519        ) {
2520            return false;
2521        }
2522
2523        let Expression::Function(function) = &cast.this else {
2524            return false;
2525        };
2526        if !matches!(function.name.to_ascii_uppercase().as_str(), "MIN" | "MAX")
2527            || function.args.len() != 1
2528        {
2529            return false;
2530        }
2531
2532        matches!(
2533            function.args.first(),
2534            Some(Expression::Case(case))
2535                if case.operand.is_none()
2536                    && case.whens.len() == 2
2537                    && matches!(case.else_.as_ref(), Some(Expression::Null(_)))
2538        )
2539    }
2540
2541    fn apply_aggregate_filter(condition: Expression, filter: Option<Expression>) -> Expression {
2542        match filter {
2543            Some(filter) => Expression::And(Box::new(crate::expressions::BinaryOp::new(
2544                filter, condition,
2545            ))),
2546            None => condition,
2547        }
2548    }
2549
2550    fn transform_function(&self, f: Function) -> Result<Expression> {
2551        let name_upper = f.name.to_uppercase();
2552        match name_upper.as_str() {
2553            // COALESCE -> ISNULL for 2 args (optimization)
2554            "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2555                "ISNULL".to_string(),
2556                f.args,
2557            )))),
2558
2559            // NVL -> ISNULL (SQL Server function)
2560            "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2561                "ISNULL".to_string(),
2562                f.args,
2563            )))),
2564
2565            // GROUP_CONCAT -> STRING_AGG in SQL Server 2017+
2566            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
2567                Function::new("STRING_AGG".to_string(), f.args),
2568            ))),
2569
2570            // STRING_AGG is native to SQL Server 2017+
2571            "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
2572
2573            // LISTAGG -> STRING_AGG
2574            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
2575                "STRING_AGG".to_string(),
2576                f.args,
2577            )))),
2578
2579            // SUBSTR -> SUBSTRING
2580            "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
2581                "SUBSTRING".to_string(),
2582                f.args,
2583            )))),
2584
2585            // LENGTH -> LEN in SQL Server
2586            "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2587                "LEN".to_string(),
2588                f.args,
2589            )))),
2590
2591            // PostgreSQL btrim(text[, characters]) -> T-SQL TRIM([characters FROM] text)
2592            "BTRIM" if f.args.len() == 1 || f.args.len() == 2 => {
2593                let mut args = f.args;
2594                let this = args.remove(0);
2595                let characters = if args.is_empty() {
2596                    None
2597                } else {
2598                    Some(args.remove(0))
2599                };
2600                Ok(Expression::Trim(Box::new(TrimFunc {
2601                    this,
2602                    sql_standard_syntax: characters.is_some(),
2603                    characters,
2604                    position: TrimPosition::Both,
2605                    position_explicit: false,
2606                    inferred_type: f.inferred_type,
2607                })))
2608            }
2609
2610            // PostgreSQL md5(text) returns lowercase hex text; HASHBYTES returns varbinary.
2611            "MD5" if f.args.len() == 1 => {
2612                let mut args = f.args;
2613                Ok(Self::tsql_md5_hex(args.remove(0)))
2614            }
2615
2616            "SHA256" if f.args.len() == 1 => {
2617                let mut args = f.args;
2618                Ok(Self::function(
2619                    "HASHBYTES",
2620                    vec![Expression::string("SHA2_256"), args.remove(0)],
2621                ))
2622            }
2623
2624            "SHA512" if f.args.len() == 1 => {
2625                let mut args = f.args;
2626                Ok(Self::function(
2627                    "HASHBYTES",
2628                    vec![Expression::string("SHA2_512"), args.remove(0)],
2629                ))
2630            }
2631
2632            // PostgreSQL octet_length(text/bytea) -> DATALENGTH(...)
2633            "OCTET_LENGTH" if f.args.len() == 1 => Ok(Self::function("DATALENGTH", f.args)),
2634
2635            // PostgreSQL bit_length(text/bytea) -> DATALENGTH(...) * 8
2636            "BIT_LENGTH" if f.args.len() == 1 => {
2637                let mut args = f.args;
2638                Ok(Expression::Mul(Box::new(BinaryOp::new(
2639                    Self::function("DATALENGTH", vec![args.remove(0)]),
2640                    Expression::number(8),
2641                ))))
2642            }
2643
2644            // PostgreSQL to_hex(int) -> unpadded lowercase hex text. SQL Server's
2645            // binary conversion preserves the integer width, so remove only leading
2646            // zeroes and retain a single zero for the all-zero value.
2647            "TO_HEX" if f.args.len() == 1 => {
2648                let mut args = f.args;
2649                Ok(Self::tsql_postgres_to_hex(args.remove(0)))
2650            }
2651
2652            // PostgreSQL encode(bytea, 'hex') -> lowercase hex text.
2653            "ENCODE" if f.args.len() == 2 => {
2654                let mut args = f.args;
2655                let this = args.remove(0);
2656                let encoding = args.remove(0);
2657                if Self::literal_string(&encoding)
2658                    .is_some_and(|encoding| encoding.eq_ignore_ascii_case("hex"))
2659                {
2660                    Ok(Self::tsql_hex_from_varbinary(this))
2661                } else {
2662                    Ok(Expression::Function(Box::new(Function::new(
2663                        "ENCODE".to_string(),
2664                        vec![this, encoding],
2665                    ))))
2666                }
2667            }
2668
2669            // Preserve support for manually constructed/generic DECODE ASTs in addition
2670            // to the parser's typed DecodeCase representation.
2671            "DECODE"
2672                if f.args.len() == 2
2673                    && Self::literal_string(&f.args[1])
2674                        .is_some_and(|format| format.eq_ignore_ascii_case("hex")) =>
2675            {
2676                let mut args = f.args;
2677                Ok(Self::tsql_convert(
2678                    DataType::Custom {
2679                        name: "VARBINARY(MAX)".to_string(),
2680                    },
2681                    args.remove(0),
2682                    Some(2),
2683                ))
2684            }
2685
2686            // PostgreSQL repeat(text, count) -> SQL Server REPLICATE(text, count)
2687            "REPEAT" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2688                "REPLICATE".to_string(),
2689                f.args,
2690            )))),
2691
2692            // PostgreSQL chr(code) -> SQL Server CHAR(code)
2693            "CHR" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2694                "CHAR".to_string(),
2695                f.args,
2696            )))),
2697
2698            // RANDOM -> RAND
2699            "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
2700                seed: None,
2701                lower: None,
2702                upper: None,
2703            }))),
2704
2705            // NOW -> GETDATE or CURRENT_TIMESTAMP (both work)
2706            "NOW" => Ok(Self::getdate()),
2707
2708            // CURRENT_TIMESTAMP -> GETDATE (SQL Server prefers GETDATE)
2709            "CURRENT_TIMESTAMP" => Ok(Self::getdate()),
2710
2711            // CURRENT_DATE -> CAST(GETDATE() AS DATE)
2712            "CURRENT_DATE" => Ok(Self::cast_getdate_to(DataType::Date)),
2713
2714            // CURRENT_TIME -> CAST(GETDATE() AS TIME)
2715            "CURRENT_TIME" => Ok(Self::cast_getdate_to(DataType::Time {
2716                precision: None,
2717                timezone: false,
2718            })),
2719
2720            // LOCALTIMESTAMP -> GETDATE()
2721            "LOCALTIMESTAMP" => Ok(Self::getdate()),
2722
2723            // PostgreSQL clock_timestamp() -> high-precision current system timestamp.
2724            "CLOCK_TIMESTAMP" if f.args.is_empty() => Ok(Self::function("SYSDATETIME", vec![])),
2725
2726            // PostgreSQL make_date(year, month, day) -> SQL Server DATEFROMPARTS.
2727            "MAKE_DATE" if f.args.len() == 3 => Ok(Self::function("DATEFROMPARTS", f.args)),
2728
2729            // PostgreSQL make_time(hour, minute, double-precision seconds) ->
2730            // SQL Server TIMEFROMPARTS(hour, minute, seconds, fractions, precision).
2731            "MAKE_TIME" if f.args.len() == 3 => Ok(Self::make_time(f.args)),
2732
2733            // PostgreSQL/Oracle-style TO_DATE(value, fmt) -> typed parse expression.
2734            // The generator will emit native CONVERT(DATE, value, style) when
2735            // the literal format maps cleanly to a T-SQL style code.
2736            "TO_DATE" if f.args.len() == 2 => {
2737                Self::formatted_str_to_date_or_fallback(f.args, "TO_DATE")
2738            }
2739
2740            // One-arg TO_DATE(value) has no format string; use a native cast shape.
2741            "TO_DATE" if f.args.len() == 1 => {
2742                let mut args = f.args;
2743                Ok(Expression::Cast(Box::new(Cast {
2744                    this: args.remove(0),
2745                    to: DataType::Date,
2746                    trailing_comments: Vec::new(),
2747                    double_colon_syntax: false,
2748                    format: None,
2749                    default: None,
2750                    inferred_type: None,
2751                })))
2752            }
2753
2754            // PostgreSQL/Oracle-style TO_TIMESTAMP(value, fmt) -> typed parse expression.
2755            // This avoids the invalid CONVERT(value, fmt) argument order.
2756            "TO_TIMESTAMP" if f.args.len() == 2 => {
2757                Self::formatted_str_to_time_or_fallback(f.args, "TO_TIMESTAMP")
2758            }
2759
2760            // PostgreSQL's one-arg TO_TIMESTAMP is epoch seconds.
2761            "TO_TIMESTAMP" if f.args.len() == 1 => {
2762                let mut args = f.args;
2763                Ok(Expression::UnixToTime(Box::new(
2764                    crate::expressions::UnixToTime {
2765                        this: Box::new(args.remove(0)),
2766                        scale: Some(0),
2767                        zone: None,
2768                        hours: None,
2769                        minutes: None,
2770                        format: None,
2771                        target_type: None,
2772                    },
2773                )))
2774            }
2775
2776            // PostgreSQL/Oracle-style TO_CHAR(value, fmt) -> typed format expression.
2777            // The generator converts the normalized strftime format to .NET FORMAT().
2778            "TO_CHAR" if f.args.len() == 2 => {
2779                Self::formatted_time_to_str_or_fallback(f.args, "TO_CHAR")
2780            }
2781
2782            // TO_CHAR(value) without a format remains a normal T-SQL FORMAT call.
2783            "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
2784                "FORMAT".to_string(),
2785                f.args,
2786            )))),
2787
2788            // DATE_FORMAT -> FORMAT
2789            "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
2790                "FORMAT".to_string(),
2791                f.args,
2792            )))),
2793
2794            // DATE_TRUNC -> DATETRUNC in SQL Server 2022+
2795            // For older versions, use DATEADD/DATEDIFF combo
2796            "DATE_TRUNC" | "DATETRUNC" => {
2797                let mut args = Self::uppercase_first_arg_if_identifier(f.args);
2798                // Cast string literal date arg to DATETIME2
2799                if args.len() >= 2 {
2800                    if let Expression::Literal(lit) = &args[1] {
2801                        if let Literal::String(_) = lit.as_ref() {
2802                            args[1] = Expression::Cast(Box::new(Cast {
2803                                this: args[1].clone(),
2804                                to: DataType::Custom {
2805                                    name: "DATETIME2".to_string(),
2806                                },
2807                                trailing_comments: Vec::new(),
2808                                double_colon_syntax: false,
2809                                format: None,
2810                                default: None,
2811                                inferred_type: None,
2812                            }));
2813                        }
2814                    }
2815                }
2816                Ok(Expression::Function(Box::new(Function::new(
2817                    "DATETRUNC".to_string(),
2818                    args,
2819                ))))
2820            }
2821
2822            // DATEADD is native to SQL Server - uppercase the unit
2823            "DATEADD" => {
2824                let args = Self::uppercase_first_arg_if_identifier(f.args);
2825                Ok(Expression::Function(Box::new(Function::new(
2826                    "DATEADD".to_string(),
2827                    args,
2828                ))))
2829            }
2830
2831            // DATEDIFF is native to SQL Server - uppercase the unit
2832            "DATEDIFF" => {
2833                let args = Self::uppercase_first_arg_if_identifier(f.args);
2834                Ok(Expression::Function(Box::new(Function::new(
2835                    "DATEDIFF".to_string(),
2836                    args,
2837                ))))
2838            }
2839
2840            // EXTRACT -> DATEPART in SQL Server
2841            "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2842                "DATEPART".to_string(),
2843                f.args,
2844            )))),
2845
2846            // STRPOS / POSITION -> CHARINDEX
2847            "STRPOS" | "POSITION" if f.args.len() >= 2 => {
2848                // CHARINDEX(substring, string) - same arg order as POSITION
2849                Ok(Expression::Function(Box::new(Function::new(
2850                    "CHARINDEX".to_string(),
2851                    f.args,
2852                ))))
2853            }
2854
2855            // CHARINDEX is native
2856            "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
2857
2858            // CEILING -> CEILING (native)
2859            "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
2860                Function::new("CEILING".to_string(), f.args),
2861            ))),
2862
2863            // ARRAY functions don't exist in SQL Server
2864            // Would need JSON or table-valued parameters
2865
2866            // JSON_EXTRACT -> JSON_VALUE or JSON_QUERY
2867            "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2868                "JSON_VALUE".to_string(),
2869                f.args,
2870            )))),
2871
2872            // JSON_EXTRACT_SCALAR -> JSON_VALUE
2873            "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
2874                "JSON_VALUE".to_string(),
2875                f.args,
2876            )))),
2877
2878            // PARSE_JSON -> strip in TSQL (just keep the string argument)
2879            "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
2880
2881            // GET_PATH(obj, path) -> ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path)) in TSQL
2882            "GET_PATH" if f.args.len() == 2 => {
2883                let mut args = f.args;
2884                let this = args.remove(0);
2885                let path = args.remove(0);
2886                let json_path = match &path {
2887                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
2888                        let Literal::String(s) = lit.as_ref() else {
2889                            unreachable!()
2890                        };
2891                        let normalized = if s.starts_with('$') {
2892                            s.clone()
2893                        } else if s.starts_with('[') {
2894                            format!("${}", s)
2895                        } else {
2896                            format!("$.{}", s)
2897                        };
2898                        Expression::Literal(Box::new(Literal::String(normalized)))
2899                    }
2900                    _ => path,
2901                };
2902                // ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path))
2903                let json_query = Expression::Function(Box::new(Function::new(
2904                    "JSON_QUERY".to_string(),
2905                    vec![this.clone(), json_path.clone()],
2906                )));
2907                let json_value = Expression::Function(Box::new(Function::new(
2908                    "JSON_VALUE".to_string(),
2909                    vec![this, json_path],
2910                )));
2911                Ok(Expression::Function(Box::new(Function::new(
2912                    "ISNULL".to_string(),
2913                    vec![json_query, json_value],
2914                ))))
2915            }
2916
2917            // JSON_QUERY with 1 arg: add '$' path and wrap in ISNULL
2918            // JSON_QUERY with 2 args: leave as-is (already processed or inside ISNULL)
2919            "JSON_QUERY" if f.args.len() == 1 => {
2920                let this = f.args.into_iter().next().unwrap();
2921                let path = Expression::Literal(Box::new(Literal::String("$".to_string())));
2922                let json_query = Expression::Function(Box::new(Function::new(
2923                    "JSON_QUERY".to_string(),
2924                    vec![this.clone(), path.clone()],
2925                )));
2926                let json_value = Expression::Function(Box::new(Function::new(
2927                    "JSON_VALUE".to_string(),
2928                    vec![this, path],
2929                )));
2930                Ok(Expression::Function(Box::new(Function::new(
2931                    "ISNULL".to_string(),
2932                    vec![json_query, json_value],
2933                ))))
2934            }
2935
2936            // SPLIT -> STRING_SPLIT (returns a table, needs CROSS APPLY)
2937            "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
2938                "STRING_SPLIT".to_string(),
2939                f.args,
2940            )))),
2941
2942            // REGEXP_LIKE -> Not directly supported, use LIKE or PATINDEX
2943            // SQL Server has limited regex support via PATINDEX and LIKE
2944            "REGEXP_LIKE" => {
2945                // Fall back to LIKE (loses regex functionality)
2946                Ok(Expression::Function(Box::new(Function::new(
2947                    "PATINDEX".to_string(),
2948                    f.args,
2949                ))))
2950            }
2951
2952            // LN -> LOG in SQL Server
2953            "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2954                "LOG".to_string(),
2955                f.args,
2956            )))),
2957
2958            // LOG with 2 args is LOG(base, value) in most DBs but LOG(value, base) in SQL Server
2959            // This needs careful handling
2960
2961            // STDDEV -> STDEV in SQL Server
2962            "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2963                "STDEV".to_string(),
2964                f.args,
2965            )))),
2966
2967            // STDDEV_POP -> STDEVP in SQL Server
2968            "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
2969                "STDEVP".to_string(),
2970                f.args,
2971            )))),
2972
2973            // VAR_SAMP -> VAR in SQL Server
2974            "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2975                "VAR".to_string(),
2976                f.args,
2977            )))),
2978
2979            // VAR_POP -> VARP in SQL Server
2980            "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
2981                "VARP".to_string(),
2982                f.args,
2983            )))),
2984
2985            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
2986            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
2987                let mut args = f.args;
2988                Self::transform_logical_aggregate(args.remove(0), None, "MIN")
2989            }
2990            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
2991                let mut args = f.args;
2992                Self::transform_logical_aggregate(args.remove(0), None, "MAX")
2993            }
2994
2995            // DATE_ADD(date, interval) -> DATEADD(DAY, interval, date)
2996            "DATE_ADD" => {
2997                if f.args.len() == 2 {
2998                    let mut args = f.args;
2999                    let date = args.remove(0);
3000                    let interval = args.remove(0);
3001                    let unit = Expression::Identifier(crate::expressions::Identifier {
3002                        name: "DAY".to_string(),
3003                        quoted: false,
3004                        trailing_comments: Vec::new(),
3005                        span: None,
3006                    });
3007                    Ok(Expression::Function(Box::new(Function::new(
3008                        "DATEADD".to_string(),
3009                        vec![unit, interval, date],
3010                    ))))
3011                } else {
3012                    let args = Self::uppercase_first_arg_if_identifier(f.args);
3013                    Ok(Expression::Function(Box::new(Function::new(
3014                        "DATEADD".to_string(),
3015                        args,
3016                    ))))
3017                }
3018            }
3019
3020            // INSERT → STUFF (Snowflake/MySQL string INSERT → T-SQL STUFF)
3021            "INSERT" => Ok(Expression::Function(Box::new(Function::new(
3022                "STUFF".to_string(),
3023                f.args,
3024            )))),
3025
3026            // SUSER_NAME(), SUSER_SNAME(), SYSTEM_USER() -> CURRENT_USER
3027            "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
3028                crate::expressions::CurrentUser { this: None },
3029            ))),
3030
3031            // Pass through everything else
3032            _ => Ok(Expression::Function(Box::new(f))),
3033        }
3034    }
3035
3036    fn literal_string(expr: &Expression) -> Option<&str> {
3037        match expr {
3038            Expression::Literal(lit) => match lit.as_ref() {
3039                Literal::String(s) => Some(s),
3040                _ => None,
3041            },
3042            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
3043                if Self::is_text_data_type(&cast.to) =>
3044            {
3045                Self::literal_string(&cast.this)
3046            }
3047            _ => None,
3048        }
3049    }
3050
3051    fn is_text_data_type(data_type: &DataType) -> bool {
3052        match data_type {
3053            DataType::Char { .. }
3054            | DataType::VarChar { .. }
3055            | DataType::String { .. }
3056            | DataType::Text
3057            | DataType::TextWithLength { .. } => true,
3058            DataType::Custom { name } => {
3059                let base = name
3060                    .split_once('(')
3061                    .map_or(name.as_str(), |(base, _)| base)
3062                    .trim();
3063                matches!(
3064                    base.to_ascii_uppercase().as_str(),
3065                    "CHAR"
3066                        | "NCHAR"
3067                        | "VARCHAR"
3068                        | "NVARCHAR"
3069                        | "TEXT"
3070                        | "NTEXT"
3071                        | "STRING"
3072                        | "CHARACTER VARYING"
3073                )
3074            }
3075            _ => false,
3076        }
3077    }
3078
3079    fn is_numeric_data_type(data_type: &DataType) -> bool {
3080        match data_type {
3081            DataType::TinyInt { .. }
3082            | DataType::SmallInt { .. }
3083            | DataType::Int { .. }
3084            | DataType::BigInt { .. }
3085            | DataType::Float { .. }
3086            | DataType::Double { .. }
3087            | DataType::Decimal { .. } => true,
3088            DataType::Custom { name } => {
3089                let base = name
3090                    .split_once('(')
3091                    .map_or(name.as_str(), |(base, _)| base)
3092                    .trim();
3093                matches!(
3094                    base.to_ascii_uppercase().as_str(),
3095                    "TINYINT"
3096                        | "SMALLINT"
3097                        | "INT"
3098                        | "INTEGER"
3099                        | "BIGINT"
3100                        | "DECIMAL"
3101                        | "NUMERIC"
3102                        | "REAL"
3103                        | "FLOAT"
3104                        | "MONEY"
3105                        | "SMALLMONEY"
3106                )
3107            }
3108            _ => false,
3109        }
3110    }
3111
3112    fn is_explicitly_numeric_expression(expr: &Expression) -> bool {
3113        if expr.inferred_type().is_some_and(Self::is_numeric_data_type) {
3114            return true;
3115        }
3116
3117        match expr {
3118            Expression::Literal(literal) => matches!(literal.as_ref(), Literal::Number(_)),
3119            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
3120                Self::is_numeric_data_type(&cast.to)
3121            }
3122            Expression::Alias(alias) => Self::is_explicitly_numeric_expression(&alias.this),
3123            Expression::Paren(paren) => Self::is_explicitly_numeric_expression(&paren.this),
3124            Expression::Neg(unary) => Self::is_explicitly_numeric_expression(&unary.this),
3125            _ => false,
3126        }
3127    }
3128
3129    fn postgres_format_to_strftime(format: &str) -> String {
3130        const POSTGRES_FORMAT_TO_STRFTIME: &[(&str, &str)] = &[
3131            ("FMHH24", "%-H"),
3132            ("FMHH12", "%-I"),
3133            ("FMDDD", "%-j"),
3134            ("TMMonth", "%B"),
3135            ("TMMon", "%b"),
3136            ("TMDay", "%A"),
3137            ("TMDy", "%a"),
3138            ("YYYY", "%Y"),
3139            ("yyyy", "%Y"),
3140            ("HH24", "%H"),
3141            ("HH12", "%I"),
3142            ("FMDD", "%-d"),
3143            ("FMMM", "%-m"),
3144            ("FMMI", "%-M"),
3145            ("FMSS", "%-S"),
3146            ("DDD", "%j"),
3147            ("ddd", "%j"),
3148            ("YY", "%y"),
3149            ("yy", "%y"),
3150            ("MM", "%m"),
3151            ("mm", "%m"),
3152            ("DD", "%d"),
3153            ("dd", "%d"),
3154            ("MI", "%M"),
3155            ("mi", "%M"),
3156            ("SS", "%S"),
3157            ("ss", "%S"),
3158            ("US", "%f"),
3159            ("OF", "%z"),
3160            ("TZ", "%Z"),
3161            ("WW", "%U"),
3162            ("ww", "%U"),
3163            ("D", "%u"),
3164            ("d", "%u"),
3165        ];
3166        crate::format_tokens::convert_format_tokens(format, POSTGRES_FORMAT_TO_STRFTIME)
3167            .unwrap_or_else(|| format.to_string())
3168    }
3169
3170    fn formatted_str_to_time_or_fallback(
3171        mut args: Vec<Expression>,
3172        original_name: &str,
3173    ) -> Result<Expression> {
3174        let this = args.remove(0);
3175        let format = args.remove(0);
3176        if let Some(format) = Self::literal_string(&format) {
3177            Ok(Expression::StrToTime(Box::new(
3178                crate::expressions::StrToTime {
3179                    this: Box::new(this),
3180                    format: Self::postgres_format_to_strftime(format),
3181                    zone: None,
3182                    safe: None,
3183                    target_type: Some(Box::new(Expression::DataType(DataType::Custom {
3184                        name: "DATETIME2".to_string(),
3185                    }))),
3186                },
3187            )))
3188        } else {
3189            Ok(Expression::Function(Box::new(Function::new(
3190                original_name.to_string(),
3191                vec![this, format],
3192            ))))
3193        }
3194    }
3195
3196    fn formatted_str_to_date_or_fallback(
3197        mut args: Vec<Expression>,
3198        original_name: &str,
3199    ) -> Result<Expression> {
3200        let this = args.remove(0);
3201        let format = args.remove(0);
3202        if let Some(format) = Self::literal_string(&format) {
3203            Ok(Expression::StrToDate(Box::new(
3204                crate::expressions::StrToDate {
3205                    this: Box::new(this),
3206                    format: Some(Self::postgres_format_to_strftime(format)),
3207                    safe: None,
3208                },
3209            )))
3210        } else {
3211            Ok(Expression::Function(Box::new(Function::new(
3212                original_name.to_string(),
3213                vec![this, format],
3214            ))))
3215        }
3216    }
3217
3218    fn push_postgres_to_char_literal(result: &mut String, ch: char) {
3219        if matches!(
3220            ch,
3221            'F' | 'H'
3222                | 'K'
3223                | 'M'
3224                | 'd'
3225                | 'f'
3226                | 'g'
3227                | 'h'
3228                | 'm'
3229                | 's'
3230                | 't'
3231                | 'y'
3232                | 'z'
3233                | '%'
3234                | ':'
3235                | '/'
3236                | '"'
3237                | '\''
3238                | '\\'
3239        ) {
3240            result.push('\\');
3241        }
3242        result.push(ch);
3243    }
3244
3245    fn classify_postgres_to_char_format(format: &str) -> PostgresToCharFormat {
3246        const SUPPORTED: &[(&str, &str, bool)] = &[
3247            ("FMHH24", "%-H", false),
3248            ("FMHH12", "%-I", false),
3249            ("FMHH", "%-I", false),
3250            ("FMMonth", "%B", true),
3251            ("FMMon", "%b", true),
3252            ("FMDay", "%A", true),
3253            ("FMDy", "%a", true),
3254            ("TMMonth", "%B", false),
3255            ("TMMon", "%b", false),
3256            ("TMDay", "%A", false),
3257            ("TMDy", "%a", false),
3258            ("YYYY", "%Y", false),
3259            ("yyyy", "%Y", false),
3260            ("HH24", "%H", false),
3261            ("HH12", "%I", false),
3262            ("FMDD", "%-d", false),
3263            ("FMMM", "%-m", false),
3264            ("FMMI", "%-M", false),
3265            ("FMSS", "%-S", false),
3266            ("HH", "%I", false),
3267            ("YY", "%y", false),
3268            ("yy", "%y", false),
3269            ("MM", "%m", false),
3270            ("mm", "%m", false),
3271            ("DD", "%d", false),
3272            ("dd", "%d", false),
3273            ("MI", "%M", false),
3274            ("mi", "%M", false),
3275            ("SS", "%S", false),
3276            ("ss", "%S", false),
3277            ("US", "%f", false),
3278            ("Dy", "%a", true),
3279            ("Mon", "%b", true),
3280        ];
3281        const POSTGRES_TOKENS: &[&str] = &[
3282            "SSSSS", "SSSS", "IYYY", "IDDD", "MONTH", "Month", "month", "HH24", "HH12", "FMMONTH",
3283            "FMMonth", "FMmonth", "FMDAY", "FMDay", "FMday", "FMMON", "FMMon", "FMmon", "FMDY",
3284            "FMDy", "FMdy", "TMMonth", "TMMon", "TMDay", "TMDy", "Y,YYY", "A.M.", "a.m.", "P.M.",
3285            "p.m.", "FMRM", "FMrm", "IYY", "YYY", "yyyy", "YYYY", "DDD", "ddd", "DAY", "Day",
3286            "day", "MON", "Mon", "mon", "DY", "Dy", "dy", "TZH", "TZM", "FF1", "FF2", "FF3", "FF4",
3287            "FF5", "FF6", "FMHH24", "FMHH12", "FMHH", "FMDDD", "FMDD", "FMMM", "FMMI", "FMSS",
3288            "HH", "IY", "YY", "yy", "MM", "mm", "DD", "dd", "MI", "mi", "SS", "ss", "MS", "US",
3289            "AM", "am", "PM", "pm", "BC", "bc", "AD", "ad", "B.C.", "b.c.", "A.D.", "a.d.", "ID",
3290            "WW", "ww", "IW", "CC", "RM", "rm", "TZ", "tz", "OF", "I", "Y", "y", "D", "d", "W",
3291            "J", "Q",
3292        ];
3293
3294        fn longest_token_at(input: &str, index: usize) -> Option<usize> {
3295            POSTGRES_TOKENS
3296                .iter()
3297                .filter(|token| input[index..].starts_with(**token))
3298                .map(|token| token.len())
3299                .max()
3300        }
3301
3302        let mut result = String::with_capacity(format.len() * 2);
3303        let mut index = 0;
3304        let mut requires_english_culture = false;
3305
3306        while index < format.len() {
3307            if format[index..].starts_with('\\') {
3308                index += 1;
3309                if format[index..].starts_with('"') {
3310                    Self::push_postgres_to_char_literal(&mut result, '"');
3311                    index += 1;
3312                } else {
3313                    // Outside double-quoted strings, PostgreSQL only treats a
3314                    // backslash as special when it escapes a double quote.
3315                    Self::push_postgres_to_char_literal(&mut result, '\\');
3316                }
3317                continue;
3318            }
3319
3320            if format[index..].starts_with('"') {
3321                index += 1;
3322                let mut closed = false;
3323                while index < format.len() {
3324                    let Some(ch) = format[index..].chars().next() else {
3325                        return PostgresToCharFormat::Unsupported;
3326                    };
3327                    index += ch.len_utf8();
3328                    if ch == '"' {
3329                        closed = true;
3330                        break;
3331                    }
3332                    if ch == '\\' && index < format.len() {
3333                        let Some(escaped) = format[index..].chars().next() else {
3334                            return PostgresToCharFormat::Unsupported;
3335                        };
3336                        index += escaped.len_utf8();
3337                        Self::push_postgres_to_char_literal(&mut result, escaped);
3338                    } else {
3339                        Self::push_postgres_to_char_literal(&mut result, ch);
3340                    }
3341                }
3342                if !closed {
3343                    return PostgresToCharFormat::Unsupported;
3344                }
3345                continue;
3346            }
3347
3348            let remaining = &format[index..];
3349            let ch = remaining
3350                .chars()
3351                .next()
3352                .expect("format index should be on a character boundary");
3353            if matches!(ch, '9' | '0')
3354                || ["PR", "SG", "PL", "RN", "EEEE"].iter().any(|token| {
3355                    remaining
3356                        .get(..token.len())
3357                        .is_some_and(|prefix| prefix.eq_ignore_ascii_case(token))
3358                })
3359            {
3360                return PostgresToCharFormat::Numeric;
3361            }
3362
3363            let supported = SUPPORTED
3364                .iter()
3365                .filter(|(token, _, _)| format[index..].starts_with(token))
3366                .max_by_key(|(token, _, _)| token.len());
3367            let recognized_len =
3368                if format[index..].starts_with("FM") || format[index..].starts_with("TM") {
3369                    longest_token_at(format, index + 2).map(|len| len + 2)
3370                } else {
3371                    longest_token_at(format, index)
3372                };
3373
3374            if let Some((token, replacement, english_culture)) = supported {
3375                if recognized_len.is_some_and(|len| len > token.len()) {
3376                    return PostgresToCharFormat::Unsupported;
3377                }
3378                let end = index + token.len();
3379                if format[end..].starts_with("TH")
3380                    || format[end..].starts_with("th")
3381                    || format[end..].starts_with("SP")
3382                {
3383                    return PostgresToCharFormat::Unsupported;
3384                }
3385                result.push_str(replacement);
3386                requires_english_culture |= english_culture;
3387                index = end;
3388                continue;
3389            }
3390
3391            if recognized_len.is_some() || format[index..].starts_with("FX") {
3392                return PostgresToCharFormat::Unsupported;
3393            }
3394
3395            Self::push_postgres_to_char_literal(&mut result, ch);
3396            index += ch.len_utf8();
3397        }
3398
3399        PostgresToCharFormat::Temporal {
3400            strftime: result,
3401            requires_english_culture,
3402        }
3403    }
3404
3405    fn formatted_time_to_str_or_fallback(
3406        mut args: Vec<Expression>,
3407        original_name: &str,
3408    ) -> Result<Expression> {
3409        let this = args.remove(0);
3410        let format = args.remove(0);
3411        if let Some(format_string) = Self::literal_string(&format).map(str::to_owned) {
3412            if Self::is_explicitly_numeric_expression(&this) {
3413                return Ok(Expression::Function(Box::new(Function::new(
3414                    original_name.to_string(),
3415                    vec![this, format],
3416                ))));
3417            }
3418
3419            let (format_string, requires_english_culture) =
3420                match Self::classify_postgres_to_char_format(&format_string) {
3421                    PostgresToCharFormat::Temporal {
3422                        strftime,
3423                        requires_english_culture,
3424                    } => (strftime, requires_english_culture),
3425                    PostgresToCharFormat::Numeric | PostgresToCharFormat::Unsupported => {
3426                        return Ok(Expression::Function(Box::new(Function::new(
3427                            original_name.to_string(),
3428                            vec![this, format],
3429                        ))));
3430                    }
3431                };
3432
3433            Ok(Expression::TimeToStr(Box::new(
3434                crate::expressions::TimeToStr {
3435                    this: Box::new(this),
3436                    format: format_string,
3437                    culture: requires_english_culture
3438                        .then(|| Box::new(Expression::string("en-US"))),
3439                    zone: None,
3440                },
3441            )))
3442        } else {
3443            Ok(Expression::Function(Box::new(Function::new(
3444                original_name.to_string(),
3445                vec![this, format],
3446            ))))
3447        }
3448    }
3449
3450    fn transform_aggregate_function(
3451        &self,
3452        mut f: Box<crate::expressions::AggregateFunction>,
3453    ) -> Result<Expression> {
3454        let name_upper = f.name.to_uppercase();
3455        if matches!(
3456            name_upper.as_str(),
3457            "SUM"
3458                | "AVG"
3459                | "MIN"
3460                | "MAX"
3461                | "COUNT"
3462                | "COUNT_BIG"
3463                | "ANY_VALUE"
3464                | "APPROX_COUNT_DISTINCT"
3465                | "STDEV"
3466                | "STDEVP"
3467                | "VAR"
3468                | "VARP"
3469                | "BOOL_AND"
3470                | "BOOL_OR"
3471                | "LOGICAL_AND"
3472                | "LOGICAL_OR"
3473                | "BIT_AND"
3474                | "BIT_OR"
3475                | "BIT_XOR"
3476        ) {
3477            f.order_by.clear();
3478        }
3479
3480        match name_upper.as_str() {
3481            // GROUP_CONCAT -> STRING_AGG
3482            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3483                Function::new("STRING_AGG".to_string(), f.args),
3484            ))),
3485
3486            // LISTAGG -> STRING_AGG
3487            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3488                "STRING_AGG".to_string(),
3489                f.args,
3490            )))),
3491
3492            // ARRAY_AGG -> Not directly supported in SQL Server
3493            // Would need to use FOR XML PATH or STRING_AGG
3494            "ARRAY_AGG" if !f.args.is_empty() => {
3495                // Fall back to STRING_AGG (loses array semantics)
3496                Ok(Expression::Function(Box::new(Function::new(
3497                    "STRING_AGG".to_string(),
3498                    f.args,
3499                ))))
3500            }
3501
3502            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
3503            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
3504                let mut args = f.args;
3505                Self::transform_logical_aggregate(args.remove(0), f.filter, "MIN")
3506            }
3507            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
3508                let mut args = f.args;
3509                Self::transform_logical_aggregate(args.remove(0), f.filter, "MAX")
3510            }
3511
3512            // Pass through everything else
3513            _ => Ok(Expression::AggregateFunction(f)),
3514        }
3515    }
3516
3517    fn without_inert_ordering(
3518        mut aggregate: Box<crate::expressions::AggFunc>,
3519    ) -> Box<crate::expressions::AggFunc> {
3520        aggregate.order_by.clear();
3521        aggregate
3522    }
3523
3524    /// Transform CTEs to add auto-aliases to bare expressions in SELECT
3525    /// In TSQL, when a CTE doesn't have explicit column aliases, bare expressions
3526    /// in the SELECT need to be aliased
3527    fn transform_cte(&self, cte: Cte) -> Result<Expression> {
3528        Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
3529    }
3530
3531    /// Inner method to transform a CTE, returning the modified Cte struct
3532    fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
3533        // Only transform if the CTE doesn't have explicit column aliases
3534        // If it has column aliases like `WITH t(a, b) AS (...)`, we don't need to auto-alias
3535        if cte.columns.is_empty() {
3536            cte.this = self.qualify_derived_table_outputs(cte.this);
3537        }
3538        cte
3539    }
3540
3541    /// Transform Subqueries to add auto-aliases to bare expressions in SELECT
3542    /// In TSQL, when a subquery has a table alias but no column aliases,
3543    /// bare expressions need to be aliased
3544    fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
3545        // Only transform if the subquery has a table alias but no column aliases
3546        // e.g., `(SELECT 1) AS subq` needs aliasing, but `(SELECT 1) AS subq(a)` doesn't
3547        if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
3548            subquery.this = self.qualify_derived_table_outputs(subquery.this);
3549        }
3550        Ok(Expression::Subquery(Box::new(subquery)))
3551    }
3552
3553    /// Add aliases to bare (unaliased) expressions in a SELECT statement
3554    /// This transforms expressions like `SELECT 1` into `SELECT 1 AS [1]`
3555    /// BUT only when the SELECT has no FROM clause (i.e., it's a value expression)
3556    fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
3557        match expr {
3558            Expression::Select(mut select) => {
3559                // Only auto-alias if the SELECT has NO from clause
3560                // If there's a FROM clause, column references already have names from the source tables
3561                let has_from = select.from.is_some();
3562                if !has_from {
3563                    select.expressions = select
3564                        .expressions
3565                        .into_iter()
3566                        .map(|e| self.maybe_alias_expression(e))
3567                        .collect();
3568                }
3569                Expression::Select(select)
3570            }
3571            // For UNION/INTERSECT/EXCEPT, transform the first SELECT
3572            Expression::Union(mut u) => {
3573                let left = std::mem::replace(&mut u.left, Expression::Null(Null));
3574                u.left = self.qualify_derived_table_outputs(left);
3575                Expression::Union(u)
3576            }
3577            Expression::Intersect(mut i) => {
3578                let left = std::mem::replace(&mut i.left, Expression::Null(Null));
3579                i.left = self.qualify_derived_table_outputs(left);
3580                Expression::Intersect(i)
3581            }
3582            Expression::Except(mut e) => {
3583                let left = std::mem::replace(&mut e.left, Expression::Null(Null));
3584                e.left = self.qualify_derived_table_outputs(left);
3585                Expression::Except(e)
3586            }
3587            // Already wrapped in a Subquery (nested), transform the inner
3588            Expression::Subquery(mut s) => {
3589                s.this = self.qualify_derived_table_outputs(s.this);
3590                Expression::Subquery(s)
3591            }
3592            // Pass through anything else
3593            other => other,
3594        }
3595    }
3596
3597    /// Add an alias to a bare expression if needed
3598    /// Returns the expression unchanged if it already has an alias or is a star
3599    /// NOTE: This is only called for SELECTs without a FROM clause, so all bare
3600    /// expressions (including identifiers and columns) need to be aliased.
3601    fn maybe_alias_expression(&self, expr: Expression) -> Expression {
3602        match &expr {
3603            // Already has an alias, leave it alone
3604            Expression::Alias(_) => expr,
3605            // Multiple aliases, leave it alone
3606            Expression::Aliases(_) => expr,
3607            // Star (including qualified star like t.*) doesn't need an alias
3608            Expression::Star(_) => expr,
3609            // When there's no FROM clause (which is the only case when this method is called),
3610            // we need to alias columns and identifiers too since they're standalone values
3611            // that need explicit names for the derived table output.
3612            // Everything else (literals, functions, columns, identifiers, etc.) needs an alias
3613            _ => {
3614                if let Some(output_name) = self.get_output_name(&expr) {
3615                    Expression::Alias(Box::new(Alias {
3616                        this: expr,
3617                        alias: Identifier {
3618                            name: output_name,
3619                            quoted: true, // Force quoting for TSQL bracket syntax
3620                            trailing_comments: Vec::new(),
3621                            span: None,
3622                        },
3623                        column_aliases: Vec::new(),
3624                        alias_explicit_as: false,
3625                        alias_keyword: None,
3626                        pre_alias_comments: Vec::new(),
3627                        trailing_comments: Vec::new(),
3628                        inferred_type: None,
3629                    }))
3630                } else {
3631                    // No output name, leave as-is (shouldn't happen for valid expressions)
3632                    expr
3633                }
3634            }
3635        }
3636    }
3637
3638    /// Get the "output name" of an expression for auto-aliasing
3639    /// For literals, this is the literal value
3640    /// For columns, this is the column name
3641    fn get_output_name(&self, expr: &Expression) -> Option<String> {
3642        match expr {
3643            // Literals - use the literal value as the name
3644            Expression::Literal(lit) => match lit.as_ref() {
3645                Literal::Number(n) => Some(n.clone()),
3646                Literal::String(s) => Some(s.clone()),
3647                Literal::HexString(h) => Some(format!("0x{}", h)),
3648                Literal::HexNumber(h) => Some(format!("0x{}", h)),
3649                Literal::BitString(b) => Some(format!("b{}", b)),
3650                Literal::ByteString(b) => Some(format!("b'{}'", b)),
3651                Literal::NationalString(s) => Some(format!("N'{}'", s)),
3652                Literal::Date(d) => Some(d.clone()),
3653                Literal::Time(t) => Some(t.clone()),
3654                Literal::Timestamp(ts) => Some(ts.clone()),
3655                Literal::Datetime(dt) => Some(dt.clone()),
3656                Literal::TripleQuotedString(s, _) => Some(s.clone()),
3657                Literal::EscapeString(s) => Some(s.clone()),
3658                Literal::DollarString(s) => Some(s.clone()),
3659                Literal::RawString(s) => Some(s.clone()),
3660            },
3661            // Columns - use the column name
3662            Expression::Column(col) => Some(col.name.name.clone()),
3663            // Identifiers - use the identifier name
3664            Expression::Identifier(ident) => Some(ident.name.clone()),
3665            // Boolean literals
3666            Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
3667            // NULL
3668            Expression::Null(_) => Some("NULL".to_string()),
3669            // For functions, use the function name as a fallback
3670            Expression::Function(f) => Some(f.name.clone()),
3671            // For aggregates, use the function name
3672            Expression::AggregateFunction(f) => Some(f.name.clone()),
3673            // For other expressions, generate a generic name
3674            _ => Some(format!("_col_{}", 0)),
3675        }
3676    }
3677
3678    /// Helper to uppercase the first argument if it's an identifier or column (for DATEDIFF, DATEADD units)
3679    fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
3680        use crate::expressions::Identifier;
3681        if !args.is_empty() {
3682            match &args[0] {
3683                Expression::Identifier(id) => {
3684                    args[0] = Expression::Identifier(Identifier {
3685                        name: id.name.to_uppercase(),
3686                        quoted: id.quoted,
3687                        trailing_comments: id.trailing_comments.clone(),
3688                        span: None,
3689                    });
3690                }
3691                Expression::Var(v) => {
3692                    args[0] = Expression::Identifier(Identifier {
3693                        name: v.this.to_uppercase(),
3694                        quoted: false,
3695                        trailing_comments: Vec::new(),
3696                        span: None,
3697                    });
3698                }
3699                Expression::Column(col) if col.table.is_none() => {
3700                    args[0] = Expression::Identifier(Identifier {
3701                        name: col.name.name.to_uppercase(),
3702                        quoted: col.name.quoted,
3703                        trailing_comments: col.name.trailing_comments.clone(),
3704                        span: None,
3705                    });
3706                }
3707                _ => {}
3708            }
3709        }
3710        args
3711    }
3712}
3713
3714#[cfg(test)]
3715mod tests {
3716    use super::*;
3717    use crate::dialects::Dialect;
3718
3719    fn transpile_to_tsql(sql: &str) -> String {
3720        let dialect = Dialect::get(DialectType::Generic);
3721        let result = dialect
3722            .transpile(sql, DialectType::TSQL)
3723            .expect("Transpile failed");
3724        result[0].clone()
3725    }
3726
3727    #[test]
3728    fn test_nvl_to_isnull() {
3729        let result = transpile_to_tsql("SELECT NVL(a, b)");
3730        assert!(
3731            result.contains("ISNULL"),
3732            "Expected ISNULL, got: {}",
3733            result
3734        );
3735    }
3736
3737    #[test]
3738    fn test_coalesce_to_isnull() {
3739        let result = transpile_to_tsql("SELECT COALESCE(a, b)");
3740        assert!(
3741            result.contains("ISNULL"),
3742            "Expected ISNULL, got: {}",
3743            result
3744        );
3745    }
3746
3747    #[test]
3748    fn test_basic_select() {
3749        let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
3750        assert!(result.contains("SELECT"));
3751        assert!(result.contains("FROM users"));
3752    }
3753
3754    #[test]
3755    fn test_length_to_len() {
3756        let result = transpile_to_tsql("SELECT LENGTH(name)");
3757        assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
3758    }
3759
3760    #[test]
3761    fn test_issue_374_tsql_parse_then_generate_uses_len() {
3762        let sql = "SELECT LEN(table.col1) - LEN(table.col2) FROM table";
3763        let ast = Dialect::get(DialectType::TSQL)
3764            .parse(sql)
3765            .expect("T-SQL should parse");
3766        let expression = &ast[0];
3767
3768        for target in [DialectType::TSQL, DialectType::Fabric] {
3769            let generated = Dialect::get(target)
3770                .generate(expression)
3771                .expect("AST should generate");
3772            assert_eq!(generated, sql, "failed for target {target:?}");
3773        }
3774
3775        let standard_sql = "SELECT LENGTH(table.col1) - LENGTH(table.col2) FROM table";
3776        for target in [DialectType::Generic, DialectType::PostgreSQL] {
3777            let generated = Dialect::get(target)
3778                .generate(expression)
3779                .expect("AST should generate");
3780            assert_eq!(generated, standard_sql, "failed for target {target:?}");
3781        }
3782    }
3783
3784    #[test]
3785    fn test_now_to_getdate() {
3786        let result = transpile_to_tsql("SELECT NOW()");
3787        assert!(
3788            result.contains("GETDATE"),
3789            "Expected GETDATE, got: {}",
3790            result
3791        );
3792    }
3793
3794    #[test]
3795    fn test_group_concat_to_string_agg() {
3796        let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
3797        assert!(
3798            result.contains("STRING_AGG"),
3799            "Expected STRING_AGG, got: {}",
3800            result
3801        );
3802    }
3803
3804    #[test]
3805    fn test_listagg_to_string_agg() {
3806        let result = transpile_to_tsql("SELECT LISTAGG(name)");
3807        assert!(
3808            result.contains("STRING_AGG"),
3809            "Expected STRING_AGG, got: {}",
3810            result
3811        );
3812    }
3813
3814    #[test]
3815    fn test_ln_to_log() {
3816        let result = transpile_to_tsql("SELECT LN(x)");
3817        assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
3818    }
3819
3820    #[test]
3821    fn test_stddev_to_stdev() {
3822        let result = transpile_to_tsql("SELECT STDDEV(x)");
3823        assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
3824    }
3825
3826    #[test]
3827    fn test_bracket_identifiers() {
3828        // SQL Server uses square brackets for identifiers
3829        let dialect = Dialect::get(DialectType::TSQL);
3830        let config = dialect.generator_config();
3831        assert_eq!(config.identifier_quote, '[');
3832    }
3833
3834    #[test]
3835    fn test_json_query_isnull_wrapper_simple() {
3836        // JSON_QUERY with two args needs ISNULL wrapper when transpiling to TSQL
3837        let dialect = Dialect::get(DialectType::TSQL);
3838        let result = dialect
3839            .transpile(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
3840            .expect("transpile failed");
3841        assert!(
3842            result[0].contains("ISNULL"),
3843            "JSON_QUERY should be wrapped with ISNULL: {}",
3844            result[0]
3845        );
3846    }
3847
3848    #[test]
3849    fn test_json_query_isnull_wrapper_nested() {
3850        let dialect = Dialect::get(DialectType::TSQL);
3851        let result = dialect
3852            .transpile(
3853                r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
3854                DialectType::TSQL,
3855            )
3856            .expect("transpile failed");
3857        let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
3858        assert_eq!(
3859            result[0], expected,
3860            "JSON_QUERY should be wrapped with ISNULL"
3861        );
3862    }
3863}