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