1use super::{DialectImpl, DialectType};
14use crate::error::Result;
15use crate::expressions::{
16 Alias, Cast, Cte, DataType, Expression, Function, Identifier, Join, JoinKind, LikeOp, Literal,
17 StringAggFunc, Subquery, UnaryFunc,
18};
19use crate::generator::GeneratorConfig;
20use crate::tokens::TokenizerConfig;
21
22pub struct TSQLDialect;
24
25impl DialectImpl for TSQLDialect {
26 fn dialect_type(&self) -> DialectType {
27 DialectType::TSQL
28 }
29
30 fn tokenizer_config(&self) -> TokenizerConfig {
31 let mut config = TokenizerConfig::default();
32 config.identifiers.insert('[', ']');
34 config.identifiers.insert('"', '"');
36 config
37 }
38
39 fn generator_config(&self) -> GeneratorConfig {
40 use crate::generator::IdentifierQuoteStyle;
41 GeneratorConfig {
42 identifier_quote: '[',
44 identifier_quote_style: IdentifierQuoteStyle::BRACKET,
45 dialect: Some(DialectType::TSQL),
46 limit_fetch_style: crate::generator::LimitFetchStyle::FetchFirst,
49 null_ordering_supported: false,
51 supports_select_into: true,
53 alter_table_include_column_keyword: false,
55 computed_column_with_type: false,
57 cte_recursive_keyword_required: false,
59 ensure_bools: true,
61 supports_single_arg_concat: false,
63 tablesample_seed_keyword: "REPEATABLE",
65 json_path_bracketed_key_supported: false,
67 supports_to_number: false,
69 set_op_modifiers: false,
71 copy_params_eq_required: true,
73 except_intersect_support_all_clause: false,
75 alter_set_wrapped: true,
77 try_supported: true,
79 nvl2_supported: false,
81 parameter_default_equals: true,
83 supports_window_exclude: false,
85 multi_arg_distinct: false,
87 locking_reads_supported: false,
89 ..Default::default()
90 }
91 }
92
93 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
94 match expr {
95 Expression::Select(mut select) => {
100 select.expressions = select
101 .expressions
102 .into_iter()
103 .map(|e| {
104 match e {
105 Expression::Eq(op) => {
106 match &op.left {
109 Expression::Column(col)
110 if col.table.is_none()
111 && !col.name.name.starts_with('@') =>
112 {
113 Expression::Alias(Box::new(Alias {
114 this: op.right,
115 alias: col.name.clone(),
116 column_aliases: Vec::new(),
117 pre_alias_comments: Vec::new(),
118 trailing_comments: Vec::new(),
119 inferred_type: None,
120 }))
121 }
122 Expression::Identifier(ident)
123 if !ident.name.starts_with('@') =>
124 {
125 Expression::Alias(Box::new(Alias {
126 this: op.right,
127 alias: ident.clone(),
128 column_aliases: Vec::new(),
129 pre_alias_comments: Vec::new(),
130 trailing_comments: Vec::new(),
131 inferred_type: None,
132 }))
133 }
134 _ => Expression::Eq(op),
135 }
136 }
137 other => other,
138 }
139 })
140 .collect();
141
142 if let Some(ref mut with) = select.with {
144 with.ctes = with
145 .ctes
146 .drain(..)
147 .map(|cte| self.transform_cte_inner(cte))
148 .collect();
149 }
150
151 Ok(Expression::Select(select))
152 }
153
154 Expression::DataType(dt) => self.transform_data_type(dt),
156
157 Expression::IsTrue(it) => {
160 let one = Expression::Literal(crate::expressions::Literal::Number("1".to_string()));
161 if it.not {
162 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
164 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
165 left: it.this,
166 right: one,
167 left_comments: vec![],
168 operator_comments: vec![],
169 trailing_comments: vec![],
170 inferred_type: None,
171 })),
172 inferred_type: None,
173 })))
174 } else {
175 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
177 left: it.this,
178 right: one,
179 left_comments: vec![],
180 operator_comments: vec![],
181 trailing_comments: vec![],
182 inferred_type: None,
183 })))
184 }
185 }
186 Expression::IsFalse(it) => {
187 let zero =
188 Expression::Literal(crate::expressions::Literal::Number("0".to_string()));
189 if it.not {
190 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
192 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
193 left: it.this,
194 right: zero,
195 left_comments: vec![],
196 operator_comments: vec![],
197 trailing_comments: vec![],
198 inferred_type: None,
199 })),
200 inferred_type: None,
201 })))
202 } else {
203 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
205 left: it.this,
206 right: zero,
207 left_comments: vec![],
208 operator_comments: vec![],
209 trailing_comments: vec![],
210 inferred_type: None,
211 })))
212 }
213 }
214
215 Expression::In(mut in_expr) if in_expr.not => {
219 in_expr.not = false;
220 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
221 this: Expression::In(in_expr),
222 inferred_type: None,
223 })))
224 }
225
226 Expression::Coalesce(f) if f.expressions.len() == 2 => Ok(Expression::Function(
229 Box::new(Function::new("ISNULL".to_string(), f.expressions)),
230 )),
231
232 Expression::Nvl(f) => Ok(Expression::Function(Box::new(Function::new(
234 "ISNULL".to_string(),
235 vec![f.this, f.expression],
236 )))),
237
238 Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
240 this: f.this,
241 separator: f.separator,
242 order_by: f.order_by,
243 distinct: f.distinct,
244 filter: f.filter,
245 limit: None,
246 }))),
247
248 Expression::ListAgg(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
250 this: f.this,
251 separator: f.separator,
252 order_by: f.order_by,
253 distinct: f.distinct,
254 filter: f.filter,
255 limit: None,
256 }))),
257
258 Expression::TryCast(c) => Ok(Expression::TryCast(c)),
260
261 Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
263
264 Expression::ILike(op) => {
266 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
269 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
270 Ok(Expression::Like(Box::new(LikeOp {
271 left: lower_left,
272 right: lower_right,
273 escape: op.escape,
274 quantifier: op.quantifier,
275 inferred_type: None,
276 })))
277 }
278
279 Expression::Concat(op) => {
282 Ok(Expression::Add(op))
284 }
285
286 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
288 seed: None,
289 lower: None,
290 upper: None,
291 }))),
292
293 Expression::Unnest(f) => {
295 Ok(Expression::Function(Box::new(Function::new(
298 "OPENJSON".to_string(),
299 vec![f.this],
300 ))))
301 }
302
303 Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
305 "OPENJSON".to_string(),
306 vec![f.this],
307 )))),
308
309 Expression::Join(join) if join.kind == JoinKind::Lateral => {
311 Ok(Expression::Join(Box::new(Join {
312 this: join.this,
313 on: None,
314 using: join.using,
315 kind: JoinKind::CrossApply,
316 use_inner_keyword: false,
317 use_outer_keyword: false,
318 deferred_condition: false,
319 join_hint: None,
320 match_condition: None,
321 pivots: join.pivots,
322 comments: join.comments,
323 nesting_group: 0,
324 directed: false,
325 })))
326 }
327
328 Expression::Join(join) if join.kind == JoinKind::LeftLateral => {
330 Ok(Expression::Join(Box::new(Join {
331 this: join.this,
332 on: None, using: join.using,
334 kind: JoinKind::OuterApply,
335 use_inner_keyword: false,
336 use_outer_keyword: false,
337 deferred_condition: false,
338 join_hint: None,
339 match_condition: None,
340 pivots: join.pivots,
341 comments: join.comments,
342 nesting_group: 0,
343 directed: false,
344 })))
345 }
346
347 Expression::Length(f) => Ok(Expression::Function(Box::new(Function::new(
349 "LEN".to_string(),
350 vec![f.this],
351 )))),
352
353 Expression::Stddev(f) => Ok(Expression::Function(Box::new(Function::new(
355 "STDEV".to_string(),
356 vec![f.this],
357 )))),
358
359 Expression::Boolean(b) => {
361 let value = if b.value { 1 } else { 0 };
362 Ok(Expression::Literal(crate::expressions::Literal::Number(
363 value.to_string(),
364 )))
365 }
366
367 Expression::Ln(f) => Ok(Expression::Function(Box::new(Function::new(
369 "LOG".to_string(),
370 vec![f.this],
371 )))),
372
373 Expression::CurrentDate(_) => {
376 let getdate =
377 Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])));
378 Ok(Expression::Cast(Box::new(crate::expressions::Cast {
379 this: getdate,
380 to: crate::expressions::DataType::Date,
381 trailing_comments: Vec::new(),
382 double_colon_syntax: false,
383 format: None,
384 default: None,
385 inferred_type: None,
386 })))
387 }
388
389 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
391 "GETDATE".to_string(),
392 vec![],
393 )))),
394
395 Expression::DateDiff(f) => {
397 let unit_str = match f.unit {
399 Some(crate::expressions::IntervalUnit::Year) => "YEAR",
400 Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
401 Some(crate::expressions::IntervalUnit::Month) => "MONTH",
402 Some(crate::expressions::IntervalUnit::Week) => "WEEK",
403 Some(crate::expressions::IntervalUnit::Day) => "DAY",
404 Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
405 Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
406 Some(crate::expressions::IntervalUnit::Second) => "SECOND",
407 Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
408 Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
409 Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
410 None => "DAY",
411 };
412 let unit = Expression::Identifier(crate::expressions::Identifier {
413 name: unit_str.to_string(),
414 quoted: false,
415 trailing_comments: Vec::new(),
416 span: None,
417 });
418 Ok(Expression::Function(Box::new(Function::new(
419 "DATEDIFF".to_string(),
420 vec![unit, f.expression, f.this], ))))
422 }
423
424 Expression::DateAdd(f) => {
426 let unit_str = match f.unit {
427 crate::expressions::IntervalUnit::Year => "YEAR",
428 crate::expressions::IntervalUnit::Quarter => "QUARTER",
429 crate::expressions::IntervalUnit::Month => "MONTH",
430 crate::expressions::IntervalUnit::Week => "WEEK",
431 crate::expressions::IntervalUnit::Day => "DAY",
432 crate::expressions::IntervalUnit::Hour => "HOUR",
433 crate::expressions::IntervalUnit::Minute => "MINUTE",
434 crate::expressions::IntervalUnit::Second => "SECOND",
435 crate::expressions::IntervalUnit::Millisecond => "MILLISECOND",
436 crate::expressions::IntervalUnit::Microsecond => "MICROSECOND",
437 crate::expressions::IntervalUnit::Nanosecond => "NANOSECOND",
438 };
439 let unit = Expression::Identifier(crate::expressions::Identifier {
440 name: unit_str.to_string(),
441 quoted: false,
442 trailing_comments: Vec::new(),
443 span: None,
444 });
445 Ok(Expression::Function(Box::new(Function::new(
446 "DATEADD".to_string(),
447 vec![unit, f.interval, f.this],
448 ))))
449 }
450
451 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
454 "NEWID".to_string(),
455 vec![],
456 )))),
457
458 Expression::IfFunc(f) => {
461 let false_val = f
462 .false_value
463 .unwrap_or(Expression::Null(crate::expressions::Null));
464 Ok(Expression::Function(Box::new(Function::new(
465 "IIF".to_string(),
466 vec![f.condition, f.true_value, false_val],
467 ))))
468 }
469
470 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
473
474 Expression::LastDay(f) => Ok(Expression::Function(Box::new(Function::new(
476 "EOMONTH".to_string(),
477 vec![f.this.clone()],
478 )))),
479
480 Expression::Ceil(f) => Ok(Expression::Function(Box::new(Function::new(
482 "CEILING".to_string(),
483 vec![f.this],
484 )))),
485
486 Expression::Repeat(f) => Ok(Expression::Function(Box::new(Function::new(
488 "REPLICATE".to_string(),
489 vec![f.this, f.times],
490 )))),
491
492 Expression::Chr(f) => Ok(Expression::Function(Box::new(Function::new(
494 "CHAR".to_string(),
495 vec![f.this],
496 )))),
497
498 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
501 "VARP".to_string(),
502 vec![f.this],
503 )))),
504
505 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
507 "VAR".to_string(),
508 vec![f.this],
509 )))),
510
511 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
514 "HASHBYTES".to_string(),
515 vec![Expression::string("MD5"), *f.this],
516 )))),
517
518 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
520 "HASHBYTES".to_string(),
521 vec![Expression::string("SHA1"), f.this],
522 )))),
523
524 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
526 "HASHBYTES".to_string(),
527 vec![Expression::string("SHA1"), f.this],
528 )))),
529
530 Expression::ArrayToString(f) => Ok(Expression::Function(Box::new(Function::new(
533 "STRING_AGG".to_string(),
534 vec![f.this],
535 )))),
536
537 Expression::AutoIncrementColumnConstraint(_) => Ok(Expression::Function(Box::new(
540 Function::new("IDENTITY".to_string(), vec![]),
541 ))),
542
543 Expression::CreateView(mut view) => {
547 view.name.catalog = None;
549 Ok(Expression::CreateView(view))
550 }
551
552 Expression::DropView(mut view) => {
553 view.name.catalog = None;
555 Ok(Expression::DropView(view))
556 }
557
558 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
562 let path = match *e.expression {
563 Expression::Literal(Literal::String(s)) => {
564 let normalized = if s.starts_with('$') {
565 s
566 } else if s.starts_with('[') {
567 format!("${}", s)
568 } else {
569 format!("$.{}", s)
570 };
571 Expression::Literal(Literal::String(normalized))
572 }
573 other => other,
574 };
575 let json_query = Expression::Function(Box::new(Function::new(
576 "JSON_QUERY".to_string(),
577 vec![(*e.this).clone(), path.clone()],
578 )));
579 let json_value = Expression::Function(Box::new(Function::new(
580 "JSON_VALUE".to_string(),
581 vec![*e.this, path],
582 )));
583 Ok(Expression::Function(Box::new(Function::new(
584 "ISNULL".to_string(),
585 vec![json_query, json_value],
586 ))))
587 }
588
589 Expression::Function(f) => self.transform_function(*f),
591
592 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
594
595 Expression::Cte(cte) => self.transform_cte(*cte),
598
599 Expression::Subquery(subquery) => self.transform_subquery(*subquery),
602
603 Expression::JsonQuery(f) => {
605 let json_query = Expression::Function(Box::new(Function::new(
606 "JSON_QUERY".to_string(),
607 vec![f.this.clone(), f.path.clone()],
608 )));
609 let json_value = Expression::Function(Box::new(Function::new(
610 "JSON_VALUE".to_string(),
611 vec![f.this, f.path],
612 )));
613 Ok(Expression::Function(Box::new(Function::new(
614 "ISNULL".to_string(),
615 vec![json_query, json_value],
616 ))))
617 }
618 Expression::JsonValue(f) => Ok(Expression::Function(Box::new(Function::new(
620 "JSON_VALUE".to_string(),
621 vec![f.this, f.path],
622 )))),
623
624 _ => Ok(expr),
626 }
627 }
628}
629
630impl TSQLDialect {
631 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
633 use crate::expressions::DataType;
634 let transformed = match dt {
635 DataType::Boolean => DataType::Custom {
637 name: "BIT".to_string(),
638 },
639 DataType::Int { .. } => dt,
641 DataType::Decimal { precision, scale } => DataType::Custom {
644 name: if let (Some(p), Some(s)) = (&precision, &scale) {
645 format!("NUMERIC({}, {})", p, s)
646 } else if let Some(p) = &precision {
647 format!("NUMERIC({})", p)
648 } else {
649 "NUMERIC".to_string()
650 },
651 },
652 DataType::Text => DataType::Custom {
654 name: "VARCHAR(MAX)".to_string(),
655 },
656 DataType::Timestamp { .. } => DataType::Custom {
658 name: "DATETIME2".to_string(),
659 },
660 DataType::Uuid => DataType::Custom {
662 name: "UNIQUEIDENTIFIER".to_string(),
663 },
664 other => other,
666 };
667 Ok(Expression::DataType(transformed))
668 }
669
670 fn transform_function(&self, f: Function) -> Result<Expression> {
671 let name_upper = f.name.to_uppercase();
672 match name_upper.as_str() {
673 "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
675 "ISNULL".to_string(),
676 f.args,
677 )))),
678
679 "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
681 "ISNULL".to_string(),
682 f.args,
683 )))),
684
685 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
687 Function::new("STRING_AGG".to_string(), f.args),
688 ))),
689
690 "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
692
693 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
695 "STRING_AGG".to_string(),
696 f.args,
697 )))),
698
699 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
701 "SUBSTRING".to_string(),
702 f.args,
703 )))),
704
705 "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
707 "LEN".to_string(),
708 f.args,
709 )))),
710
711 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
713 seed: None,
714 lower: None,
715 upper: None,
716 }))),
717
718 "NOW" => Ok(Expression::Function(Box::new(Function::new(
720 "GETDATE".to_string(),
721 vec![],
722 )))),
723
724 "CURRENT_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
726 "GETDATE".to_string(),
727 vec![],
728 )))),
729
730 "CURRENT_DATE" => {
732 Ok(Expression::Function(Box::new(Function::new(
734 "CAST".to_string(),
735 vec![
736 Expression::Function(Box::new(Function::new(
737 "GETDATE".to_string(),
738 vec![],
739 ))),
740 Expression::Identifier(crate::expressions::Identifier::new("DATE")),
741 ],
742 ))))
743 }
744
745 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
747 "CONVERT".to_string(),
748 f.args,
749 )))),
750
751 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
753 "CONVERT".to_string(),
754 f.args,
755 )))),
756
757 "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
759 "FORMAT".to_string(),
760 f.args,
761 )))),
762
763 "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
765 "FORMAT".to_string(),
766 f.args,
767 )))),
768
769 "DATE_TRUNC" | "DATETRUNC" => {
772 let mut args = Self::uppercase_first_arg_if_identifier(f.args);
773 if args.len() >= 2 {
775 if let Expression::Literal(Literal::String(_)) = &args[1] {
776 args[1] = Expression::Cast(Box::new(Cast {
777 this: args[1].clone(),
778 to: DataType::Custom {
779 name: "DATETIME2".to_string(),
780 },
781 trailing_comments: Vec::new(),
782 double_colon_syntax: false,
783 format: None,
784 default: None,
785 inferred_type: None,
786 }));
787 }
788 }
789 Ok(Expression::Function(Box::new(Function::new(
790 "DATETRUNC".to_string(),
791 args,
792 ))))
793 }
794
795 "DATEADD" => {
797 let args = Self::uppercase_first_arg_if_identifier(f.args);
798 Ok(Expression::Function(Box::new(Function::new(
799 "DATEADD".to_string(),
800 args,
801 ))))
802 }
803
804 "DATEDIFF" => {
806 let args = Self::uppercase_first_arg_if_identifier(f.args);
807 Ok(Expression::Function(Box::new(Function::new(
808 "DATEDIFF".to_string(),
809 args,
810 ))))
811 }
812
813 "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
815 "DATEPART".to_string(),
816 f.args,
817 )))),
818
819 "STRPOS" | "POSITION" if f.args.len() >= 2 => {
821 Ok(Expression::Function(Box::new(Function::new(
823 "CHARINDEX".to_string(),
824 f.args,
825 ))))
826 }
827
828 "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
830
831 "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
833 Function::new("CEILING".to_string(), f.args),
834 ))),
835
836 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
841 "JSON_VALUE".to_string(),
842 f.args,
843 )))),
844
845 "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
847 "JSON_VALUE".to_string(),
848 f.args,
849 )))),
850
851 "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
853
854 "GET_PATH" if f.args.len() == 2 => {
856 let mut args = f.args;
857 let this = args.remove(0);
858 let path = args.remove(0);
859 let json_path = match &path {
860 Expression::Literal(Literal::String(s)) => {
861 let normalized = if s.starts_with('$') {
862 s.clone()
863 } else if s.starts_with('[') {
864 format!("${}", s)
865 } else {
866 format!("$.{}", s)
867 };
868 Expression::Literal(Literal::String(normalized))
869 }
870 _ => path,
871 };
872 let json_query = Expression::Function(Box::new(Function::new(
874 "JSON_QUERY".to_string(),
875 vec![this.clone(), json_path.clone()],
876 )));
877 let json_value = Expression::Function(Box::new(Function::new(
878 "JSON_VALUE".to_string(),
879 vec![this, json_path],
880 )));
881 Ok(Expression::Function(Box::new(Function::new(
882 "ISNULL".to_string(),
883 vec![json_query, json_value],
884 ))))
885 }
886
887 "JSON_QUERY" if f.args.len() == 1 => {
890 let this = f.args.into_iter().next().unwrap();
891 let path = Expression::Literal(Literal::String("$".to_string()));
892 let json_query = Expression::Function(Box::new(Function::new(
893 "JSON_QUERY".to_string(),
894 vec![this.clone(), path.clone()],
895 )));
896 let json_value = Expression::Function(Box::new(Function::new(
897 "JSON_VALUE".to_string(),
898 vec![this, path],
899 )));
900 Ok(Expression::Function(Box::new(Function::new(
901 "ISNULL".to_string(),
902 vec![json_query, json_value],
903 ))))
904 }
905
906 "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
908 "STRING_SPLIT".to_string(),
909 f.args,
910 )))),
911
912 "REGEXP_LIKE" => {
915 Ok(Expression::Function(Box::new(Function::new(
917 "PATINDEX".to_string(),
918 f.args,
919 ))))
920 }
921
922 "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
924 "LOG".to_string(),
925 f.args,
926 )))),
927
928 "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
933 "STDEV".to_string(),
934 f.args,
935 )))),
936
937 "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
939 "STDEVP".to_string(),
940 f.args,
941 )))),
942
943 "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
945 "VAR".to_string(),
946 f.args,
947 )))),
948
949 "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
951 "VARP".to_string(),
952 f.args,
953 )))),
954
955 "DATE_ADD" => {
957 if f.args.len() == 2 {
958 let mut args = f.args;
959 let date = args.remove(0);
960 let interval = args.remove(0);
961 let unit = Expression::Identifier(crate::expressions::Identifier {
962 name: "DAY".to_string(),
963 quoted: false,
964 trailing_comments: Vec::new(),
965 span: None,
966 });
967 Ok(Expression::Function(Box::new(Function::new(
968 "DATEADD".to_string(),
969 vec![unit, interval, date],
970 ))))
971 } else {
972 let args = Self::uppercase_first_arg_if_identifier(f.args);
973 Ok(Expression::Function(Box::new(Function::new(
974 "DATEADD".to_string(),
975 args,
976 ))))
977 }
978 }
979
980 "INSERT" => Ok(Expression::Function(Box::new(Function::new(
982 "STUFF".to_string(),
983 f.args,
984 )))),
985
986 "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
988 crate::expressions::CurrentUser { this: None },
989 ))),
990
991 _ => Ok(Expression::Function(Box::new(f))),
993 }
994 }
995
996 fn transform_aggregate_function(
997 &self,
998 f: Box<crate::expressions::AggregateFunction>,
999 ) -> Result<Expression> {
1000 let name_upper = f.name.to_uppercase();
1001 match name_upper.as_str() {
1002 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1004 Function::new("STRING_AGG".to_string(), f.args),
1005 ))),
1006
1007 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1009 "STRING_AGG".to_string(),
1010 f.args,
1011 )))),
1012
1013 "ARRAY_AGG" if !f.args.is_empty() => {
1016 Ok(Expression::Function(Box::new(Function::new(
1018 "STRING_AGG".to_string(),
1019 f.args,
1020 ))))
1021 }
1022
1023 _ => Ok(Expression::AggregateFunction(f)),
1025 }
1026 }
1027
1028 fn transform_cte(&self, cte: Cte) -> Result<Expression> {
1032 Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
1033 }
1034
1035 fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
1037 if cte.columns.is_empty() {
1040 cte.this = self.qualify_derived_table_outputs(cte.this);
1041 }
1042 cte
1043 }
1044
1045 fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
1049 if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
1052 subquery.this = self.qualify_derived_table_outputs(subquery.this);
1053 }
1054 Ok(Expression::Subquery(Box::new(subquery)))
1055 }
1056
1057 fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
1061 match expr {
1062 Expression::Select(mut select) => {
1063 let has_from = select.from.is_some();
1066 if !has_from {
1067 select.expressions = select
1068 .expressions
1069 .into_iter()
1070 .map(|e| self.maybe_alias_expression(e))
1071 .collect();
1072 }
1073 Expression::Select(select)
1074 }
1075 Expression::Union(mut u) => {
1077 u.left = self.qualify_derived_table_outputs(u.left);
1078 Expression::Union(u)
1079 }
1080 Expression::Intersect(mut i) => {
1081 i.left = self.qualify_derived_table_outputs(i.left);
1082 Expression::Intersect(i)
1083 }
1084 Expression::Except(mut e) => {
1085 e.left = self.qualify_derived_table_outputs(e.left);
1086 Expression::Except(e)
1087 }
1088 Expression::Subquery(mut s) => {
1090 s.this = self.qualify_derived_table_outputs(s.this);
1091 Expression::Subquery(s)
1092 }
1093 other => other,
1095 }
1096 }
1097
1098 fn maybe_alias_expression(&self, expr: Expression) -> Expression {
1103 match &expr {
1104 Expression::Alias(_) => expr,
1106 Expression::Aliases(_) => expr,
1108 Expression::Star(_) => expr,
1110 _ => {
1115 if let Some(output_name) = self.get_output_name(&expr) {
1116 Expression::Alias(Box::new(Alias {
1117 this: expr,
1118 alias: Identifier {
1119 name: output_name,
1120 quoted: true, trailing_comments: Vec::new(),
1122 span: None,
1123 },
1124 column_aliases: Vec::new(),
1125 pre_alias_comments: Vec::new(),
1126 trailing_comments: Vec::new(),
1127 inferred_type: None,
1128 }))
1129 } else {
1130 expr
1132 }
1133 }
1134 }
1135 }
1136
1137 fn get_output_name(&self, expr: &Expression) -> Option<String> {
1141 match expr {
1142 Expression::Literal(lit) => match lit {
1144 Literal::Number(n) => Some(n.clone()),
1145 Literal::String(s) => Some(s.clone()),
1146 Literal::HexString(h) => Some(format!("0x{}", h)),
1147 Literal::HexNumber(h) => Some(format!("0x{}", h)),
1148 Literal::BitString(b) => Some(format!("b{}", b)),
1149 Literal::ByteString(b) => Some(format!("b'{}'", b)),
1150 Literal::NationalString(s) => Some(format!("N'{}'", s)),
1151 Literal::Date(d) => Some(d.clone()),
1152 Literal::Time(t) => Some(t.clone()),
1153 Literal::Timestamp(ts) => Some(ts.clone()),
1154 Literal::Datetime(dt) => Some(dt.clone()),
1155 Literal::TripleQuotedString(s, _) => Some(s.clone()),
1156 Literal::EscapeString(s) => Some(s.clone()),
1157 Literal::DollarString(s) => Some(s.clone()),
1158 Literal::RawString(s) => Some(s.clone()),
1159 },
1160 Expression::Column(col) => Some(col.name.name.clone()),
1162 Expression::Identifier(ident) => Some(ident.name.clone()),
1164 Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
1166 Expression::Null(_) => Some("NULL".to_string()),
1168 Expression::Function(f) => Some(f.name.clone()),
1170 Expression::AggregateFunction(f) => Some(f.name.clone()),
1172 _ => Some(format!("_col_{}", 0)),
1174 }
1175 }
1176
1177 fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
1179 use crate::expressions::Identifier;
1180 if !args.is_empty() {
1181 match &args[0] {
1182 Expression::Identifier(id) => {
1183 args[0] = Expression::Identifier(Identifier {
1184 name: id.name.to_uppercase(),
1185 quoted: id.quoted,
1186 trailing_comments: id.trailing_comments.clone(),
1187 span: None,
1188 });
1189 }
1190 Expression::Column(col) if col.table.is_none() => {
1191 args[0] = Expression::Identifier(Identifier {
1192 name: col.name.name.to_uppercase(),
1193 quoted: col.name.quoted,
1194 trailing_comments: col.name.trailing_comments.clone(),
1195 span: None,
1196 });
1197 }
1198 _ => {}
1199 }
1200 }
1201 args
1202 }
1203}
1204
1205#[cfg(test)]
1206mod tests {
1207 use super::*;
1208 use crate::dialects::Dialect;
1209
1210 fn transpile_to_tsql(sql: &str) -> String {
1211 let dialect = Dialect::get(DialectType::Generic);
1212 let result = dialect
1213 .transpile_to(sql, DialectType::TSQL)
1214 .expect("Transpile failed");
1215 result[0].clone()
1216 }
1217
1218 #[test]
1219 fn test_nvl_to_isnull() {
1220 let result = transpile_to_tsql("SELECT NVL(a, b)");
1221 assert!(
1222 result.contains("ISNULL"),
1223 "Expected ISNULL, got: {}",
1224 result
1225 );
1226 }
1227
1228 #[test]
1229 fn test_coalesce_to_isnull() {
1230 let result = transpile_to_tsql("SELECT COALESCE(a, b)");
1231 assert!(
1232 result.contains("ISNULL"),
1233 "Expected ISNULL, got: {}",
1234 result
1235 );
1236 }
1237
1238 #[test]
1239 fn test_basic_select() {
1240 let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
1241 assert!(result.contains("SELECT"));
1242 assert!(result.contains("FROM users"));
1243 }
1244
1245 #[test]
1246 fn test_length_to_len() {
1247 let result = transpile_to_tsql("SELECT LENGTH(name)");
1248 assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
1249 }
1250
1251 #[test]
1252 fn test_now_to_getdate() {
1253 let result = transpile_to_tsql("SELECT NOW()");
1254 assert!(
1255 result.contains("GETDATE"),
1256 "Expected GETDATE, got: {}",
1257 result
1258 );
1259 }
1260
1261 #[test]
1262 fn test_group_concat_to_string_agg() {
1263 let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
1264 assert!(
1265 result.contains("STRING_AGG"),
1266 "Expected STRING_AGG, got: {}",
1267 result
1268 );
1269 }
1270
1271 #[test]
1272 fn test_listagg_to_string_agg() {
1273 let result = transpile_to_tsql("SELECT LISTAGG(name)");
1274 assert!(
1275 result.contains("STRING_AGG"),
1276 "Expected STRING_AGG, got: {}",
1277 result
1278 );
1279 }
1280
1281 #[test]
1282 fn test_ln_to_log() {
1283 let result = transpile_to_tsql("SELECT LN(x)");
1284 assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
1285 }
1286
1287 #[test]
1288 fn test_stddev_to_stdev() {
1289 let result = transpile_to_tsql("SELECT STDDEV(x)");
1290 assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
1291 }
1292
1293 #[test]
1294 fn test_bracket_identifiers() {
1295 let dialect = Dialect::get(DialectType::TSQL);
1297 let config = dialect.generator_config();
1298 assert_eq!(config.identifier_quote, '[');
1299 }
1300
1301 #[test]
1302 fn test_json_query_isnull_wrapper_simple() {
1303 let dialect = Dialect::get(DialectType::TSQL);
1305 let result = dialect
1306 .transpile_to(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
1307 .expect("transpile failed");
1308 assert!(
1309 result[0].contains("ISNULL"),
1310 "JSON_QUERY should be wrapped with ISNULL: {}",
1311 result[0]
1312 );
1313 }
1314
1315 #[test]
1316 fn test_json_query_isnull_wrapper_nested() {
1317 std::thread::Builder::new()
1319 .stack_size(16 * 1024 * 1024)
1320 .spawn(|| {
1321 let dialect = Dialect::get(DialectType::TSQL);
1322 let result = dialect.transpile_to(
1323 r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
1324 DialectType::TSQL,
1325 ).expect("transpile failed");
1326 let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
1327 assert_eq!(result[0], expected, "JSON_QUERY should be wrapped with ISNULL");
1328 })
1329 .expect("Failed to spawn test thread")
1330 .join()
1331 .expect("Test thread panicked");
1332 }
1333}