Skip to main content

polyglot_sql/dialects/
tsql.rs

1//! T-SQL (SQL Server) Dialect
2//!
3//! SQL Server-specific transformations based on sqlglot patterns.
4//! Key differences:
5//! - TOP instead of LIMIT
6//! - ISNULL instead of COALESCE (though COALESCE also works)
7//! - Square brackets for identifiers
8//! - + for string concatenation
9//! - CONVERT vs CAST
10//! - CROSS APPLY / OUTER APPLY for lateral joins
11//! - Different date functions (GETDATE, DATEADD, DATEDIFF, DATENAME)
12
13use super::{DialectImpl, DialectType};
14use crate::error::Result;
15use crate::expressions::{
16    Alias, BinaryOp, Cast, Column, Cte, DataType, Exists, Expression, From, Function, Identifier,
17    In, Join, JoinKind, LikeOp, Literal, Null, Over, Paren, QuantifiedExpr, QuantifiedOp, Select,
18    Star, StringAggFunc, Subquery, TrimFunc, TrimPosition, Tuple, UnaryFunc, Values, Where,
19    WindowFunction,
20};
21#[cfg(feature = "generate")]
22use crate::generator::GeneratorConfig;
23use crate::tokens::TokenizerConfig;
24use std::collections::HashMap;
25
26/// T-SQL (SQL Server) dialect
27pub struct TSQLDialect;
28
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 scalar predicates for
701            // these shapes because quantified comparisons only accept subqueries.
702            Expression::Any(q) => {
703                Ok(Self::lower_scalar_array_quantifier(&q, true).unwrap_or(Expression::Any(q)))
704            }
705            Expression::All(q) => {
706                Ok(Self::lower_scalar_array_quantifier(&q, false).unwrap_or(Expression::All(q)))
707            }
708
709            // Pass through everything else
710            _ => Ok(expr),
711        }
712    }
713}
714
715#[cfg(feature = "transpile")]
716impl TSQLDialect {
717    fn getdate() -> Expression {
718        Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])))
719    }
720
721    fn cast_getdate_to(to: DataType) -> Expression {
722        Expression::Cast(Box::new(Cast {
723            this: Self::getdate(),
724            to,
725            trailing_comments: Vec::new(),
726            double_colon_syntax: false,
727            format: None,
728            default: None,
729            inferred_type: None,
730        }))
731    }
732
733    fn cast(this: Expression, to: DataType) -> Expression {
734        Expression::Cast(Box::new(Cast {
735            this,
736            to,
737            trailing_comments: Vec::new(),
738            double_colon_syntax: false,
739            format: None,
740            default: None,
741            inferred_type: None,
742        }))
743    }
744
745    fn function(name: impl Into<String>, args: Vec<Expression>) -> Expression {
746        Expression::Function(Box::new(Function::new(name, args)))
747    }
748
749    fn make_time(mut args: Vec<Expression>) -> Expression {
750        let seconds = args.pop().expect("MAKE_TIME has three arguments");
751        let minute = args.pop().expect("MAKE_TIME has three arguments");
752        let hour = args.pop().expect("MAKE_TIME has three arguments");
753
754        if let Some((whole_seconds, microseconds)) = Self::literal_time_parts(&seconds) {
755            let (fractions, precision) = if microseconds == 0 {
756                (Expression::number(0), Expression::number(0))
757            } else {
758                (Expression::number(microseconds), Expression::number(6))
759            };
760
761            return Self::function(
762                "TIMEFROMPARTS",
763                vec![
764                    hour,
765                    minute,
766                    Expression::number(whole_seconds),
767                    fractions,
768                    precision,
769                ],
770            );
771        }
772
773        // TIMEFROMPARTS requires integral seconds and fractions. Round the
774        // PostgreSQL double-precision seconds argument to microseconds before
775        // splitting the integer value, without dropping fractional seconds.
776        let rounded_microseconds = Self::cast(
777            Self::function(
778                "ROUND",
779                vec![
780                    Expression::Mul(Box::new(BinaryOp::new(
781                        seconds,
782                        Expression::number(1_000_000),
783                    ))),
784                    Expression::number(0),
785                ],
786            ),
787            DataType::BigInt { length: None },
788        );
789
790        Self::function(
791            "TIMEFROMPARTS",
792            vec![
793                hour,
794                minute,
795                Expression::Div(Box::new(BinaryOp::new(
796                    rounded_microseconds.clone(),
797                    Expression::number(1_000_000),
798                ))),
799                Expression::Mod(Box::new(BinaryOp::new(
800                    rounded_microseconds,
801                    Expression::number(1_000_000),
802                ))),
803                Expression::number(6),
804            ],
805        )
806    }
807
808    fn literal_time_parts(expr: &Expression) -> Option<(i64, i64)> {
809        let value = match expr {
810            Expression::Literal(lit) => match lit.as_ref() {
811                Literal::Number(value) => value.parse::<f64>().ok()?,
812                _ => return None,
813            },
814            Expression::Paren(paren) => return Self::literal_time_parts(&paren.this),
815            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
816                if Self::is_numeric_data_type(&cast.to) =>
817            {
818                let parts = Self::literal_time_parts(&cast.this);
819                return match (&cast.to, parts) {
820                    // Zero is unchanged by every numeric cast. Other literal
821                    // casts are folded only when their floating-point
822                    // semantics cannot truncate the seconds value.
823                    (_, Some((0, 0))) => Some((0, 0)),
824                    (DataType::Float { .. } | DataType::Double { .. }, parts) => parts,
825                    _ => None,
826                };
827            }
828            _ => return None,
829        };
830
831        if !value.is_finite() || value < 0.0 || value > i64::MAX as f64 / 1_000_000.0 {
832            return None;
833        }
834
835        let total_microseconds = (value * 1_000_000.0).round() as i64;
836        Some((
837            total_microseconds / 1_000_000,
838            total_microseconds % 1_000_000,
839        ))
840    }
841
842    fn lower(this: Expression) -> Expression {
843        Expression::Lower(Box::new(UnaryFunc::new(this)))
844    }
845
846    fn tsql_convert(to: DataType, expression: Expression, style: Option<i64>) -> Expression {
847        let mut args = vec![Expression::DataType(to), expression];
848        if let Some(style) = style {
849            args.push(Expression::number(style));
850        }
851        Self::function("CONVERT", args)
852    }
853
854    fn tsql_hex_text(expression: Expression, varchar_type: DataType) -> Expression {
855        Self::lower(Self::tsql_convert(varchar_type, expression, Some(2)))
856    }
857
858    fn tsql_hex_from_varbinary(expression: Expression) -> Expression {
859        Self::tsql_hex_text(
860            Self::cast(
861                expression,
862                DataType::Custom {
863                    name: "VARBINARY(MAX)".to_string(),
864                },
865            ),
866            DataType::Text,
867        )
868    }
869
870    fn tsql_postgres_to_hex(expression: Expression) -> Expression {
871        let hex = Self::tsql_hex_from_varbinary(expression);
872        let without_leading_zeroes = Self::function("LTRIM", vec![hex, Expression::string("0")]);
873        let non_empty = Self::function(
874            "NULLIF",
875            vec![without_leading_zeroes, Expression::string("")],
876        );
877        Self::function("ISNULL", vec![non_empty, Expression::string("0")])
878    }
879
880    fn tsql_md5_hex(expression: Expression) -> Expression {
881        let hashbytes = Self::function("HASHBYTES", vec![Expression::string("MD5"), expression]);
882        Self::tsql_hex_text(
883            hashbytes,
884            DataType::VarChar {
885                length: Some(32),
886                parenthesized_length: false,
887            },
888        )
889    }
890
891    fn overlay_to_stuff(f: crate::expressions::OverlayFunc) -> Expression {
892        let length = f
893            .length
894            .unwrap_or_else(|| Self::function("LEN", vec![f.replacement.clone()]));
895        Self::function("STUFF", vec![f.this, f.from, length, f.replacement])
896    }
897
898    fn starts_with_predicate(this: Expression, prefix: Expression) -> Expression {
899        let prefix_len = Self::function("LEN", vec![prefix.clone()]);
900        let left_prefix = Self::function("LEFT", vec![this, prefix_len]);
901        Self::eq(left_prefix, prefix)
902    }
903
904    fn to_number_or_fallback(f: crate::expressions::ToNumber) -> Expression {
905        let crate::expressions::ToNumber {
906            this,
907            format,
908            nlsparam,
909            precision,
910            scale,
911            safe,
912            safe_name,
913        } = f;
914
915        if nlsparam.is_none()
916            && precision.is_none()
917            && scale.is_none()
918            && safe.is_none()
919            && safe_name.is_none()
920        {
921            if let Some(format) = format.as_deref() {
922                if let Some(scale) = Self::simple_to_number_scale(format) {
923                    return Self::function(
924                        "TRY_CONVERT",
925                        vec![
926                            Expression::DataType(DataType::Decimal {
927                                precision: Some(18),
928                                scale: Some(scale),
929                            }),
930                            *this,
931                        ],
932                    );
933                }
934            }
935        }
936
937        Expression::ToNumber(Box::new(crate::expressions::ToNumber {
938            this,
939            format,
940            nlsparam,
941            precision,
942            scale,
943            safe,
944            safe_name,
945        }))
946    }
947
948    fn simple_to_number_scale(format: &Expression) -> Option<u32> {
949        let format = Self::literal_string(format)?;
950        let format = format.strip_prefix("FM").unwrap_or(format);
951        let mut saw_digit = false;
952        let mut saw_decimal = false;
953        let mut scale = 0u32;
954
955        for ch in format.chars() {
956            match ch {
957                '9' | '0' => {
958                    saw_digit = true;
959                    if saw_decimal {
960                        scale = scale.checked_add(1)?;
961                    }
962                }
963                '.' if !saw_decimal => saw_decimal = true,
964                // Group separators and positional whitespace have format-model
965                // semantics that a raw TRY_CONVERT cannot reproduce.
966                ',' | ' ' => return None,
967                _ => return None,
968            }
969        }
970
971        saw_digit.then_some(scale)
972    }
973
974    fn binary(
975        left: Expression,
976        right: Expression,
977        op: fn(Box<BinaryOp>) -> Expression,
978    ) -> Expression {
979        op(Box::new(BinaryOp {
980            left,
981            right,
982            left_comments: Vec::new(),
983            operator_comments: Vec::new(),
984            trailing_comments: Vec::new(),
985            inferred_type: None,
986        }))
987    }
988
989    fn eq(left: Expression, right: Expression) -> Expression {
990        Self::binary(left, right, Expression::Eq)
991    }
992
993    fn or(left: Expression, right: Expression) -> Expression {
994        Self::binary(left, right, Expression::Or)
995    }
996
997    fn not(this: Expression) -> Expression {
998        Expression::Not(Box::new(crate::expressions::UnaryOp {
999            this,
1000            inferred_type: None,
1001        }))
1002    }
1003
1004    fn is_null(this: Expression) -> Expression {
1005        Expression::IsNull(Box::new(crate::expressions::IsNull {
1006            this,
1007            not: false,
1008            postfix_form: false,
1009        }))
1010    }
1011
1012    fn paren(this: Expression) -> Expression {
1013        Expression::Paren(Box::new(Paren {
1014            this,
1015            trailing_comments: Vec::new(),
1016        }))
1017    }
1018
1019    fn boolean_test_case_for_predicate(
1020        predicate: Expression,
1021        test_true: bool,
1022        negated: bool,
1023    ) -> Expression {
1024        let condition = match (test_true, negated) {
1025            (true, false) => predicate,
1026            (false, false) => Self::not(predicate),
1027            (true, true) => {
1028                return Expression::Case(Box::new(crate::expressions::Case {
1029                    operand: None,
1030                    whens: vec![(predicate, Expression::number(0))],
1031                    else_: Some(Expression::number(1)),
1032                    comments: Vec::new(),
1033                    inferred_type: None,
1034                }))
1035            }
1036            (false, true) => {
1037                return Expression::Case(Box::new(crate::expressions::Case {
1038                    operand: None,
1039                    whens: vec![(Self::not(predicate), Expression::number(0))],
1040                    else_: Some(Expression::number(1)),
1041                    comments: Vec::new(),
1042                    inferred_type: None,
1043                }))
1044            }
1045        };
1046
1047        Expression::Case(Box::new(crate::expressions::Case {
1048            operand: None,
1049            whens: vec![(condition, Expression::number(1))],
1050            else_: Some(Expression::number(0)),
1051            comments: Vec::new(),
1052            inferred_type: None,
1053        }))
1054    }
1055
1056    fn boolean_test_predicate(operand: Expression, test_true: bool, negated: bool) -> Expression {
1057        if Self::is_boolean_predicate_operand(&operand) {
1058            return match (test_true, negated) {
1059                (true, false) => operand,
1060                (false, false) => Self::not(operand),
1061                _ => Self::eq(
1062                    Self::boolean_test_case_for_predicate(operand, test_true, negated),
1063                    Expression::number(1),
1064                ),
1065            };
1066        }
1067
1068        match (test_true, negated) {
1069            (true, false) => Self::eq(operand, Expression::number(1)),
1070            (false, false) => Self::eq(operand, Expression::number(0)),
1071            (true, true) => Self::or(
1072                Self::eq(operand.clone(), Expression::number(0)),
1073                Self::is_null(operand),
1074            ),
1075            (false, true) => Self::or(
1076                Self::eq(operand.clone(), Expression::number(1)),
1077                Self::is_null(operand),
1078            ),
1079        }
1080    }
1081
1082    fn is_boolean_predicate_operand(expr: &Expression) -> bool {
1083        match expr {
1084            Expression::Paren(paren) => Self::is_boolean_predicate_operand(&paren.this),
1085            Expression::Eq(_)
1086            | Expression::Neq(_)
1087            | Expression::Lt(_)
1088            | Expression::Lte(_)
1089            | Expression::Gt(_)
1090            | Expression::Gte(_)
1091            | Expression::Is(_)
1092            | Expression::IsNull(_)
1093            | Expression::IsTrue(_)
1094            | Expression::IsFalse(_)
1095            | Expression::Like(_)
1096            | Expression::ILike(_)
1097            | Expression::SimilarTo(_)
1098            | Expression::Glob(_)
1099            | Expression::RegexpLike(_)
1100            | Expression::In(_)
1101            | Expression::Between(_)
1102            | Expression::Exists(_)
1103            | Expression::And(_)
1104            | Expression::Or(_)
1105            | Expression::Not(_)
1106            | Expression::Any(_)
1107            | Expression::All(_)
1108            | Expression::EqualNull(_) => true,
1109            _ => false,
1110        }
1111    }
1112
1113    fn scalar_array_comparison_values(expr: &Expression) -> Option<Vec<Expression>> {
1114        let (mut values, element_type) = Self::scalar_array_comparison_values_inner(expr)?;
1115        if let Some(to) = element_type {
1116            values = values
1117                .into_iter()
1118                .map(|value| Self::cast_scalar_array_comparison_value(value, to.clone()))
1119                .collect();
1120        }
1121        Some(values)
1122    }
1123
1124    fn lower_scalar_array_quantifier(
1125        quantified: &QuantifiedExpr,
1126        is_any: bool,
1127    ) -> Option<Expression> {
1128        let op = quantified.op.as_ref()?;
1129        let expressions = Self::scalar_array_comparison_values(&quantified.subquery)?;
1130
1131        if expressions.is_empty() {
1132            return Some(Self::eq(
1133                Expression::number(1),
1134                Expression::number(if is_any { 0 } else { 1 }),
1135            ));
1136        }
1137
1138        if is_any && matches!(op, QuantifiedOp::Eq) {
1139            return Some(Self::in_list(quantified.this.clone(), expressions, false));
1140        }
1141
1142        if !is_any && matches!(op, QuantifiedOp::Neq) {
1143            return Some(Self::in_list(quantified.this.clone(), expressions, true));
1144        }
1145
1146        let mut comparisons = expressions
1147            .into_iter()
1148            .map(|expression| Self::quantified_comparison(quantified.this.clone(), expression, op));
1149        let first = comparisons.next()?;
1150        let combined = comparisons.fold(first, |left, right| {
1151            if is_any {
1152                Expression::Or(Box::new(BinaryOp::new(left, right)))
1153            } else {
1154                Expression::And(Box::new(BinaryOp::new(left, right)))
1155            }
1156        });
1157
1158        Some(Self::paren(combined))
1159    }
1160
1161    fn in_list(this: Expression, expressions: Vec<Expression>, not: bool) -> Expression {
1162        Expression::In(Box::new(In {
1163            this,
1164            expressions,
1165            query: None,
1166            not,
1167            global: false,
1168            unnest: None,
1169            is_field: false,
1170        }))
1171    }
1172
1173    fn quantified_comparison(left: Expression, right: Expression, op: &QuantifiedOp) -> Expression {
1174        let binary = Box::new(BinaryOp::new(left, right));
1175        match op {
1176            QuantifiedOp::Eq => Expression::Eq(binary),
1177            QuantifiedOp::Neq => Expression::Neq(binary),
1178            QuantifiedOp::Lt => Expression::Lt(binary),
1179            QuantifiedOp::Lte => Expression::Lte(binary),
1180            QuantifiedOp::Gt => Expression::Gt(binary),
1181            QuantifiedOp::Gte => Expression::Gte(binary),
1182        }
1183    }
1184
1185    fn scalar_array_comparison_values_inner(
1186        expr: &Expression,
1187    ) -> Option<(Vec<Expression>, Option<DataType>)> {
1188        match expr {
1189            Expression::ArrayFunc(a) => Some((a.expressions.clone(), None)),
1190            Expression::Array(a) => Some((a.expressions.clone(), None)),
1191            Expression::Tuple(t) => Some((t.expressions.clone(), None)),
1192            Expression::Paren(p) => Self::scalar_array_comparison_values_inner(&p.this),
1193            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
1194                let DataType::Array { element_type, .. } = &c.to else {
1195                    return None;
1196                };
1197                let (values, _) = Self::scalar_array_comparison_values_inner(&c.this)?;
1198                Some((values, Some((**element_type).clone())))
1199            }
1200            _ => None,
1201        }
1202    }
1203
1204    fn cast_scalar_array_comparison_value(value: Expression, to: DataType) -> Expression {
1205        if matches!(&value, Expression::Cast(c) if c.to == to) {
1206            return value;
1207        }
1208
1209        Expression::Cast(Box::new(Cast {
1210            this: value,
1211            to,
1212            trailing_comments: Vec::new(),
1213            double_colon_syntax: false,
1214            format: None,
1215            default: None,
1216            inferred_type: None,
1217        }))
1218    }
1219
1220    fn normalize_frame_incompatible_window_functions(select: &mut Select) {
1221        let window_map: HashMap<String, Over> = select
1222            .windows
1223            .as_ref()
1224            .map(|windows| {
1225                windows
1226                    .iter()
1227                    .map(|window| (window.name.name.to_lowercase(), window.spec.clone()))
1228                    .collect()
1229            })
1230            .unwrap_or_default();
1231
1232        for expr in &mut select.expressions {
1233            Self::normalize_frame_incompatible_window_expr(expr, &window_map);
1234        }
1235
1236        if let Some(order_by) = &mut select.order_by {
1237            for ordered in &mut order_by.expressions {
1238                Self::normalize_frame_incompatible_window_expr(&mut ordered.this, &window_map);
1239            }
1240        }
1241
1242        if let Some(qualify) = &mut select.qualify {
1243            Self::normalize_frame_incompatible_window_expr(&mut qualify.this, &window_map);
1244        }
1245    }
1246
1247    fn normalize_frame_incompatible_window_expr(
1248        expr: &mut Expression,
1249        window_map: &HashMap<String, Over>,
1250    ) {
1251        match expr {
1252            Expression::WindowFunction(wf) => {
1253                Self::normalize_frame_incompatible_window_expr(&mut wf.this, window_map);
1254
1255                if !Self::is_tsql_frame_incompatible_window_function(&wf.this) {
1256                    return;
1257                }
1258
1259                wf.over.frame = None;
1260
1261                let Some(window_name) = wf.over.window_name.clone() else {
1262                    return;
1263                };
1264                let Some(named_spec) =
1265                    Self::resolve_named_window_spec(&window_name.name, window_map, &mut Vec::new())
1266                else {
1267                    return;
1268                };
1269
1270                if named_spec.frame.is_none() {
1271                    return;
1272                }
1273
1274                if wf.over.partition_by.is_empty() {
1275                    wf.over.partition_by = named_spec.partition_by;
1276                }
1277                if wf.over.order_by.is_empty() {
1278                    wf.over.order_by = named_spec.order_by;
1279                }
1280                wf.over.window_name = None;
1281                wf.over.frame = None;
1282            }
1283            Expression::Alias(alias) => {
1284                Self::normalize_frame_incompatible_window_expr(&mut alias.this, window_map);
1285            }
1286            Expression::Paren(paren) => {
1287                Self::normalize_frame_incompatible_window_expr(&mut paren.this, window_map);
1288            }
1289            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
1290                Self::normalize_frame_incompatible_window_expr(&mut cast.this, window_map);
1291            }
1292            Expression::Function(function) => {
1293                for arg in &mut function.args {
1294                    Self::normalize_frame_incompatible_window_expr(arg, window_map);
1295                }
1296            }
1297            Expression::Case(case) => {
1298                if let Some(operand) = &mut case.operand {
1299                    Self::normalize_frame_incompatible_window_expr(operand, window_map);
1300                }
1301                for (condition, result) in &mut case.whens {
1302                    Self::normalize_frame_incompatible_window_expr(condition, window_map);
1303                    Self::normalize_frame_incompatible_window_expr(result, window_map);
1304                }
1305                if let Some(else_expr) = &mut case.else_ {
1306                    Self::normalize_frame_incompatible_window_expr(else_expr, window_map);
1307                }
1308            }
1309            Expression::And(op)
1310            | Expression::Or(op)
1311            | Expression::Add(op)
1312            | Expression::Sub(op)
1313            | Expression::Mul(op)
1314            | Expression::Div(op)
1315            | Expression::Mod(op)
1316            | Expression::Eq(op)
1317            | Expression::Neq(op)
1318            | Expression::Lt(op)
1319            | Expression::Lte(op)
1320            | Expression::Gt(op)
1321            | Expression::Gte(op)
1322            | Expression::Match(op)
1323            | Expression::BitwiseAnd(op)
1324            | Expression::BitwiseOr(op)
1325            | Expression::BitwiseXor(op)
1326            | Expression::Concat(op)
1327            | Expression::Adjacent(op)
1328            | Expression::TsMatch(op)
1329            | Expression::PropertyEQ(op)
1330            | Expression::ArrayContainsAll(op)
1331            | Expression::ArrayContainedBy(op)
1332            | Expression::ArrayOverlaps(op)
1333            | Expression::JSONBContainsAllTopKeys(op)
1334            | Expression::JSONBContainsAnyTopKeys(op)
1335            | Expression::JSONBDeleteAtPath(op)
1336            | Expression::ExtendsLeft(op)
1337            | Expression::ExtendsRight(op)
1338            | Expression::Is(op)
1339            | Expression::MemberOf(op) => {
1340                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
1341                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
1342            }
1343            Expression::Like(op) | Expression::ILike(op) => {
1344                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
1345                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
1346                if let Some(escape) = &mut op.escape {
1347                    Self::normalize_frame_incompatible_window_expr(escape, window_map);
1348                }
1349            }
1350            Expression::Not(op) | Expression::Neg(op) | Expression::BitwiseNot(op) => {
1351                Self::normalize_frame_incompatible_window_expr(&mut op.this, window_map);
1352            }
1353            Expression::In(in_expr) => {
1354                Self::normalize_frame_incompatible_window_expr(&mut in_expr.this, window_map);
1355                for value in &mut in_expr.expressions {
1356                    Self::normalize_frame_incompatible_window_expr(value, window_map);
1357                }
1358            }
1359            Expression::Between(between) => {
1360                Self::normalize_frame_incompatible_window_expr(&mut between.this, window_map);
1361                Self::normalize_frame_incompatible_window_expr(&mut between.low, window_map);
1362                Self::normalize_frame_incompatible_window_expr(&mut between.high, window_map);
1363            }
1364            Expression::IsNull(is_null) => {
1365                Self::normalize_frame_incompatible_window_expr(&mut is_null.this, window_map);
1366            }
1367            Expression::IsTrue(is_true) | Expression::IsFalse(is_true) => {
1368                Self::normalize_frame_incompatible_window_expr(&mut is_true.this, window_map);
1369            }
1370            _ => {}
1371        }
1372    }
1373
1374    fn is_tsql_frame_incompatible_window_function(expr: &Expression) -> bool {
1375        matches!(
1376            expr,
1377            Expression::RowNumber(_)
1378                | Expression::Rank(_)
1379                | Expression::DenseRank(_)
1380                | Expression::NTile(_)
1381                | Expression::Ntile(_)
1382                | Expression::Lead(_)
1383                | Expression::Lag(_)
1384                | Expression::PercentRank(_)
1385                | Expression::CumeDist(_)
1386        )
1387    }
1388
1389    fn resolve_named_window_spec(
1390        name: &str,
1391        window_map: &HashMap<String, Over>,
1392        seen: &mut Vec<String>,
1393    ) -> Option<Over> {
1394        let key = name.to_lowercase();
1395        if seen.iter().any(|seen_name| seen_name == &key) {
1396            return None;
1397        }
1398
1399        let named_spec = window_map.get(&key)?.clone();
1400        seen.push(key);
1401
1402        let mut resolved = if let Some(base_window) = &named_spec.window_name {
1403            Self::resolve_named_window_spec(&base_window.name, window_map, seen)
1404                .unwrap_or_else(Self::empty_over)
1405        } else {
1406            Self::empty_over()
1407        };
1408
1409        if !named_spec.partition_by.is_empty() {
1410            resolved.partition_by = named_spec.partition_by;
1411        }
1412        if !named_spec.order_by.is_empty() {
1413            resolved.order_by = named_spec.order_by;
1414        }
1415        if named_spec.frame.is_some() {
1416            resolved.frame = named_spec.frame;
1417        }
1418
1419        Some(resolved)
1420    }
1421
1422    fn empty_over() -> Over {
1423        Over {
1424            window_name: None,
1425            partition_by: Vec::new(),
1426            order_by: Vec::new(),
1427            frame: None,
1428            alias: None,
1429        }
1430    }
1431
1432    const LATERAL_WRAPPER_SOURCE_ALIAS: &'static str = "_polyglot_lateral_source";
1433    const LATERAL_WRAPPER_OUTPUT_ALIAS: &'static str = "_polyglot_lateral";
1434
1435    fn transform_lateral_join_to_apply(mut join: Join) -> Result<Join> {
1436        let Some(apply_kind) = Self::lateral_apply_kind(&join) else {
1437            return Ok(join);
1438        };
1439
1440        let original_alias = Self::table_expression_alias(&join.this);
1441        let on = join.on.take();
1442        let rhs = Self::remove_lateral_marker(join.this);
1443        join.this = if on
1444            .as_ref()
1445            .is_some_and(|expr| !Self::is_true_condition(expr))
1446        {
1447            Self::wrap_lateral_apply_rhs(rhs, on.expect("checked as Some"), original_alias)?
1448        } else {
1449            rhs
1450        };
1451        join.using.clear();
1452        join.kind = apply_kind;
1453        join.use_inner_keyword = false;
1454        join.use_outer_keyword = false;
1455        join.deferred_condition = false;
1456        join.join_hint = None;
1457        join.match_condition = None;
1458        join.directed = false;
1459        Ok(join)
1460    }
1461
1462    fn rewrite_comma_lateral_sources_to_joins(select: &mut Select) {
1463        let Some(from) = select.from.as_mut() else {
1464            return;
1465        };
1466        let has_comma_lateral = from
1467            .expressions
1468            .iter()
1469            .skip(1)
1470            .any(Self::is_lateral_table_expression);
1471        let has_apply_join = select
1472            .joins
1473            .iter()
1474            .any(|join| matches!(join.kind, JoinKind::CrossApply | JoinKind::OuterApply));
1475
1476        if from.expressions.len() < 2 || (!has_comma_lateral && !has_apply_join) {
1477            return;
1478        }
1479
1480        let mut expressions = std::mem::take(&mut from.expressions).into_iter();
1481        let Some(first) = expressions.next() else {
1482            return;
1483        };
1484        from.expressions = vec![first];
1485
1486        let mut joins = expressions
1487            .map(|source| {
1488                if Self::is_lateral_table_expression(&source) {
1489                    Self::new_join(Self::remove_lateral_marker(source), JoinKind::CrossApply)
1490                } else {
1491                    Self::new_join(source, JoinKind::Cross)
1492                }
1493            })
1494            .collect::<Vec<_>>();
1495        joins.append(&mut select.joins);
1496        select.joins = joins;
1497    }
1498
1499    fn new_join(this: Expression, kind: JoinKind) -> Join {
1500        Join {
1501            this,
1502            on: None,
1503            using: Vec::new(),
1504            kind,
1505            use_inner_keyword: false,
1506            use_outer_keyword: false,
1507            deferred_condition: false,
1508            join_hint: None,
1509            match_condition: None,
1510            pivots: Vec::new(),
1511            comments: Vec::new(),
1512            nesting_group: 0,
1513            directed: false,
1514        }
1515    }
1516
1517    fn lateral_apply_kind(join: &Join) -> Option<JoinKind> {
1518        if !join.using.is_empty() {
1519            return None;
1520        }
1521
1522        match join.kind {
1523            JoinKind::Lateral => Some(JoinKind::CrossApply),
1524            JoinKind::LeftLateral => Some(JoinKind::OuterApply),
1525            JoinKind::Cross | JoinKind::Inner | JoinKind::Implicit
1526                if Self::is_lateral_table_expression(&join.this) =>
1527            {
1528                Some(JoinKind::CrossApply)
1529            }
1530            JoinKind::Left if Self::is_lateral_table_expression(&join.this) => {
1531                Some(JoinKind::OuterApply)
1532            }
1533            _ => None,
1534        }
1535    }
1536
1537    fn is_true_condition(expr: &Expression) -> bool {
1538        match expr {
1539            Expression::Boolean(boolean) => boolean.value,
1540            Expression::Literal(lit) => {
1541                matches!(lit.as_ref(), Literal::Number(value) if value.trim() == "1")
1542            }
1543            Expression::Eq(op) => {
1544                Self::is_true_condition(&op.left) && Self::is_true_condition(&op.right)
1545            }
1546            Expression::Paren(paren) => Self::is_true_condition(&paren.this),
1547            _ => false,
1548        }
1549    }
1550
1551    fn table_expression_alias(expr: &Expression) -> Option<(Identifier, Vec<Identifier>)> {
1552        match expr {
1553            Expression::Subquery(subquery) => subquery
1554                .alias
1555                .clone()
1556                .map(|alias| (alias, subquery.column_aliases.clone())),
1557            Expression::Alias(alias) if !alias.alias.is_empty() => {
1558                Some((alias.alias.clone(), alias.column_aliases.clone()))
1559            }
1560            Expression::Lateral(lateral) => lateral.alias.as_ref().map(|alias| {
1561                (
1562                    if lateral.alias_quoted {
1563                        Identifier::quoted(alias)
1564                    } else {
1565                        Identifier::new(alias)
1566                    },
1567                    lateral
1568                        .column_aliases
1569                        .iter()
1570                        .map(|column| Identifier::new(column.clone()))
1571                        .collect(),
1572                )
1573            }),
1574            _ => None,
1575        }
1576    }
1577
1578    fn wrap_lateral_apply_rhs(
1579        rhs: Expression,
1580        predicate: Expression,
1581        original_alias: Option<(Identifier, Vec<Identifier>)>,
1582    ) -> Result<Expression> {
1583        let (outer_alias, column_aliases) = original_alias.unwrap_or_else(|| {
1584            (
1585                Identifier::new(Self::LATERAL_WRAPPER_OUTPUT_ALIAS),
1586                Vec::new(),
1587            )
1588        });
1589        let inner_alias = Identifier::new(Self::LATERAL_WRAPPER_SOURCE_ALIAS);
1590        let source =
1591            Self::with_table_expression_alias(rhs, inner_alias.clone(), column_aliases.clone());
1592        let predicate = Self::rewrite_column_qualifier(predicate, &outer_alias, &inner_alias)?;
1593
1594        let mut select = Select::new();
1595        select.expressions = vec![Expression::Star(Star {
1596            table: None,
1597            except: None,
1598            replace: None,
1599            rename: None,
1600            trailing_comments: Vec::new(),
1601            span: None,
1602        })];
1603        select.from = Some(crate::expressions::From {
1604            expressions: vec![source],
1605        });
1606        select.where_clause = Some(Where { this: predicate });
1607
1608        Ok(Expression::Subquery(Box::new(Subquery {
1609            this: Expression::Select(Box::new(select)),
1610            alias: Some(outer_alias),
1611            column_aliases,
1612            alias_explicit_as: true,
1613            alias_keyword: None,
1614            order_by: None,
1615            limit: None,
1616            offset: None,
1617            distribute_by: None,
1618            sort_by: None,
1619            cluster_by: None,
1620            lateral: false,
1621            modifiers_inside: false,
1622            trailing_comments: Vec::new(),
1623            inferred_type: None,
1624        })))
1625    }
1626
1627    fn with_table_expression_alias(
1628        expr: Expression,
1629        alias: Identifier,
1630        column_aliases: Vec<Identifier>,
1631    ) -> Expression {
1632        match expr {
1633            Expression::Subquery(mut subquery) => {
1634                subquery.alias = Some(alias);
1635                subquery.column_aliases = column_aliases;
1636                subquery.alias_explicit_as = true;
1637                subquery.alias_keyword = None;
1638                Expression::Subquery(subquery)
1639            }
1640            Expression::Alias(mut aliased) => {
1641                aliased.alias = alias;
1642                aliased.column_aliases = column_aliases;
1643                aliased.alias_explicit_as = true;
1644                aliased.alias_keyword = None;
1645                Expression::Alias(aliased)
1646            }
1647            Expression::Table(mut table) => {
1648                table.alias = Some(alias);
1649                table.alias_explicit_as = true;
1650                table.column_aliases = column_aliases;
1651                Expression::Table(table)
1652            }
1653            other => Expression::Alias(Box::new(Alias {
1654                this: other,
1655                alias,
1656                column_aliases,
1657                alias_explicit_as: true,
1658                alias_keyword: None,
1659                pre_alias_comments: Vec::new(),
1660                trailing_comments: Vec::new(),
1661                inferred_type: None,
1662            })),
1663        }
1664    }
1665
1666    fn rewrite_column_qualifier(
1667        expr: Expression,
1668        from: &Identifier,
1669        to: &Identifier,
1670    ) -> Result<Expression> {
1671        super::transform_recursive(expr, &|expr| {
1672            Ok(match expr {
1673                Expression::Column(mut column)
1674                    if column
1675                        .table
1676                        .as_ref()
1677                        .is_some_and(|table| Self::same_identifier(table, from)) =>
1678                {
1679                    column.table = Some(to.clone());
1680                    Expression::Column(column)
1681                }
1682                other => other,
1683            })
1684        })
1685    }
1686
1687    fn same_identifier(left: &Identifier, right: &Identifier) -> bool {
1688        if left.quoted || right.quoted {
1689            left.quoted == right.quoted && left.name == right.name
1690        } else {
1691            left.name.eq_ignore_ascii_case(&right.name)
1692        }
1693    }
1694
1695    fn is_lateral_table_expression(expr: &Expression) -> bool {
1696        match expr {
1697            Expression::Subquery(subquery) => subquery.lateral,
1698            Expression::Lateral(_) => true,
1699            Expression::Alias(alias) => Self::is_lateral_table_expression(&alias.this),
1700            _ => false,
1701        }
1702    }
1703
1704    fn remove_lateral_marker(expr: Expression) -> Expression {
1705        match expr {
1706            Expression::Subquery(mut subquery) => {
1707                subquery.lateral = false;
1708                Expression::Subquery(subquery)
1709            }
1710            Expression::Lateral(lateral) => Self::lateral_to_table_expression(*lateral),
1711            Expression::Alias(mut alias) => {
1712                alias.this = Self::remove_lateral_marker(alias.this);
1713                Expression::Alias(alias)
1714            }
1715            other => other,
1716        }
1717    }
1718
1719    fn lateral_to_table_expression(lateral: crate::expressions::Lateral) -> Expression {
1720        let expr = *lateral.this;
1721        let Some(alias) = lateral.alias else {
1722            return expr;
1723        };
1724
1725        Expression::Alias(Box::new(Alias {
1726            this: expr,
1727            alias: if lateral.alias_quoted {
1728                Identifier::quoted(alias)
1729            } else {
1730                Identifier::new(alias)
1731            },
1732            column_aliases: lateral
1733                .column_aliases
1734                .into_iter()
1735                .map(Identifier::new)
1736                .collect(),
1737            alias_explicit_as: true,
1738            alias_keyword: None,
1739            pre_alias_comments: Vec::new(),
1740            trailing_comments: Vec::new(),
1741            inferred_type: None,
1742        }))
1743    }
1744
1745    fn rewrite_tuple_in_subquery_predicates(
1746        expr: Expression,
1747        outer_qualifier: Option<&Identifier>,
1748        under_not: bool,
1749    ) -> Expression {
1750        match expr {
1751            Expression::In(in_expr) if !under_not => {
1752                let in_expr = *in_expr;
1753                Self::tuple_in_subquery_to_exists(&in_expr, outer_qualifier, in_expr.not)
1754                    .unwrap_or_else(|| Expression::In(Box::new(in_expr)))
1755            }
1756            Expression::Eq(op) if !under_not => {
1757                let op = *op;
1758                Self::tuple_subquery_eq_to_exists(&op, outer_qualifier)
1759                    .unwrap_or_else(|| Expression::Eq(Box::new(op)))
1760            }
1761            Expression::And(mut op) => {
1762                op.left =
1763                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1764                op.right = Self::rewrite_tuple_in_subquery_predicates(
1765                    op.right,
1766                    outer_qualifier,
1767                    under_not,
1768                );
1769                Expression::And(op)
1770            }
1771            Expression::Or(mut op) => {
1772                op.left =
1773                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1774                op.right = Self::rewrite_tuple_in_subquery_predicates(
1775                    op.right,
1776                    outer_qualifier,
1777                    under_not,
1778                );
1779                Expression::Or(op)
1780            }
1781            Expression::Paren(mut paren) => {
1782                paren.this = Self::rewrite_tuple_in_subquery_predicates(
1783                    paren.this,
1784                    outer_qualifier,
1785                    under_not,
1786                );
1787                Expression::Paren(paren)
1788            }
1789            Expression::Not(mut not) => {
1790                if let Some(rewritten) = Self::direct_tuple_subquery_predicate_to_exists(
1791                    &not.this,
1792                    outer_qualifier,
1793                    true,
1794                ) {
1795                    rewritten
1796                } else {
1797                    not.this =
1798                        Self::rewrite_tuple_in_subquery_predicates(not.this, outer_qualifier, true);
1799                    Expression::Not(not)
1800                }
1801            }
1802            Expression::Alias(mut alias) => {
1803                alias.this = Self::rewrite_tuple_in_subquery_predicates(
1804                    alias.this,
1805                    outer_qualifier,
1806                    under_not,
1807                );
1808                Expression::Alias(alias)
1809            }
1810            Expression::Cast(mut cast) => {
1811                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1812                    cast.this,
1813                    outer_qualifier,
1814                    under_not,
1815                );
1816                if let Some(format) = cast.format.take() {
1817                    cast.format = Some(Box::new(Self::rewrite_tuple_in_subquery_predicates(
1818                        *format,
1819                        outer_qualifier,
1820                        under_not,
1821                    )));
1822                }
1823                if let Some(default) = cast.default.take() {
1824                    cast.default = Some(Box::new(Self::rewrite_tuple_in_subquery_predicates(
1825                        *default,
1826                        outer_qualifier,
1827                        under_not,
1828                    )));
1829                }
1830                Expression::Cast(cast)
1831            }
1832            Expression::TryCast(mut cast) => {
1833                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1834                    cast.this,
1835                    outer_qualifier,
1836                    under_not,
1837                );
1838                Expression::TryCast(cast)
1839            }
1840            Expression::SafeCast(mut cast) => {
1841                cast.this = Self::rewrite_tuple_in_subquery_predicates(
1842                    cast.this,
1843                    outer_qualifier,
1844                    under_not,
1845                );
1846                Expression::SafeCast(cast)
1847            }
1848            Expression::Case(mut case) => {
1849                if let Some(operand) = case.operand.take() {
1850                    case.operand = Some(Self::rewrite_tuple_in_subquery_predicates(
1851                        operand,
1852                        outer_qualifier,
1853                        under_not,
1854                    ));
1855                }
1856                case.whens = case
1857                    .whens
1858                    .into_iter()
1859                    .map(|(condition, result)| {
1860                        (
1861                            Self::rewrite_tuple_in_subquery_predicates(
1862                                condition,
1863                                outer_qualifier,
1864                                false,
1865                            ),
1866                            Self::rewrite_tuple_in_subquery_predicates(
1867                                result,
1868                                outer_qualifier,
1869                                under_not,
1870                            ),
1871                        )
1872                    })
1873                    .collect();
1874                if let Some(else_) = case.else_.take() {
1875                    case.else_ = Some(Self::rewrite_tuple_in_subquery_predicates(
1876                        else_,
1877                        outer_qualifier,
1878                        under_not,
1879                    ));
1880                }
1881                Expression::Case(case)
1882            }
1883            Expression::IfFunc(mut if_func) => {
1884                if_func.condition = Self::rewrite_tuple_in_subquery_predicates(
1885                    if_func.condition,
1886                    outer_qualifier,
1887                    false,
1888                );
1889                if_func.true_value = Self::rewrite_tuple_in_subquery_predicates(
1890                    if_func.true_value,
1891                    outer_qualifier,
1892                    under_not,
1893                );
1894                if let Some(false_value) = if_func.false_value.take() {
1895                    if_func.false_value = Some(Self::rewrite_tuple_in_subquery_predicates(
1896                        false_value,
1897                        outer_qualifier,
1898                        under_not,
1899                    ));
1900                }
1901                Expression::IfFunc(if_func)
1902            }
1903            other => other,
1904        }
1905    }
1906
1907    fn direct_tuple_subquery_predicate_to_exists(
1908        expr: &Expression,
1909        outer_qualifier: Option<&Identifier>,
1910        negated: bool,
1911    ) -> Option<Expression> {
1912        match expr {
1913            Expression::In(in_expr) => {
1914                Self::tuple_in_subquery_to_exists(in_expr, outer_qualifier, negated ^ in_expr.not)
1915            }
1916            Expression::Paren(paren) => Self::direct_tuple_subquery_predicate_to_exists(
1917                &paren.this,
1918                outer_qualifier,
1919                negated,
1920            ),
1921            _ => None,
1922        }
1923    }
1924
1925    fn tuple_in_subquery_to_exists(
1926        in_expr: &In,
1927        outer_qualifier: Option<&Identifier>,
1928        negated: bool,
1929    ) -> Option<Expression> {
1930        if in_expr.unnest.is_some() {
1931            return None;
1932        }
1933
1934        let left_expressions = Self::tuple_expressions(&in_expr.this)?;
1935        let mut select = Self::select_from_in_rhs(in_expr)?;
1936
1937        if left_expressions.len() != select.expressions.len() || left_expressions.is_empty() {
1938            return None;
1939        }
1940
1941        let inner_qualifier = Self::single_select_source_qualifier(&select);
1942        let mut predicates = Vec::with_capacity(left_expressions.len() + 1);
1943        for (projection, left) in select
1944            .expressions
1945            .iter()
1946            .cloned()
1947            .zip(left_expressions.iter().cloned())
1948        {
1949            let inner = Self::tuple_in_projection_expr(projection, inner_qualifier.as_ref())?;
1950            let outer = Self::qualify_tuple_operand(left, outer_qualifier);
1951            predicates.push(if negated {
1952                Self::tuple_component_may_match(inner, outer)
1953            } else {
1954                Expression::Eq(Box::new(BinaryOp::new(inner, outer)))
1955            });
1956        }
1957
1958        if let Some(where_clause) = select.where_clause.take() {
1959            predicates.push(where_clause.this);
1960        }
1961
1962        select.expressions = vec![Expression::number(1)];
1963        select.where_clause = Some(Where {
1964            this: Self::and_all(predicates)?,
1965        });
1966
1967        Some(Expression::Exists(Box::new(Exists {
1968            this: Expression::Select(Box::new(select)),
1969            not: negated,
1970        })))
1971    }
1972
1973    fn tuple_subquery_eq_to_exists(
1974        op: &BinaryOp,
1975        outer_qualifier: Option<&Identifier>,
1976    ) -> Option<Expression> {
1977        if let Some((tuple_expr, query_expr)) = Self::tuple_and_query_operands(&op.left, &op.right)
1978        {
1979            return Self::tuple_subquery_eq_to_exists_inner(
1980                tuple_expr,
1981                query_expr,
1982                outer_qualifier,
1983            );
1984        }
1985
1986        if let Some((tuple_expr, query_expr)) = Self::tuple_and_query_operands(&op.right, &op.left)
1987        {
1988            return Self::tuple_subquery_eq_to_exists_inner(
1989                tuple_expr,
1990                query_expr,
1991                outer_qualifier,
1992            );
1993        }
1994
1995        None
1996    }
1997
1998    fn tuple_subquery_eq_to_exists_inner(
1999        tuple_expr: &Expression,
2000        query_expr: &Expression,
2001        outer_qualifier: Option<&Identifier>,
2002    ) -> Option<Expression> {
2003        let tuple_expressions = Self::tuple_expressions(tuple_expr)?;
2004        let mut select = Self::select_from_query_expression(query_expr)?;
2005
2006        if tuple_expressions.len() != select.expressions.len() || tuple_expressions.is_empty() {
2007            return None;
2008        }
2009
2010        let inner_qualifier = Self::single_select_source_qualifier(&select);
2011        let mut predicates = Vec::with_capacity(tuple_expressions.len() + 1);
2012        for (projection, tuple_operand) in select
2013            .expressions
2014            .iter()
2015            .cloned()
2016            .zip(tuple_expressions.iter().cloned())
2017        {
2018            let inner = Self::tuple_in_projection_expr(projection, inner_qualifier.as_ref())?;
2019            let outer = Self::qualify_tuple_operand(tuple_operand, outer_qualifier);
2020            predicates.push(Expression::Eq(Box::new(BinaryOp::new(inner, outer))));
2021        }
2022
2023        if let Some(where_clause) = select.where_clause.take() {
2024            predicates.push(where_clause.this);
2025        }
2026
2027        select.expressions = vec![Expression::number(1)];
2028        select.where_clause = Some(Where {
2029            this: Self::and_all(predicates)?,
2030        });
2031
2032        Some(Expression::Exists(Box::new(Exists {
2033            this: Expression::Select(Box::new(select)),
2034            not: false,
2035        })))
2036    }
2037
2038    fn tuple_and_query_operands<'a>(
2039        tuple_candidate: &'a Expression,
2040        query_candidate: &'a Expression,
2041    ) -> Option<(&'a Expression, &'a Expression)> {
2042        if Self::tuple_expressions(tuple_candidate).is_some()
2043            && Self::select_from_query_expression(query_candidate).is_some()
2044        {
2045            Some((tuple_candidate, query_candidate))
2046        } else {
2047            None
2048        }
2049    }
2050
2051    fn select_from_query_expression(expr: &Expression) -> Option<Select> {
2052        match expr {
2053            Expression::Select(select) => Some((**select).clone()),
2054            Expression::Subquery(subquery) => Self::select_from_query_expression(&subquery.this),
2055            Expression::Paren(paren) => Self::select_from_query_expression(&paren.this),
2056            _ => None,
2057        }
2058    }
2059
2060    fn select_from_in_rhs(in_expr: &In) -> Option<Select> {
2061        if let Some(values) = Self::values_from_in_rhs(in_expr) {
2062            return Self::select_from_values(&values);
2063        }
2064
2065        if let Some(query) = &in_expr.query {
2066            return if in_expr.expressions.is_empty() {
2067                Self::select_from_query_expression(query)
2068            } else {
2069                None
2070            };
2071        }
2072
2073        if in_expr.expressions.len() == 1 {
2074            Self::select_from_query_expression(&in_expr.expressions[0])
2075        } else {
2076            None
2077        }
2078    }
2079
2080    fn values_from_in_rhs(in_expr: &In) -> Option<Values> {
2081        if let Some(query) = &in_expr.query {
2082            return if in_expr.expressions.is_empty() {
2083                Self::values_from_expression(query)
2084            } else {
2085                None
2086            };
2087        }
2088
2089        if in_expr.expressions.len() == 1 {
2090            if let Some(values) = Self::values_from_expression(&in_expr.expressions[0]) {
2091                return Some(values);
2092            }
2093        }
2094
2095        // IN (VALUES ...) currently parses as VALUES(first_row), followed by tuple rows.
2096        let Expression::Function(first_row) = in_expr.expressions.first()? else {
2097            return None;
2098        };
2099        if !first_row.name.eq_ignore_ascii_case("VALUES") {
2100            return None;
2101        }
2102
2103        let mut rows = Vec::with_capacity(in_expr.expressions.len());
2104        rows.push(Tuple {
2105            expressions: first_row.args.clone(),
2106        });
2107        for row in &in_expr.expressions[1..] {
2108            rows.push(Self::tuple_from_values_row(row)?);
2109        }
2110
2111        Some(Values {
2112            expressions: rows,
2113            alias: None,
2114            column_aliases: Vec::new(),
2115        })
2116    }
2117
2118    fn values_from_expression(expr: &Expression) -> Option<Values> {
2119        match expr {
2120            Expression::Values(values) => Some((**values).clone()),
2121            Expression::Paren(paren) => Self::values_from_expression(&paren.this),
2122            Expression::Subquery(subquery) => Self::values_from_expression(&subquery.this),
2123            _ => None,
2124        }
2125    }
2126
2127    fn tuple_from_values_row(expr: &Expression) -> Option<Tuple> {
2128        match expr {
2129            Expression::Tuple(tuple) => Some((**tuple).clone()),
2130            Expression::Paren(paren) => match &paren.this {
2131                Expression::Tuple(tuple) => Some((**tuple).clone()),
2132                other => Some(Tuple {
2133                    expressions: vec![other.clone()],
2134                }),
2135            },
2136            _ => None,
2137        }
2138    }
2139
2140    fn select_from_values(values: &Values) -> Option<Select> {
2141        let column_count = values.expressions.first()?.expressions.len();
2142        if column_count == 0
2143            || values
2144                .expressions
2145                .iter()
2146                .any(|row| row.expressions.len() != column_count)
2147        {
2148            return None;
2149        }
2150
2151        let source_alias = Identifier::new("_polyglot_values");
2152        let column_aliases = (1..=column_count)
2153            .map(|index| Identifier::new(format!("_polyglot_value_{index}")))
2154            .collect::<Vec<_>>();
2155        let projections = column_aliases
2156            .iter()
2157            .cloned()
2158            .map(|column| Self::column_from_identifier(column, Some(source_alias.clone())))
2159            .collect();
2160
2161        let mut source_values = values.clone();
2162        source_values.alias = None;
2163        source_values.column_aliases.clear();
2164
2165        let source = Expression::Subquery(Box::new(Subquery {
2166            this: Expression::Values(Box::new(source_values)),
2167            alias: Some(source_alias),
2168            column_aliases,
2169            alias_explicit_as: true,
2170            alias_keyword: None,
2171            order_by: None,
2172            limit: None,
2173            offset: None,
2174            distribute_by: None,
2175            sort_by: None,
2176            cluster_by: None,
2177            lateral: false,
2178            modifiers_inside: false,
2179            trailing_comments: Vec::new(),
2180            inferred_type: None,
2181        }));
2182
2183        let mut select = Select::new();
2184        select.expressions = projections;
2185        select.from = Some(From {
2186            expressions: vec![source],
2187        });
2188        Some(select)
2189    }
2190
2191    fn tuple_expressions(expr: &Expression) -> Option<&[Expression]> {
2192        match expr {
2193            Expression::Tuple(tuple) => Some(&tuple.expressions),
2194            Expression::Function(function) if function.name.eq_ignore_ascii_case("ROW") => {
2195                Some(&function.args)
2196            }
2197            Expression::Paren(paren) => Self::tuple_expressions(&paren.this),
2198            _ => None,
2199        }
2200    }
2201
2202    fn tuple_in_projection_expr(
2203        expr: Expression,
2204        qualifier: Option<&Identifier>,
2205    ) -> Option<Expression> {
2206        match expr {
2207            Expression::Alias(alias) => Self::tuple_in_projection_expr(alias.this, qualifier),
2208            Expression::Column(mut column) => {
2209                if column.table.is_none() {
2210                    column.table = qualifier.cloned();
2211                }
2212                Some(Expression::Column(column))
2213            }
2214            Expression::Identifier(identifier) => {
2215                Some(Self::column_from_identifier(identifier, qualifier.cloned()))
2216            }
2217            Expression::Dot(_) => Some(expr),
2218            other => Some(Self::qualify_tuple_expression(other, qualifier)),
2219        }
2220    }
2221
2222    fn qualify_tuple_operand(expr: Expression, qualifier: Option<&Identifier>) -> Expression {
2223        Self::qualify_tuple_expression(expr, qualifier)
2224    }
2225
2226    fn qualify_tuple_expression(expr: Expression, qualifier: Option<&Identifier>) -> Expression {
2227        match expr {
2228            Expression::Column(mut column) => {
2229                if column.table.is_none() {
2230                    column.table = qualifier.cloned();
2231                }
2232                Expression::Column(column)
2233            }
2234            Expression::Identifier(identifier) => {
2235                Self::column_from_identifier(identifier, qualifier.cloned())
2236            }
2237            Expression::Alias(mut alias) => {
2238                alias.this = Self::qualify_tuple_expression(alias.this, qualifier);
2239                Expression::Alias(alias)
2240            }
2241            Expression::Paren(mut paren) => {
2242                paren.this = Self::qualify_tuple_expression(paren.this, qualifier);
2243                Expression::Paren(paren)
2244            }
2245            Expression::Cast(mut cast) => {
2246                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2247                if let Some(format) = cast.format.take() {
2248                    cast.format =
2249                        Some(Box::new(Self::qualify_tuple_expression(*format, qualifier)));
2250                }
2251                if let Some(default) = cast.default.take() {
2252                    cast.default = Some(Box::new(Self::qualify_tuple_expression(
2253                        *default, qualifier,
2254                    )));
2255                }
2256                Expression::Cast(cast)
2257            }
2258            Expression::TryCast(mut cast) => {
2259                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2260                Expression::TryCast(cast)
2261            }
2262            Expression::SafeCast(mut cast) => {
2263                cast.this = Self::qualify_tuple_expression(cast.this, qualifier);
2264                Expression::SafeCast(cast)
2265            }
2266            Expression::Function(mut function) => {
2267                function.args = function
2268                    .args
2269                    .into_iter()
2270                    .map(|arg| Self::qualify_tuple_expression(arg, qualifier))
2271                    .collect();
2272                Expression::Function(function)
2273            }
2274            Expression::Add(mut op) => {
2275                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2276                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2277                Expression::Add(op)
2278            }
2279            Expression::Sub(mut op) => {
2280                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2281                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2282                Expression::Sub(op)
2283            }
2284            Expression::Mul(mut op) => {
2285                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2286                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2287                Expression::Mul(op)
2288            }
2289            Expression::Div(mut op) => {
2290                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2291                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2292                Expression::Div(op)
2293            }
2294            Expression::Mod(mut op) => {
2295                op.left = Self::qualify_tuple_expression(op.left, qualifier);
2296                op.right = Self::qualify_tuple_expression(op.right, qualifier);
2297                Expression::Mod(op)
2298            }
2299            other => other,
2300        }
2301    }
2302
2303    fn tuple_component_may_match(inner: Expression, outer: Expression) -> Expression {
2304        Self::paren(
2305            Self::or_all(vec![
2306                Expression::Eq(Box::new(BinaryOp::new(inner.clone(), outer.clone()))),
2307                Self::is_null(inner),
2308                Self::is_null(outer),
2309            ])
2310            .expect("tuple component match condition is non-empty"),
2311        )
2312    }
2313
2314    fn column_from_identifier(identifier: Identifier, table: Option<Identifier>) -> Expression {
2315        Expression::Column(Box::new(Column {
2316            name: identifier,
2317            table,
2318            join_mark: false,
2319            trailing_comments: Vec::new(),
2320            span: None,
2321            inferred_type: None,
2322        }))
2323    }
2324
2325    fn single_select_source_qualifier(select: &Select) -> Option<Identifier> {
2326        if !select.joins.is_empty() {
2327            return None;
2328        }
2329
2330        let from = select.from.as_ref()?;
2331        if from.expressions.len() != 1 {
2332            return None;
2333        }
2334
2335        Self::source_qualifier(&from.expressions[0])
2336    }
2337
2338    fn source_qualifier(source: &Expression) -> Option<Identifier> {
2339        match source {
2340            Expression::Table(table) => table.alias.clone().or_else(|| Some(table.name.clone())),
2341            Expression::Subquery(subquery) => subquery.alias.clone(),
2342            _ => None,
2343        }
2344    }
2345
2346    fn and_all(mut predicates: Vec<Expression>) -> Option<Expression> {
2347        if predicates.is_empty() {
2348            return None;
2349        }
2350
2351        let first = predicates.remove(0);
2352        Some(predicates.into_iter().fold(first, |left, right| {
2353            Expression::And(Box::new(BinaryOp::new(left, right)))
2354        }))
2355    }
2356
2357    fn or_all(mut predicates: Vec<Expression>) -> Option<Expression> {
2358        if predicates.is_empty() {
2359            return None;
2360        }
2361
2362        let first = predicates.remove(0);
2363        Some(predicates.into_iter().fold(first, |left, right| {
2364            Expression::Or(Box::new(BinaryOp::new(left, right)))
2365        }))
2366    }
2367
2368    /// Transform data types according to T-SQL TYPE_MAPPING
2369    pub(super) fn transform_data_type(
2370        &self,
2371        dt: crate::expressions::DataType,
2372    ) -> Result<Expression> {
2373        use crate::expressions::DataType;
2374        let transformed = match dt {
2375            // BOOLEAN -> BIT
2376            DataType::Boolean => DataType::Custom {
2377                name: "BIT".to_string(),
2378            },
2379            // INT stays as INT in TSQL (native type)
2380            DataType::Int { .. } => dt,
2381            // DOUBLE stays as Double internally (TSQL generator outputs FLOAT for it)
2382            // DECIMAL -> NUMERIC
2383            DataType::Decimal { precision, scale } => DataType::Custom {
2384                name: if let (Some(p), Some(s)) = (&precision, &scale) {
2385                    format!("NUMERIC({}, {})", p, s)
2386                } else if let Some(p) = &precision {
2387                    format!("NUMERIC({})", p)
2388                } else {
2389                    "NUMERIC".to_string()
2390                },
2391            },
2392            // TEXT -> VARCHAR(MAX)
2393            DataType::Text => DataType::Custom {
2394                name: "VARCHAR(MAX)".to_string(),
2395            },
2396            // TIMESTAMP -> DATETIME2
2397            DataType::Timestamp { .. } => DataType::Custom {
2398                name: "DATETIME2".to_string(),
2399            },
2400            // UUID -> UNIQUEIDENTIFIER
2401            DataType::Uuid => DataType::Custom {
2402                name: "UNIQUEIDENTIFIER".to_string(),
2403            },
2404            // Normalise custom type names that have PostgreSQL aliases
2405            DataType::Custom { ref name } => {
2406                let upper = name.trim().to_uppercase();
2407                let (base_name, precision, _scale) = Self::parse_type_precision_and_scale(&upper);
2408                match base_name.as_str() {
2409                    // PostgreSQL DOUBLE PRECISION is SQL Server FLOAT.
2410                    "DOUBLE PRECISION" => DataType::Custom {
2411                        name: "FLOAT".to_string(),
2412                    },
2413                    // BPCHAR is PostgreSQL's blank-padded CHAR alias — map to CHAR
2414                    "BPCHAR" => {
2415                        if let Some(len) = precision {
2416                            DataType::Char { length: Some(len) }
2417                        } else {
2418                            DataType::Char { length: None }
2419                        }
2420                    }
2421                    _ => dt,
2422                }
2423            }
2424            // Keep all other types as-is
2425            other => other,
2426        };
2427        Ok(Expression::DataType(transformed))
2428    }
2429
2430    /// Parse a type name that may embed precision/scale: `"TYPENAME(n, m)"` → `("TYPENAME", Some(n), Some(m))`.
2431    pub(super) fn parse_type_precision_and_scale(name: &str) -> (String, Option<u32>, Option<u32>) {
2432        if let Some(paren_pos) = name.find('(') {
2433            let base = name[..paren_pos].to_string();
2434            let rest = &name[paren_pos + 1..];
2435            if let Some(close_pos) = rest.find(')') {
2436                let args = &rest[..close_pos];
2437                let parts: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
2438                let precision = parts.first().and_then(|s| s.parse::<u32>().ok());
2439                let scale = parts.get(1).and_then(|s| s.parse::<u32>().ok());
2440                return (base, precision, scale);
2441            }
2442            (base, None, None)
2443        } else {
2444            (name.to_string(), None, None)
2445        }
2446    }
2447
2448    fn transform_logical_aggregate(
2449        condition: Expression,
2450        filter: Option<Expression>,
2451        aggregate_name: &str,
2452    ) -> Result<Expression> {
2453        let false_condition = Expression::Not(Box::new(crate::expressions::UnaryOp {
2454            this: condition.clone(),
2455            inferred_type: None,
2456        }));
2457        let true_condition = Self::apply_aggregate_filter(condition, filter.clone());
2458        let false_condition = Self::apply_aggregate_filter(false_condition, filter);
2459
2460        let case_expr = Expression::Case(Box::new(crate::expressions::Case {
2461            operand: None,
2462            whens: vec![
2463                (true_condition, Expression::number(1)),
2464                (false_condition, Expression::number(0)),
2465            ],
2466            else_: Some(Expression::null()),
2467            comments: Vec::new(),
2468            inferred_type: None,
2469        }));
2470
2471        let case_expr = crate::transforms::ensure_bools(case_expr)?;
2472        let aggregate = Expression::Function(Box::new(Function::new(
2473            aggregate_name.to_string(),
2474            vec![case_expr],
2475        )));
2476
2477        Ok(Expression::Cast(Box::new(Cast {
2478            this: aggregate,
2479            to: DataType::Custom {
2480                name: "BIT".to_string(),
2481            },
2482            trailing_comments: Vec::new(),
2483            double_colon_syntax: false,
2484            format: None,
2485            default: None,
2486            inferred_type: None,
2487        })))
2488    }
2489
2490    fn reassociate_logical_aggregate_window(mut window: WindowFunction) -> Expression {
2491        let Expression::Cast(mut cast) = window.this else {
2492            return Expression::WindowFunction(Box::new(window));
2493        };
2494
2495        if !Self::is_transformed_logical_aggregate_cast(&cast) {
2496            window.this = Expression::Cast(cast);
2497            return Expression::WindowFunction(Box::new(window));
2498        }
2499
2500        window.this = cast.this;
2501        cast.this = Expression::WindowFunction(Box::new(window));
2502        Expression::Cast(cast)
2503    }
2504
2505    fn is_transformed_logical_aggregate_cast(cast: &Cast) -> bool {
2506        if !matches!(
2507            &cast.to,
2508            DataType::Custom { name } if name.eq_ignore_ascii_case("BIT")
2509        ) {
2510            return false;
2511        }
2512
2513        let Expression::Function(function) = &cast.this else {
2514            return false;
2515        };
2516        if !matches!(function.name.to_ascii_uppercase().as_str(), "MIN" | "MAX")
2517            || function.args.len() != 1
2518        {
2519            return false;
2520        }
2521
2522        matches!(
2523            function.args.first(),
2524            Some(Expression::Case(case))
2525                if case.operand.is_none()
2526                    && case.whens.len() == 2
2527                    && matches!(case.else_.as_ref(), Some(Expression::Null(_)))
2528        )
2529    }
2530
2531    fn apply_aggregate_filter(condition: Expression, filter: Option<Expression>) -> Expression {
2532        match filter {
2533            Some(filter) => Expression::And(Box::new(crate::expressions::BinaryOp::new(
2534                filter, condition,
2535            ))),
2536            None => condition,
2537        }
2538    }
2539
2540    fn transform_function(&self, f: Function) -> Result<Expression> {
2541        let name_upper = f.name.to_uppercase();
2542        match name_upper.as_str() {
2543            // COALESCE -> ISNULL for 2 args (optimization)
2544            "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2545                "ISNULL".to_string(),
2546                f.args,
2547            )))),
2548
2549            // NVL -> ISNULL (SQL Server function)
2550            "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2551                "ISNULL".to_string(),
2552                f.args,
2553            )))),
2554
2555            // GROUP_CONCAT -> STRING_AGG in SQL Server 2017+
2556            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
2557                Function::new("STRING_AGG".to_string(), f.args),
2558            ))),
2559
2560            // STRING_AGG is native to SQL Server 2017+
2561            "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
2562
2563            // LISTAGG -> STRING_AGG
2564            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
2565                "STRING_AGG".to_string(),
2566                f.args,
2567            )))),
2568
2569            // SUBSTR -> SUBSTRING
2570            "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
2571                "SUBSTRING".to_string(),
2572                f.args,
2573            )))),
2574
2575            // LENGTH -> LEN in SQL Server
2576            "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2577                "LEN".to_string(),
2578                f.args,
2579            )))),
2580
2581            // PostgreSQL btrim(text[, characters]) -> T-SQL TRIM([characters FROM] text)
2582            "BTRIM" if f.args.len() == 1 || f.args.len() == 2 => {
2583                let mut args = f.args;
2584                let this = args.remove(0);
2585                let characters = if args.is_empty() {
2586                    None
2587                } else {
2588                    Some(args.remove(0))
2589                };
2590                Ok(Expression::Trim(Box::new(TrimFunc {
2591                    this,
2592                    sql_standard_syntax: characters.is_some(),
2593                    characters,
2594                    position: TrimPosition::Both,
2595                    position_explicit: false,
2596                })))
2597            }
2598
2599            // PostgreSQL md5(text) returns lowercase hex text; HASHBYTES returns varbinary.
2600            "MD5" if f.args.len() == 1 => {
2601                let mut args = f.args;
2602                Ok(Self::tsql_md5_hex(args.remove(0)))
2603            }
2604
2605            "SHA256" if f.args.len() == 1 => {
2606                let mut args = f.args;
2607                Ok(Self::function(
2608                    "HASHBYTES",
2609                    vec![Expression::string("SHA2_256"), args.remove(0)],
2610                ))
2611            }
2612
2613            "SHA512" if f.args.len() == 1 => {
2614                let mut args = f.args;
2615                Ok(Self::function(
2616                    "HASHBYTES",
2617                    vec![Expression::string("SHA2_512"), args.remove(0)],
2618                ))
2619            }
2620
2621            // PostgreSQL octet_length(text/bytea) -> DATALENGTH(...)
2622            "OCTET_LENGTH" if f.args.len() == 1 => Ok(Self::function("DATALENGTH", f.args)),
2623
2624            // PostgreSQL bit_length(text/bytea) -> DATALENGTH(...) * 8
2625            "BIT_LENGTH" if f.args.len() == 1 => {
2626                let mut args = f.args;
2627                Ok(Expression::Mul(Box::new(BinaryOp::new(
2628                    Self::function("DATALENGTH", vec![args.remove(0)]),
2629                    Expression::number(8),
2630                ))))
2631            }
2632
2633            // PostgreSQL to_hex(int) -> unpadded lowercase hex text. SQL Server's
2634            // binary conversion preserves the integer width, so remove only leading
2635            // zeroes and retain a single zero for the all-zero value.
2636            "TO_HEX" if f.args.len() == 1 => {
2637                let mut args = f.args;
2638                Ok(Self::tsql_postgres_to_hex(args.remove(0)))
2639            }
2640
2641            // PostgreSQL encode(bytea, 'hex') -> lowercase hex text.
2642            "ENCODE" if f.args.len() == 2 => {
2643                let mut args = f.args;
2644                let this = args.remove(0);
2645                let encoding = args.remove(0);
2646                if Self::literal_string(&encoding)
2647                    .is_some_and(|encoding| encoding.eq_ignore_ascii_case("hex"))
2648                {
2649                    Ok(Self::tsql_hex_from_varbinary(this))
2650                } else {
2651                    Ok(Expression::Function(Box::new(Function::new(
2652                        "ENCODE".to_string(),
2653                        vec![this, encoding],
2654                    ))))
2655                }
2656            }
2657
2658            // Preserve support for manually constructed/generic DECODE ASTs in addition
2659            // to the parser's typed DecodeCase representation.
2660            "DECODE"
2661                if f.args.len() == 2
2662                    && Self::literal_string(&f.args[1])
2663                        .is_some_and(|format| format.eq_ignore_ascii_case("hex")) =>
2664            {
2665                let mut args = f.args;
2666                Ok(Self::tsql_convert(
2667                    DataType::Custom {
2668                        name: "VARBINARY(MAX)".to_string(),
2669                    },
2670                    args.remove(0),
2671                    Some(2),
2672                ))
2673            }
2674
2675            // PostgreSQL repeat(text, count) -> SQL Server REPLICATE(text, count)
2676            "REPEAT" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
2677                "REPLICATE".to_string(),
2678                f.args,
2679            )))),
2680
2681            // PostgreSQL chr(code) -> SQL Server CHAR(code)
2682            "CHR" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2683                "CHAR".to_string(),
2684                f.args,
2685            )))),
2686
2687            // RANDOM -> RAND
2688            "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
2689                seed: None,
2690                lower: None,
2691                upper: None,
2692            }))),
2693
2694            // NOW -> GETDATE or CURRENT_TIMESTAMP (both work)
2695            "NOW" => Ok(Self::getdate()),
2696
2697            // CURRENT_TIMESTAMP -> GETDATE (SQL Server prefers GETDATE)
2698            "CURRENT_TIMESTAMP" => Ok(Self::getdate()),
2699
2700            // CURRENT_DATE -> CAST(GETDATE() AS DATE)
2701            "CURRENT_DATE" => Ok(Self::cast_getdate_to(DataType::Date)),
2702
2703            // CURRENT_TIME -> CAST(GETDATE() AS TIME)
2704            "CURRENT_TIME" => Ok(Self::cast_getdate_to(DataType::Time {
2705                precision: None,
2706                timezone: false,
2707            })),
2708
2709            // LOCALTIMESTAMP -> GETDATE()
2710            "LOCALTIMESTAMP" => Ok(Self::getdate()),
2711
2712            // PostgreSQL clock_timestamp() -> high-precision current system timestamp.
2713            "CLOCK_TIMESTAMP" if f.args.is_empty() => Ok(Self::function("SYSDATETIME", vec![])),
2714
2715            // PostgreSQL make_date(year, month, day) -> SQL Server DATEFROMPARTS.
2716            "MAKE_DATE" if f.args.len() == 3 => Ok(Self::function("DATEFROMPARTS", f.args)),
2717
2718            // PostgreSQL make_time(hour, minute, double-precision seconds) ->
2719            // SQL Server TIMEFROMPARTS(hour, minute, seconds, fractions, precision).
2720            "MAKE_TIME" if f.args.len() == 3 => Ok(Self::make_time(f.args)),
2721
2722            // PostgreSQL/Oracle-style TO_DATE(value, fmt) -> typed parse expression.
2723            // The generator will emit native CONVERT(DATE, value, style) when
2724            // the literal format maps cleanly to a T-SQL style code.
2725            "TO_DATE" if f.args.len() == 2 => {
2726                Self::formatted_str_to_date_or_fallback(f.args, "TO_DATE")
2727            }
2728
2729            // One-arg TO_DATE(value) has no format string; use a native cast shape.
2730            "TO_DATE" if f.args.len() == 1 => {
2731                let mut args = f.args;
2732                Ok(Expression::Cast(Box::new(Cast {
2733                    this: args.remove(0),
2734                    to: DataType::Date,
2735                    trailing_comments: Vec::new(),
2736                    double_colon_syntax: false,
2737                    format: None,
2738                    default: None,
2739                    inferred_type: None,
2740                })))
2741            }
2742
2743            // PostgreSQL/Oracle-style TO_TIMESTAMP(value, fmt) -> typed parse expression.
2744            // This avoids the invalid CONVERT(value, fmt) argument order.
2745            "TO_TIMESTAMP" if f.args.len() == 2 => {
2746                Self::formatted_str_to_time_or_fallback(f.args, "TO_TIMESTAMP")
2747            }
2748
2749            // PostgreSQL's one-arg TO_TIMESTAMP is epoch seconds.
2750            "TO_TIMESTAMP" if f.args.len() == 1 => {
2751                let mut args = f.args;
2752                Ok(Expression::UnixToTime(Box::new(
2753                    crate::expressions::UnixToTime {
2754                        this: Box::new(args.remove(0)),
2755                        scale: Some(0),
2756                        zone: None,
2757                        hours: None,
2758                        minutes: None,
2759                        format: None,
2760                        target_type: None,
2761                    },
2762                )))
2763            }
2764
2765            // PostgreSQL/Oracle-style TO_CHAR(value, fmt) -> typed format expression.
2766            // The generator converts the normalized strftime format to .NET FORMAT().
2767            "TO_CHAR" if f.args.len() == 2 => {
2768                Self::formatted_time_to_str_or_fallback(f.args, "TO_CHAR")
2769            }
2770
2771            // TO_CHAR(value) without a format remains a normal T-SQL FORMAT call.
2772            "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
2773                "FORMAT".to_string(),
2774                f.args,
2775            )))),
2776
2777            // DATE_FORMAT -> FORMAT
2778            "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
2779                "FORMAT".to_string(),
2780                f.args,
2781            )))),
2782
2783            // DATE_TRUNC -> DATETRUNC in SQL Server 2022+
2784            // For older versions, use DATEADD/DATEDIFF combo
2785            "DATE_TRUNC" | "DATETRUNC" => {
2786                let mut args = Self::uppercase_first_arg_if_identifier(f.args);
2787                // Cast string literal date arg to DATETIME2
2788                if args.len() >= 2 {
2789                    if let Expression::Literal(lit) = &args[1] {
2790                        if let Literal::String(_) = lit.as_ref() {
2791                            args[1] = Expression::Cast(Box::new(Cast {
2792                                this: args[1].clone(),
2793                                to: DataType::Custom {
2794                                    name: "DATETIME2".to_string(),
2795                                },
2796                                trailing_comments: Vec::new(),
2797                                double_colon_syntax: false,
2798                                format: None,
2799                                default: None,
2800                                inferred_type: None,
2801                            }));
2802                        }
2803                    }
2804                }
2805                Ok(Expression::Function(Box::new(Function::new(
2806                    "DATETRUNC".to_string(),
2807                    args,
2808                ))))
2809            }
2810
2811            // DATEADD is native to SQL Server - uppercase the unit
2812            "DATEADD" => {
2813                let args = Self::uppercase_first_arg_if_identifier(f.args);
2814                Ok(Expression::Function(Box::new(Function::new(
2815                    "DATEADD".to_string(),
2816                    args,
2817                ))))
2818            }
2819
2820            // DATEDIFF is native to SQL Server - uppercase the unit
2821            "DATEDIFF" => {
2822                let args = Self::uppercase_first_arg_if_identifier(f.args);
2823                Ok(Expression::Function(Box::new(Function::new(
2824                    "DATEDIFF".to_string(),
2825                    args,
2826                ))))
2827            }
2828
2829            // EXTRACT -> DATEPART in SQL Server
2830            "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2831                "DATEPART".to_string(),
2832                f.args,
2833            )))),
2834
2835            // STRPOS / POSITION -> CHARINDEX
2836            "STRPOS" | "POSITION" if f.args.len() >= 2 => {
2837                // CHARINDEX(substring, string) - same arg order as POSITION
2838                Ok(Expression::Function(Box::new(Function::new(
2839                    "CHARINDEX".to_string(),
2840                    f.args,
2841                ))))
2842            }
2843
2844            // CHARINDEX is native
2845            "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
2846
2847            // CEILING -> CEILING (native)
2848            "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
2849                Function::new("CEILING".to_string(), f.args),
2850            ))),
2851
2852            // ARRAY functions don't exist in SQL Server
2853            // Would need JSON or table-valued parameters
2854
2855            // JSON_EXTRACT -> JSON_VALUE or JSON_QUERY
2856            "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2857                "JSON_VALUE".to_string(),
2858                f.args,
2859            )))),
2860
2861            // JSON_EXTRACT_SCALAR -> JSON_VALUE
2862            "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
2863                "JSON_VALUE".to_string(),
2864                f.args,
2865            )))),
2866
2867            // PARSE_JSON -> strip in TSQL (just keep the string argument)
2868            "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
2869
2870            // GET_PATH(obj, path) -> ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path)) in TSQL
2871            "GET_PATH" if f.args.len() == 2 => {
2872                let mut args = f.args;
2873                let this = args.remove(0);
2874                let path = args.remove(0);
2875                let json_path = match &path {
2876                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
2877                        let Literal::String(s) = lit.as_ref() else {
2878                            unreachable!()
2879                        };
2880                        let normalized = if s.starts_with('$') {
2881                            s.clone()
2882                        } else if s.starts_with('[') {
2883                            format!("${}", s)
2884                        } else {
2885                            format!("$.{}", s)
2886                        };
2887                        Expression::Literal(Box::new(Literal::String(normalized)))
2888                    }
2889                    _ => path,
2890                };
2891                // ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path))
2892                let json_query = Expression::Function(Box::new(Function::new(
2893                    "JSON_QUERY".to_string(),
2894                    vec![this.clone(), json_path.clone()],
2895                )));
2896                let json_value = Expression::Function(Box::new(Function::new(
2897                    "JSON_VALUE".to_string(),
2898                    vec![this, json_path],
2899                )));
2900                Ok(Expression::Function(Box::new(Function::new(
2901                    "ISNULL".to_string(),
2902                    vec![json_query, json_value],
2903                ))))
2904            }
2905
2906            // JSON_QUERY with 1 arg: add '$' path and wrap in ISNULL
2907            // JSON_QUERY with 2 args: leave as-is (already processed or inside ISNULL)
2908            "JSON_QUERY" if f.args.len() == 1 => {
2909                let this = f.args.into_iter().next().unwrap();
2910                let path = Expression::Literal(Box::new(Literal::String("$".to_string())));
2911                let json_query = Expression::Function(Box::new(Function::new(
2912                    "JSON_QUERY".to_string(),
2913                    vec![this.clone(), path.clone()],
2914                )));
2915                let json_value = Expression::Function(Box::new(Function::new(
2916                    "JSON_VALUE".to_string(),
2917                    vec![this, path],
2918                )));
2919                Ok(Expression::Function(Box::new(Function::new(
2920                    "ISNULL".to_string(),
2921                    vec![json_query, json_value],
2922                ))))
2923            }
2924
2925            // SPLIT -> STRING_SPLIT (returns a table, needs CROSS APPLY)
2926            "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
2927                "STRING_SPLIT".to_string(),
2928                f.args,
2929            )))),
2930
2931            // REGEXP_LIKE -> Not directly supported, use LIKE or PATINDEX
2932            // SQL Server has limited regex support via PATINDEX and LIKE
2933            "REGEXP_LIKE" => {
2934                // Fall back to LIKE (loses regex functionality)
2935                Ok(Expression::Function(Box::new(Function::new(
2936                    "PATINDEX".to_string(),
2937                    f.args,
2938                ))))
2939            }
2940
2941            // LN -> LOG in SQL Server
2942            "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2943                "LOG".to_string(),
2944                f.args,
2945            )))),
2946
2947            // LOG with 2 args is LOG(base, value) in most DBs but LOG(value, base) in SQL Server
2948            // This needs careful handling
2949
2950            // STDDEV -> STDEV in SQL Server
2951            "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2952                "STDEV".to_string(),
2953                f.args,
2954            )))),
2955
2956            // STDDEV_POP -> STDEVP in SQL Server
2957            "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
2958                "STDEVP".to_string(),
2959                f.args,
2960            )))),
2961
2962            // VAR_SAMP -> VAR in SQL Server
2963            "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2964                "VAR".to_string(),
2965                f.args,
2966            )))),
2967
2968            // VAR_POP -> VARP in SQL Server
2969            "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
2970                "VARP".to_string(),
2971                f.args,
2972            )))),
2973
2974            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
2975            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
2976                let mut args = f.args;
2977                Self::transform_logical_aggregate(args.remove(0), None, "MIN")
2978            }
2979            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
2980                let mut args = f.args;
2981                Self::transform_logical_aggregate(args.remove(0), None, "MAX")
2982            }
2983
2984            // DATE_ADD(date, interval) -> DATEADD(DAY, interval, date)
2985            "DATE_ADD" => {
2986                if f.args.len() == 2 {
2987                    let mut args = f.args;
2988                    let date = args.remove(0);
2989                    let interval = args.remove(0);
2990                    let unit = Expression::Identifier(crate::expressions::Identifier {
2991                        name: "DAY".to_string(),
2992                        quoted: false,
2993                        trailing_comments: Vec::new(),
2994                        span: None,
2995                    });
2996                    Ok(Expression::Function(Box::new(Function::new(
2997                        "DATEADD".to_string(),
2998                        vec![unit, interval, date],
2999                    ))))
3000                } else {
3001                    let args = Self::uppercase_first_arg_if_identifier(f.args);
3002                    Ok(Expression::Function(Box::new(Function::new(
3003                        "DATEADD".to_string(),
3004                        args,
3005                    ))))
3006                }
3007            }
3008
3009            // INSERT → STUFF (Snowflake/MySQL string INSERT → T-SQL STUFF)
3010            "INSERT" => Ok(Expression::Function(Box::new(Function::new(
3011                "STUFF".to_string(),
3012                f.args,
3013            )))),
3014
3015            // SUSER_NAME(), SUSER_SNAME(), SYSTEM_USER() -> CURRENT_USER
3016            "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
3017                crate::expressions::CurrentUser { this: None },
3018            ))),
3019
3020            // Pass through everything else
3021            _ => Ok(Expression::Function(Box::new(f))),
3022        }
3023    }
3024
3025    fn literal_string(expr: &Expression) -> Option<&str> {
3026        match expr {
3027            Expression::Literal(lit) => match lit.as_ref() {
3028                Literal::String(s) => Some(s),
3029                _ => None,
3030            },
3031            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast)
3032                if Self::is_text_data_type(&cast.to) =>
3033            {
3034                Self::literal_string(&cast.this)
3035            }
3036            _ => None,
3037        }
3038    }
3039
3040    fn is_text_data_type(data_type: &DataType) -> bool {
3041        match data_type {
3042            DataType::Char { .. }
3043            | DataType::VarChar { .. }
3044            | DataType::String { .. }
3045            | DataType::Text
3046            | DataType::TextWithLength { .. } => true,
3047            DataType::Custom { name } => {
3048                let base = name
3049                    .split_once('(')
3050                    .map_or(name.as_str(), |(base, _)| base)
3051                    .trim();
3052                matches!(
3053                    base.to_ascii_uppercase().as_str(),
3054                    "CHAR"
3055                        | "NCHAR"
3056                        | "VARCHAR"
3057                        | "NVARCHAR"
3058                        | "TEXT"
3059                        | "NTEXT"
3060                        | "STRING"
3061                        | "CHARACTER VARYING"
3062                )
3063            }
3064            _ => false,
3065        }
3066    }
3067
3068    fn is_numeric_data_type(data_type: &DataType) -> bool {
3069        match data_type {
3070            DataType::TinyInt { .. }
3071            | DataType::SmallInt { .. }
3072            | DataType::Int { .. }
3073            | DataType::BigInt { .. }
3074            | DataType::Float { .. }
3075            | DataType::Double { .. }
3076            | DataType::Decimal { .. } => true,
3077            DataType::Custom { name } => {
3078                let base = name
3079                    .split_once('(')
3080                    .map_or(name.as_str(), |(base, _)| base)
3081                    .trim();
3082                matches!(
3083                    base.to_ascii_uppercase().as_str(),
3084                    "TINYINT"
3085                        | "SMALLINT"
3086                        | "INT"
3087                        | "INTEGER"
3088                        | "BIGINT"
3089                        | "DECIMAL"
3090                        | "NUMERIC"
3091                        | "REAL"
3092                        | "FLOAT"
3093                        | "MONEY"
3094                        | "SMALLMONEY"
3095                )
3096            }
3097            _ => false,
3098        }
3099    }
3100
3101    fn is_explicitly_numeric_expression(expr: &Expression) -> bool {
3102        if expr.inferred_type().is_some_and(Self::is_numeric_data_type) {
3103            return true;
3104        }
3105
3106        match expr {
3107            Expression::Literal(literal) => matches!(literal.as_ref(), Literal::Number(_)),
3108            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
3109                Self::is_numeric_data_type(&cast.to)
3110            }
3111            Expression::Alias(alias) => Self::is_explicitly_numeric_expression(&alias.this),
3112            Expression::Paren(paren) => Self::is_explicitly_numeric_expression(&paren.this),
3113            Expression::Neg(unary) => Self::is_explicitly_numeric_expression(&unary.this),
3114            _ => false,
3115        }
3116    }
3117
3118    fn is_postgres_numeric_to_char_format(format: &str) -> bool {
3119        let mut unquoted = String::with_capacity(format.len());
3120        let mut quoted = false;
3121        let mut chars = format.chars().peekable();
3122
3123        while let Some(ch) = chars.next() {
3124            if ch == '"' {
3125                if quoted && chars.peek() == Some(&'"') {
3126                    chars.next();
3127                } else {
3128                    quoted = !quoted;
3129                }
3130            } else if !quoted {
3131                unquoted.extend(ch.to_uppercase());
3132            }
3133        }
3134
3135        unquoted.contains(['9', '0'])
3136            || ["PR", "SG", "PL", "RN", "EEEE"]
3137                .iter()
3138                .any(|token| unquoted.contains(token))
3139    }
3140
3141    fn postgres_format_to_strftime(format: &str) -> String {
3142        const POSTGRES_FORMAT_TO_STRFTIME: &[(&str, &str)] = &[
3143            ("FMHH24", "%-H"),
3144            ("FMHH12", "%-I"),
3145            ("FMDDD", "%-j"),
3146            ("TMMonth", "%B"),
3147            ("TMMon", "%b"),
3148            ("TMDay", "%A"),
3149            ("TMDy", "%a"),
3150            ("YYYY", "%Y"),
3151            ("yyyy", "%Y"),
3152            ("HH24", "%H"),
3153            ("HH12", "%I"),
3154            ("FMDD", "%-d"),
3155            ("FMMM", "%-m"),
3156            ("FMMI", "%-M"),
3157            ("FMSS", "%-S"),
3158            ("DDD", "%j"),
3159            ("ddd", "%j"),
3160            ("YY", "%y"),
3161            ("yy", "%y"),
3162            ("MM", "%m"),
3163            ("mm", "%m"),
3164            ("DD", "%d"),
3165            ("dd", "%d"),
3166            ("MI", "%M"),
3167            ("mi", "%M"),
3168            ("SS", "%S"),
3169            ("ss", "%S"),
3170            ("US", "%f"),
3171            ("OF", "%z"),
3172            ("TZ", "%Z"),
3173            ("WW", "%U"),
3174            ("ww", "%U"),
3175            ("D", "%u"),
3176            ("d", "%u"),
3177        ];
3178        crate::format_tokens::convert_format_tokens(format, POSTGRES_FORMAT_TO_STRFTIME)
3179            .unwrap_or_else(|| format.to_string())
3180    }
3181
3182    fn formatted_str_to_time_or_fallback(
3183        mut args: Vec<Expression>,
3184        original_name: &str,
3185    ) -> Result<Expression> {
3186        let this = args.remove(0);
3187        let format = args.remove(0);
3188        if let Some(format) = Self::literal_string(&format) {
3189            Ok(Expression::StrToTime(Box::new(
3190                crate::expressions::StrToTime {
3191                    this: Box::new(this),
3192                    format: Self::postgres_format_to_strftime(format),
3193                    zone: None,
3194                    safe: None,
3195                    target_type: Some(Box::new(Expression::DataType(DataType::Custom {
3196                        name: "DATETIME2".to_string(),
3197                    }))),
3198                },
3199            )))
3200        } else {
3201            Ok(Expression::Function(Box::new(Function::new(
3202                original_name.to_string(),
3203                vec![this, format],
3204            ))))
3205        }
3206    }
3207
3208    fn formatted_str_to_date_or_fallback(
3209        mut args: Vec<Expression>,
3210        original_name: &str,
3211    ) -> Result<Expression> {
3212        let this = args.remove(0);
3213        let format = args.remove(0);
3214        if let Some(format) = Self::literal_string(&format) {
3215            Ok(Expression::StrToDate(Box::new(
3216                crate::expressions::StrToDate {
3217                    this: Box::new(this),
3218                    format: Some(Self::postgres_format_to_strftime(format)),
3219                    safe: None,
3220                },
3221            )))
3222        } else {
3223            Ok(Expression::Function(Box::new(Function::new(
3224                original_name.to_string(),
3225                vec![this, format],
3226            ))))
3227        }
3228    }
3229
3230    fn formatted_time_to_str_or_fallback(
3231        mut args: Vec<Expression>,
3232        original_name: &str,
3233    ) -> Result<Expression> {
3234        let this = args.remove(0);
3235        let format = args.remove(0);
3236        if let Some(format_string) = Self::literal_string(&format).map(str::to_owned) {
3237            if Self::is_explicitly_numeric_expression(&this)
3238                || Self::is_postgres_numeric_to_char_format(&format_string)
3239            {
3240                return Ok(Expression::Function(Box::new(Function::new(
3241                    original_name.to_string(),
3242                    vec![this, format],
3243                ))));
3244            }
3245
3246            Ok(Expression::TimeToStr(Box::new(
3247                crate::expressions::TimeToStr {
3248                    this: Box::new(this),
3249                    format: Self::postgres_format_to_strftime(&format_string),
3250                    culture: None,
3251                    zone: None,
3252                },
3253            )))
3254        } else {
3255            Ok(Expression::Function(Box::new(Function::new(
3256                original_name.to_string(),
3257                vec![this, format],
3258            ))))
3259        }
3260    }
3261
3262    fn transform_aggregate_function(
3263        &self,
3264        mut f: Box<crate::expressions::AggregateFunction>,
3265    ) -> Result<Expression> {
3266        let name_upper = f.name.to_uppercase();
3267        if matches!(
3268            name_upper.as_str(),
3269            "SUM"
3270                | "AVG"
3271                | "MIN"
3272                | "MAX"
3273                | "COUNT"
3274                | "COUNT_BIG"
3275                | "ANY_VALUE"
3276                | "APPROX_COUNT_DISTINCT"
3277                | "STDEV"
3278                | "STDEVP"
3279                | "VAR"
3280                | "VARP"
3281                | "BOOL_AND"
3282                | "BOOL_OR"
3283                | "LOGICAL_AND"
3284                | "LOGICAL_OR"
3285                | "BIT_AND"
3286                | "BIT_OR"
3287                | "BIT_XOR"
3288        ) {
3289            f.order_by.clear();
3290        }
3291
3292        match name_upper.as_str() {
3293            // GROUP_CONCAT -> STRING_AGG
3294            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3295                Function::new("STRING_AGG".to_string(), f.args),
3296            ))),
3297
3298            // LISTAGG -> STRING_AGG
3299            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3300                "STRING_AGG".to_string(),
3301                f.args,
3302            )))),
3303
3304            // ARRAY_AGG -> Not directly supported in SQL Server
3305            // Would need to use FOR XML PATH or STRING_AGG
3306            "ARRAY_AGG" if !f.args.is_empty() => {
3307                // Fall back to STRING_AGG (loses array semantics)
3308                Ok(Expression::Function(Box::new(Function::new(
3309                    "STRING_AGG".to_string(),
3310                    f.args,
3311                ))))
3312            }
3313
3314            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
3315            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
3316                let mut args = f.args;
3317                Self::transform_logical_aggregate(args.remove(0), f.filter, "MIN")
3318            }
3319            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
3320                let mut args = f.args;
3321                Self::transform_logical_aggregate(args.remove(0), f.filter, "MAX")
3322            }
3323
3324            // Pass through everything else
3325            _ => Ok(Expression::AggregateFunction(f)),
3326        }
3327    }
3328
3329    fn without_inert_ordering(
3330        mut aggregate: Box<crate::expressions::AggFunc>,
3331    ) -> Box<crate::expressions::AggFunc> {
3332        aggregate.order_by.clear();
3333        aggregate
3334    }
3335
3336    /// Transform CTEs to add auto-aliases to bare expressions in SELECT
3337    /// In TSQL, when a CTE doesn't have explicit column aliases, bare expressions
3338    /// in the SELECT need to be aliased
3339    fn transform_cte(&self, cte: Cte) -> Result<Expression> {
3340        Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
3341    }
3342
3343    /// Inner method to transform a CTE, returning the modified Cte struct
3344    fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
3345        // Only transform if the CTE doesn't have explicit column aliases
3346        // If it has column aliases like `WITH t(a, b) AS (...)`, we don't need to auto-alias
3347        if cte.columns.is_empty() {
3348            cte.this = self.qualify_derived_table_outputs(cte.this);
3349        }
3350        cte
3351    }
3352
3353    /// Transform Subqueries to add auto-aliases to bare expressions in SELECT
3354    /// In TSQL, when a subquery has a table alias but no column aliases,
3355    /// bare expressions need to be aliased
3356    fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
3357        // Only transform if the subquery has a table alias but no column aliases
3358        // e.g., `(SELECT 1) AS subq` needs aliasing, but `(SELECT 1) AS subq(a)` doesn't
3359        if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
3360            subquery.this = self.qualify_derived_table_outputs(subquery.this);
3361        }
3362        Ok(Expression::Subquery(Box::new(subquery)))
3363    }
3364
3365    /// Add aliases to bare (unaliased) expressions in a SELECT statement
3366    /// This transforms expressions like `SELECT 1` into `SELECT 1 AS [1]`
3367    /// BUT only when the SELECT has no FROM clause (i.e., it's a value expression)
3368    fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
3369        match expr {
3370            Expression::Select(mut select) => {
3371                // Only auto-alias if the SELECT has NO from clause
3372                // If there's a FROM clause, column references already have names from the source tables
3373                let has_from = select.from.is_some();
3374                if !has_from {
3375                    select.expressions = select
3376                        .expressions
3377                        .into_iter()
3378                        .map(|e| self.maybe_alias_expression(e))
3379                        .collect();
3380                }
3381                Expression::Select(select)
3382            }
3383            // For UNION/INTERSECT/EXCEPT, transform the first SELECT
3384            Expression::Union(mut u) => {
3385                let left = std::mem::replace(&mut u.left, Expression::Null(Null));
3386                u.left = self.qualify_derived_table_outputs(left);
3387                Expression::Union(u)
3388            }
3389            Expression::Intersect(mut i) => {
3390                let left = std::mem::replace(&mut i.left, Expression::Null(Null));
3391                i.left = self.qualify_derived_table_outputs(left);
3392                Expression::Intersect(i)
3393            }
3394            Expression::Except(mut e) => {
3395                let left = std::mem::replace(&mut e.left, Expression::Null(Null));
3396                e.left = self.qualify_derived_table_outputs(left);
3397                Expression::Except(e)
3398            }
3399            // Already wrapped in a Subquery (nested), transform the inner
3400            Expression::Subquery(mut s) => {
3401                s.this = self.qualify_derived_table_outputs(s.this);
3402                Expression::Subquery(s)
3403            }
3404            // Pass through anything else
3405            other => other,
3406        }
3407    }
3408
3409    /// Add an alias to a bare expression if needed
3410    /// Returns the expression unchanged if it already has an alias or is a star
3411    /// NOTE: This is only called for SELECTs without a FROM clause, so all bare
3412    /// expressions (including identifiers and columns) need to be aliased.
3413    fn maybe_alias_expression(&self, expr: Expression) -> Expression {
3414        match &expr {
3415            // Already has an alias, leave it alone
3416            Expression::Alias(_) => expr,
3417            // Multiple aliases, leave it alone
3418            Expression::Aliases(_) => expr,
3419            // Star (including qualified star like t.*) doesn't need an alias
3420            Expression::Star(_) => expr,
3421            // When there's no FROM clause (which is the only case when this method is called),
3422            // we need to alias columns and identifiers too since they're standalone values
3423            // that need explicit names for the derived table output.
3424            // Everything else (literals, functions, columns, identifiers, etc.) needs an alias
3425            _ => {
3426                if let Some(output_name) = self.get_output_name(&expr) {
3427                    Expression::Alias(Box::new(Alias {
3428                        this: expr,
3429                        alias: Identifier {
3430                            name: output_name,
3431                            quoted: true, // Force quoting for TSQL bracket syntax
3432                            trailing_comments: Vec::new(),
3433                            span: None,
3434                        },
3435                        column_aliases: Vec::new(),
3436                        alias_explicit_as: false,
3437                        alias_keyword: None,
3438                        pre_alias_comments: Vec::new(),
3439                        trailing_comments: Vec::new(),
3440                        inferred_type: None,
3441                    }))
3442                } else {
3443                    // No output name, leave as-is (shouldn't happen for valid expressions)
3444                    expr
3445                }
3446            }
3447        }
3448    }
3449
3450    /// Get the "output name" of an expression for auto-aliasing
3451    /// For literals, this is the literal value
3452    /// For columns, this is the column name
3453    fn get_output_name(&self, expr: &Expression) -> Option<String> {
3454        match expr {
3455            // Literals - use the literal value as the name
3456            Expression::Literal(lit) => match lit.as_ref() {
3457                Literal::Number(n) => Some(n.clone()),
3458                Literal::String(s) => Some(s.clone()),
3459                Literal::HexString(h) => Some(format!("0x{}", h)),
3460                Literal::HexNumber(h) => Some(format!("0x{}", h)),
3461                Literal::BitString(b) => Some(format!("b{}", b)),
3462                Literal::ByteString(b) => Some(format!("b'{}'", b)),
3463                Literal::NationalString(s) => Some(format!("N'{}'", s)),
3464                Literal::Date(d) => Some(d.clone()),
3465                Literal::Time(t) => Some(t.clone()),
3466                Literal::Timestamp(ts) => Some(ts.clone()),
3467                Literal::Datetime(dt) => Some(dt.clone()),
3468                Literal::TripleQuotedString(s, _) => Some(s.clone()),
3469                Literal::EscapeString(s) => Some(s.clone()),
3470                Literal::DollarString(s) => Some(s.clone()),
3471                Literal::RawString(s) => Some(s.clone()),
3472            },
3473            // Columns - use the column name
3474            Expression::Column(col) => Some(col.name.name.clone()),
3475            // Identifiers - use the identifier name
3476            Expression::Identifier(ident) => Some(ident.name.clone()),
3477            // Boolean literals
3478            Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
3479            // NULL
3480            Expression::Null(_) => Some("NULL".to_string()),
3481            // For functions, use the function name as a fallback
3482            Expression::Function(f) => Some(f.name.clone()),
3483            // For aggregates, use the function name
3484            Expression::AggregateFunction(f) => Some(f.name.clone()),
3485            // For other expressions, generate a generic name
3486            _ => Some(format!("_col_{}", 0)),
3487        }
3488    }
3489
3490    /// Helper to uppercase the first argument if it's an identifier or column (for DATEDIFF, DATEADD units)
3491    fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
3492        use crate::expressions::Identifier;
3493        if !args.is_empty() {
3494            match &args[0] {
3495                Expression::Identifier(id) => {
3496                    args[0] = Expression::Identifier(Identifier {
3497                        name: id.name.to_uppercase(),
3498                        quoted: id.quoted,
3499                        trailing_comments: id.trailing_comments.clone(),
3500                        span: None,
3501                    });
3502                }
3503                Expression::Var(v) => {
3504                    args[0] = Expression::Identifier(Identifier {
3505                        name: v.this.to_uppercase(),
3506                        quoted: false,
3507                        trailing_comments: Vec::new(),
3508                        span: None,
3509                    });
3510                }
3511                Expression::Column(col) if col.table.is_none() => {
3512                    args[0] = Expression::Identifier(Identifier {
3513                        name: col.name.name.to_uppercase(),
3514                        quoted: col.name.quoted,
3515                        trailing_comments: col.name.trailing_comments.clone(),
3516                        span: None,
3517                    });
3518                }
3519                _ => {}
3520            }
3521        }
3522        args
3523    }
3524}
3525
3526#[cfg(test)]
3527mod tests {
3528    use super::*;
3529    use crate::dialects::Dialect;
3530
3531    fn transpile_to_tsql(sql: &str) -> String {
3532        let dialect = Dialect::get(DialectType::Generic);
3533        let result = dialect
3534            .transpile(sql, DialectType::TSQL)
3535            .expect("Transpile failed");
3536        result[0].clone()
3537    }
3538
3539    #[test]
3540    fn test_nvl_to_isnull() {
3541        let result = transpile_to_tsql("SELECT NVL(a, b)");
3542        assert!(
3543            result.contains("ISNULL"),
3544            "Expected ISNULL, got: {}",
3545            result
3546        );
3547    }
3548
3549    #[test]
3550    fn test_coalesce_to_isnull() {
3551        let result = transpile_to_tsql("SELECT COALESCE(a, b)");
3552        assert!(
3553            result.contains("ISNULL"),
3554            "Expected ISNULL, got: {}",
3555            result
3556        );
3557    }
3558
3559    #[test]
3560    fn test_basic_select() {
3561        let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
3562        assert!(result.contains("SELECT"));
3563        assert!(result.contains("FROM users"));
3564    }
3565
3566    #[test]
3567    fn test_length_to_len() {
3568        let result = transpile_to_tsql("SELECT LENGTH(name)");
3569        assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
3570    }
3571
3572    #[test]
3573    fn test_issue_374_tsql_parse_then_generate_uses_len() {
3574        let sql = "SELECT LEN(table.col1) - LEN(table.col2) FROM table";
3575        let ast = Dialect::get(DialectType::TSQL)
3576            .parse(sql)
3577            .expect("T-SQL should parse");
3578        let expression = &ast[0];
3579
3580        for target in [DialectType::TSQL, DialectType::Fabric] {
3581            let generated = Dialect::get(target)
3582                .generate(expression)
3583                .expect("AST should generate");
3584            assert_eq!(generated, sql, "failed for target {target:?}");
3585        }
3586
3587        let standard_sql = "SELECT LENGTH(table.col1) - LENGTH(table.col2) FROM table";
3588        for target in [DialectType::Generic, DialectType::PostgreSQL] {
3589            let generated = Dialect::get(target)
3590                .generate(expression)
3591                .expect("AST should generate");
3592            assert_eq!(generated, standard_sql, "failed for target {target:?}");
3593        }
3594    }
3595
3596    #[test]
3597    fn test_now_to_getdate() {
3598        let result = transpile_to_tsql("SELECT NOW()");
3599        assert!(
3600            result.contains("GETDATE"),
3601            "Expected GETDATE, got: {}",
3602            result
3603        );
3604    }
3605
3606    #[test]
3607    fn test_group_concat_to_string_agg() {
3608        let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
3609        assert!(
3610            result.contains("STRING_AGG"),
3611            "Expected STRING_AGG, got: {}",
3612            result
3613        );
3614    }
3615
3616    #[test]
3617    fn test_listagg_to_string_agg() {
3618        let result = transpile_to_tsql("SELECT LISTAGG(name)");
3619        assert!(
3620            result.contains("STRING_AGG"),
3621            "Expected STRING_AGG, got: {}",
3622            result
3623        );
3624    }
3625
3626    #[test]
3627    fn test_ln_to_log() {
3628        let result = transpile_to_tsql("SELECT LN(x)");
3629        assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
3630    }
3631
3632    #[test]
3633    fn test_stddev_to_stdev() {
3634        let result = transpile_to_tsql("SELECT STDDEV(x)");
3635        assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
3636    }
3637
3638    #[test]
3639    fn test_bracket_identifiers() {
3640        // SQL Server uses square brackets for identifiers
3641        let dialect = Dialect::get(DialectType::TSQL);
3642        let config = dialect.generator_config();
3643        assert_eq!(config.identifier_quote, '[');
3644    }
3645
3646    #[test]
3647    fn test_json_query_isnull_wrapper_simple() {
3648        // JSON_QUERY with two args needs ISNULL wrapper when transpiling to TSQL
3649        let dialect = Dialect::get(DialectType::TSQL);
3650        let result = dialect
3651            .transpile(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
3652            .expect("transpile failed");
3653        assert!(
3654            result[0].contains("ISNULL"),
3655            "JSON_QUERY should be wrapped with ISNULL: {}",
3656            result[0]
3657        );
3658    }
3659
3660    #[test]
3661    fn test_json_query_isnull_wrapper_nested() {
3662        let dialect = Dialect::get(DialectType::TSQL);
3663        let result = dialect
3664            .transpile(
3665                r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
3666                DialectType::TSQL,
3667            )
3668            .expect("transpile failed");
3669        let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
3670        assert_eq!(
3671            result[0], expected,
3672            "JSON_QUERY should be wrapped with ISNULL"
3673        );
3674    }
3675}