Skip to main content

polyglot_sql/dialects/
tsql.rs

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