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(Box::new(crate::expressions::Literal::Number(
161 "1".to_string(),
162 )));
163 if it.not {
164 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
166 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
167 left: it.this,
168 right: one,
169 left_comments: vec![],
170 operator_comments: vec![],
171 trailing_comments: vec![],
172 inferred_type: None,
173 })),
174 inferred_type: None,
175 })))
176 } else {
177 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
179 left: it.this,
180 right: one,
181 left_comments: vec![],
182 operator_comments: vec![],
183 trailing_comments: vec![],
184 inferred_type: None,
185 })))
186 }
187 }
188 Expression::IsFalse(it) => {
189 let zero = Expression::Literal(Box::new(crate::expressions::Literal::Number(
190 "0".to_string(),
191 )));
192 if it.not {
193 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
195 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
196 left: it.this,
197 right: zero,
198 left_comments: vec![],
199 operator_comments: vec![],
200 trailing_comments: vec![],
201 inferred_type: None,
202 })),
203 inferred_type: None,
204 })))
205 } else {
206 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
208 left: it.this,
209 right: zero,
210 left_comments: vec![],
211 operator_comments: vec![],
212 trailing_comments: vec![],
213 inferred_type: None,
214 })))
215 }
216 }
217
218 Expression::In(mut in_expr) if in_expr.not => {
222 in_expr.not = false;
223 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
224 this: Expression::In(in_expr),
225 inferred_type: None,
226 })))
227 }
228
229 Expression::Coalesce(f) if f.expressions.len() == 2 => Ok(Expression::Function(
232 Box::new(Function::new("ISNULL".to_string(), f.expressions)),
233 )),
234
235 Expression::Nvl(f) => Ok(Expression::Function(Box::new(Function::new(
237 "ISNULL".to_string(),
238 vec![f.this, f.expression],
239 )))),
240
241 Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
243 this: f.this,
244 separator: f.separator,
245 order_by: f.order_by,
246 distinct: f.distinct,
247 filter: f.filter,
248 limit: None,
249 inferred_type: None,
250 }))),
251
252 Expression::ListAgg(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
254 this: f.this,
255 separator: f.separator,
256 order_by: f.order_by,
257 distinct: f.distinct,
258 filter: f.filter,
259 limit: None,
260 inferred_type: None,
261 }))),
262
263 Expression::TryCast(c) => Ok(Expression::TryCast(c)),
265
266 Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
268
269 Expression::ILike(op) => {
271 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
274 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
275 Ok(Expression::Like(Box::new(LikeOp {
276 left: lower_left,
277 right: lower_right,
278 escape: op.escape,
279 quantifier: op.quantifier,
280 inferred_type: None,
281 })))
282 }
283
284 Expression::Concat(op) => {
287 Ok(Expression::Add(op))
289 }
290
291 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
293 seed: None,
294 lower: None,
295 upper: None,
296 }))),
297
298 Expression::Unnest(f) => {
300 Ok(Expression::Function(Box::new(Function::new(
303 "OPENJSON".to_string(),
304 vec![f.this],
305 ))))
306 }
307
308 Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
310 "OPENJSON".to_string(),
311 vec![f.this],
312 )))),
313
314 Expression::Join(join) if join.kind == JoinKind::Lateral => {
316 Ok(Expression::Join(Box::new(Join {
317 this: join.this,
318 on: None,
319 using: join.using,
320 kind: JoinKind::CrossApply,
321 use_inner_keyword: false,
322 use_outer_keyword: false,
323 deferred_condition: false,
324 join_hint: None,
325 match_condition: None,
326 pivots: join.pivots,
327 comments: join.comments,
328 nesting_group: 0,
329 directed: false,
330 })))
331 }
332
333 Expression::Join(join) if join.kind == JoinKind::LeftLateral => {
335 Ok(Expression::Join(Box::new(Join {
336 this: join.this,
337 on: None, using: join.using,
339 kind: JoinKind::OuterApply,
340 use_inner_keyword: false,
341 use_outer_keyword: false,
342 deferred_condition: false,
343 join_hint: None,
344 match_condition: None,
345 pivots: join.pivots,
346 comments: join.comments,
347 nesting_group: 0,
348 directed: false,
349 })))
350 }
351
352 Expression::Length(f) => Ok(Expression::Function(Box::new(Function::new(
354 "LEN".to_string(),
355 vec![f.this],
356 )))),
357
358 Expression::Stddev(f) => Ok(Expression::Function(Box::new(Function::new(
360 "STDEV".to_string(),
361 vec![f.this],
362 )))),
363
364 Expression::Boolean(b) => {
366 let value = if b.value { 1 } else { 0 };
367 Ok(Expression::Literal(Box::new(
368 crate::expressions::Literal::Number(value.to_string()),
369 )))
370 }
371
372 Expression::Ln(f) => Ok(Expression::Function(Box::new(Function::new(
374 "LOG".to_string(),
375 vec![f.this],
376 )))),
377
378 Expression::CurrentDate(_) => {
381 let getdate =
382 Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])));
383 Ok(Expression::Cast(Box::new(crate::expressions::Cast {
384 this: getdate,
385 to: crate::expressions::DataType::Date,
386 trailing_comments: Vec::new(),
387 double_colon_syntax: false,
388 format: None,
389 default: None,
390 inferred_type: None,
391 })))
392 }
393
394 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
396 "GETDATE".to_string(),
397 vec![],
398 )))),
399
400 Expression::DateDiff(f) => {
402 let unit_str = match f.unit {
404 Some(crate::expressions::IntervalUnit::Year) => "YEAR",
405 Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
406 Some(crate::expressions::IntervalUnit::Month) => "MONTH",
407 Some(crate::expressions::IntervalUnit::Week) => "WEEK",
408 Some(crate::expressions::IntervalUnit::Day) => "DAY",
409 Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
410 Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
411 Some(crate::expressions::IntervalUnit::Second) => "SECOND",
412 Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
413 Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
414 Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
415 None => "DAY",
416 };
417 let unit = Expression::Identifier(crate::expressions::Identifier {
418 name: unit_str.to_string(),
419 quoted: false,
420 trailing_comments: Vec::new(),
421 span: None,
422 });
423 Ok(Expression::Function(Box::new(Function::new(
424 "DATEDIFF".to_string(),
425 vec![unit, f.expression, f.this], ))))
427 }
428
429 Expression::DateAdd(f) => {
431 let unit_str = match f.unit {
432 crate::expressions::IntervalUnit::Year => "YEAR",
433 crate::expressions::IntervalUnit::Quarter => "QUARTER",
434 crate::expressions::IntervalUnit::Month => "MONTH",
435 crate::expressions::IntervalUnit::Week => "WEEK",
436 crate::expressions::IntervalUnit::Day => "DAY",
437 crate::expressions::IntervalUnit::Hour => "HOUR",
438 crate::expressions::IntervalUnit::Minute => "MINUTE",
439 crate::expressions::IntervalUnit::Second => "SECOND",
440 crate::expressions::IntervalUnit::Millisecond => "MILLISECOND",
441 crate::expressions::IntervalUnit::Microsecond => "MICROSECOND",
442 crate::expressions::IntervalUnit::Nanosecond => "NANOSECOND",
443 };
444 let unit = Expression::Identifier(crate::expressions::Identifier {
445 name: unit_str.to_string(),
446 quoted: false,
447 trailing_comments: Vec::new(),
448 span: None,
449 });
450 Ok(Expression::Function(Box::new(Function::new(
451 "DATEADD".to_string(),
452 vec![unit, f.interval, f.this],
453 ))))
454 }
455
456 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
459 "NEWID".to_string(),
460 vec![],
461 )))),
462
463 Expression::IfFunc(f) => {
466 let false_val = f
467 .false_value
468 .unwrap_or(Expression::Null(crate::expressions::Null));
469 Ok(Expression::Function(Box::new(Function::new(
470 "IIF".to_string(),
471 vec![f.condition, f.true_value, false_val],
472 ))))
473 }
474
475 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
478
479 Expression::LastDay(f) => Ok(Expression::Function(Box::new(Function::new(
481 "EOMONTH".to_string(),
482 vec![f.this.clone()],
483 )))),
484
485 Expression::Ceil(f) => Ok(Expression::Function(Box::new(Function::new(
487 "CEILING".to_string(),
488 vec![f.this],
489 )))),
490
491 Expression::Repeat(f) => Ok(Expression::Function(Box::new(Function::new(
493 "REPLICATE".to_string(),
494 vec![f.this, f.times],
495 )))),
496
497 Expression::Chr(f) => Ok(Expression::Function(Box::new(Function::new(
499 "CHAR".to_string(),
500 vec![f.this],
501 )))),
502
503 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
506 "VARP".to_string(),
507 vec![f.this],
508 )))),
509
510 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
512 "VAR".to_string(),
513 vec![f.this],
514 )))),
515
516 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
519 "HASHBYTES".to_string(),
520 vec![Expression::string("MD5"), *f.this],
521 )))),
522
523 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
525 "HASHBYTES".to_string(),
526 vec![Expression::string("SHA1"), f.this],
527 )))),
528
529 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
531 "HASHBYTES".to_string(),
532 vec![Expression::string("SHA1"), f.this],
533 )))),
534
535 Expression::ArrayToString(f) => Ok(Expression::Function(Box::new(Function::new(
538 "STRING_AGG".to_string(),
539 vec![f.this],
540 )))),
541
542 Expression::AutoIncrementColumnConstraint(_) => Ok(Expression::Function(Box::new(
545 Function::new("IDENTITY".to_string(), vec![]),
546 ))),
547
548 Expression::CreateView(mut view) => {
552 view.name.catalog = None;
554 Ok(Expression::CreateView(view))
555 }
556
557 Expression::DropView(mut view) => {
558 view.name.catalog = None;
560 Ok(Expression::DropView(view))
561 }
562
563 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
567 let path = match *e.expression {
568 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
569 let Literal::String(s) = lit.as_ref() else {
570 unreachable!()
571 };
572 let normalized = if s.starts_with('$') {
573 s.clone()
574 } else if s.starts_with('[') {
575 format!("${}", s)
576 } else {
577 format!("$.{}", s)
578 };
579 Expression::Literal(Box::new(Literal::String(normalized)))
580 }
581 other => other,
582 };
583 let json_query = Expression::Function(Box::new(Function::new(
584 "JSON_QUERY".to_string(),
585 vec![(*e.this).clone(), path.clone()],
586 )));
587 let json_value = Expression::Function(Box::new(Function::new(
588 "JSON_VALUE".to_string(),
589 vec![*e.this, path],
590 )));
591 Ok(Expression::Function(Box::new(Function::new(
592 "ISNULL".to_string(),
593 vec![json_query, json_value],
594 ))))
595 }
596
597 Expression::Function(f) => self.transform_function(*f),
599
600 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
602
603 Expression::Cte(cte) => self.transform_cte(*cte),
606
607 Expression::Subquery(subquery) => self.transform_subquery(*subquery),
610
611 Expression::JsonQuery(f) => {
613 let json_query = Expression::Function(Box::new(Function::new(
614 "JSON_QUERY".to_string(),
615 vec![f.this.clone(), f.path.clone()],
616 )));
617 let json_value = Expression::Function(Box::new(Function::new(
618 "JSON_VALUE".to_string(),
619 vec![f.this, f.path],
620 )));
621 Ok(Expression::Function(Box::new(Function::new(
622 "ISNULL".to_string(),
623 vec![json_query, json_value],
624 ))))
625 }
626 Expression::JsonValue(f) => Ok(Expression::Function(Box::new(Function::new(
628 "JSON_VALUE".to_string(),
629 vec![f.this, f.path],
630 )))),
631
632 _ => Ok(expr),
634 }
635 }
636}
637
638impl TSQLDialect {
639 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
641 use crate::expressions::DataType;
642 let transformed = match dt {
643 DataType::Boolean => DataType::Custom {
645 name: "BIT".to_string(),
646 },
647 DataType::Int { .. } => dt,
649 DataType::Decimal { precision, scale } => DataType::Custom {
652 name: if let (Some(p), Some(s)) = (&precision, &scale) {
653 format!("NUMERIC({}, {})", p, s)
654 } else if let Some(p) = &precision {
655 format!("NUMERIC({})", p)
656 } else {
657 "NUMERIC".to_string()
658 },
659 },
660 DataType::Text => DataType::Custom {
662 name: "VARCHAR(MAX)".to_string(),
663 },
664 DataType::Timestamp { .. } => DataType::Custom {
666 name: "DATETIME2".to_string(),
667 },
668 DataType::Uuid => DataType::Custom {
670 name: "UNIQUEIDENTIFIER".to_string(),
671 },
672 other => other,
674 };
675 Ok(Expression::DataType(transformed))
676 }
677
678 fn transform_function(&self, f: Function) -> Result<Expression> {
679 let name_upper = f.name.to_uppercase();
680 match name_upper.as_str() {
681 "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
683 "ISNULL".to_string(),
684 f.args,
685 )))),
686
687 "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
689 "ISNULL".to_string(),
690 f.args,
691 )))),
692
693 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
695 Function::new("STRING_AGG".to_string(), f.args),
696 ))),
697
698 "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
700
701 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
703 "STRING_AGG".to_string(),
704 f.args,
705 )))),
706
707 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
709 "SUBSTRING".to_string(),
710 f.args,
711 )))),
712
713 "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
715 "LEN".to_string(),
716 f.args,
717 )))),
718
719 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
721 seed: None,
722 lower: None,
723 upper: None,
724 }))),
725
726 "NOW" => Ok(Expression::Function(Box::new(Function::new(
728 "GETDATE".to_string(),
729 vec![],
730 )))),
731
732 "CURRENT_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
734 "GETDATE".to_string(),
735 vec![],
736 )))),
737
738 "CURRENT_DATE" => {
740 Ok(Expression::Function(Box::new(Function::new(
742 "CAST".to_string(),
743 vec![
744 Expression::Function(Box::new(Function::new(
745 "GETDATE".to_string(),
746 vec![],
747 ))),
748 Expression::Identifier(crate::expressions::Identifier::new("DATE")),
749 ],
750 ))))
751 }
752
753 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
755 "CONVERT".to_string(),
756 f.args,
757 )))),
758
759 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
761 "CONVERT".to_string(),
762 f.args,
763 )))),
764
765 "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
767 "FORMAT".to_string(),
768 f.args,
769 )))),
770
771 "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
773 "FORMAT".to_string(),
774 f.args,
775 )))),
776
777 "DATE_TRUNC" | "DATETRUNC" => {
780 let mut args = Self::uppercase_first_arg_if_identifier(f.args);
781 if args.len() >= 2 {
783 if let Expression::Literal(lit) = &args[1] {
784 if let Literal::String(_) = lit.as_ref() {
785 args[1] = Expression::Cast(Box::new(Cast {
786 this: args[1].clone(),
787 to: DataType::Custom {
788 name: "DATETIME2".to_string(),
789 },
790 trailing_comments: Vec::new(),
791 double_colon_syntax: false,
792 format: None,
793 default: None,
794 inferred_type: None,
795 }));
796 }
797 }
798 }
799 Ok(Expression::Function(Box::new(Function::new(
800 "DATETRUNC".to_string(),
801 args,
802 ))))
803 }
804
805 "DATEADD" => {
807 let args = Self::uppercase_first_arg_if_identifier(f.args);
808 Ok(Expression::Function(Box::new(Function::new(
809 "DATEADD".to_string(),
810 args,
811 ))))
812 }
813
814 "DATEDIFF" => {
816 let args = Self::uppercase_first_arg_if_identifier(f.args);
817 Ok(Expression::Function(Box::new(Function::new(
818 "DATEDIFF".to_string(),
819 args,
820 ))))
821 }
822
823 "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
825 "DATEPART".to_string(),
826 f.args,
827 )))),
828
829 "STRPOS" | "POSITION" if f.args.len() >= 2 => {
831 Ok(Expression::Function(Box::new(Function::new(
833 "CHARINDEX".to_string(),
834 f.args,
835 ))))
836 }
837
838 "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
840
841 "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
843 Function::new("CEILING".to_string(), f.args),
844 ))),
845
846 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
851 "JSON_VALUE".to_string(),
852 f.args,
853 )))),
854
855 "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
857 "JSON_VALUE".to_string(),
858 f.args,
859 )))),
860
861 "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
863
864 "GET_PATH" if f.args.len() == 2 => {
866 let mut args = f.args;
867 let this = args.remove(0);
868 let path = args.remove(0);
869 let json_path = match &path {
870 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
871 let Literal::String(s) = lit.as_ref() else {
872 unreachable!()
873 };
874 let normalized = if s.starts_with('$') {
875 s.clone()
876 } else if s.starts_with('[') {
877 format!("${}", s)
878 } else {
879 format!("$.{}", s)
880 };
881 Expression::Literal(Box::new(Literal::String(normalized)))
882 }
883 _ => path,
884 };
885 let json_query = Expression::Function(Box::new(Function::new(
887 "JSON_QUERY".to_string(),
888 vec![this.clone(), json_path.clone()],
889 )));
890 let json_value = Expression::Function(Box::new(Function::new(
891 "JSON_VALUE".to_string(),
892 vec![this, json_path],
893 )));
894 Ok(Expression::Function(Box::new(Function::new(
895 "ISNULL".to_string(),
896 vec![json_query, json_value],
897 ))))
898 }
899
900 "JSON_QUERY" if f.args.len() == 1 => {
903 let this = f.args.into_iter().next().unwrap();
904 let path = Expression::Literal(Box::new(Literal::String("$".to_string())));
905 let json_query = Expression::Function(Box::new(Function::new(
906 "JSON_QUERY".to_string(),
907 vec![this.clone(), path.clone()],
908 )));
909 let json_value = Expression::Function(Box::new(Function::new(
910 "JSON_VALUE".to_string(),
911 vec![this, path],
912 )));
913 Ok(Expression::Function(Box::new(Function::new(
914 "ISNULL".to_string(),
915 vec![json_query, json_value],
916 ))))
917 }
918
919 "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
921 "STRING_SPLIT".to_string(),
922 f.args,
923 )))),
924
925 "REGEXP_LIKE" => {
928 Ok(Expression::Function(Box::new(Function::new(
930 "PATINDEX".to_string(),
931 f.args,
932 ))))
933 }
934
935 "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
937 "LOG".to_string(),
938 f.args,
939 )))),
940
941 "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
946 "STDEV".to_string(),
947 f.args,
948 )))),
949
950 "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
952 "STDEVP".to_string(),
953 f.args,
954 )))),
955
956 "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
958 "VAR".to_string(),
959 f.args,
960 )))),
961
962 "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
964 "VARP".to_string(),
965 f.args,
966 )))),
967
968 "DATE_ADD" => {
970 if f.args.len() == 2 {
971 let mut args = f.args;
972 let date = args.remove(0);
973 let interval = args.remove(0);
974 let unit = Expression::Identifier(crate::expressions::Identifier {
975 name: "DAY".to_string(),
976 quoted: false,
977 trailing_comments: Vec::new(),
978 span: None,
979 });
980 Ok(Expression::Function(Box::new(Function::new(
981 "DATEADD".to_string(),
982 vec![unit, interval, date],
983 ))))
984 } else {
985 let args = Self::uppercase_first_arg_if_identifier(f.args);
986 Ok(Expression::Function(Box::new(Function::new(
987 "DATEADD".to_string(),
988 args,
989 ))))
990 }
991 }
992
993 "INSERT" => Ok(Expression::Function(Box::new(Function::new(
995 "STUFF".to_string(),
996 f.args,
997 )))),
998
999 "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
1001 crate::expressions::CurrentUser { this: None },
1002 ))),
1003
1004 _ => Ok(Expression::Function(Box::new(f))),
1006 }
1007 }
1008
1009 fn transform_aggregate_function(
1010 &self,
1011 f: Box<crate::expressions::AggregateFunction>,
1012 ) -> Result<Expression> {
1013 let name_upper = f.name.to_uppercase();
1014 match name_upper.as_str() {
1015 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1017 Function::new("STRING_AGG".to_string(), f.args),
1018 ))),
1019
1020 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1022 "STRING_AGG".to_string(),
1023 f.args,
1024 )))),
1025
1026 "ARRAY_AGG" if !f.args.is_empty() => {
1029 Ok(Expression::Function(Box::new(Function::new(
1031 "STRING_AGG".to_string(),
1032 f.args,
1033 ))))
1034 }
1035
1036 _ => Ok(Expression::AggregateFunction(f)),
1038 }
1039 }
1040
1041 fn transform_cte(&self, cte: Cte) -> Result<Expression> {
1045 Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
1046 }
1047
1048 fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
1050 if cte.columns.is_empty() {
1053 cte.this = self.qualify_derived_table_outputs(cte.this);
1054 }
1055 cte
1056 }
1057
1058 fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
1062 if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
1065 subquery.this = self.qualify_derived_table_outputs(subquery.this);
1066 }
1067 Ok(Expression::Subquery(Box::new(subquery)))
1068 }
1069
1070 fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
1074 match expr {
1075 Expression::Select(mut select) => {
1076 let has_from = select.from.is_some();
1079 if !has_from {
1080 select.expressions = select
1081 .expressions
1082 .into_iter()
1083 .map(|e| self.maybe_alias_expression(e))
1084 .collect();
1085 }
1086 Expression::Select(select)
1087 }
1088 Expression::Union(mut u) => {
1090 u.left = self.qualify_derived_table_outputs(u.left);
1091 Expression::Union(u)
1092 }
1093 Expression::Intersect(mut i) => {
1094 i.left = self.qualify_derived_table_outputs(i.left);
1095 Expression::Intersect(i)
1096 }
1097 Expression::Except(mut e) => {
1098 e.left = self.qualify_derived_table_outputs(e.left);
1099 Expression::Except(e)
1100 }
1101 Expression::Subquery(mut s) => {
1103 s.this = self.qualify_derived_table_outputs(s.this);
1104 Expression::Subquery(s)
1105 }
1106 other => other,
1108 }
1109 }
1110
1111 fn maybe_alias_expression(&self, expr: Expression) -> Expression {
1116 match &expr {
1117 Expression::Alias(_) => expr,
1119 Expression::Aliases(_) => expr,
1121 Expression::Star(_) => expr,
1123 _ => {
1128 if let Some(output_name) = self.get_output_name(&expr) {
1129 Expression::Alias(Box::new(Alias {
1130 this: expr,
1131 alias: Identifier {
1132 name: output_name,
1133 quoted: true, trailing_comments: Vec::new(),
1135 span: None,
1136 },
1137 column_aliases: Vec::new(),
1138 pre_alias_comments: Vec::new(),
1139 trailing_comments: Vec::new(),
1140 inferred_type: None,
1141 }))
1142 } else {
1143 expr
1145 }
1146 }
1147 }
1148 }
1149
1150 fn get_output_name(&self, expr: &Expression) -> Option<String> {
1154 match expr {
1155 Expression::Literal(lit) => match lit.as_ref() {
1157 Literal::Number(n) => Some(n.clone()),
1158 Literal::String(s) => Some(s.clone()),
1159 Literal::HexString(h) => Some(format!("0x{}", h)),
1160 Literal::HexNumber(h) => Some(format!("0x{}", h)),
1161 Literal::BitString(b) => Some(format!("b{}", b)),
1162 Literal::ByteString(b) => Some(format!("b'{}'", b)),
1163 Literal::NationalString(s) => Some(format!("N'{}'", s)),
1164 Literal::Date(d) => Some(d.clone()),
1165 Literal::Time(t) => Some(t.clone()),
1166 Literal::Timestamp(ts) => Some(ts.clone()),
1167 Literal::Datetime(dt) => Some(dt.clone()),
1168 Literal::TripleQuotedString(s, _) => Some(s.clone()),
1169 Literal::EscapeString(s) => Some(s.clone()),
1170 Literal::DollarString(s) => Some(s.clone()),
1171 Literal::RawString(s) => Some(s.clone()),
1172 },
1173 Expression::Column(col) => Some(col.name.name.clone()),
1175 Expression::Identifier(ident) => Some(ident.name.clone()),
1177 Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
1179 Expression::Null(_) => Some("NULL".to_string()),
1181 Expression::Function(f) => Some(f.name.clone()),
1183 Expression::AggregateFunction(f) => Some(f.name.clone()),
1185 _ => Some(format!("_col_{}", 0)),
1187 }
1188 }
1189
1190 fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
1192 use crate::expressions::Identifier;
1193 if !args.is_empty() {
1194 match &args[0] {
1195 Expression::Identifier(id) => {
1196 args[0] = Expression::Identifier(Identifier {
1197 name: id.name.to_uppercase(),
1198 quoted: id.quoted,
1199 trailing_comments: id.trailing_comments.clone(),
1200 span: None,
1201 });
1202 }
1203 Expression::Column(col) if col.table.is_none() => {
1204 args[0] = Expression::Identifier(Identifier {
1205 name: col.name.name.to_uppercase(),
1206 quoted: col.name.quoted,
1207 trailing_comments: col.name.trailing_comments.clone(),
1208 span: None,
1209 });
1210 }
1211 _ => {}
1212 }
1213 }
1214 args
1215 }
1216}
1217
1218#[cfg(test)]
1219mod tests {
1220 use super::*;
1221 use crate::dialects::Dialect;
1222
1223 fn transpile_to_tsql(sql: &str) -> String {
1224 let dialect = Dialect::get(DialectType::Generic);
1225 let result = dialect
1226 .transpile_to(sql, DialectType::TSQL)
1227 .expect("Transpile failed");
1228 result[0].clone()
1229 }
1230
1231 #[test]
1232 fn test_nvl_to_isnull() {
1233 let result = transpile_to_tsql("SELECT NVL(a, b)");
1234 assert!(
1235 result.contains("ISNULL"),
1236 "Expected ISNULL, got: {}",
1237 result
1238 );
1239 }
1240
1241 #[test]
1242 fn test_coalesce_to_isnull() {
1243 let result = transpile_to_tsql("SELECT COALESCE(a, b)");
1244 assert!(
1245 result.contains("ISNULL"),
1246 "Expected ISNULL, got: {}",
1247 result
1248 );
1249 }
1250
1251 #[test]
1252 fn test_basic_select() {
1253 let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
1254 assert!(result.contains("SELECT"));
1255 assert!(result.contains("FROM users"));
1256 }
1257
1258 #[test]
1259 fn test_length_to_len() {
1260 let result = transpile_to_tsql("SELECT LENGTH(name)");
1261 assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
1262 }
1263
1264 #[test]
1265 fn test_now_to_getdate() {
1266 let result = transpile_to_tsql("SELECT NOW()");
1267 assert!(
1268 result.contains("GETDATE"),
1269 "Expected GETDATE, got: {}",
1270 result
1271 );
1272 }
1273
1274 #[test]
1275 fn test_group_concat_to_string_agg() {
1276 let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
1277 assert!(
1278 result.contains("STRING_AGG"),
1279 "Expected STRING_AGG, got: {}",
1280 result
1281 );
1282 }
1283
1284 #[test]
1285 fn test_listagg_to_string_agg() {
1286 let result = transpile_to_tsql("SELECT LISTAGG(name)");
1287 assert!(
1288 result.contains("STRING_AGG"),
1289 "Expected STRING_AGG, got: {}",
1290 result
1291 );
1292 }
1293
1294 #[test]
1295 fn test_ln_to_log() {
1296 let result = transpile_to_tsql("SELECT LN(x)");
1297 assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
1298 }
1299
1300 #[test]
1301 fn test_stddev_to_stdev() {
1302 let result = transpile_to_tsql("SELECT STDDEV(x)");
1303 assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
1304 }
1305
1306 #[test]
1307 fn test_bracket_identifiers() {
1308 let dialect = Dialect::get(DialectType::TSQL);
1310 let config = dialect.generator_config();
1311 assert_eq!(config.identifier_quote, '[');
1312 }
1313
1314 #[test]
1315 fn test_json_query_isnull_wrapper_simple() {
1316 let dialect = Dialect::get(DialectType::TSQL);
1318 let result = dialect
1319 .transpile_to(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
1320 .expect("transpile failed");
1321 assert!(
1322 result[0].contains("ISNULL"),
1323 "JSON_QUERY should be wrapped with ISNULL: {}",
1324 result[0]
1325 );
1326 }
1327
1328 #[test]
1329 fn test_json_query_isnull_wrapper_nested() {
1330 std::thread::Builder::new()
1332 .stack_size(16 * 1024 * 1024)
1333 .spawn(|| {
1334 let dialect = Dialect::get(DialectType::TSQL);
1335 let result = dialect.transpile_to(
1336 r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
1337 DialectType::TSQL,
1338 ).expect("transpile failed");
1339 let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
1340 assert_eq!(result[0], expected, "JSON_QUERY should be wrapped with ISNULL");
1341 })
1342 .expect("Failed to spawn test thread")
1343 .join()
1344 .expect("Test thread panicked");
1345 }
1346}