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