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/FETCH instead of LIMIT
66            limit_fetch_style: crate::generator::LimitFetchStyle::FetchFirst,
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                })))
2607            }
2608
2609            // PostgreSQL md5(text) returns lowercase hex text; HASHBYTES returns varbinary.
2610            "MD5" if f.args.len() == 1 => {
2611                let mut args = f.args;
2612                Ok(Self::tsql_md5_hex(args.remove(0)))
2613            }
2614
2615            "SHA256" if f.args.len() == 1 => {
2616                let mut args = f.args;
2617                Ok(Self::function(
2618                    "HASHBYTES",
2619                    vec![Expression::string("SHA2_256"), args.remove(0)],
2620                ))
2621            }
2622
2623            "SHA512" if f.args.len() == 1 => {
2624                let mut args = f.args;
2625                Ok(Self::function(
2626                    "HASHBYTES",
2627                    vec![Expression::string("SHA2_512"), args.remove(0)],
2628                ))
2629            }
2630
2631            // PostgreSQL octet_length(text/bytea) -> DATALENGTH(...)
2632            "OCTET_LENGTH" if f.args.len() == 1 => Ok(Self::function("DATALENGTH", f.args)),
2633
2634            // PostgreSQL bit_length(text/bytea) -> DATALENGTH(...) * 8
2635            "BIT_LENGTH" if f.args.len() == 1 => {
2636                let mut args = f.args;
2637                Ok(Expression::Mul(Box::new(BinaryOp::new(
2638                    Self::function("DATALENGTH", vec![args.remove(0)]),
2639                    Expression::number(8),
2640                ))))
2641            }
2642
2643            // PostgreSQL to_hex(int) -> unpadded lowercase hex text. SQL Server's
2644            // binary conversion preserves the integer width, so remove only leading
2645            // zeroes and retain a single zero for the all-zero value.
2646            "TO_HEX" if f.args.len() == 1 => {
2647                let mut args = f.args;
2648                Ok(Self::tsql_postgres_to_hex(args.remove(0)))
2649            }
2650
2651            // PostgreSQL encode(bytea, 'hex') -> lowercase hex text.
2652            "ENCODE" if f.args.len() == 2 => {
2653                let mut args = f.args;
2654                let this = args.remove(0);
2655                let encoding = args.remove(0);
2656                if Self::literal_string(&encoding)
2657                    .is_some_and(|encoding| encoding.eq_ignore_ascii_case("hex"))
2658                {
2659                    Ok(Self::tsql_hex_from_varbinary(this))
2660                } else {
2661                    Ok(Expression::Function(Box::new(Function::new(
2662                        "ENCODE".to_string(),
2663                        vec![this, encoding],
2664                    ))))
2665                }
2666            }
2667
2668            // Preserve support for manually constructed/generic DECODE ASTs in addition
2669            // to the parser's typed DecodeCase representation.
2670            "DECODE"
2671                if f.args.len() == 2
2672                    && Self::literal_string(&f.args[1])
2673                        .is_some_and(|format| format.eq_ignore_ascii_case("hex")) =>
2674            {
2675                let mut args = f.args;
2676                Ok(Self::tsql_convert(
2677                    DataType::Custom {
2678                        name: "VARBINARY(MAX)".to_string(),
2679                    },
2680                    args.remove(0),
2681                    Some(2),
2682                ))
2683            }
2684
2685            // PostgreSQL repeat(text, count) -> SQL Server REPLICATE(text, count)
2686            "REPEAT" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2687                "REPLICATE".to_string(),
2688                f.args,
2689            )))),
2690
2691            // PostgreSQL chr(code) -> SQL Server CHAR(code)
2692            "CHR" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2693                "CHAR".to_string(),
2694                f.args,
2695            )))),
2696
2697            // RANDOM -> RAND
2698            "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
2699                seed: None,
2700                lower: None,
2701                upper: None,
2702            }))),
2703
2704            // NOW -> GETDATE or CURRENT_TIMESTAMP (both work)
2705            "NOW" => Ok(Self::getdate()),
2706
2707            // CURRENT_TIMESTAMP -> GETDATE (SQL Server prefers GETDATE)
2708            "CURRENT_TIMESTAMP" => Ok(Self::getdate()),
2709
2710            // CURRENT_DATE -> CAST(GETDATE() AS DATE)
2711            "CURRENT_DATE" => Ok(Self::cast_getdate_to(DataType::Date)),
2712
2713            // CURRENT_TIME -> CAST(GETDATE() AS TIME)
2714            "CURRENT_TIME" => Ok(Self::cast_getdate_to(DataType::Time {
2715                precision: None,
2716                timezone: false,
2717            })),
2718
2719            // LOCALTIMESTAMP -> GETDATE()
2720            "LOCALTIMESTAMP" => Ok(Self::getdate()),
2721
2722            // PostgreSQL clock_timestamp() -> high-precision current system timestamp.
2723            "CLOCK_TIMESTAMP" if f.args.is_empty() => Ok(Self::function("SYSDATETIME", vec![])),
2724
2725            // PostgreSQL make_date(year, month, day) -> SQL Server DATEFROMPARTS.
2726            "MAKE_DATE" if f.args.len() == 3 => Ok(Self::function("DATEFROMPARTS", f.args)),
2727
2728            // PostgreSQL make_time(hour, minute, double-precision seconds) ->
2729            // SQL Server TIMEFROMPARTS(hour, minute, seconds, fractions, precision).
2730            "MAKE_TIME" if f.args.len() == 3 => Ok(Self::make_time(f.args)),
2731
2732            // PostgreSQL/Oracle-style TO_DATE(value, fmt) -> typed parse expression.
2733            // The generator will emit native CONVERT(DATE, value, style) when
2734            // the literal format maps cleanly to a T-SQL style code.
2735            "TO_DATE" if f.args.len() == 2 => {
2736                Self::formatted_str_to_date_or_fallback(f.args, "TO_DATE")
2737            }
2738
2739            // One-arg TO_DATE(value) has no format string; use a native cast shape.
2740            "TO_DATE" if f.args.len() == 1 => {
2741                let mut args = f.args;
2742                Ok(Expression::Cast(Box::new(Cast {
2743                    this: args.remove(0),
2744                    to: DataType::Date,
2745                    trailing_comments: Vec::new(),
2746                    double_colon_syntax: false,
2747                    format: None,
2748                    default: None,
2749                    inferred_type: None,
2750                })))
2751            }
2752
2753            // PostgreSQL/Oracle-style TO_TIMESTAMP(value, fmt) -> typed parse expression.
2754            // This avoids the invalid CONVERT(value, fmt) argument order.
2755            "TO_TIMESTAMP" if f.args.len() == 2 => {
2756                Self::formatted_str_to_time_or_fallback(f.args, "TO_TIMESTAMP")
2757            }
2758
2759            // PostgreSQL's one-arg TO_TIMESTAMP is epoch seconds.
2760            "TO_TIMESTAMP" if f.args.len() == 1 => {
2761                let mut args = f.args;
2762                Ok(Expression::UnixToTime(Box::new(
2763                    crate::expressions::UnixToTime {
2764                        this: Box::new(args.remove(0)),
2765                        scale: Some(0),
2766                        zone: None,
2767                        hours: None,
2768                        minutes: None,
2769                        format: None,
2770                        target_type: None,
2771                    },
2772                )))
2773            }
2774
2775            // PostgreSQL/Oracle-style TO_CHAR(value, fmt) -> typed format expression.
2776            // The generator converts the normalized strftime format to .NET FORMAT().
2777            "TO_CHAR" if f.args.len() == 2 => {
2778                Self::formatted_time_to_str_or_fallback(f.args, "TO_CHAR")
2779            }
2780
2781            // TO_CHAR(value) without a format remains a normal T-SQL FORMAT call.
2782            "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
2783                "FORMAT".to_string(),
2784                f.args,
2785            )))),
2786
2787            // DATE_FORMAT -> FORMAT
2788            "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
2789                "FORMAT".to_string(),
2790                f.args,
2791            )))),
2792
2793            // DATE_TRUNC -> DATETRUNC in SQL Server 2022+
2794            // For older versions, use DATEADD/DATEDIFF combo
2795            "DATE_TRUNC" | "DATETRUNC" => {
2796                let mut args = Self::uppercase_first_arg_if_identifier(f.args);
2797                // Cast string literal date arg to DATETIME2
2798                if args.len() >= 2 {
2799                    if let Expression::Literal(lit) = &args[1] {
2800                        if let Literal::String(_) = lit.as_ref() {
2801                            args[1] = Expression::Cast(Box::new(Cast {
2802                                this: args[1].clone(),
2803                                to: DataType::Custom {
2804                                    name: "DATETIME2".to_string(),
2805                                },
2806                                trailing_comments: Vec::new(),
2807                                double_colon_syntax: false,
2808                                format: None,
2809                                default: None,
2810                                inferred_type: None,
2811                            }));
2812                        }
2813                    }
2814                }
2815                Ok(Expression::Function(Box::new(Function::new(
2816                    "DATETRUNC".to_string(),
2817                    args,
2818                ))))
2819            }
2820
2821            // DATEADD is native to SQL Server - uppercase the unit
2822            "DATEADD" => {
2823                let args = Self::uppercase_first_arg_if_identifier(f.args);
2824                Ok(Expression::Function(Box::new(Function::new(
2825                    "DATEADD".to_string(),
2826                    args,
2827                ))))
2828            }
2829
2830            // DATEDIFF is native to SQL Server - uppercase the unit
2831            "DATEDIFF" => {
2832                let args = Self::uppercase_first_arg_if_identifier(f.args);
2833                Ok(Expression::Function(Box::new(Function::new(
2834                    "DATEDIFF".to_string(),
2835                    args,
2836                ))))
2837            }
2838
2839            // EXTRACT -> DATEPART in SQL Server
2840            "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2841                "DATEPART".to_string(),
2842                f.args,
2843            )))),
2844
2845            // STRPOS / POSITION -> CHARINDEX
2846            "STRPOS" | "POSITION" if f.args.len() >= 2 => {
2847                // CHARINDEX(substring, string) - same arg order as POSITION
2848                Ok(Expression::Function(Box::new(Function::new(
2849                    "CHARINDEX".to_string(),
2850                    f.args,
2851                ))))
2852            }
2853
2854            // CHARINDEX is native
2855            "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
2856
2857            // CEILING -> CEILING (native)
2858            "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
2859                Function::new("CEILING".to_string(), f.args),
2860            ))),
2861
2862            // ARRAY functions don't exist in SQL Server
2863            // Would need JSON or table-valued parameters
2864
2865            // JSON_EXTRACT -> JSON_VALUE or JSON_QUERY
2866            "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2867                "JSON_VALUE".to_string(),
2868                f.args,
2869            )))),
2870
2871            // JSON_EXTRACT_SCALAR -> JSON_VALUE
2872            "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
2873                "JSON_VALUE".to_string(),
2874                f.args,
2875            )))),
2876
2877            // PARSE_JSON -> strip in TSQL (just keep the string argument)
2878            "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
2879
2880            // GET_PATH(obj, path) -> ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path)) in TSQL
2881            "GET_PATH" if f.args.len() == 2 => {
2882                let mut args = f.args;
2883                let this = args.remove(0);
2884                let path = args.remove(0);
2885                let json_path = match &path {
2886                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
2887                        let Literal::String(s) = lit.as_ref() else {
2888                            unreachable!()
2889                        };
2890                        let normalized = if s.starts_with('$') {
2891                            s.clone()
2892                        } else if s.starts_with('[') {
2893                            format!("${}", s)
2894                        } else {
2895                            format!("$.{}", s)
2896                        };
2897                        Expression::Literal(Box::new(Literal::String(normalized)))
2898                    }
2899                    _ => path,
2900                };
2901                // ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path))
2902                let json_query = Expression::Function(Box::new(Function::new(
2903                    "JSON_QUERY".to_string(),
2904                    vec![this.clone(), json_path.clone()],
2905                )));
2906                let json_value = Expression::Function(Box::new(Function::new(
2907                    "JSON_VALUE".to_string(),
2908                    vec![this, json_path],
2909                )));
2910                Ok(Expression::Function(Box::new(Function::new(
2911                    "ISNULL".to_string(),
2912                    vec![json_query, json_value],
2913                ))))
2914            }
2915
2916            // JSON_QUERY with 1 arg: add '$' path and wrap in ISNULL
2917            // JSON_QUERY with 2 args: leave as-is (already processed or inside ISNULL)
2918            "JSON_QUERY" if f.args.len() == 1 => {
2919                let this = f.args.into_iter().next().unwrap();
2920                let path = Expression::Literal(Box::new(Literal::String("$".to_string())));
2921                let json_query = Expression::Function(Box::new(Function::new(
2922                    "JSON_QUERY".to_string(),
2923                    vec![this.clone(), path.clone()],
2924                )));
2925                let json_value = Expression::Function(Box::new(Function::new(
2926                    "JSON_VALUE".to_string(),
2927                    vec![this, path],
2928                )));
2929                Ok(Expression::Function(Box::new(Function::new(
2930                    "ISNULL".to_string(),
2931                    vec![json_query, json_value],
2932                ))))
2933            }
2934
2935            // SPLIT -> STRING_SPLIT (returns a table, needs CROSS APPLY)
2936            "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
2937                "STRING_SPLIT".to_string(),
2938                f.args,
2939            )))),
2940
2941            // REGEXP_LIKE -> Not directly supported, use LIKE or PATINDEX
2942            // SQL Server has limited regex support via PATINDEX and LIKE
2943            "REGEXP_LIKE" => {
2944                // Fall back to LIKE (loses regex functionality)
2945                Ok(Expression::Function(Box::new(Function::new(
2946                    "PATINDEX".to_string(),
2947                    f.args,
2948                ))))
2949            }
2950
2951            // LN -> LOG in SQL Server
2952            "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2953                "LOG".to_string(),
2954                f.args,
2955            )))),
2956
2957            // LOG with 2 args is LOG(base, value) in most DBs but LOG(value, base) in SQL Server
2958            // This needs careful handling
2959
2960            // STDDEV -> STDEV in SQL Server
2961            "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2962                "STDEV".to_string(),
2963                f.args,
2964            )))),
2965
2966            // STDDEV_POP -> STDEVP in SQL Server
2967            "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
2968                "STDEVP".to_string(),
2969                f.args,
2970            )))),
2971
2972            // VAR_SAMP -> VAR in SQL Server
2973            "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2974                "VAR".to_string(),
2975                f.args,
2976            )))),
2977
2978            // VAR_POP -> VARP in SQL Server
2979            "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
2980                "VARP".to_string(),
2981                f.args,
2982            )))),
2983
2984            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
2985            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
2986                let mut args = f.args;
2987                Self::transform_logical_aggregate(args.remove(0), None, "MIN")
2988            }
2989            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
2990                let mut args = f.args;
2991                Self::transform_logical_aggregate(args.remove(0), None, "MAX")
2992            }
2993
2994            // DATE_ADD(date, interval) -> DATEADD(DAY, interval, date)
2995            "DATE_ADD" => {
2996                if f.args.len() == 2 {
2997                    let mut args = f.args;
2998                    let date = args.remove(0);
2999                    let interval = args.remove(0);
3000                    let unit = Expression::Identifier(crate::expressions::Identifier {
3001                        name: "DAY".to_string(),
3002                        quoted: false,
3003                        trailing_comments: Vec::new(),
3004                        span: None,
3005                    });
3006                    Ok(Expression::Function(Box::new(Function::new(
3007                        "DATEADD".to_string(),
3008                        vec![unit, interval, date],
3009                    ))))
3010                } else {
3011                    let args = Self::uppercase_first_arg_if_identifier(f.args);
3012                    Ok(Expression::Function(Box::new(Function::new(
3013                        "DATEADD".to_string(),
3014                        args,
3015                    ))))
3016                }
3017            }
3018
3019            // INSERT → STUFF (Snowflake/MySQL string INSERT → T-SQL STUFF)
3020            "INSERT" => Ok(Expression::Function(Box::new(Function::new(
3021                "STUFF".to_string(),
3022                f.args,
3023            )))),
3024
3025            // SUSER_NAME(), SUSER_SNAME(), SYSTEM_USER() -> CURRENT_USER
3026            "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
3027                crate::expressions::CurrentUser { this: None },
3028            ))),
3029
3030            // Pass through everything else
3031            _ => Ok(Expression::Function(Box::new(f))),
3032        }
3033    }
3034
3035    fn literal_string(expr: &Expression) -> Option<&str> {
3036        match expr {
3037            Expression::Literal(lit) => match lit.as_ref() {
3038                Literal::String(s) => Some(s),
3039                _ => None,
3040            },
3041            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
3042                if Self::is_text_data_type(&cast.to) =>
3043            {
3044                Self::literal_string(&cast.this)
3045            }
3046            _ => None,
3047        }
3048    }
3049
3050    fn is_text_data_type(data_type: &DataType) -> bool {
3051        match data_type {
3052            DataType::Char { .. }
3053            | DataType::VarChar { .. }
3054            | DataType::String { .. }
3055            | DataType::Text
3056            | DataType::TextWithLength { .. } => true,
3057            DataType::Custom { name } => {
3058                let base = name
3059                    .split_once('(')
3060                    .map_or(name.as_str(), |(base, _)| base)
3061                    .trim();
3062                matches!(
3063                    base.to_ascii_uppercase().as_str(),
3064                    "CHAR"
3065                        | "NCHAR"
3066                        | "VARCHAR"
3067                        | "NVARCHAR"
3068                        | "TEXT"
3069                        | "NTEXT"
3070                        | "STRING"
3071                        | "CHARACTER VARYING"
3072                )
3073            }
3074            _ => false,
3075        }
3076    }
3077
3078    fn is_numeric_data_type(data_type: &DataType) -> bool {
3079        match data_type {
3080            DataType::TinyInt { .. }
3081            | DataType::SmallInt { .. }
3082            | DataType::Int { .. }
3083            | DataType::BigInt { .. }
3084            | DataType::Float { .. }
3085            | DataType::Double { .. }
3086            | DataType::Decimal { .. } => true,
3087            DataType::Custom { name } => {
3088                let base = name
3089                    .split_once('(')
3090                    .map_or(name.as_str(), |(base, _)| base)
3091                    .trim();
3092                matches!(
3093                    base.to_ascii_uppercase().as_str(),
3094                    "TINYINT"
3095                        | "SMALLINT"
3096                        | "INT"
3097                        | "INTEGER"
3098                        | "BIGINT"
3099                        | "DECIMAL"
3100                        | "NUMERIC"
3101                        | "REAL"
3102                        | "FLOAT"
3103                        | "MONEY"
3104                        | "SMALLMONEY"
3105                )
3106            }
3107            _ => false,
3108        }
3109    }
3110
3111    fn is_explicitly_numeric_expression(expr: &Expression) -> bool {
3112        if expr.inferred_type().is_some_and(Self::is_numeric_data_type) {
3113            return true;
3114        }
3115
3116        match expr {
3117            Expression::Literal(literal) => matches!(literal.as_ref(), Literal::Number(_)),
3118            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
3119                Self::is_numeric_data_type(&cast.to)
3120            }
3121            Expression::Alias(alias) => Self::is_explicitly_numeric_expression(&alias.this),
3122            Expression::Paren(paren) => Self::is_explicitly_numeric_expression(&paren.this),
3123            Expression::Neg(unary) => Self::is_explicitly_numeric_expression(&unary.this),
3124            _ => false,
3125        }
3126    }
3127
3128    fn postgres_format_to_strftime(format: &str) -> String {
3129        const POSTGRES_FORMAT_TO_STRFTIME: &[(&str, &str)] = &[
3130            ("FMHH24", "%-H"),
3131            ("FMHH12", "%-I"),
3132            ("FMDDD", "%-j"),
3133            ("TMMonth", "%B"),
3134            ("TMMon", "%b"),
3135            ("TMDay", "%A"),
3136            ("TMDy", "%a"),
3137            ("YYYY", "%Y"),
3138            ("yyyy", "%Y"),
3139            ("HH24", "%H"),
3140            ("HH12", "%I"),
3141            ("FMDD", "%-d"),
3142            ("FMMM", "%-m"),
3143            ("FMMI", "%-M"),
3144            ("FMSS", "%-S"),
3145            ("DDD", "%j"),
3146            ("ddd", "%j"),
3147            ("YY", "%y"),
3148            ("yy", "%y"),
3149            ("MM", "%m"),
3150            ("mm", "%m"),
3151            ("DD", "%d"),
3152            ("dd", "%d"),
3153            ("MI", "%M"),
3154            ("mi", "%M"),
3155            ("SS", "%S"),
3156            ("ss", "%S"),
3157            ("US", "%f"),
3158            ("OF", "%z"),
3159            ("TZ", "%Z"),
3160            ("WW", "%U"),
3161            ("ww", "%U"),
3162            ("D", "%u"),
3163            ("d", "%u"),
3164        ];
3165        crate::format_tokens::convert_format_tokens(format, POSTGRES_FORMAT_TO_STRFTIME)
3166            .unwrap_or_else(|| format.to_string())
3167    }
3168
3169    fn formatted_str_to_time_or_fallback(
3170        mut args: Vec<Expression>,
3171        original_name: &str,
3172    ) -> Result<Expression> {
3173        let this = args.remove(0);
3174        let format = args.remove(0);
3175        if let Some(format) = Self::literal_string(&format) {
3176            Ok(Expression::StrToTime(Box::new(
3177                crate::expressions::StrToTime {
3178                    this: Box::new(this),
3179                    format: Self::postgres_format_to_strftime(format),
3180                    zone: None,
3181                    safe: None,
3182                    target_type: Some(Box::new(Expression::DataType(DataType::Custom {
3183                        name: "DATETIME2".to_string(),
3184                    }))),
3185                },
3186            )))
3187        } else {
3188            Ok(Expression::Function(Box::new(Function::new(
3189                original_name.to_string(),
3190                vec![this, format],
3191            ))))
3192        }
3193    }
3194
3195    fn formatted_str_to_date_or_fallback(
3196        mut args: Vec<Expression>,
3197        original_name: &str,
3198    ) -> Result<Expression> {
3199        let this = args.remove(0);
3200        let format = args.remove(0);
3201        if let Some(format) = Self::literal_string(&format) {
3202            Ok(Expression::StrToDate(Box::new(
3203                crate::expressions::StrToDate {
3204                    this: Box::new(this),
3205                    format: Some(Self::postgres_format_to_strftime(format)),
3206                    safe: None,
3207                },
3208            )))
3209        } else {
3210            Ok(Expression::Function(Box::new(Function::new(
3211                original_name.to_string(),
3212                vec![this, format],
3213            ))))
3214        }
3215    }
3216
3217    fn push_postgres_to_char_literal(result: &mut String, ch: char) {
3218        if matches!(
3219            ch,
3220            'F' | 'H'
3221                | 'K'
3222                | 'M'
3223                | 'd'
3224                | 'f'
3225                | 'g'
3226                | 'h'
3227                | 'm'
3228                | 's'
3229                | 't'
3230                | 'y'
3231                | 'z'
3232                | '%'
3233                | ':'
3234                | '/'
3235                | '"'
3236                | '\''
3237                | '\\'
3238        ) {
3239            result.push('\\');
3240        }
3241        result.push(ch);
3242    }
3243
3244    fn classify_postgres_to_char_format(format: &str) -> PostgresToCharFormat {
3245        const SUPPORTED: &[(&str, &str, bool)] = &[
3246            ("FMHH24", "%-H", false),
3247            ("FMHH12", "%-I", false),
3248            ("FMHH", "%-I", false),
3249            ("FMMonth", "%B", true),
3250            ("FMMon", "%b", true),
3251            ("FMDay", "%A", true),
3252            ("FMDy", "%a", true),
3253            ("TMMonth", "%B", false),
3254            ("TMMon", "%b", false),
3255            ("TMDay", "%A", false),
3256            ("TMDy", "%a", false),
3257            ("YYYY", "%Y", false),
3258            ("yyyy", "%Y", false),
3259            ("HH24", "%H", false),
3260            ("HH12", "%I", false),
3261            ("FMDD", "%-d", false),
3262            ("FMMM", "%-m", false),
3263            ("FMMI", "%-M", false),
3264            ("FMSS", "%-S", false),
3265            ("HH", "%I", false),
3266            ("YY", "%y", false),
3267            ("yy", "%y", false),
3268            ("MM", "%m", false),
3269            ("mm", "%m", false),
3270            ("DD", "%d", false),
3271            ("dd", "%d", false),
3272            ("MI", "%M", false),
3273            ("mi", "%M", false),
3274            ("SS", "%S", false),
3275            ("ss", "%S", false),
3276            ("US", "%f", false),
3277            ("Dy", "%a", true),
3278            ("Mon", "%b", true),
3279        ];
3280        const POSTGRES_TOKENS: &[&str] = &[
3281            "SSSSS", "SSSS", "IYYY", "IDDD", "MONTH", "Month", "month", "HH24", "HH12", "FMMONTH",
3282            "FMMonth", "FMmonth", "FMDAY", "FMDay", "FMday", "FMMON", "FMMon", "FMmon", "FMDY",
3283            "FMDy", "FMdy", "TMMonth", "TMMon", "TMDay", "TMDy", "Y,YYY", "A.M.", "a.m.", "P.M.",
3284            "p.m.", "FMRM", "FMrm", "IYY", "YYY", "yyyy", "YYYY", "DDD", "ddd", "DAY", "Day",
3285            "day", "MON", "Mon", "mon", "DY", "Dy", "dy", "TZH", "TZM", "FF1", "FF2", "FF3", "FF4",
3286            "FF5", "FF6", "FMHH24", "FMHH12", "FMHH", "FMDDD", "FMDD", "FMMM", "FMMI", "FMSS",
3287            "HH", "IY", "YY", "yy", "MM", "mm", "DD", "dd", "MI", "mi", "SS", "ss", "MS", "US",
3288            "AM", "am", "PM", "pm", "BC", "bc", "AD", "ad", "B.C.", "b.c.", "A.D.", "a.d.", "ID",
3289            "WW", "ww", "IW", "CC", "RM", "rm", "TZ", "tz", "OF", "I", "Y", "y", "D", "d", "W",
3290            "J", "Q",
3291        ];
3292
3293        fn longest_token_at(input: &str, index: usize) -> Option<usize> {
3294            POSTGRES_TOKENS
3295                .iter()
3296                .filter(|token| input[index..].starts_with(**token))
3297                .map(|token| token.len())
3298                .max()
3299        }
3300
3301        let mut result = String::with_capacity(format.len() * 2);
3302        let mut index = 0;
3303        let mut requires_english_culture = false;
3304
3305        while index < format.len() {
3306            if format[index..].starts_with('\\') {
3307                index += 1;
3308                if format[index..].starts_with('"') {
3309                    Self::push_postgres_to_char_literal(&mut result, '"');
3310                    index += 1;
3311                } else {
3312                    // Outside double-quoted strings, PostgreSQL only treats a
3313                    // backslash as special when it escapes a double quote.
3314                    Self::push_postgres_to_char_literal(&mut result, '\\');
3315                }
3316                continue;
3317            }
3318
3319            if format[index..].starts_with('"') {
3320                index += 1;
3321                let mut closed = false;
3322                while index < format.len() {
3323                    let Some(ch) = format[index..].chars().next() else {
3324                        return PostgresToCharFormat::Unsupported;
3325                    };
3326                    index += ch.len_utf8();
3327                    if ch == '"' {
3328                        closed = true;
3329                        break;
3330                    }
3331                    if ch == '\\' && index < format.len() {
3332                        let Some(escaped) = format[index..].chars().next() else {
3333                            return PostgresToCharFormat::Unsupported;
3334                        };
3335                        index += escaped.len_utf8();
3336                        Self::push_postgres_to_char_literal(&mut result, escaped);
3337                    } else {
3338                        Self::push_postgres_to_char_literal(&mut result, ch);
3339                    }
3340                }
3341                if !closed {
3342                    return PostgresToCharFormat::Unsupported;
3343                }
3344                continue;
3345            }
3346
3347            let remaining = &format[index..];
3348            let ch = remaining
3349                .chars()
3350                .next()
3351                .expect("format index should be on a character boundary");
3352            if matches!(ch, '9' | '0')
3353                || ["PR", "SG", "PL", "RN", "EEEE"].iter().any(|token| {
3354                    remaining
3355                        .get(..token.len())
3356                        .is_some_and(|prefix| prefix.eq_ignore_ascii_case(token))
3357                })
3358            {
3359                return PostgresToCharFormat::Numeric;
3360            }
3361
3362            let supported = SUPPORTED
3363                .iter()
3364                .filter(|(token, _, _)| format[index..].starts_with(token))
3365                .max_by_key(|(token, _, _)| token.len());
3366            let recognized_len =
3367                if format[index..].starts_with("FM") || format[index..].starts_with("TM") {
3368                    longest_token_at(format, index + 2).map(|len| len + 2)
3369                } else {
3370                    longest_token_at(format, index)
3371                };
3372
3373            if let Some((token, replacement, english_culture)) = supported {
3374                if recognized_len.is_some_and(|len| len > token.len()) {
3375                    return PostgresToCharFormat::Unsupported;
3376                }
3377                let end = index + token.len();
3378                if format[end..].starts_with("TH")
3379                    || format[end..].starts_with("th")
3380                    || format[end..].starts_with("SP")
3381                {
3382                    return PostgresToCharFormat::Unsupported;
3383                }
3384                result.push_str(replacement);
3385                requires_english_culture |= english_culture;
3386                index = end;
3387                continue;
3388            }
3389
3390            if recognized_len.is_some() || format[index..].starts_with("FX") {
3391                return PostgresToCharFormat::Unsupported;
3392            }
3393
3394            Self::push_postgres_to_char_literal(&mut result, ch);
3395            index += ch.len_utf8();
3396        }
3397
3398        PostgresToCharFormat::Temporal {
3399            strftime: result,
3400            requires_english_culture,
3401        }
3402    }
3403
3404    fn formatted_time_to_str_or_fallback(
3405        mut args: Vec<Expression>,
3406        original_name: &str,
3407    ) -> Result<Expression> {
3408        let this = args.remove(0);
3409        let format = args.remove(0);
3410        if let Some(format_string) = Self::literal_string(&format).map(str::to_owned) {
3411            if Self::is_explicitly_numeric_expression(&this) {
3412                return Ok(Expression::Function(Box::new(Function::new(
3413                    original_name.to_string(),
3414                    vec![this, format],
3415                ))));
3416            }
3417
3418            let (format_string, requires_english_culture) =
3419                match Self::classify_postgres_to_char_format(&format_string) {
3420                    PostgresToCharFormat::Temporal {
3421                        strftime,
3422                        requires_english_culture,
3423                    } => (strftime, requires_english_culture),
3424                    PostgresToCharFormat::Numeric | PostgresToCharFormat::Unsupported => {
3425                        return Ok(Expression::Function(Box::new(Function::new(
3426                            original_name.to_string(),
3427                            vec![this, format],
3428                        ))));
3429                    }
3430                };
3431
3432            Ok(Expression::TimeToStr(Box::new(
3433                crate::expressions::TimeToStr {
3434                    this: Box::new(this),
3435                    format: format_string,
3436                    culture: requires_english_culture
3437                        .then(|| Box::new(Expression::string("en-US"))),
3438                    zone: None,
3439                },
3440            )))
3441        } else {
3442            Ok(Expression::Function(Box::new(Function::new(
3443                original_name.to_string(),
3444                vec![this, format],
3445            ))))
3446        }
3447    }
3448
3449    fn transform_aggregate_function(
3450        &self,
3451        mut f: Box<crate::expressions::AggregateFunction>,
3452    ) -> Result<Expression> {
3453        let name_upper = f.name.to_uppercase();
3454        if matches!(
3455            name_upper.as_str(),
3456            "SUM"
3457                | "AVG"
3458                | "MIN"
3459                | "MAX"
3460                | "COUNT"
3461                | "COUNT_BIG"
3462                | "ANY_VALUE"
3463                | "APPROX_COUNT_DISTINCT"
3464                | "STDEV"
3465                | "STDEVP"
3466                | "VAR"
3467                | "VARP"
3468                | "BOOL_AND"
3469                | "BOOL_OR"
3470                | "LOGICAL_AND"
3471                | "LOGICAL_OR"
3472                | "BIT_AND"
3473                | "BIT_OR"
3474                | "BIT_XOR"
3475        ) {
3476            f.order_by.clear();
3477        }
3478
3479        match name_upper.as_str() {
3480            // GROUP_CONCAT -> STRING_AGG
3481            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3482                Function::new("STRING_AGG".to_string(), f.args),
3483            ))),
3484
3485            // LISTAGG -> STRING_AGG
3486            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3487                "STRING_AGG".to_string(),
3488                f.args,
3489            )))),
3490
3491            // ARRAY_AGG -> Not directly supported in SQL Server
3492            // Would need to use FOR XML PATH or STRING_AGG
3493            "ARRAY_AGG" if !f.args.is_empty() => {
3494                // Fall back to STRING_AGG (loses array semantics)
3495                Ok(Expression::Function(Box::new(Function::new(
3496                    "STRING_AGG".to_string(),
3497                    f.args,
3498                ))))
3499            }
3500
3501            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
3502            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
3503                let mut args = f.args;
3504                Self::transform_logical_aggregate(args.remove(0), f.filter, "MIN")
3505            }
3506            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
3507                let mut args = f.args;
3508                Self::transform_logical_aggregate(args.remove(0), f.filter, "MAX")
3509            }
3510
3511            // Pass through everything else
3512            _ => Ok(Expression::AggregateFunction(f)),
3513        }
3514    }
3515
3516    fn without_inert_ordering(
3517        mut aggregate: Box<crate::expressions::AggFunc>,
3518    ) -> Box<crate::expressions::AggFunc> {
3519        aggregate.order_by.clear();
3520        aggregate
3521    }
3522
3523    /// Transform CTEs to add auto-aliases to bare expressions in SELECT
3524    /// In TSQL, when a CTE doesn't have explicit column aliases, bare expressions
3525    /// in the SELECT need to be aliased
3526    fn transform_cte(&self, cte: Cte) -> Result<Expression> {
3527        Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
3528    }
3529
3530    /// Inner method to transform a CTE, returning the modified Cte struct
3531    fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
3532        // Only transform if the CTE doesn't have explicit column aliases
3533        // If it has column aliases like `WITH t(a, b) AS (...)`, we don't need to auto-alias
3534        if cte.columns.is_empty() {
3535            cte.this = self.qualify_derived_table_outputs(cte.this);
3536        }
3537        cte
3538    }
3539
3540    /// Transform Subqueries to add auto-aliases to bare expressions in SELECT
3541    /// In TSQL, when a subquery has a table alias but no column aliases,
3542    /// bare expressions need to be aliased
3543    fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
3544        // Only transform if the subquery has a table alias but no column aliases
3545        // e.g., `(SELECT 1) AS subq` needs aliasing, but `(SELECT 1) AS subq(a)` doesn't
3546        if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
3547            subquery.this = self.qualify_derived_table_outputs(subquery.this);
3548        }
3549        Ok(Expression::Subquery(Box::new(subquery)))
3550    }
3551
3552    /// Add aliases to bare (unaliased) expressions in a SELECT statement
3553    /// This transforms expressions like `SELECT 1` into `SELECT 1 AS [1]`
3554    /// BUT only when the SELECT has no FROM clause (i.e., it's a value expression)
3555    fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
3556        match expr {
3557            Expression::Select(mut select) => {
3558                // Only auto-alias if the SELECT has NO from clause
3559                // If there's a FROM clause, column references already have names from the source tables
3560                let has_from = select.from.is_some();
3561                if !has_from {
3562                    select.expressions = select
3563                        .expressions
3564                        .into_iter()
3565                        .map(|e| self.maybe_alias_expression(e))
3566                        .collect();
3567                }
3568                Expression::Select(select)
3569            }
3570            // For UNION/INTERSECT/EXCEPT, transform the first SELECT
3571            Expression::Union(mut u) => {
3572                let left = std::mem::replace(&mut u.left, Expression::Null(Null));
3573                u.left = self.qualify_derived_table_outputs(left);
3574                Expression::Union(u)
3575            }
3576            Expression::Intersect(mut i) => {
3577                let left = std::mem::replace(&mut i.left, Expression::Null(Null));
3578                i.left = self.qualify_derived_table_outputs(left);
3579                Expression::Intersect(i)
3580            }
3581            Expression::Except(mut e) => {
3582                let left = std::mem::replace(&mut e.left, Expression::Null(Null));
3583                e.left = self.qualify_derived_table_outputs(left);
3584                Expression::Except(e)
3585            }
3586            // Already wrapped in a Subquery (nested), transform the inner
3587            Expression::Subquery(mut s) => {
3588                s.this = self.qualify_derived_table_outputs(s.this);
3589                Expression::Subquery(s)
3590            }
3591            // Pass through anything else
3592            other => other,
3593        }
3594    }
3595
3596    /// Add an alias to a bare expression if needed
3597    /// Returns the expression unchanged if it already has an alias or is a star
3598    /// NOTE: This is only called for SELECTs without a FROM clause, so all bare
3599    /// expressions (including identifiers and columns) need to be aliased.
3600    fn maybe_alias_expression(&self, expr: Expression) -> Expression {
3601        match &expr {
3602            // Already has an alias, leave it alone
3603            Expression::Alias(_) => expr,
3604            // Multiple aliases, leave it alone
3605            Expression::Aliases(_) => expr,
3606            // Star (including qualified star like t.*) doesn't need an alias
3607            Expression::Star(_) => expr,
3608            // When there's no FROM clause (which is the only case when this method is called),
3609            // we need to alias columns and identifiers too since they're standalone values
3610            // that need explicit names for the derived table output.
3611            // Everything else (literals, functions, columns, identifiers, etc.) needs an alias
3612            _ => {
3613                if let Some(output_name) = self.get_output_name(&expr) {
3614                    Expression::Alias(Box::new(Alias {
3615                        this: expr,
3616                        alias: Identifier {
3617                            name: output_name,
3618                            quoted: true, // Force quoting for TSQL bracket syntax
3619                            trailing_comments: Vec::new(),
3620                            span: None,
3621                        },
3622                        column_aliases: Vec::new(),
3623                        alias_explicit_as: false,
3624                        alias_keyword: None,
3625                        pre_alias_comments: Vec::new(),
3626                        trailing_comments: Vec::new(),
3627                        inferred_type: None,
3628                    }))
3629                } else {
3630                    // No output name, leave as-is (shouldn't happen for valid expressions)
3631                    expr
3632                }
3633            }
3634        }
3635    }
3636
3637    /// Get the "output name" of an expression for auto-aliasing
3638    /// For literals, this is the literal value
3639    /// For columns, this is the column name
3640    fn get_output_name(&self, expr: &Expression) -> Option<String> {
3641        match expr {
3642            // Literals - use the literal value as the name
3643            Expression::Literal(lit) => match lit.as_ref() {
3644                Literal::Number(n) => Some(n.clone()),
3645                Literal::String(s) => Some(s.clone()),
3646                Literal::HexString(h) => Some(format!("0x{}", h)),
3647                Literal::HexNumber(h) => Some(format!("0x{}", h)),
3648                Literal::BitString(b) => Some(format!("b{}", b)),
3649                Literal::ByteString(b) => Some(format!("b'{}'", b)),
3650                Literal::NationalString(s) => Some(format!("N'{}'", s)),
3651                Literal::Date(d) => Some(d.clone()),
3652                Literal::Time(t) => Some(t.clone()),
3653                Literal::Timestamp(ts) => Some(ts.clone()),
3654                Literal::Datetime(dt) => Some(dt.clone()),
3655                Literal::TripleQuotedString(s, _) => Some(s.clone()),
3656                Literal::EscapeString(s) => Some(s.clone()),
3657                Literal::DollarString(s) => Some(s.clone()),
3658                Literal::RawString(s) => Some(s.clone()),
3659            },
3660            // Columns - use the column name
3661            Expression::Column(col) => Some(col.name.name.clone()),
3662            // Identifiers - use the identifier name
3663            Expression::Identifier(ident) => Some(ident.name.clone()),
3664            // Boolean literals
3665            Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
3666            // NULL
3667            Expression::Null(_) => Some("NULL".to_string()),
3668            // For functions, use the function name as a fallback
3669            Expression::Function(f) => Some(f.name.clone()),
3670            // For aggregates, use the function name
3671            Expression::AggregateFunction(f) => Some(f.name.clone()),
3672            // For other expressions, generate a generic name
3673            _ => Some(format!("_col_{}", 0)),
3674        }
3675    }
3676
3677    /// Helper to uppercase the first argument if it's an identifier or column (for DATEDIFF, DATEADD units)
3678    fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
3679        use crate::expressions::Identifier;
3680        if !args.is_empty() {
3681            match &args[0] {
3682                Expression::Identifier(id) => {
3683                    args[0] = Expression::Identifier(Identifier {
3684                        name: id.name.to_uppercase(),
3685                        quoted: id.quoted,
3686                        trailing_comments: id.trailing_comments.clone(),
3687                        span: None,
3688                    });
3689                }
3690                Expression::Var(v) => {
3691                    args[0] = Expression::Identifier(Identifier {
3692                        name: v.this.to_uppercase(),
3693                        quoted: false,
3694                        trailing_comments: Vec::new(),
3695                        span: None,
3696                    });
3697                }
3698                Expression::Column(col) if col.table.is_none() => {
3699                    args[0] = Expression::Identifier(Identifier {
3700                        name: col.name.name.to_uppercase(),
3701                        quoted: col.name.quoted,
3702                        trailing_comments: col.name.trailing_comments.clone(),
3703                        span: None,
3704                    });
3705                }
3706                _ => {}
3707            }
3708        }
3709        args
3710    }
3711}
3712
3713#[cfg(test)]
3714mod tests {
3715    use super::*;
3716    use crate::dialects::Dialect;
3717
3718    fn transpile_to_tsql(sql: &str) -> String {
3719        let dialect = Dialect::get(DialectType::Generic);
3720        let result = dialect
3721            .transpile(sql, DialectType::TSQL)
3722            .expect("Transpile failed");
3723        result[0].clone()
3724    }
3725
3726    #[test]
3727    fn test_nvl_to_isnull() {
3728        let result = transpile_to_tsql("SELECT NVL(a, b)");
3729        assert!(
3730            result.contains("ISNULL"),
3731            "Expected ISNULL, got: {}",
3732            result
3733        );
3734    }
3735
3736    #[test]
3737    fn test_coalesce_to_isnull() {
3738        let result = transpile_to_tsql("SELECT COALESCE(a, b)");
3739        assert!(
3740            result.contains("ISNULL"),
3741            "Expected ISNULL, got: {}",
3742            result
3743        );
3744    }
3745
3746    #[test]
3747    fn test_basic_select() {
3748        let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
3749        assert!(result.contains("SELECT"));
3750        assert!(result.contains("FROM users"));
3751    }
3752
3753    #[test]
3754    fn test_length_to_len() {
3755        let result = transpile_to_tsql("SELECT LENGTH(name)");
3756        assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
3757    }
3758
3759    #[test]
3760    fn test_issue_374_tsql_parse_then_generate_uses_len() {
3761        let sql = "SELECT LEN(table.col1) - LEN(table.col2) FROM table";
3762        let ast = Dialect::get(DialectType::TSQL)
3763            .parse(sql)
3764            .expect("T-SQL should parse");
3765        let expression = &ast[0];
3766
3767        for target in [DialectType::TSQL, DialectType::Fabric] {
3768            let generated = Dialect::get(target)
3769                .generate(expression)
3770                .expect("AST should generate");
3771            assert_eq!(generated, sql, "failed for target {target:?}");
3772        }
3773
3774        let standard_sql = "SELECT LENGTH(table.col1) - LENGTH(table.col2) FROM table";
3775        for target in [DialectType::Generic, DialectType::PostgreSQL] {
3776            let generated = Dialect::get(target)
3777                .generate(expression)
3778                .expect("AST should generate");
3779            assert_eq!(generated, standard_sql, "failed for target {target:?}");
3780        }
3781    }
3782
3783    #[test]
3784    fn test_now_to_getdate() {
3785        let result = transpile_to_tsql("SELECT NOW()");
3786        assert!(
3787            result.contains("GETDATE"),
3788            "Expected GETDATE, got: {}",
3789            result
3790        );
3791    }
3792
3793    #[test]
3794    fn test_group_concat_to_string_agg() {
3795        let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
3796        assert!(
3797            result.contains("STRING_AGG"),
3798            "Expected STRING_AGG, got: {}",
3799            result
3800        );
3801    }
3802
3803    #[test]
3804    fn test_listagg_to_string_agg() {
3805        let result = transpile_to_tsql("SELECT LISTAGG(name)");
3806        assert!(
3807            result.contains("STRING_AGG"),
3808            "Expected STRING_AGG, got: {}",
3809            result
3810        );
3811    }
3812
3813    #[test]
3814    fn test_ln_to_log() {
3815        let result = transpile_to_tsql("SELECT LN(x)");
3816        assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
3817    }
3818
3819    #[test]
3820    fn test_stddev_to_stdev() {
3821        let result = transpile_to_tsql("SELECT STDDEV(x)");
3822        assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
3823    }
3824
3825    #[test]
3826    fn test_bracket_identifiers() {
3827        // SQL Server uses square brackets for identifiers
3828        let dialect = Dialect::get(DialectType::TSQL);
3829        let config = dialect.generator_config();
3830        assert_eq!(config.identifier_quote, '[');
3831    }
3832
3833    #[test]
3834    fn test_json_query_isnull_wrapper_simple() {
3835        // JSON_QUERY with two args needs ISNULL wrapper when transpiling to TSQL
3836        let dialect = Dialect::get(DialectType::TSQL);
3837        let result = dialect
3838            .transpile(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
3839            .expect("transpile failed");
3840        assert!(
3841            result[0].contains("ISNULL"),
3842            "JSON_QUERY should be wrapped with ISNULL: {}",
3843            result[0]
3844        );
3845    }
3846
3847    #[test]
3848    fn test_json_query_isnull_wrapper_nested() {
3849        let dialect = Dialect::get(DialectType::TSQL);
3850        let result = dialect
3851            .transpile(
3852                r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
3853                DialectType::TSQL,
3854            )
3855            .expect("transpile failed");
3856        let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
3857        assert_eq!(
3858            result[0], expected,
3859            "JSON_QUERY should be wrapped with ISNULL"
3860        );
3861    }
3862}