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, Function, Identifier, In,
17    Join, JoinKind, LikeOp, Literal, Null, Over, QuantifiedOp, Select, Star, StringAggFunc,
18    Subquery, UnaryFunc, Where,
19};
20#[cfg(feature = "generate")]
21use crate::generator::GeneratorConfig;
22use crate::tokens::TokenizerConfig;
23use std::collections::HashMap;
24
25/// T-SQL (SQL Server) dialect
26pub struct TSQLDialect;
27
28impl DialectImpl for TSQLDialect {
29    fn dialect_type(&self) -> DialectType {
30        DialectType::TSQL
31    }
32
33    fn tokenizer_config(&self) -> TokenizerConfig {
34        let mut config = TokenizerConfig::default();
35        // SQL Server uses square brackets for identifiers
36        config.identifiers.insert('[', ']');
37        // SQL Server also supports double quotes (when QUOTED_IDENTIFIER is ON)
38        config.identifiers.insert('"', '"');
39        // SQL Server uses 0x-prefixed binary/varbinary hex literals.
40        config.hex_number_strings = true;
41        config
42    }
43
44    #[cfg(feature = "generate")]
45
46    fn generator_config(&self) -> GeneratorConfig {
47        use crate::generator::IdentifierQuoteStyle;
48        GeneratorConfig {
49            // Use square brackets by default for SQL Server
50            identifier_quote: '[',
51            identifier_quote_style: IdentifierQuoteStyle::BRACKET,
52            dialect: Some(DialectType::TSQL),
53            // T-SQL specific settings from Python sqlglot
54            // SQL Server uses TOP/FETCH instead of LIMIT
55            limit_fetch_style: crate::generator::LimitFetchStyle::FetchFirst,
56            // NULLS FIRST/LAST not supported in SQL Server
57            null_ordering_supported: false,
58            // SQL Server does not support SQL:2003 aggregate FILTER clauses.
59            aggregate_filter_supported: false,
60            // SQL Server supports SELECT INTO
61            supports_select_into: true,
62            // ALTER TABLE doesn't require COLUMN keyword
63            alter_table_include_column_keyword: false,
64            // Computed columns don't need type declaration
65            computed_column_with_type: false,
66            // RECURSIVE keyword not required in CTEs
67            cte_recursive_keyword_required: false,
68            // Ensure boolean expressions
69            ensure_bools: true,
70            // CONCAT requires at least 2 args
71            supports_single_arg_concat: false,
72            // TABLESAMPLE REPEATABLE
73            tablesample_seed_keyword: "REPEATABLE",
74            // JSON path without brackets
75            json_path_bracketed_key_supported: false,
76            // No TO_NUMBER function
77            supports_to_number: false,
78            // SET operation modifiers not supported
79            set_op_modifiers: false,
80            // COPY params need equals sign
81            copy_params_eq_required: true,
82            // No ALL clause for EXCEPT/INTERSECT
83            except_intersect_support_all_clause: false,
84            // ALTER SET is wrapped
85            alter_set_wrapped: true,
86            // T-SQL supports TRY_CAST
87            try_supported: true,
88            // No NVL2 support
89            nvl2_supported: false,
90            // TSQL uses = instead of DEFAULT for parameter defaults
91            parameter_default_equals: true,
92            // No window EXCLUDE support
93            supports_window_exclude: false,
94            // No DISTINCT with multiple args
95            multi_arg_distinct: false,
96            // TSQL doesn't support FOR UPDATE/SHARE
97            locking_reads_supported: false,
98            ..Default::default()
99        }
100    }
101
102    #[cfg(feature = "transpile")]
103
104    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
105        // Transform column data types in DDL (transform_recursive skips them by design).
106        if let Expression::CreateTable(mut ct) = expr {
107            for col in &mut ct.columns {
108                if let Ok(Expression::DataType(new_dt)) =
109                    self.transform_data_type(col.data_type.clone())
110                {
111                    col.data_type = new_dt;
112                }
113            }
114            return Ok(Expression::CreateTable(ct));
115        }
116
117        match expr {
118            // ===== SELECT a = 1 → SELECT 1 AS a =====
119            // In T-SQL, `SELECT a = expr` is equivalent to `SELECT expr AS a`
120            // BUT: `SELECT @a = expr` is a variable assignment, not an alias!
121            // Python sqlglot handles this at parser level via _parse_projections()
122            Expression::Select(mut select) => {
123                select.expressions = select
124                    .expressions
125                    .into_iter()
126                    .map(|e| {
127                        match e {
128                            Expression::Eq(op) => {
129                                // Check if left side is an identifier (column name)
130                                // Don't transform if it's a variable (starts with @)
131                                match &op.left {
132                                    Expression::Column(col)
133                                        if col.table.is_none()
134                                            && !col.name.name.starts_with('@') =>
135                                    {
136                                        Expression::Alias(Box::new(Alias {
137                                            this: op.right,
138                                            alias: col.name.clone(),
139                                            column_aliases: Vec::new(),
140                                            alias_explicit_as: false,
141                                            alias_keyword: None,
142                                            pre_alias_comments: Vec::new(),
143                                            trailing_comments: Vec::new(),
144                                            inferred_type: None,
145                                        }))
146                                    }
147                                    Expression::Identifier(ident)
148                                        if !ident.name.starts_with('@') =>
149                                    {
150                                        Expression::Alias(Box::new(Alias {
151                                            this: op.right,
152                                            alias: ident.clone(),
153                                            column_aliases: Vec::new(),
154                                            alias_explicit_as: false,
155                                            alias_keyword: None,
156                                            pre_alias_comments: Vec::new(),
157                                            trailing_comments: Vec::new(),
158                                            inferred_type: None,
159                                        }))
160                                    }
161                                    _ => Expression::Eq(op),
162                                }
163                            }
164                            other => other,
165                        }
166                    })
167                    .collect();
168
169                Self::normalize_frame_incompatible_window_functions(&mut select);
170
171                let outer_qualifier = Self::single_select_source_qualifier(&select);
172                if let Some(ref mut where_clause) = select.where_clause {
173                    where_clause.this = Self::rewrite_tuple_in_subquery_predicates(
174                        std::mem::replace(&mut where_clause.this, Expression::Null(Null)),
175                        outer_qualifier.as_ref(),
176                        false,
177                    );
178                }
179
180                // Transform CTEs in the WITH clause to add auto-aliases
181                if let Some(ref mut with) = select.with {
182                    with.ctes = with
183                        .ctes
184                        .drain(..)
185                        .map(|cte| self.transform_cte_inner(cte))
186                        .collect();
187                }
188
189                Self::rewrite_comma_lateral_sources_to_joins(&mut select);
190
191                Ok(Expression::Select(select))
192            }
193
194            // ===== Data Type Mappings =====
195            Expression::DataType(dt) => self.transform_data_type(dt),
196
197            // ===== Boolean IS TRUE/FALSE -> T-SQL 3VL truth table =====
198            // T-SQL doesn't have IS TRUE/IS FALSE syntax. Negated forms must
199            // explicitly preserve UNKNOWN/NULL rows instead of using NOT (x = n).
200            Expression::IsTrue(it) => Ok(Self::boolean_test_predicate(it.this, true, it.not)),
201            Expression::IsFalse(it) => Ok(Self::boolean_test_predicate(it.this, false, it.not)),
202
203            // Note: CASE WHEN boolean conditions are handled in ensure_bools preprocessing
204
205            // NOT IN -> NOT ... IN for TSQL (TSQL prefers NOT prefix)
206            Expression::In(mut in_expr) if in_expr.not => {
207                in_expr.not = false;
208                Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
209                    this: Expression::In(in_expr),
210                    inferred_type: None,
211                })))
212            }
213
214            // COALESCE with 2 args -> ISNULL in SQL Server (optimization)
215            // Note: COALESCE works in SQL Server, ISNULL is just more idiomatic
216            Expression::Coalesce(f) if f.expressions.len() == 2 => Ok(Expression::Function(
217                Box::new(Function::new("ISNULL".to_string(), f.expressions)),
218            )),
219
220            // NVL -> ISNULL in SQL Server
221            Expression::Nvl(f) => Ok(Expression::Function(Box::new(Function::new(
222                "ISNULL".to_string(),
223                vec![f.this, f.expression],
224            )))),
225
226            // GROUP_CONCAT -> STRING_AGG in SQL Server (SQL Server 2017+)
227            Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
228                this: f.this,
229                separator: f.separator,
230                order_by: f.order_by,
231                distinct: f.distinct,
232                filter: f.filter,
233                limit: None,
234                inferred_type: None,
235            }))),
236
237            // LISTAGG -> STRING_AGG in SQL Server (SQL Server 2017+)
238            Expression::ListAgg(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
239                this: f.this,
240                separator: f.separator,
241                order_by: f.order_by,
242                distinct: f.distinct,
243                filter: f.filter,
244                limit: None,
245                inferred_type: None,
246            }))),
247
248            // T-SQL/Fabric do not have boolean aggregates. Preserve PostgreSQL NULL
249            // semantics by returning NULL for unknown input predicates.
250            Expression::LogicalAnd(f) => Self::transform_logical_aggregate(f.this, f.filter, "MIN"),
251            Expression::LogicalOr(f) => Self::transform_logical_aggregate(f.this, f.filter, "MAX"),
252
253            // TryCast -> TRY_CAST (SQL Server supports TRY_CAST starting from 2012)
254            Expression::TryCast(c) => Ok(Expression::TryCast(c)),
255
256            // SafeCast -> TRY_CAST
257            Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
258
259            // ILIKE -> LOWER() LIKE LOWER() in SQL Server (no ILIKE support)
260            Expression::ILike(op) => {
261                // SQL Server is case-insensitive by default based on collation
262                // But for explicit case-insensitive matching, use LOWER
263                let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
264                let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
265                Ok(Expression::Like(Box::new(LikeOp {
266                    left: lower_left,
267                    right: lower_right,
268                    escape: op.escape,
269                    quantifier: op.quantifier,
270                    inferred_type: None,
271                })))
272            }
273
274            // || (Concat operator) -> + in SQL Server
275            // SQL Server uses + for string concatenation
276            Expression::Concat(op) => {
277                // Convert || to + operator (Add)
278                Ok(Expression::Add(op))
279            }
280
281            // RANDOM -> RAND in SQL Server
282            Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
283                seed: None,
284                lower: None,
285                upper: None,
286            }))),
287
288            // UNNEST -> Not directly supported, use CROSS APPLY with STRING_SPLIT or OPENJSON
289            Expression::Unnest(f) => {
290                // For basic cases, we'll use a placeholder
291                // Full support would require context-specific transformation
292                Ok(Expression::Function(Box::new(Function::new(
293                    "OPENJSON".to_string(),
294                    vec![f.this],
295                ))))
296            }
297
298            // EXPLODE -> Similar to UNNEST, use CROSS APPLY
299            Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
300                "OPENJSON".to_string(),
301                vec![f.this],
302            )))),
303
304            // PostgreSQL LATERAL join forms -> SQL Server APPLY.
305            Expression::Join(join) => Ok(Expression::Join(Box::new(
306                Self::transform_lateral_join_to_apply(*join)?,
307            ))),
308
309            // LENGTH -> LEN in SQL Server
310            Expression::Length(f) => Ok(Expression::Function(Box::new(Function::new(
311                "LEN".to_string(),
312                vec![f.this],
313            )))),
314
315            // STDDEV -> STDEV in SQL Server
316            Expression::Stddev(f) => Ok(Expression::Function(Box::new(Function::new(
317                "STDEV".to_string(),
318                vec![f.this],
319            )))),
320            Expression::StddevSamp(f) => Ok(Expression::Function(Box::new(Function::new(
321                "STDEV".to_string(),
322                vec![f.this],
323            )))),
324            Expression::StddevPop(f) => Ok(Expression::Function(Box::new(Function::new(
325                "STDEVP".to_string(),
326                vec![f.this],
327            )))),
328
329            // Boolean literals TRUE/FALSE -> 1/0 in SQL Server
330            Expression::Boolean(b) => {
331                let value = if b.value { 1 } else { 0 };
332                Ok(Expression::Literal(Box::new(
333                    crate::expressions::Literal::Number(value.to_string()),
334                )))
335            }
336
337            // LN -> LOG in SQL Server
338            Expression::Ln(f) => Ok(Expression::Function(Box::new(Function::new(
339                "LOG".to_string(),
340                vec![f.this],
341            )))),
342
343            // ===== Date/time =====
344            // CurrentDate -> CAST(GETDATE() AS DATE) in SQL Server
345            Expression::CurrentDate(_) => {
346                let getdate =
347                    Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])));
348                Ok(Expression::Cast(Box::new(crate::expressions::Cast {
349                    this: getdate,
350                    to: crate::expressions::DataType::Date,
351                    trailing_comments: Vec::new(),
352                    double_colon_syntax: false,
353                    format: None,
354                    default: None,
355                    inferred_type: None,
356                })))
357            }
358
359            // CurrentTimestamp -> GETDATE() in SQL Server
360            Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
361                "GETDATE".to_string(),
362                vec![],
363            )))),
364
365            // DateDiff -> DATEDIFF
366            Expression::DateDiff(f) => {
367                // TSQL: DATEDIFF(unit, start, end)
368                let unit_str = match f.unit {
369                    Some(crate::expressions::IntervalUnit::Year) => "YEAR",
370                    Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
371                    Some(crate::expressions::IntervalUnit::Month) => "MONTH",
372                    Some(crate::expressions::IntervalUnit::Week) => "WEEK",
373                    Some(crate::expressions::IntervalUnit::Day) => "DAY",
374                    Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
375                    Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
376                    Some(crate::expressions::IntervalUnit::Second) => "SECOND",
377                    Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
378                    Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
379                    Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
380                    None => "DAY",
381                };
382                let unit = Expression::Identifier(crate::expressions::Identifier {
383                    name: unit_str.to_string(),
384                    quoted: false,
385                    trailing_comments: Vec::new(),
386                    span: None,
387                });
388                Ok(Expression::Function(Box::new(Function::new(
389                    "DATEDIFF".to_string(),
390                    vec![unit, f.expression, f.this], // Note: order is different in TSQL
391                ))))
392            }
393
394            // DateAdd -> DATEADD
395            Expression::DateAdd(f) => {
396                let unit_str = match f.unit {
397                    crate::expressions::IntervalUnit::Year => "YEAR",
398                    crate::expressions::IntervalUnit::Quarter => "QUARTER",
399                    crate::expressions::IntervalUnit::Month => "MONTH",
400                    crate::expressions::IntervalUnit::Week => "WEEK",
401                    crate::expressions::IntervalUnit::Day => "DAY",
402                    crate::expressions::IntervalUnit::Hour => "HOUR",
403                    crate::expressions::IntervalUnit::Minute => "MINUTE",
404                    crate::expressions::IntervalUnit::Second => "SECOND",
405                    crate::expressions::IntervalUnit::Millisecond => "MILLISECOND",
406                    crate::expressions::IntervalUnit::Microsecond => "MICROSECOND",
407                    crate::expressions::IntervalUnit::Nanosecond => "NANOSECOND",
408                };
409                let unit = Expression::Identifier(crate::expressions::Identifier {
410                    name: unit_str.to_string(),
411                    quoted: false,
412                    trailing_comments: Vec::new(),
413                    span: None,
414                });
415                Ok(Expression::Function(Box::new(Function::new(
416                    "DATEADD".to_string(),
417                    vec![unit, f.interval, f.this],
418                ))))
419            }
420
421            // ===== UUID =====
422            // Uuid -> NEWID in SQL Server
423            Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
424                "NEWID".to_string(),
425                vec![],
426            )))),
427
428            // ===== Conditional =====
429            // IfFunc -> IIF in SQL Server
430            Expression::IfFunc(f) => {
431                let false_val = f
432                    .false_value
433                    .unwrap_or(Expression::Null(crate::expressions::Null));
434                Ok(Expression::Function(Box::new(Function::new(
435                    "IIF".to_string(),
436                    vec![f.condition, f.true_value, false_val],
437                ))))
438            }
439
440            // ===== String functions =====
441            // StringAgg -> STRING_AGG in SQL Server 2017+ - keep as-is to preserve ORDER BY
442            Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
443
444            // LastDay -> EOMONTH (note: TSQL doesn't support date part argument)
445            Expression::LastDay(f) => Ok(Expression::Function(Box::new(Function::new(
446                "EOMONTH".to_string(),
447                vec![f.this.clone()],
448            )))),
449
450            // Ceil -> CEILING
451            Expression::Ceil(f) => Ok(Expression::Function(Box::new(Function::new(
452                "CEILING".to_string(),
453                vec![f.this],
454            )))),
455
456            // Repeat -> REPLICATE in SQL Server
457            Expression::Repeat(f) => Ok(Expression::Function(Box::new(Function::new(
458                "REPLICATE".to_string(),
459                vec![f.this, f.times],
460            )))),
461
462            // Chr -> CHAR in SQL Server
463            Expression::Chr(f) => Ok(Expression::Function(Box::new(Function::new(
464                "CHAR".to_string(),
465                vec![f.this],
466            )))),
467
468            // ===== Variance =====
469            // VarPop -> VARP
470            Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
471                "VARP".to_string(),
472                vec![f.this],
473            )))),
474
475            // Variance -> VAR
476            Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
477                "VAR".to_string(),
478                vec![f.this],
479            )))),
480            Expression::VarSamp(f) => Ok(Expression::Function(Box::new(Function::new(
481                "VAR".to_string(),
482                vec![f.this],
483            )))),
484
485            // ===== Hash functions =====
486            // MD5Digest -> HASHBYTES('MD5', ...)
487            Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
488                "HASHBYTES".to_string(),
489                vec![Expression::string("MD5"), *f.this],
490            )))),
491
492            // SHA -> HASHBYTES('SHA1', ...)
493            Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
494                "HASHBYTES".to_string(),
495                vec![Expression::string("SHA1"), f.this],
496            )))),
497
498            // SHA1Digest -> HASHBYTES('SHA1', ...)
499            Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
500                "HASHBYTES".to_string(),
501                vec![Expression::string("SHA1"), f.this],
502            )))),
503
504            // ===== Array functions =====
505            // ArrayToString -> STRING_AGG
506            Expression::ArrayToString(f) => Ok(Expression::Function(Box::new(Function::new(
507                "STRING_AGG".to_string(),
508                vec![f.this],
509            )))),
510
511            // ===== DDL Column Constraints =====
512            // AutoIncrementColumnConstraint -> IDENTITY in SQL Server
513            Expression::AutoIncrementColumnConstraint(_) => Ok(Expression::Function(Box::new(
514                Function::new("IDENTITY".to_string(), vec![]),
515            ))),
516
517            // ===== DDL three-part name stripping =====
518            // TSQL strips database (catalog) prefix from 3-part names for CREATE VIEW/DROP VIEW
519            // Python sqlglot: expression.this.set("catalog", None)
520            Expression::CreateView(mut view) => {
521                // Strip catalog from three-part name (a.b.c -> b.c)
522                view.name.catalog = None;
523                Ok(Expression::CreateView(view))
524            }
525
526            Expression::DropView(mut view) => {
527                // Strip catalog from three-part name (a.b.c -> b.c)
528                view.name.catalog = None;
529                Ok(Expression::DropView(view))
530            }
531
532            // ParseJson: handled by generator (emits just the string literal for TSQL)
533
534            // JSONExtract with variant_extract (Snowflake colon syntax) -> ISNULL(JSON_QUERY, JSON_VALUE)
535            Expression::JSONExtract(e) if e.variant_extract.is_some() => {
536                let path = match *e.expression {
537                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
538                        let Literal::String(s) = lit.as_ref() else {
539                            unreachable!()
540                        };
541                        let normalized = if s.starts_with('$') {
542                            s.clone()
543                        } else if s.starts_with('[') {
544                            format!("${}", s)
545                        } else {
546                            format!("$.{}", s)
547                        };
548                        Expression::Literal(Box::new(Literal::String(normalized)))
549                    }
550                    other => other,
551                };
552                let json_query = Expression::Function(Box::new(Function::new(
553                    "JSON_QUERY".to_string(),
554                    vec![(*e.this).clone(), path.clone()],
555                )));
556                let json_value = Expression::Function(Box::new(Function::new(
557                    "JSON_VALUE".to_string(),
558                    vec![*e.this, path],
559                )));
560                Ok(Expression::Function(Box::new(Function::new(
561                    "ISNULL".to_string(),
562                    vec![json_query, json_value],
563                ))))
564            }
565
566            // Generic function transformations
567            Expression::Function(f) => self.transform_function(*f),
568
569            // Generic aggregate function transformations
570            Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
571
572            // ===== CTEs need auto-aliased outputs =====
573            // In TSQL, bare expressions in CTEs need explicit aliases
574            Expression::Cte(cte) => self.transform_cte(*cte),
575
576            // ===== Subqueries need auto-aliased outputs =====
577            // In TSQL, bare expressions in aliased subqueries need explicit aliases
578            Expression::Subquery(subquery) => self.transform_subquery(*subquery),
579
580            // Convert JsonQuery struct to ISNULL(JSON_QUERY(..., path), JSON_VALUE(..., path))
581            Expression::JsonQuery(f) => {
582                let json_query = Expression::Function(Box::new(Function::new(
583                    "JSON_QUERY".to_string(),
584                    vec![f.this.clone(), f.path.clone()],
585                )));
586                let json_value = Expression::Function(Box::new(Function::new(
587                    "JSON_VALUE".to_string(),
588                    vec![f.this, f.path],
589                )));
590                Ok(Expression::Function(Box::new(Function::new(
591                    "ISNULL".to_string(),
592                    vec![json_query, json_value],
593                ))))
594            }
595            // Convert JsonValue struct to Function("JSON_VALUE", ...) for uniform handling
596            Expression::JsonValue(f) => Ok(Expression::Function(Box::new(Function::new(
597                "JSON_VALUE".to_string(),
598                vec![f.this, f.path],
599            )))),
600
601            // PostgreSQL pg_get_querydef can emit scalar array comparisons for
602            // literal arrays/tuples. T-SQL/Fabric require IN for this shape.
603            Expression::Any(ref q) if matches!(&q.op, Some(QuantifiedOp::Eq)) => {
604                match Self::scalar_array_comparison_values(&q.subquery) {
605                    Some(expressions) if expressions.is_empty() => {
606                        Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp::new(
607                            Expression::Literal(Box::new(Literal::Number("1".to_string()))),
608                            Expression::Literal(Box::new(Literal::Number("0".to_string()))),
609                        ))))
610                    }
611                    Some(expressions) => Ok(Expression::In(Box::new(In {
612                        this: q.this.clone(),
613                        expressions,
614                        query: None,
615                        not: false,
616                        global: false,
617                        unnest: None,
618                        is_field: false,
619                    }))),
620                    None => Ok(expr.clone()),
621                }
622            }
623
624            // Pass through everything else
625            _ => Ok(expr),
626        }
627    }
628}
629
630#[cfg(feature = "transpile")]
631impl TSQLDialect {
632    fn binary(
633        left: Expression,
634        right: Expression,
635        op: fn(Box<BinaryOp>) -> Expression,
636    ) -> Expression {
637        op(Box::new(BinaryOp {
638            left,
639            right,
640            left_comments: Vec::new(),
641            operator_comments: Vec::new(),
642            trailing_comments: Vec::new(),
643            inferred_type: None,
644        }))
645    }
646
647    fn eq(left: Expression, right: Expression) -> Expression {
648        Self::binary(left, right, Expression::Eq)
649    }
650
651    fn or(left: Expression, right: Expression) -> Expression {
652        Self::binary(left, right, Expression::Or)
653    }
654
655    fn not(this: Expression) -> Expression {
656        Expression::Not(Box::new(crate::expressions::UnaryOp {
657            this,
658            inferred_type: None,
659        }))
660    }
661
662    fn is_null(this: Expression) -> Expression {
663        Expression::IsNull(Box::new(crate::expressions::IsNull {
664            this,
665            not: false,
666            postfix_form: false,
667        }))
668    }
669
670    fn boolean_test_case_for_predicate(
671        predicate: Expression,
672        test_true: bool,
673        negated: bool,
674    ) -> Expression {
675        let condition = match (test_true, negated) {
676            (true, false) => predicate,
677            (false, false) => Self::not(predicate),
678            (true, true) => {
679                return Expression::Case(Box::new(crate::expressions::Case {
680                    operand: None,
681                    whens: vec![(predicate, Expression::number(0))],
682                    else_: Some(Expression::number(1)),
683                    comments: Vec::new(),
684                    inferred_type: None,
685                }))
686            }
687            (false, true) => {
688                return Expression::Case(Box::new(crate::expressions::Case {
689                    operand: None,
690                    whens: vec![(Self::not(predicate), Expression::number(0))],
691                    else_: Some(Expression::number(1)),
692                    comments: Vec::new(),
693                    inferred_type: None,
694                }))
695            }
696        };
697
698        Expression::Case(Box::new(crate::expressions::Case {
699            operand: None,
700            whens: vec![(condition, Expression::number(1))],
701            else_: Some(Expression::number(0)),
702            comments: Vec::new(),
703            inferred_type: None,
704        }))
705    }
706
707    fn boolean_test_predicate(operand: Expression, test_true: bool, negated: bool) -> Expression {
708        if Self::is_boolean_predicate_operand(&operand) {
709            return match (test_true, negated) {
710                (true, false) => operand,
711                (false, false) => Self::not(operand),
712                _ => Self::eq(
713                    Self::boolean_test_case_for_predicate(operand, test_true, negated),
714                    Expression::number(1),
715                ),
716            };
717        }
718
719        match (test_true, negated) {
720            (true, false) => Self::eq(operand, Expression::number(1)),
721            (false, false) => Self::eq(operand, Expression::number(0)),
722            (true, true) => Self::or(
723                Self::eq(operand.clone(), Expression::number(0)),
724                Self::is_null(operand),
725            ),
726            (false, true) => Self::or(
727                Self::eq(operand.clone(), Expression::number(1)),
728                Self::is_null(operand),
729            ),
730        }
731    }
732
733    fn is_boolean_predicate_operand(expr: &Expression) -> bool {
734        match expr {
735            Expression::Paren(paren) => Self::is_boolean_predicate_operand(&paren.this),
736            Expression::Eq(_)
737            | Expression::Neq(_)
738            | Expression::Lt(_)
739            | Expression::Lte(_)
740            | Expression::Gt(_)
741            | Expression::Gte(_)
742            | Expression::Is(_)
743            | Expression::IsNull(_)
744            | Expression::IsTrue(_)
745            | Expression::IsFalse(_)
746            | Expression::Like(_)
747            | Expression::ILike(_)
748            | Expression::SimilarTo(_)
749            | Expression::Glob(_)
750            | Expression::RegexpLike(_)
751            | Expression::In(_)
752            | Expression::Between(_)
753            | Expression::Exists(_)
754            | Expression::And(_)
755            | Expression::Or(_)
756            | Expression::Not(_)
757            | Expression::Any(_)
758            | Expression::All(_)
759            | Expression::EqualNull(_) => true,
760            _ => false,
761        }
762    }
763
764    fn scalar_array_comparison_values(expr: &Expression) -> Option<Vec<Expression>> {
765        let (mut values, element_type) = Self::scalar_array_comparison_values_inner(expr)?;
766        if let Some(to) = element_type {
767            values = values
768                .into_iter()
769                .map(|value| Self::cast_scalar_array_comparison_value(value, to.clone()))
770                .collect();
771        }
772        Some(values)
773    }
774
775    fn scalar_array_comparison_values_inner(
776        expr: &Expression,
777    ) -> Option<(Vec<Expression>, Option<DataType>)> {
778        match expr {
779            Expression::ArrayFunc(a) => Some((a.expressions.clone(), None)),
780            Expression::Array(a) => Some((a.expressions.clone(), None)),
781            Expression::Tuple(t) => Some((t.expressions.clone(), None)),
782            Expression::Paren(p) => Self::scalar_array_comparison_values_inner(&p.this),
783            Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
784                let DataType::Array { element_type, .. } = &c.to else {
785                    return None;
786                };
787                let (values, _) = Self::scalar_array_comparison_values_inner(&c.this)?;
788                Some((values, Some((**element_type).clone())))
789            }
790            _ => None,
791        }
792    }
793
794    fn cast_scalar_array_comparison_value(value: Expression, to: DataType) -> Expression {
795        if matches!(&value, Expression::Cast(c) if c.to == to) {
796            return value;
797        }
798
799        Expression::Cast(Box::new(Cast {
800            this: value,
801            to,
802            trailing_comments: Vec::new(),
803            double_colon_syntax: false,
804            format: None,
805            default: None,
806            inferred_type: None,
807        }))
808    }
809
810    fn normalize_frame_incompatible_window_functions(select: &mut Select) {
811        let window_map: HashMap<String, Over> = select
812            .windows
813            .as_ref()
814            .map(|windows| {
815                windows
816                    .iter()
817                    .map(|window| (window.name.name.to_lowercase(), window.spec.clone()))
818                    .collect()
819            })
820            .unwrap_or_default();
821
822        for expr in &mut select.expressions {
823            Self::normalize_frame_incompatible_window_expr(expr, &window_map);
824        }
825
826        if let Some(order_by) = &mut select.order_by {
827            for ordered in &mut order_by.expressions {
828                Self::normalize_frame_incompatible_window_expr(&mut ordered.this, &window_map);
829            }
830        }
831
832        if let Some(qualify) = &mut select.qualify {
833            Self::normalize_frame_incompatible_window_expr(&mut qualify.this, &window_map);
834        }
835    }
836
837    fn normalize_frame_incompatible_window_expr(
838        expr: &mut Expression,
839        window_map: &HashMap<String, Over>,
840    ) {
841        match expr {
842            Expression::WindowFunction(wf) => {
843                Self::normalize_frame_incompatible_window_expr(&mut wf.this, window_map);
844
845                if !Self::is_tsql_frame_incompatible_window_function(&wf.this) {
846                    return;
847                }
848
849                wf.over.frame = None;
850
851                let Some(window_name) = wf.over.window_name.clone() else {
852                    return;
853                };
854                let Some(named_spec) =
855                    Self::resolve_named_window_spec(&window_name.name, window_map, &mut Vec::new())
856                else {
857                    return;
858                };
859
860                if named_spec.frame.is_none() {
861                    return;
862                }
863
864                if wf.over.partition_by.is_empty() {
865                    wf.over.partition_by = named_spec.partition_by;
866                }
867                if wf.over.order_by.is_empty() {
868                    wf.over.order_by = named_spec.order_by;
869                }
870                wf.over.window_name = None;
871                wf.over.frame = None;
872            }
873            Expression::Alias(alias) => {
874                Self::normalize_frame_incompatible_window_expr(&mut alias.this, window_map);
875            }
876            Expression::Paren(paren) => {
877                Self::normalize_frame_incompatible_window_expr(&mut paren.this, window_map);
878            }
879            Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
880                Self::normalize_frame_incompatible_window_expr(&mut cast.this, window_map);
881            }
882            Expression::Function(function) => {
883                for arg in &mut function.args {
884                    Self::normalize_frame_incompatible_window_expr(arg, window_map);
885                }
886            }
887            Expression::Case(case) => {
888                if let Some(operand) = &mut case.operand {
889                    Self::normalize_frame_incompatible_window_expr(operand, window_map);
890                }
891                for (condition, result) in &mut case.whens {
892                    Self::normalize_frame_incompatible_window_expr(condition, window_map);
893                    Self::normalize_frame_incompatible_window_expr(result, window_map);
894                }
895                if let Some(else_expr) = &mut case.else_ {
896                    Self::normalize_frame_incompatible_window_expr(else_expr, window_map);
897                }
898            }
899            Expression::And(op)
900            | Expression::Or(op)
901            | Expression::Add(op)
902            | Expression::Sub(op)
903            | Expression::Mul(op)
904            | Expression::Div(op)
905            | Expression::Mod(op)
906            | Expression::Eq(op)
907            | Expression::Neq(op)
908            | Expression::Lt(op)
909            | Expression::Lte(op)
910            | Expression::Gt(op)
911            | Expression::Gte(op)
912            | Expression::Match(op)
913            | Expression::BitwiseAnd(op)
914            | Expression::BitwiseOr(op)
915            | Expression::BitwiseXor(op)
916            | Expression::Concat(op)
917            | Expression::Adjacent(op)
918            | Expression::TsMatch(op)
919            | Expression::PropertyEQ(op)
920            | Expression::ArrayContainsAll(op)
921            | Expression::ArrayContainedBy(op)
922            | Expression::ArrayOverlaps(op)
923            | Expression::JSONBContainsAllTopKeys(op)
924            | Expression::JSONBContainsAnyTopKeys(op)
925            | Expression::JSONBDeleteAtPath(op)
926            | Expression::ExtendsLeft(op)
927            | Expression::ExtendsRight(op)
928            | Expression::Is(op)
929            | Expression::MemberOf(op) => {
930                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
931                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
932            }
933            Expression::Like(op) | Expression::ILike(op) => {
934                Self::normalize_frame_incompatible_window_expr(&mut op.left, window_map);
935                Self::normalize_frame_incompatible_window_expr(&mut op.right, window_map);
936                if let Some(escape) = &mut op.escape {
937                    Self::normalize_frame_incompatible_window_expr(escape, window_map);
938                }
939            }
940            Expression::Not(op) | Expression::Neg(op) | Expression::BitwiseNot(op) => {
941                Self::normalize_frame_incompatible_window_expr(&mut op.this, window_map);
942            }
943            Expression::In(in_expr) => {
944                Self::normalize_frame_incompatible_window_expr(&mut in_expr.this, window_map);
945                for value in &mut in_expr.expressions {
946                    Self::normalize_frame_incompatible_window_expr(value, window_map);
947                }
948            }
949            Expression::Between(between) => {
950                Self::normalize_frame_incompatible_window_expr(&mut between.this, window_map);
951                Self::normalize_frame_incompatible_window_expr(&mut between.low, window_map);
952                Self::normalize_frame_incompatible_window_expr(&mut between.high, window_map);
953            }
954            Expression::IsNull(is_null) => {
955                Self::normalize_frame_incompatible_window_expr(&mut is_null.this, window_map);
956            }
957            Expression::IsTrue(is_true) | Expression::IsFalse(is_true) => {
958                Self::normalize_frame_incompatible_window_expr(&mut is_true.this, window_map);
959            }
960            _ => {}
961        }
962    }
963
964    fn is_tsql_frame_incompatible_window_function(expr: &Expression) -> bool {
965        matches!(
966            expr,
967            Expression::RowNumber(_)
968                | Expression::Rank(_)
969                | Expression::DenseRank(_)
970                | Expression::NTile(_)
971                | Expression::Ntile(_)
972                | Expression::Lead(_)
973                | Expression::Lag(_)
974                | Expression::PercentRank(_)
975                | Expression::CumeDist(_)
976        )
977    }
978
979    fn resolve_named_window_spec(
980        name: &str,
981        window_map: &HashMap<String, Over>,
982        seen: &mut Vec<String>,
983    ) -> Option<Over> {
984        let key = name.to_lowercase();
985        if seen.iter().any(|seen_name| seen_name == &key) {
986            return None;
987        }
988
989        let named_spec = window_map.get(&key)?.clone();
990        seen.push(key);
991
992        let mut resolved = if let Some(base_window) = &named_spec.window_name {
993            Self::resolve_named_window_spec(&base_window.name, window_map, seen)
994                .unwrap_or_else(Self::empty_over)
995        } else {
996            Self::empty_over()
997        };
998
999        if !named_spec.partition_by.is_empty() {
1000            resolved.partition_by = named_spec.partition_by;
1001        }
1002        if !named_spec.order_by.is_empty() {
1003            resolved.order_by = named_spec.order_by;
1004        }
1005        if named_spec.frame.is_some() {
1006            resolved.frame = named_spec.frame;
1007        }
1008
1009        Some(resolved)
1010    }
1011
1012    fn empty_over() -> Over {
1013        Over {
1014            window_name: None,
1015            partition_by: Vec::new(),
1016            order_by: Vec::new(),
1017            frame: None,
1018            alias: None,
1019        }
1020    }
1021
1022    const LATERAL_WRAPPER_SOURCE_ALIAS: &'static str = "_polyglot_lateral_source";
1023    const LATERAL_WRAPPER_OUTPUT_ALIAS: &'static str = "_polyglot_lateral";
1024
1025    fn transform_lateral_join_to_apply(mut join: Join) -> Result<Join> {
1026        let Some(apply_kind) = Self::lateral_apply_kind(&join) else {
1027            return Ok(join);
1028        };
1029
1030        let original_alias = Self::table_expression_alias(&join.this);
1031        let on = join.on.take();
1032        let rhs = Self::remove_lateral_marker(join.this);
1033        join.this = if on
1034            .as_ref()
1035            .is_some_and(|expr| !Self::is_true_condition(expr))
1036        {
1037            Self::wrap_lateral_apply_rhs(rhs, on.expect("checked as Some"), original_alias)?
1038        } else {
1039            rhs
1040        };
1041        join.using.clear();
1042        join.kind = apply_kind;
1043        join.use_inner_keyword = false;
1044        join.use_outer_keyword = false;
1045        join.deferred_condition = false;
1046        join.join_hint = None;
1047        join.match_condition = None;
1048        join.directed = false;
1049        Ok(join)
1050    }
1051
1052    fn rewrite_comma_lateral_sources_to_joins(select: &mut Select) {
1053        let Some(from) = select.from.as_mut() else {
1054            return;
1055        };
1056        if from.expressions.len() < 2
1057            || !from
1058                .expressions
1059                .iter()
1060                .skip(1)
1061                .any(Self::is_lateral_table_expression)
1062        {
1063            return;
1064        }
1065
1066        let mut expressions = std::mem::take(&mut from.expressions).into_iter();
1067        let Some(first) = expressions.next() else {
1068            return;
1069        };
1070        from.expressions = vec![first];
1071
1072        let mut joins = expressions
1073            .map(|source| {
1074                if Self::is_lateral_table_expression(&source) {
1075                    Self::new_join(Self::remove_lateral_marker(source), JoinKind::CrossApply)
1076                } else {
1077                    Self::new_join(source, JoinKind::Cross)
1078                }
1079            })
1080            .collect::<Vec<_>>();
1081        joins.append(&mut select.joins);
1082        select.joins = joins;
1083    }
1084
1085    fn new_join(this: Expression, kind: JoinKind) -> Join {
1086        Join {
1087            this,
1088            on: None,
1089            using: Vec::new(),
1090            kind,
1091            use_inner_keyword: false,
1092            use_outer_keyword: false,
1093            deferred_condition: false,
1094            join_hint: None,
1095            match_condition: None,
1096            pivots: Vec::new(),
1097            comments: Vec::new(),
1098            nesting_group: 0,
1099            directed: false,
1100        }
1101    }
1102
1103    fn lateral_apply_kind(join: &Join) -> Option<JoinKind> {
1104        if !join.using.is_empty() {
1105            return None;
1106        }
1107
1108        match join.kind {
1109            JoinKind::Lateral => Some(JoinKind::CrossApply),
1110            JoinKind::LeftLateral => Some(JoinKind::OuterApply),
1111            JoinKind::Cross | JoinKind::Inner | JoinKind::Implicit
1112                if Self::is_lateral_table_expression(&join.this) =>
1113            {
1114                Some(JoinKind::CrossApply)
1115            }
1116            JoinKind::Left if Self::is_lateral_table_expression(&join.this) => {
1117                Some(JoinKind::OuterApply)
1118            }
1119            _ => None,
1120        }
1121    }
1122
1123    fn is_true_condition(expr: &Expression) -> bool {
1124        match expr {
1125            Expression::Boolean(boolean) => boolean.value,
1126            Expression::Literal(lit) => {
1127                matches!(lit.as_ref(), Literal::Number(value) if value.trim() == "1")
1128            }
1129            Expression::Eq(op) => {
1130                Self::is_true_condition(&op.left) && Self::is_true_condition(&op.right)
1131            }
1132            Expression::Paren(paren) => Self::is_true_condition(&paren.this),
1133            _ => false,
1134        }
1135    }
1136
1137    fn table_expression_alias(expr: &Expression) -> Option<(Identifier, Vec<Identifier>)> {
1138        match expr {
1139            Expression::Subquery(subquery) => subquery
1140                .alias
1141                .clone()
1142                .map(|alias| (alias, subquery.column_aliases.clone())),
1143            Expression::Alias(alias) if !alias.alias.is_empty() => {
1144                Some((alias.alias.clone(), alias.column_aliases.clone()))
1145            }
1146            Expression::Lateral(lateral) => lateral.alias.as_ref().map(|alias| {
1147                (
1148                    if lateral.alias_quoted {
1149                        Identifier::quoted(alias)
1150                    } else {
1151                        Identifier::new(alias)
1152                    },
1153                    lateral
1154                        .column_aliases
1155                        .iter()
1156                        .map(|column| Identifier::new(column.clone()))
1157                        .collect(),
1158                )
1159            }),
1160            _ => None,
1161        }
1162    }
1163
1164    fn wrap_lateral_apply_rhs(
1165        rhs: Expression,
1166        predicate: Expression,
1167        original_alias: Option<(Identifier, Vec<Identifier>)>,
1168    ) -> Result<Expression> {
1169        let (outer_alias, column_aliases) = original_alias.unwrap_or_else(|| {
1170            (
1171                Identifier::new(Self::LATERAL_WRAPPER_OUTPUT_ALIAS),
1172                Vec::new(),
1173            )
1174        });
1175        let inner_alias = Identifier::new(Self::LATERAL_WRAPPER_SOURCE_ALIAS);
1176        let source =
1177            Self::with_table_expression_alias(rhs, inner_alias.clone(), column_aliases.clone());
1178        let predicate = Self::rewrite_column_qualifier(predicate, &outer_alias, &inner_alias)?;
1179
1180        let mut select = Select::new();
1181        select.expressions = vec![Expression::Star(Star {
1182            table: None,
1183            except: None,
1184            replace: None,
1185            rename: None,
1186            trailing_comments: Vec::new(),
1187            span: None,
1188        })];
1189        select.from = Some(crate::expressions::From {
1190            expressions: vec![source],
1191        });
1192        select.where_clause = Some(Where { this: predicate });
1193
1194        Ok(Expression::Subquery(Box::new(Subquery {
1195            this: Expression::Select(Box::new(select)),
1196            alias: Some(outer_alias),
1197            column_aliases,
1198            alias_explicit_as: true,
1199            alias_keyword: None,
1200            order_by: None,
1201            limit: None,
1202            offset: None,
1203            distribute_by: None,
1204            sort_by: None,
1205            cluster_by: None,
1206            lateral: false,
1207            modifiers_inside: false,
1208            trailing_comments: Vec::new(),
1209            inferred_type: None,
1210        })))
1211    }
1212
1213    fn with_table_expression_alias(
1214        expr: Expression,
1215        alias: Identifier,
1216        column_aliases: Vec<Identifier>,
1217    ) -> Expression {
1218        match expr {
1219            Expression::Subquery(mut subquery) => {
1220                subquery.alias = Some(alias);
1221                subquery.column_aliases = column_aliases;
1222                subquery.alias_explicit_as = true;
1223                subquery.alias_keyword = None;
1224                Expression::Subquery(subquery)
1225            }
1226            Expression::Alias(mut aliased) => {
1227                aliased.alias = alias;
1228                aliased.column_aliases = column_aliases;
1229                aliased.alias_explicit_as = true;
1230                aliased.alias_keyword = None;
1231                Expression::Alias(aliased)
1232            }
1233            Expression::Table(mut table) => {
1234                table.alias = Some(alias);
1235                table.alias_explicit_as = true;
1236                table.column_aliases = column_aliases;
1237                Expression::Table(table)
1238            }
1239            other => Expression::Alias(Box::new(Alias {
1240                this: other,
1241                alias,
1242                column_aliases,
1243                alias_explicit_as: true,
1244                alias_keyword: None,
1245                pre_alias_comments: Vec::new(),
1246                trailing_comments: Vec::new(),
1247                inferred_type: None,
1248            })),
1249        }
1250    }
1251
1252    fn rewrite_column_qualifier(
1253        expr: Expression,
1254        from: &Identifier,
1255        to: &Identifier,
1256    ) -> Result<Expression> {
1257        super::transform_recursive(expr, &|expr| {
1258            Ok(match expr {
1259                Expression::Column(mut column)
1260                    if column
1261                        .table
1262                        .as_ref()
1263                        .is_some_and(|table| Self::same_identifier(table, from)) =>
1264                {
1265                    column.table = Some(to.clone());
1266                    Expression::Column(column)
1267                }
1268                other => other,
1269            })
1270        })
1271    }
1272
1273    fn same_identifier(left: &Identifier, right: &Identifier) -> bool {
1274        if left.quoted || right.quoted {
1275            left.quoted == right.quoted && left.name == right.name
1276        } else {
1277            left.name.eq_ignore_ascii_case(&right.name)
1278        }
1279    }
1280
1281    fn is_lateral_table_expression(expr: &Expression) -> bool {
1282        match expr {
1283            Expression::Subquery(subquery) => subquery.lateral,
1284            Expression::Lateral(_) => true,
1285            Expression::Alias(alias) => Self::is_lateral_table_expression(&alias.this),
1286            _ => false,
1287        }
1288    }
1289
1290    fn remove_lateral_marker(expr: Expression) -> Expression {
1291        match expr {
1292            Expression::Subquery(mut subquery) => {
1293                subquery.lateral = false;
1294                Expression::Subquery(subquery)
1295            }
1296            Expression::Lateral(lateral) => Self::lateral_to_table_expression(*lateral),
1297            Expression::Alias(mut alias) => {
1298                alias.this = Self::remove_lateral_marker(alias.this);
1299                Expression::Alias(alias)
1300            }
1301            other => other,
1302        }
1303    }
1304
1305    fn lateral_to_table_expression(lateral: crate::expressions::Lateral) -> Expression {
1306        let expr = *lateral.this;
1307        let Some(alias) = lateral.alias else {
1308            return expr;
1309        };
1310
1311        Expression::Alias(Box::new(Alias {
1312            this: expr,
1313            alias: if lateral.alias_quoted {
1314                Identifier::quoted(alias)
1315            } else {
1316                Identifier::new(alias)
1317            },
1318            column_aliases: lateral
1319                .column_aliases
1320                .into_iter()
1321                .map(Identifier::new)
1322                .collect(),
1323            alias_explicit_as: true,
1324            alias_keyword: None,
1325            pre_alias_comments: Vec::new(),
1326            trailing_comments: Vec::new(),
1327            inferred_type: None,
1328        }))
1329    }
1330
1331    fn rewrite_tuple_in_subquery_predicates(
1332        expr: Expression,
1333        outer_qualifier: Option<&Identifier>,
1334        under_not: bool,
1335    ) -> Expression {
1336        match expr {
1337            Expression::In(in_expr) if !under_not => {
1338                let in_expr = *in_expr;
1339                Self::tuple_in_subquery_to_exists(&in_expr, outer_qualifier)
1340                    .unwrap_or_else(|| Expression::In(Box::new(in_expr)))
1341            }
1342            Expression::And(mut op) => {
1343                op.left =
1344                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1345                op.right = Self::rewrite_tuple_in_subquery_predicates(
1346                    op.right,
1347                    outer_qualifier,
1348                    under_not,
1349                );
1350                Expression::And(op)
1351            }
1352            Expression::Or(mut op) => {
1353                op.left =
1354                    Self::rewrite_tuple_in_subquery_predicates(op.left, outer_qualifier, under_not);
1355                op.right = Self::rewrite_tuple_in_subquery_predicates(
1356                    op.right,
1357                    outer_qualifier,
1358                    under_not,
1359                );
1360                Expression::Or(op)
1361            }
1362            Expression::Paren(mut paren) => {
1363                paren.this = Self::rewrite_tuple_in_subquery_predicates(
1364                    paren.this,
1365                    outer_qualifier,
1366                    under_not,
1367                );
1368                Expression::Paren(paren)
1369            }
1370            Expression::Not(mut not) => {
1371                not.this =
1372                    Self::rewrite_tuple_in_subquery_predicates(not.this, outer_qualifier, true);
1373                Expression::Not(not)
1374            }
1375            other => other,
1376        }
1377    }
1378
1379    fn tuple_in_subquery_to_exists(
1380        in_expr: &In,
1381        outer_qualifier: Option<&Identifier>,
1382    ) -> Option<Expression> {
1383        if in_expr.not || !in_expr.expressions.is_empty() || in_expr.unnest.is_some() {
1384            return None;
1385        }
1386
1387        let left_expressions = Self::tuple_expressions(&in_expr.this)?;
1388        let mut select = match in_expr.query.as_ref()? {
1389            Expression::Select(select) => (**select).clone(),
1390            _ => return None,
1391        };
1392
1393        if left_expressions.len() != select.expressions.len() || left_expressions.is_empty() {
1394            return None;
1395        }
1396
1397        let inner_qualifier = Self::single_select_source_qualifier(&select);
1398        let mut predicates = Vec::with_capacity(left_expressions.len() + 1);
1399        for (projection, left) in select
1400            .expressions
1401            .iter()
1402            .cloned()
1403            .zip(left_expressions.iter().cloned())
1404        {
1405            let inner = Self::tuple_in_projection_expr(projection, inner_qualifier.as_ref())?;
1406            let outer = Self::qualify_tuple_operand(left, outer_qualifier);
1407            predicates.push(Expression::Eq(Box::new(BinaryOp::new(inner, outer))));
1408        }
1409
1410        if let Some(where_clause) = select.where_clause.take() {
1411            predicates.push(where_clause.this);
1412        }
1413
1414        select.expressions = vec![Expression::number(1)];
1415        select.where_clause = Some(Where {
1416            this: Self::and_all(predicates)?,
1417        });
1418
1419        Some(Expression::Exists(Box::new(Exists {
1420            this: Expression::Select(Box::new(select)),
1421            not: false,
1422        })))
1423    }
1424
1425    fn tuple_expressions(expr: &Expression) -> Option<&[Expression]> {
1426        match expr {
1427            Expression::Tuple(tuple) => Some(&tuple.expressions),
1428            Expression::Paren(paren) => Self::tuple_expressions(&paren.this),
1429            _ => None,
1430        }
1431    }
1432
1433    fn tuple_in_projection_expr(
1434        expr: Expression,
1435        qualifier: Option<&Identifier>,
1436    ) -> Option<Expression> {
1437        match expr {
1438            Expression::Alias(alias) => Self::tuple_in_projection_expr(alias.this, qualifier),
1439            Expression::Column(mut column) => {
1440                if column.table.is_none() {
1441                    column.table = qualifier.cloned();
1442                }
1443                Some(Expression::Column(column))
1444            }
1445            Expression::Identifier(identifier) => {
1446                Some(Self::column_from_identifier(identifier, qualifier.cloned()))
1447            }
1448            Expression::Dot(_) => Some(expr),
1449            _ => None,
1450        }
1451    }
1452
1453    fn qualify_tuple_operand(expr: Expression, qualifier: Option<&Identifier>) -> Expression {
1454        match expr {
1455            Expression::Column(mut column) => {
1456                if column.table.is_none() {
1457                    column.table = qualifier.cloned();
1458                }
1459                Expression::Column(column)
1460            }
1461            Expression::Identifier(identifier) => {
1462                Self::column_from_identifier(identifier, qualifier.cloned())
1463            }
1464            other => other,
1465        }
1466    }
1467
1468    fn column_from_identifier(identifier: Identifier, table: Option<Identifier>) -> Expression {
1469        Expression::Column(Box::new(Column {
1470            name: identifier,
1471            table,
1472            join_mark: false,
1473            trailing_comments: Vec::new(),
1474            span: None,
1475            inferred_type: None,
1476        }))
1477    }
1478
1479    fn single_select_source_qualifier(select: &Select) -> Option<Identifier> {
1480        if !select.joins.is_empty() {
1481            return None;
1482        }
1483
1484        let from = select.from.as_ref()?;
1485        if from.expressions.len() != 1 {
1486            return None;
1487        }
1488
1489        Self::source_qualifier(&from.expressions[0])
1490    }
1491
1492    fn source_qualifier(source: &Expression) -> Option<Identifier> {
1493        match source {
1494            Expression::Table(table) => table.alias.clone().or_else(|| Some(table.name.clone())),
1495            Expression::Subquery(subquery) => subquery.alias.clone(),
1496            _ => None,
1497        }
1498    }
1499
1500    fn and_all(mut predicates: Vec<Expression>) -> Option<Expression> {
1501        if predicates.is_empty() {
1502            return None;
1503        }
1504
1505        let first = predicates.remove(0);
1506        Some(predicates.into_iter().fold(first, |left, right| {
1507            Expression::And(Box::new(BinaryOp::new(left, right)))
1508        }))
1509    }
1510
1511    /// Transform data types according to T-SQL TYPE_MAPPING
1512    pub(super) fn transform_data_type(
1513        &self,
1514        dt: crate::expressions::DataType,
1515    ) -> Result<Expression> {
1516        use crate::expressions::DataType;
1517        let transformed = match dt {
1518            // BOOLEAN -> BIT
1519            DataType::Boolean => DataType::Custom {
1520                name: "BIT".to_string(),
1521            },
1522            // INT stays as INT in TSQL (native type)
1523            DataType::Int { .. } => dt,
1524            // DOUBLE stays as Double internally (TSQL generator outputs FLOAT for it)
1525            // DECIMAL -> NUMERIC
1526            DataType::Decimal { precision, scale } => DataType::Custom {
1527                name: if let (Some(p), Some(s)) = (&precision, &scale) {
1528                    format!("NUMERIC({}, {})", p, s)
1529                } else if let Some(p) = &precision {
1530                    format!("NUMERIC({})", p)
1531                } else {
1532                    "NUMERIC".to_string()
1533                },
1534            },
1535            // TEXT -> VARCHAR(MAX)
1536            DataType::Text => DataType::Custom {
1537                name: "VARCHAR(MAX)".to_string(),
1538            },
1539            // TIMESTAMP -> DATETIME2
1540            DataType::Timestamp { .. } => DataType::Custom {
1541                name: "DATETIME2".to_string(),
1542            },
1543            // UUID -> UNIQUEIDENTIFIER
1544            DataType::Uuid => DataType::Custom {
1545                name: "UNIQUEIDENTIFIER".to_string(),
1546            },
1547            // Normalise custom type names that have PostgreSQL aliases
1548            DataType::Custom { ref name } => {
1549                let upper = name.trim().to_uppercase();
1550                let (base_name, precision, _scale) = Self::parse_type_precision_and_scale(&upper);
1551                match base_name.as_str() {
1552                    // BPCHAR is PostgreSQL's blank-padded CHAR alias — map to CHAR
1553                    "BPCHAR" => {
1554                        if let Some(len) = precision {
1555                            DataType::Char { length: Some(len) }
1556                        } else {
1557                            DataType::Char { length: None }
1558                        }
1559                    }
1560                    _ => dt,
1561                }
1562            }
1563            // Keep all other types as-is
1564            other => other,
1565        };
1566        Ok(Expression::DataType(transformed))
1567    }
1568
1569    /// Parse a type name that may embed precision/scale: `"TYPENAME(n, m)"` → `("TYPENAME", Some(n), Some(m))`.
1570    pub(super) fn parse_type_precision_and_scale(name: &str) -> (String, Option<u32>, Option<u32>) {
1571        if let Some(paren_pos) = name.find('(') {
1572            let base = name[..paren_pos].to_string();
1573            let rest = &name[paren_pos + 1..];
1574            if let Some(close_pos) = rest.find(')') {
1575                let args = &rest[..close_pos];
1576                let parts: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
1577                let precision = parts.first().and_then(|s| s.parse::<u32>().ok());
1578                let scale = parts.get(1).and_then(|s| s.parse::<u32>().ok());
1579                return (base, precision, scale);
1580            }
1581            (base, None, None)
1582        } else {
1583            (name.to_string(), None, None)
1584        }
1585    }
1586
1587    fn transform_logical_aggregate(
1588        condition: Expression,
1589        filter: Option<Expression>,
1590        aggregate_name: &str,
1591    ) -> Result<Expression> {
1592        let false_condition = Expression::Not(Box::new(crate::expressions::UnaryOp {
1593            this: condition.clone(),
1594            inferred_type: None,
1595        }));
1596        let true_condition = Self::apply_aggregate_filter(condition, filter.clone());
1597        let false_condition = Self::apply_aggregate_filter(false_condition, filter);
1598
1599        let case_expr = Expression::Case(Box::new(crate::expressions::Case {
1600            operand: None,
1601            whens: vec![
1602                (true_condition, Expression::number(1)),
1603                (false_condition, Expression::number(0)),
1604            ],
1605            else_: Some(Expression::null()),
1606            comments: Vec::new(),
1607            inferred_type: None,
1608        }));
1609
1610        let case_expr = crate::transforms::ensure_bools(case_expr)?;
1611        let aggregate = Expression::Function(Box::new(Function::new(
1612            aggregate_name.to_string(),
1613            vec![case_expr],
1614        )));
1615
1616        Ok(Expression::Cast(Box::new(Cast {
1617            this: aggregate,
1618            to: DataType::Custom {
1619                name: "BIT".to_string(),
1620            },
1621            trailing_comments: Vec::new(),
1622            double_colon_syntax: false,
1623            format: None,
1624            default: None,
1625            inferred_type: None,
1626        })))
1627    }
1628
1629    fn apply_aggregate_filter(condition: Expression, filter: Option<Expression>) -> Expression {
1630        match filter {
1631            Some(filter) => Expression::And(Box::new(crate::expressions::BinaryOp::new(
1632                filter, condition,
1633            ))),
1634            None => condition,
1635        }
1636    }
1637
1638    fn transform_function(&self, f: Function) -> Result<Expression> {
1639        let name_upper = f.name.to_uppercase();
1640        match name_upper.as_str() {
1641            // COALESCE -> ISNULL for 2 args (optimization)
1642            "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
1643                "ISNULL".to_string(),
1644                f.args,
1645            )))),
1646
1647            // NVL -> ISNULL (SQL Server function)
1648            "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
1649                "ISNULL".to_string(),
1650                f.args,
1651            )))),
1652
1653            // GROUP_CONCAT -> STRING_AGG in SQL Server 2017+
1654            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1655                Function::new("STRING_AGG".to_string(), f.args),
1656            ))),
1657
1658            // STRING_AGG is native to SQL Server 2017+
1659            "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
1660
1661            // LISTAGG -> STRING_AGG
1662            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1663                "STRING_AGG".to_string(),
1664                f.args,
1665            )))),
1666
1667            // SUBSTR -> SUBSTRING
1668            "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1669                "SUBSTRING".to_string(),
1670                f.args,
1671            )))),
1672
1673            // LENGTH -> LEN in SQL Server
1674            "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1675                "LEN".to_string(),
1676                f.args,
1677            )))),
1678
1679            // RANDOM -> RAND
1680            "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
1681                seed: None,
1682                lower: None,
1683                upper: None,
1684            }))),
1685
1686            // NOW -> GETDATE or CURRENT_TIMESTAMP (both work)
1687            "NOW" => Ok(Expression::Function(Box::new(Function::new(
1688                "GETDATE".to_string(),
1689                vec![],
1690            )))),
1691
1692            // CURRENT_TIMESTAMP -> GETDATE (SQL Server prefers GETDATE)
1693            "CURRENT_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1694                "GETDATE".to_string(),
1695                vec![],
1696            )))),
1697
1698            // CURRENT_DATE -> CAST(GETDATE() AS DATE)
1699            "CURRENT_DATE" => {
1700                // In SQL Server, use CAST(GETDATE() AS DATE)
1701                Ok(Expression::Function(Box::new(Function::new(
1702                    "CAST".to_string(),
1703                    vec![
1704                        Expression::Function(Box::new(Function::new(
1705                            "GETDATE".to_string(),
1706                            vec![],
1707                        ))),
1708                        Expression::Identifier(crate::expressions::Identifier::new("DATE")),
1709                    ],
1710                ))))
1711            }
1712
1713            // TO_DATE -> CONVERT or CAST
1714            "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
1715                "CONVERT".to_string(),
1716                f.args,
1717            )))),
1718
1719            // TO_TIMESTAMP -> CONVERT
1720            "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1721                "CONVERT".to_string(),
1722                f.args,
1723            )))),
1724
1725            // TO_CHAR -> FORMAT in SQL Server 2012+
1726            "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
1727                "FORMAT".to_string(),
1728                f.args,
1729            )))),
1730
1731            // DATE_FORMAT -> FORMAT
1732            "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
1733                "FORMAT".to_string(),
1734                f.args,
1735            )))),
1736
1737            // DATE_TRUNC -> DATETRUNC in SQL Server 2022+
1738            // For older versions, use DATEADD/DATEDIFF combo
1739            "DATE_TRUNC" | "DATETRUNC" => {
1740                let mut args = Self::uppercase_first_arg_if_identifier(f.args);
1741                // Cast string literal date arg to DATETIME2
1742                if args.len() >= 2 {
1743                    if let Expression::Literal(lit) = &args[1] {
1744                        if let Literal::String(_) = lit.as_ref() {
1745                            args[1] = Expression::Cast(Box::new(Cast {
1746                                this: args[1].clone(),
1747                                to: DataType::Custom {
1748                                    name: "DATETIME2".to_string(),
1749                                },
1750                                trailing_comments: Vec::new(),
1751                                double_colon_syntax: false,
1752                                format: None,
1753                                default: None,
1754                                inferred_type: None,
1755                            }));
1756                        }
1757                    }
1758                }
1759                Ok(Expression::Function(Box::new(Function::new(
1760                    "DATETRUNC".to_string(),
1761                    args,
1762                ))))
1763            }
1764
1765            // DATEADD is native to SQL Server - uppercase the unit
1766            "DATEADD" => {
1767                let args = Self::uppercase_first_arg_if_identifier(f.args);
1768                Ok(Expression::Function(Box::new(Function::new(
1769                    "DATEADD".to_string(),
1770                    args,
1771                ))))
1772            }
1773
1774            // DATEDIFF is native to SQL Server - uppercase the unit
1775            "DATEDIFF" => {
1776                let args = Self::uppercase_first_arg_if_identifier(f.args);
1777                Ok(Expression::Function(Box::new(Function::new(
1778                    "DATEDIFF".to_string(),
1779                    args,
1780                ))))
1781            }
1782
1783            // EXTRACT -> DATEPART in SQL Server
1784            "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
1785                "DATEPART".to_string(),
1786                f.args,
1787            )))),
1788
1789            // STRPOS / POSITION -> CHARINDEX
1790            "STRPOS" | "POSITION" if f.args.len() >= 2 => {
1791                // CHARINDEX(substring, string) - same arg order as POSITION
1792                Ok(Expression::Function(Box::new(Function::new(
1793                    "CHARINDEX".to_string(),
1794                    f.args,
1795                ))))
1796            }
1797
1798            // CHARINDEX is native
1799            "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
1800
1801            // CEILING -> CEILING (native)
1802            "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
1803                Function::new("CEILING".to_string(), f.args),
1804            ))),
1805
1806            // ARRAY functions don't exist in SQL Server
1807            // Would need JSON or table-valued parameters
1808
1809            // JSON_EXTRACT -> JSON_VALUE or JSON_QUERY
1810            "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
1811                "JSON_VALUE".to_string(),
1812                f.args,
1813            )))),
1814
1815            // JSON_EXTRACT_SCALAR -> JSON_VALUE
1816            "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
1817                "JSON_VALUE".to_string(),
1818                f.args,
1819            )))),
1820
1821            // PARSE_JSON -> strip in TSQL (just keep the string argument)
1822            "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
1823
1824            // GET_PATH(obj, path) -> ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path)) in TSQL
1825            "GET_PATH" if f.args.len() == 2 => {
1826                let mut args = f.args;
1827                let this = args.remove(0);
1828                let path = args.remove(0);
1829                let json_path = match &path {
1830                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
1831                        let Literal::String(s) = lit.as_ref() else {
1832                            unreachable!()
1833                        };
1834                        let normalized = if s.starts_with('$') {
1835                            s.clone()
1836                        } else if s.starts_with('[') {
1837                            format!("${}", s)
1838                        } else {
1839                            format!("$.{}", s)
1840                        };
1841                        Expression::Literal(Box::new(Literal::String(normalized)))
1842                    }
1843                    _ => path,
1844                };
1845                // ISNULL(JSON_QUERY(obj, path), JSON_VALUE(obj, path))
1846                let json_query = Expression::Function(Box::new(Function::new(
1847                    "JSON_QUERY".to_string(),
1848                    vec![this.clone(), json_path.clone()],
1849                )));
1850                let json_value = Expression::Function(Box::new(Function::new(
1851                    "JSON_VALUE".to_string(),
1852                    vec![this, json_path],
1853                )));
1854                Ok(Expression::Function(Box::new(Function::new(
1855                    "ISNULL".to_string(),
1856                    vec![json_query, json_value],
1857                ))))
1858            }
1859
1860            // JSON_QUERY with 1 arg: add '$' path and wrap in ISNULL
1861            // JSON_QUERY with 2 args: leave as-is (already processed or inside ISNULL)
1862            "JSON_QUERY" if f.args.len() == 1 => {
1863                let this = f.args.into_iter().next().unwrap();
1864                let path = Expression::Literal(Box::new(Literal::String("$".to_string())));
1865                let json_query = Expression::Function(Box::new(Function::new(
1866                    "JSON_QUERY".to_string(),
1867                    vec![this.clone(), path.clone()],
1868                )));
1869                let json_value = Expression::Function(Box::new(Function::new(
1870                    "JSON_VALUE".to_string(),
1871                    vec![this, path],
1872                )));
1873                Ok(Expression::Function(Box::new(Function::new(
1874                    "ISNULL".to_string(),
1875                    vec![json_query, json_value],
1876                ))))
1877            }
1878
1879            // SPLIT -> STRING_SPLIT (returns a table, needs CROSS APPLY)
1880            "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
1881                "STRING_SPLIT".to_string(),
1882                f.args,
1883            )))),
1884
1885            // REGEXP_LIKE -> Not directly supported, use LIKE or PATINDEX
1886            // SQL Server has limited regex support via PATINDEX and LIKE
1887            "REGEXP_LIKE" => {
1888                // Fall back to LIKE (loses regex functionality)
1889                Ok(Expression::Function(Box::new(Function::new(
1890                    "PATINDEX".to_string(),
1891                    f.args,
1892                ))))
1893            }
1894
1895            // LN -> LOG in SQL Server
1896            "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1897                "LOG".to_string(),
1898                f.args,
1899            )))),
1900
1901            // LOG with 2 args is LOG(base, value) in most DBs but LOG(value, base) in SQL Server
1902            // This needs careful handling
1903
1904            // STDDEV -> STDEV in SQL Server
1905            "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
1906                "STDEV".to_string(),
1907                f.args,
1908            )))),
1909
1910            // STDDEV_POP -> STDEVP in SQL Server
1911            "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
1912                "STDEVP".to_string(),
1913                f.args,
1914            )))),
1915
1916            // VAR_SAMP -> VAR in SQL Server
1917            "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
1918                "VAR".to_string(),
1919                f.args,
1920            )))),
1921
1922            // VAR_POP -> VARP in SQL Server
1923            "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
1924                "VARP".to_string(),
1925                f.args,
1926            )))),
1927
1928            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
1929            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
1930                let mut args = f.args;
1931                Self::transform_logical_aggregate(args.remove(0), None, "MIN")
1932            }
1933            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
1934                let mut args = f.args;
1935                Self::transform_logical_aggregate(args.remove(0), None, "MAX")
1936            }
1937
1938            // DATE_ADD(date, interval) -> DATEADD(DAY, interval, date)
1939            "DATE_ADD" => {
1940                if f.args.len() == 2 {
1941                    let mut args = f.args;
1942                    let date = args.remove(0);
1943                    let interval = args.remove(0);
1944                    let unit = Expression::Identifier(crate::expressions::Identifier {
1945                        name: "DAY".to_string(),
1946                        quoted: false,
1947                        trailing_comments: Vec::new(),
1948                        span: None,
1949                    });
1950                    Ok(Expression::Function(Box::new(Function::new(
1951                        "DATEADD".to_string(),
1952                        vec![unit, interval, date],
1953                    ))))
1954                } else {
1955                    let args = Self::uppercase_first_arg_if_identifier(f.args);
1956                    Ok(Expression::Function(Box::new(Function::new(
1957                        "DATEADD".to_string(),
1958                        args,
1959                    ))))
1960                }
1961            }
1962
1963            // INSERT → STUFF (Snowflake/MySQL string INSERT → T-SQL STUFF)
1964            "INSERT" => Ok(Expression::Function(Box::new(Function::new(
1965                "STUFF".to_string(),
1966                f.args,
1967            )))),
1968
1969            // SUSER_NAME(), SUSER_SNAME(), SYSTEM_USER() -> CURRENT_USER
1970            "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
1971                crate::expressions::CurrentUser { this: None },
1972            ))),
1973
1974            // Pass through everything else
1975            _ => Ok(Expression::Function(Box::new(f))),
1976        }
1977    }
1978
1979    fn transform_aggregate_function(
1980        &self,
1981        f: Box<crate::expressions::AggregateFunction>,
1982    ) -> Result<Expression> {
1983        let name_upper = f.name.to_uppercase();
1984        match name_upper.as_str() {
1985            // GROUP_CONCAT -> STRING_AGG
1986            "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1987                Function::new("STRING_AGG".to_string(), f.args),
1988            ))),
1989
1990            // LISTAGG -> STRING_AGG
1991            "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1992                "STRING_AGG".to_string(),
1993                f.args,
1994            )))),
1995
1996            // ARRAY_AGG -> Not directly supported in SQL Server
1997            // Would need to use FOR XML PATH or STRING_AGG
1998            "ARRAY_AGG" if !f.args.is_empty() => {
1999                // Fall back to STRING_AGG (loses array semantics)
2000                Ok(Expression::Function(Box::new(Function::new(
2001                    "STRING_AGG".to_string(),
2002                    f.args,
2003                ))))
2004            }
2005
2006            // Boolean aggregates -> MIN/MAX over a null-preserving CASE, cast back to BIT.
2007            "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" | "EVERY" if f.args.len() == 1 => {
2008                let mut args = f.args;
2009                Self::transform_logical_aggregate(args.remove(0), f.filter, "MIN")
2010            }
2011            "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if f.args.len() == 1 => {
2012                let mut args = f.args;
2013                Self::transform_logical_aggregate(args.remove(0), f.filter, "MAX")
2014            }
2015
2016            // Pass through everything else
2017            _ => Ok(Expression::AggregateFunction(f)),
2018        }
2019    }
2020
2021    /// Transform CTEs to add auto-aliases to bare expressions in SELECT
2022    /// In TSQL, when a CTE doesn't have explicit column aliases, bare expressions
2023    /// in the SELECT need to be aliased
2024    fn transform_cte(&self, cte: Cte) -> Result<Expression> {
2025        Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
2026    }
2027
2028    /// Inner method to transform a CTE, returning the modified Cte struct
2029    fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
2030        // Only transform if the CTE doesn't have explicit column aliases
2031        // If it has column aliases like `WITH t(a, b) AS (...)`, we don't need to auto-alias
2032        if cte.columns.is_empty() {
2033            cte.this = self.qualify_derived_table_outputs(cte.this);
2034        }
2035        cte
2036    }
2037
2038    /// Transform Subqueries to add auto-aliases to bare expressions in SELECT
2039    /// In TSQL, when a subquery has a table alias but no column aliases,
2040    /// bare expressions need to be aliased
2041    fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
2042        // Only transform if the subquery has a table alias but no column aliases
2043        // e.g., `(SELECT 1) AS subq` needs aliasing, but `(SELECT 1) AS subq(a)` doesn't
2044        if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
2045            subquery.this = self.qualify_derived_table_outputs(subquery.this);
2046        }
2047        Ok(Expression::Subquery(Box::new(subquery)))
2048    }
2049
2050    /// Add aliases to bare (unaliased) expressions in a SELECT statement
2051    /// This transforms expressions like `SELECT 1` into `SELECT 1 AS [1]`
2052    /// BUT only when the SELECT has no FROM clause (i.e., it's a value expression)
2053    fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
2054        match expr {
2055            Expression::Select(mut select) => {
2056                // Only auto-alias if the SELECT has NO from clause
2057                // If there's a FROM clause, column references already have names from the source tables
2058                let has_from = select.from.is_some();
2059                if !has_from {
2060                    select.expressions = select
2061                        .expressions
2062                        .into_iter()
2063                        .map(|e| self.maybe_alias_expression(e))
2064                        .collect();
2065                }
2066                Expression::Select(select)
2067            }
2068            // For UNION/INTERSECT/EXCEPT, transform the first SELECT
2069            Expression::Union(mut u) => {
2070                let left = std::mem::replace(&mut u.left, Expression::Null(Null));
2071                u.left = self.qualify_derived_table_outputs(left);
2072                Expression::Union(u)
2073            }
2074            Expression::Intersect(mut i) => {
2075                let left = std::mem::replace(&mut i.left, Expression::Null(Null));
2076                i.left = self.qualify_derived_table_outputs(left);
2077                Expression::Intersect(i)
2078            }
2079            Expression::Except(mut e) => {
2080                let left = std::mem::replace(&mut e.left, Expression::Null(Null));
2081                e.left = self.qualify_derived_table_outputs(left);
2082                Expression::Except(e)
2083            }
2084            // Already wrapped in a Subquery (nested), transform the inner
2085            Expression::Subquery(mut s) => {
2086                s.this = self.qualify_derived_table_outputs(s.this);
2087                Expression::Subquery(s)
2088            }
2089            // Pass through anything else
2090            other => other,
2091        }
2092    }
2093
2094    /// Add an alias to a bare expression if needed
2095    /// Returns the expression unchanged if it already has an alias or is a star
2096    /// NOTE: This is only called for SELECTs without a FROM clause, so all bare
2097    /// expressions (including identifiers and columns) need to be aliased.
2098    fn maybe_alias_expression(&self, expr: Expression) -> Expression {
2099        match &expr {
2100            // Already has an alias, leave it alone
2101            Expression::Alias(_) => expr,
2102            // Multiple aliases, leave it alone
2103            Expression::Aliases(_) => expr,
2104            // Star (including qualified star like t.*) doesn't need an alias
2105            Expression::Star(_) => expr,
2106            // When there's no FROM clause (which is the only case when this method is called),
2107            // we need to alias columns and identifiers too since they're standalone values
2108            // that need explicit names for the derived table output.
2109            // Everything else (literals, functions, columns, identifiers, etc.) needs an alias
2110            _ => {
2111                if let Some(output_name) = self.get_output_name(&expr) {
2112                    Expression::Alias(Box::new(Alias {
2113                        this: expr,
2114                        alias: Identifier {
2115                            name: output_name,
2116                            quoted: true, // Force quoting for TSQL bracket syntax
2117                            trailing_comments: Vec::new(),
2118                            span: None,
2119                        },
2120                        column_aliases: Vec::new(),
2121                        alias_explicit_as: false,
2122                        alias_keyword: None,
2123                        pre_alias_comments: Vec::new(),
2124                        trailing_comments: Vec::new(),
2125                        inferred_type: None,
2126                    }))
2127                } else {
2128                    // No output name, leave as-is (shouldn't happen for valid expressions)
2129                    expr
2130                }
2131            }
2132        }
2133    }
2134
2135    /// Get the "output name" of an expression for auto-aliasing
2136    /// For literals, this is the literal value
2137    /// For columns, this is the column name
2138    fn get_output_name(&self, expr: &Expression) -> Option<String> {
2139        match expr {
2140            // Literals - use the literal value as the name
2141            Expression::Literal(lit) => match lit.as_ref() {
2142                Literal::Number(n) => Some(n.clone()),
2143                Literal::String(s) => Some(s.clone()),
2144                Literal::HexString(h) => Some(format!("0x{}", h)),
2145                Literal::HexNumber(h) => Some(format!("0x{}", h)),
2146                Literal::BitString(b) => Some(format!("b{}", b)),
2147                Literal::ByteString(b) => Some(format!("b'{}'", b)),
2148                Literal::NationalString(s) => Some(format!("N'{}'", s)),
2149                Literal::Date(d) => Some(d.clone()),
2150                Literal::Time(t) => Some(t.clone()),
2151                Literal::Timestamp(ts) => Some(ts.clone()),
2152                Literal::Datetime(dt) => Some(dt.clone()),
2153                Literal::TripleQuotedString(s, _) => Some(s.clone()),
2154                Literal::EscapeString(s) => Some(s.clone()),
2155                Literal::DollarString(s) => Some(s.clone()),
2156                Literal::RawString(s) => Some(s.clone()),
2157            },
2158            // Columns - use the column name
2159            Expression::Column(col) => Some(col.name.name.clone()),
2160            // Identifiers - use the identifier name
2161            Expression::Identifier(ident) => Some(ident.name.clone()),
2162            // Boolean literals
2163            Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
2164            // NULL
2165            Expression::Null(_) => Some("NULL".to_string()),
2166            // For functions, use the function name as a fallback
2167            Expression::Function(f) => Some(f.name.clone()),
2168            // For aggregates, use the function name
2169            Expression::AggregateFunction(f) => Some(f.name.clone()),
2170            // For other expressions, generate a generic name
2171            _ => Some(format!("_col_{}", 0)),
2172        }
2173    }
2174
2175    /// Helper to uppercase the first argument if it's an identifier or column (for DATEDIFF, DATEADD units)
2176    fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
2177        use crate::expressions::Identifier;
2178        if !args.is_empty() {
2179            match &args[0] {
2180                Expression::Identifier(id) => {
2181                    args[0] = Expression::Identifier(Identifier {
2182                        name: id.name.to_uppercase(),
2183                        quoted: id.quoted,
2184                        trailing_comments: id.trailing_comments.clone(),
2185                        span: None,
2186                    });
2187                }
2188                Expression::Var(v) => {
2189                    args[0] = Expression::Identifier(Identifier {
2190                        name: v.this.to_uppercase(),
2191                        quoted: false,
2192                        trailing_comments: Vec::new(),
2193                        span: None,
2194                    });
2195                }
2196                Expression::Column(col) if col.table.is_none() => {
2197                    args[0] = Expression::Identifier(Identifier {
2198                        name: col.name.name.to_uppercase(),
2199                        quoted: col.name.quoted,
2200                        trailing_comments: col.name.trailing_comments.clone(),
2201                        span: None,
2202                    });
2203                }
2204                _ => {}
2205            }
2206        }
2207        args
2208    }
2209}
2210
2211#[cfg(test)]
2212mod tests {
2213    use super::*;
2214    use crate::dialects::Dialect;
2215
2216    fn transpile_to_tsql(sql: &str) -> String {
2217        let dialect = Dialect::get(DialectType::Generic);
2218        let result = dialect
2219            .transpile(sql, DialectType::TSQL)
2220            .expect("Transpile failed");
2221        result[0].clone()
2222    }
2223
2224    #[test]
2225    fn test_nvl_to_isnull() {
2226        let result = transpile_to_tsql("SELECT NVL(a, b)");
2227        assert!(
2228            result.contains("ISNULL"),
2229            "Expected ISNULL, got: {}",
2230            result
2231        );
2232    }
2233
2234    #[test]
2235    fn test_coalesce_to_isnull() {
2236        let result = transpile_to_tsql("SELECT COALESCE(a, b)");
2237        assert!(
2238            result.contains("ISNULL"),
2239            "Expected ISNULL, got: {}",
2240            result
2241        );
2242    }
2243
2244    #[test]
2245    fn test_basic_select() {
2246        let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
2247        assert!(result.contains("SELECT"));
2248        assert!(result.contains("FROM users"));
2249    }
2250
2251    #[test]
2252    fn test_length_to_len() {
2253        let result = transpile_to_tsql("SELECT LENGTH(name)");
2254        assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
2255    }
2256
2257    #[test]
2258    fn test_now_to_getdate() {
2259        let result = transpile_to_tsql("SELECT NOW()");
2260        assert!(
2261            result.contains("GETDATE"),
2262            "Expected GETDATE, got: {}",
2263            result
2264        );
2265    }
2266
2267    #[test]
2268    fn test_group_concat_to_string_agg() {
2269        let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
2270        assert!(
2271            result.contains("STRING_AGG"),
2272            "Expected STRING_AGG, got: {}",
2273            result
2274        );
2275    }
2276
2277    #[test]
2278    fn test_listagg_to_string_agg() {
2279        let result = transpile_to_tsql("SELECT LISTAGG(name)");
2280        assert!(
2281            result.contains("STRING_AGG"),
2282            "Expected STRING_AGG, got: {}",
2283            result
2284        );
2285    }
2286
2287    #[test]
2288    fn test_ln_to_log() {
2289        let result = transpile_to_tsql("SELECT LN(x)");
2290        assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
2291    }
2292
2293    #[test]
2294    fn test_stddev_to_stdev() {
2295        let result = transpile_to_tsql("SELECT STDDEV(x)");
2296        assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
2297    }
2298
2299    #[test]
2300    fn test_bracket_identifiers() {
2301        // SQL Server uses square brackets for identifiers
2302        let dialect = Dialect::get(DialectType::TSQL);
2303        let config = dialect.generator_config();
2304        assert_eq!(config.identifier_quote, '[');
2305    }
2306
2307    #[test]
2308    fn test_json_query_isnull_wrapper_simple() {
2309        // JSON_QUERY with two args needs ISNULL wrapper when transpiling to TSQL
2310        let dialect = Dialect::get(DialectType::TSQL);
2311        let result = dialect
2312            .transpile(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
2313            .expect("transpile failed");
2314        assert!(
2315            result[0].contains("ISNULL"),
2316            "JSON_QUERY should be wrapped with ISNULL: {}",
2317            result[0]
2318        );
2319    }
2320
2321    #[test]
2322    fn test_json_query_isnull_wrapper_nested() {
2323        let dialect = Dialect::get(DialectType::TSQL);
2324        let result = dialect
2325            .transpile(
2326                r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
2327                DialectType::TSQL,
2328            )
2329            .expect("transpile failed");
2330        let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
2331        assert_eq!(
2332            result[0], expected,
2333            "JSON_QUERY should be wrapped with ISNULL"
2334        );
2335    }
2336}