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 }))
120 }
121 Expression::Identifier(ident)
122 if !ident.name.starts_with('@') =>
123 {
124 Expression::Alias(Box::new(Alias {
125 this: op.right,
126 alias: ident.clone(),
127 column_aliases: Vec::new(),
128 pre_alias_comments: Vec::new(),
129 trailing_comments: Vec::new(),
130 }))
131 }
132 _ => Expression::Eq(op),
133 }
134 }
135 other => other,
136 }
137 })
138 .collect();
139
140 if let Some(ref mut with) = select.with {
142 with.ctes = with
143 .ctes
144 .drain(..)
145 .map(|cte| self.transform_cte_inner(cte))
146 .collect();
147 }
148
149 Ok(Expression::Select(select))
150 }
151
152 Expression::DataType(dt) => self.transform_data_type(dt),
154
155 Expression::IsTrue(it) => {
158 let one = Expression::Literal(crate::expressions::Literal::Number("1".to_string()));
159 if it.not {
160 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
162 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
163 left: it.this,
164 right: one,
165 left_comments: vec![],
166 operator_comments: vec![],
167 trailing_comments: vec![],
168 })),
169 })))
170 } else {
171 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
173 left: it.this,
174 right: one,
175 left_comments: vec![],
176 operator_comments: vec![],
177 trailing_comments: vec![],
178 })))
179 }
180 }
181 Expression::IsFalse(it) => {
182 let zero =
183 Expression::Literal(crate::expressions::Literal::Number("0".to_string()));
184 if it.not {
185 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
187 this: Expression::Eq(Box::new(crate::expressions::BinaryOp {
188 left: it.this,
189 right: zero,
190 left_comments: vec![],
191 operator_comments: vec![],
192 trailing_comments: vec![],
193 })),
194 })))
195 } else {
196 Ok(Expression::Eq(Box::new(crate::expressions::BinaryOp {
198 left: it.this,
199 right: zero,
200 left_comments: vec![],
201 operator_comments: vec![],
202 trailing_comments: vec![],
203 })))
204 }
205 }
206
207 Expression::In(mut in_expr) if in_expr.not => {
211 in_expr.not = false;
212 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
213 this: Expression::In(in_expr),
214 })))
215 }
216
217 Expression::Coalesce(f) if f.expressions.len() == 2 => Ok(Expression::Function(
220 Box::new(Function::new("ISNULL".to_string(), f.expressions)),
221 )),
222
223 Expression::Nvl(f) => Ok(Expression::Function(Box::new(Function::new(
225 "ISNULL".to_string(),
226 vec![f.this, f.expression],
227 )))),
228
229 Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
231 this: f.this,
232 separator: f.separator,
233 order_by: f.order_by,
234 distinct: f.distinct,
235 filter: f.filter,
236 limit: None,
237 }))),
238
239 Expression::ListAgg(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
241 this: f.this,
242 separator: f.separator,
243 order_by: f.order_by,
244 distinct: f.distinct,
245 filter: f.filter,
246 limit: None,
247 }))),
248
249 Expression::TryCast(c) => Ok(Expression::TryCast(c)),
251
252 Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
254
255 Expression::ILike(op) => {
257 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
260 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
261 Ok(Expression::Like(Box::new(LikeOp {
262 left: lower_left,
263 right: lower_right,
264 escape: op.escape,
265 quantifier: op.quantifier,
266 })))
267 }
268
269 Expression::Concat(op) => {
272 Ok(Expression::Add(op))
274 }
275
276 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
278 seed: None,
279 lower: None,
280 upper: None,
281 }))),
282
283 Expression::Unnest(f) => {
285 Ok(Expression::Function(Box::new(Function::new(
288 "OPENJSON".to_string(),
289 vec![f.this],
290 ))))
291 }
292
293 Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
295 "OPENJSON".to_string(),
296 vec![f.this],
297 )))),
298
299 Expression::Join(join) if join.kind == JoinKind::Lateral => {
301 Ok(Expression::Join(Box::new(Join {
302 this: join.this,
303 on: None,
304 using: join.using,
305 kind: JoinKind::CrossApply,
306 use_inner_keyword: false,
307 use_outer_keyword: false,
308 deferred_condition: false,
309 join_hint: None,
310 match_condition: None,
311 pivots: join.pivots,
312 comments: join.comments,
313 nesting_group: 0,
314 directed: false,
315 })))
316 }
317
318 Expression::Join(join) if join.kind == JoinKind::LeftLateral => {
320 Ok(Expression::Join(Box::new(Join {
321 this: join.this,
322 on: None, using: join.using,
324 kind: JoinKind::OuterApply,
325 use_inner_keyword: false,
326 use_outer_keyword: false,
327 deferred_condition: false,
328 join_hint: None,
329 match_condition: None,
330 pivots: join.pivots,
331 comments: join.comments,
332 nesting_group: 0,
333 directed: false,
334 })))
335 }
336
337 Expression::Length(f) => Ok(Expression::Function(Box::new(Function::new(
339 "LEN".to_string(),
340 vec![f.this],
341 )))),
342
343 Expression::Stddev(f) => Ok(Expression::Function(Box::new(Function::new(
345 "STDEV".to_string(),
346 vec![f.this],
347 )))),
348
349 Expression::Boolean(b) => {
351 let value = if b.value { 1 } else { 0 };
352 Ok(Expression::Literal(crate::expressions::Literal::Number(
353 value.to_string(),
354 )))
355 }
356
357 Expression::Ln(f) => Ok(Expression::Function(Box::new(Function::new(
359 "LOG".to_string(),
360 vec![f.this],
361 )))),
362
363 Expression::CurrentDate(_) => {
366 let getdate =
367 Expression::Function(Box::new(Function::new("GETDATE".to_string(), vec![])));
368 Ok(Expression::Cast(Box::new(crate::expressions::Cast {
369 this: getdate,
370 to: crate::expressions::DataType::Date,
371 trailing_comments: Vec::new(),
372 double_colon_syntax: false,
373 format: None,
374 default: None,
375 })))
376 }
377
378 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
380 "GETDATE".to_string(),
381 vec![],
382 )))),
383
384 Expression::DateDiff(f) => {
386 let unit_str = match f.unit {
388 Some(crate::expressions::IntervalUnit::Year) => "YEAR",
389 Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
390 Some(crate::expressions::IntervalUnit::Month) => "MONTH",
391 Some(crate::expressions::IntervalUnit::Week) => "WEEK",
392 Some(crate::expressions::IntervalUnit::Day) => "DAY",
393 Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
394 Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
395 Some(crate::expressions::IntervalUnit::Second) => "SECOND",
396 Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
397 Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
398 Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
399 None => "DAY",
400 };
401 let unit = Expression::Identifier(crate::expressions::Identifier {
402 name: unit_str.to_string(),
403 quoted: false,
404 trailing_comments: Vec::new(),
405 span: None,
406 });
407 Ok(Expression::Function(Box::new(Function::new(
408 "DATEDIFF".to_string(),
409 vec![unit, f.expression, f.this], ))))
411 }
412
413 Expression::DateAdd(f) => {
415 let unit_str = match f.unit {
416 crate::expressions::IntervalUnit::Year => "YEAR",
417 crate::expressions::IntervalUnit::Quarter => "QUARTER",
418 crate::expressions::IntervalUnit::Month => "MONTH",
419 crate::expressions::IntervalUnit::Week => "WEEK",
420 crate::expressions::IntervalUnit::Day => "DAY",
421 crate::expressions::IntervalUnit::Hour => "HOUR",
422 crate::expressions::IntervalUnit::Minute => "MINUTE",
423 crate::expressions::IntervalUnit::Second => "SECOND",
424 crate::expressions::IntervalUnit::Millisecond => "MILLISECOND",
425 crate::expressions::IntervalUnit::Microsecond => "MICROSECOND",
426 crate::expressions::IntervalUnit::Nanosecond => "NANOSECOND",
427 };
428 let unit = Expression::Identifier(crate::expressions::Identifier {
429 name: unit_str.to_string(),
430 quoted: false,
431 trailing_comments: Vec::new(),
432 span: None,
433 });
434 Ok(Expression::Function(Box::new(Function::new(
435 "DATEADD".to_string(),
436 vec![unit, f.interval, f.this],
437 ))))
438 }
439
440 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
443 "NEWID".to_string(),
444 vec![],
445 )))),
446
447 Expression::IfFunc(f) => {
450 let false_val = f
451 .false_value
452 .unwrap_or(Expression::Null(crate::expressions::Null));
453 Ok(Expression::Function(Box::new(Function::new(
454 "IIF".to_string(),
455 vec![f.condition, f.true_value, false_val],
456 ))))
457 }
458
459 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
462
463 Expression::LastDay(f) => Ok(Expression::Function(Box::new(Function::new(
465 "EOMONTH".to_string(),
466 vec![f.this.clone()],
467 )))),
468
469 Expression::Ceil(f) => Ok(Expression::Function(Box::new(Function::new(
471 "CEILING".to_string(),
472 vec![f.this],
473 )))),
474
475 Expression::Repeat(f) => Ok(Expression::Function(Box::new(Function::new(
477 "REPLICATE".to_string(),
478 vec![f.this, f.times],
479 )))),
480
481 Expression::Chr(f) => Ok(Expression::Function(Box::new(Function::new(
483 "CHAR".to_string(),
484 vec![f.this],
485 )))),
486
487 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
490 "VARP".to_string(),
491 vec![f.this],
492 )))),
493
494 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
496 "VAR".to_string(),
497 vec![f.this],
498 )))),
499
500 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
503 "HASHBYTES".to_string(),
504 vec![Expression::string("MD5"), *f.this],
505 )))),
506
507 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
509 "HASHBYTES".to_string(),
510 vec![Expression::string("SHA1"), f.this],
511 )))),
512
513 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
515 "HASHBYTES".to_string(),
516 vec![Expression::string("SHA1"), f.this],
517 )))),
518
519 Expression::ArrayToString(f) => Ok(Expression::Function(Box::new(Function::new(
522 "STRING_AGG".to_string(),
523 vec![f.this],
524 )))),
525
526 Expression::AutoIncrementColumnConstraint(_) => Ok(Expression::Function(Box::new(
529 Function::new("IDENTITY".to_string(), vec![]),
530 ))),
531
532 Expression::CreateView(mut view) => {
536 view.name.catalog = None;
538 Ok(Expression::CreateView(view))
539 }
540
541 Expression::DropView(mut view) => {
542 view.name.catalog = None;
544 Ok(Expression::DropView(view))
545 }
546
547 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
551 let path = match *e.expression {
552 Expression::Literal(Literal::String(s)) => {
553 let normalized = if s.starts_with('$') {
554 s
555 } else if s.starts_with('[') {
556 format!("${}", s)
557 } else {
558 format!("$.{}", s)
559 };
560 Expression::Literal(Literal::String(normalized))
561 }
562 other => other,
563 };
564 let json_query = Expression::Function(Box::new(Function::new(
565 "JSON_QUERY".to_string(),
566 vec![(*e.this).clone(), path.clone()],
567 )));
568 let json_value = Expression::Function(Box::new(Function::new(
569 "JSON_VALUE".to_string(),
570 vec![*e.this, path],
571 )));
572 Ok(Expression::Function(Box::new(Function::new(
573 "ISNULL".to_string(),
574 vec![json_query, json_value],
575 ))))
576 }
577
578 Expression::Function(f) => self.transform_function(*f),
580
581 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
583
584 Expression::Cte(cte) => self.transform_cte(*cte),
587
588 Expression::Subquery(subquery) => self.transform_subquery(*subquery),
591
592 Expression::JsonQuery(f) => {
594 let json_query = Expression::Function(Box::new(Function::new(
595 "JSON_QUERY".to_string(),
596 vec![f.this.clone(), f.path.clone()],
597 )));
598 let json_value = Expression::Function(Box::new(Function::new(
599 "JSON_VALUE".to_string(),
600 vec![f.this, f.path],
601 )));
602 Ok(Expression::Function(Box::new(Function::new(
603 "ISNULL".to_string(),
604 vec![json_query, json_value],
605 ))))
606 }
607 Expression::JsonValue(f) => Ok(Expression::Function(Box::new(Function::new(
609 "JSON_VALUE".to_string(),
610 vec![f.this, f.path],
611 )))),
612
613 _ => Ok(expr),
615 }
616 }
617}
618
619impl TSQLDialect {
620 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
622 use crate::expressions::DataType;
623 let transformed = match dt {
624 DataType::Boolean => DataType::Custom {
626 name: "BIT".to_string(),
627 },
628 DataType::Int { .. } => dt,
630 DataType::Decimal { precision, scale } => DataType::Custom {
633 name: if let (Some(p), Some(s)) = (&precision, &scale) {
634 format!("NUMERIC({}, {})", p, s)
635 } else if let Some(p) = &precision {
636 format!("NUMERIC({})", p)
637 } else {
638 "NUMERIC".to_string()
639 },
640 },
641 DataType::Text => DataType::Custom {
643 name: "VARCHAR(MAX)".to_string(),
644 },
645 DataType::Timestamp { .. } => DataType::Custom {
647 name: "DATETIME2".to_string(),
648 },
649 DataType::Uuid => DataType::Custom {
651 name: "UNIQUEIDENTIFIER".to_string(),
652 },
653 other => other,
655 };
656 Ok(Expression::DataType(transformed))
657 }
658
659 fn transform_function(&self, f: Function) -> Result<Expression> {
660 let name_upper = f.name.to_uppercase();
661 match name_upper.as_str() {
662 "COALESCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
664 "ISNULL".to_string(),
665 f.args,
666 )))),
667
668 "NVL" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
670 "ISNULL".to_string(),
671 f.args,
672 )))),
673
674 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
676 Function::new("STRING_AGG".to_string(), f.args),
677 ))),
678
679 "STRING_AGG" => Ok(Expression::Function(Box::new(f))),
681
682 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
684 "STRING_AGG".to_string(),
685 f.args,
686 )))),
687
688 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
690 "SUBSTRING".to_string(),
691 f.args,
692 )))),
693
694 "LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
696 "LEN".to_string(),
697 f.args,
698 )))),
699
700 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
702 seed: None,
703 lower: None,
704 upper: None,
705 }))),
706
707 "NOW" => Ok(Expression::Function(Box::new(Function::new(
709 "GETDATE".to_string(),
710 vec![],
711 )))),
712
713 "CURRENT_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
715 "GETDATE".to_string(),
716 vec![],
717 )))),
718
719 "CURRENT_DATE" => {
721 Ok(Expression::Function(Box::new(Function::new(
723 "CAST".to_string(),
724 vec![
725 Expression::Function(Box::new(Function::new(
726 "GETDATE".to_string(),
727 vec![],
728 ))),
729 Expression::Identifier(crate::expressions::Identifier::new("DATE")),
730 ],
731 ))))
732 }
733
734 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
736 "CONVERT".to_string(),
737 f.args,
738 )))),
739
740 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
742 "CONVERT".to_string(),
743 f.args,
744 )))),
745
746 "TO_CHAR" => Ok(Expression::Function(Box::new(Function::new(
748 "FORMAT".to_string(),
749 f.args,
750 )))),
751
752 "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
754 "FORMAT".to_string(),
755 f.args,
756 )))),
757
758 "DATE_TRUNC" | "DATETRUNC" => {
761 let mut args = Self::uppercase_first_arg_if_identifier(f.args);
762 if args.len() >= 2 {
764 if let Expression::Literal(Literal::String(_)) = &args[1] {
765 args[1] = Expression::Cast(Box::new(Cast {
766 this: args[1].clone(),
767 to: DataType::Custom {
768 name: "DATETIME2".to_string(),
769 },
770 trailing_comments: Vec::new(),
771 double_colon_syntax: false,
772 format: None,
773 default: None,
774 }));
775 }
776 }
777 Ok(Expression::Function(Box::new(Function::new(
778 "DATETRUNC".to_string(),
779 args,
780 ))))
781 }
782
783 "DATEADD" => {
785 let args = Self::uppercase_first_arg_if_identifier(f.args);
786 Ok(Expression::Function(Box::new(Function::new(
787 "DATEADD".to_string(),
788 args,
789 ))))
790 }
791
792 "DATEDIFF" => {
794 let args = Self::uppercase_first_arg_if_identifier(f.args);
795 Ok(Expression::Function(Box::new(Function::new(
796 "DATEDIFF".to_string(),
797 args,
798 ))))
799 }
800
801 "EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
803 "DATEPART".to_string(),
804 f.args,
805 )))),
806
807 "STRPOS" | "POSITION" if f.args.len() >= 2 => {
809 Ok(Expression::Function(Box::new(Function::new(
811 "CHARINDEX".to_string(),
812 f.args,
813 ))))
814 }
815
816 "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
818
819 "CEILING" | "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
821 Function::new("CEILING".to_string(), f.args),
822 ))),
823
824 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
829 "JSON_VALUE".to_string(),
830 f.args,
831 )))),
832
833 "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
835 "JSON_VALUE".to_string(),
836 f.args,
837 )))),
838
839 "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
841
842 "GET_PATH" if f.args.len() == 2 => {
844 let mut args = f.args;
845 let this = args.remove(0);
846 let path = args.remove(0);
847 let json_path = match &path {
848 Expression::Literal(Literal::String(s)) => {
849 let normalized = if s.starts_with('$') {
850 s.clone()
851 } else if s.starts_with('[') {
852 format!("${}", s)
853 } else {
854 format!("$.{}", s)
855 };
856 Expression::Literal(Literal::String(normalized))
857 }
858 _ => path,
859 };
860 let json_query = Expression::Function(Box::new(Function::new(
862 "JSON_QUERY".to_string(),
863 vec![this.clone(), json_path.clone()],
864 )));
865 let json_value = Expression::Function(Box::new(Function::new(
866 "JSON_VALUE".to_string(),
867 vec![this, json_path],
868 )));
869 Ok(Expression::Function(Box::new(Function::new(
870 "ISNULL".to_string(),
871 vec![json_query, json_value],
872 ))))
873 }
874
875 "JSON_QUERY" if f.args.len() == 1 => {
878 let this = f.args.into_iter().next().unwrap();
879 let path = Expression::Literal(Literal::String("$".to_string()));
880 let json_query = Expression::Function(Box::new(Function::new(
881 "JSON_QUERY".to_string(),
882 vec![this.clone(), path.clone()],
883 )));
884 let json_value = Expression::Function(Box::new(Function::new(
885 "JSON_VALUE".to_string(),
886 vec![this, path],
887 )));
888 Ok(Expression::Function(Box::new(Function::new(
889 "ISNULL".to_string(),
890 vec![json_query, json_value],
891 ))))
892 }
893
894 "SPLIT" => Ok(Expression::Function(Box::new(Function::new(
896 "STRING_SPLIT".to_string(),
897 f.args,
898 )))),
899
900 "REGEXP_LIKE" => {
903 Ok(Expression::Function(Box::new(Function::new(
905 "PATINDEX".to_string(),
906 f.args,
907 ))))
908 }
909
910 "LN" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
912 "LOG".to_string(),
913 f.args,
914 )))),
915
916 "STDDEV" | "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
921 "STDEV".to_string(),
922 f.args,
923 )))),
924
925 "STDDEV_POP" => Ok(Expression::Function(Box::new(Function::new(
927 "STDEVP".to_string(),
928 f.args,
929 )))),
930
931 "VARIANCE" | "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
933 "VAR".to_string(),
934 f.args,
935 )))),
936
937 "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
939 "VARP".to_string(),
940 f.args,
941 )))),
942
943 "DATE_ADD" => {
945 if f.args.len() == 2 {
946 let mut args = f.args;
947 let date = args.remove(0);
948 let interval = args.remove(0);
949 let unit = Expression::Identifier(crate::expressions::Identifier {
950 name: "DAY".to_string(),
951 quoted: false,
952 trailing_comments: Vec::new(),
953 span: None,
954 });
955 Ok(Expression::Function(Box::new(Function::new(
956 "DATEADD".to_string(),
957 vec![unit, interval, date],
958 ))))
959 } else {
960 let args = Self::uppercase_first_arg_if_identifier(f.args);
961 Ok(Expression::Function(Box::new(Function::new(
962 "DATEADD".to_string(),
963 args,
964 ))))
965 }
966 }
967
968 "INSERT" => Ok(Expression::Function(Box::new(Function::new(
970 "STUFF".to_string(),
971 f.args,
972 )))),
973
974 "SUSER_NAME" | "SUSER_SNAME" | "SYSTEM_USER" => Ok(Expression::CurrentUser(Box::new(
976 crate::expressions::CurrentUser { this: None },
977 ))),
978
979 _ => Ok(Expression::Function(Box::new(f))),
981 }
982 }
983
984 fn transform_aggregate_function(
985 &self,
986 f: Box<crate::expressions::AggregateFunction>,
987 ) -> Result<Expression> {
988 let name_upper = f.name.to_uppercase();
989 match name_upper.as_str() {
990 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
992 Function::new("STRING_AGG".to_string(), f.args),
993 ))),
994
995 "LISTAGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
997 "STRING_AGG".to_string(),
998 f.args,
999 )))),
1000
1001 "ARRAY_AGG" if !f.args.is_empty() => {
1004 Ok(Expression::Function(Box::new(Function::new(
1006 "STRING_AGG".to_string(),
1007 f.args,
1008 ))))
1009 }
1010
1011 _ => Ok(Expression::AggregateFunction(f)),
1013 }
1014 }
1015
1016 fn transform_cte(&self, cte: Cte) -> Result<Expression> {
1020 Ok(Expression::Cte(Box::new(self.transform_cte_inner(cte))))
1021 }
1022
1023 fn transform_cte_inner(&self, mut cte: Cte) -> Cte {
1025 if cte.columns.is_empty() {
1028 cte.this = self.qualify_derived_table_outputs(cte.this);
1029 }
1030 cte
1031 }
1032
1033 fn transform_subquery(&self, mut subquery: Subquery) -> Result<Expression> {
1037 if subquery.alias.is_some() && subquery.column_aliases.is_empty() {
1040 subquery.this = self.qualify_derived_table_outputs(subquery.this);
1041 }
1042 Ok(Expression::Subquery(Box::new(subquery)))
1043 }
1044
1045 fn qualify_derived_table_outputs(&self, expr: Expression) -> Expression {
1049 match expr {
1050 Expression::Select(mut select) => {
1051 let has_from = select.from.is_some();
1054 if !has_from {
1055 select.expressions = select
1056 .expressions
1057 .into_iter()
1058 .map(|e| self.maybe_alias_expression(e))
1059 .collect();
1060 }
1061 Expression::Select(select)
1062 }
1063 Expression::Union(mut u) => {
1065 u.left = self.qualify_derived_table_outputs(u.left);
1066 Expression::Union(u)
1067 }
1068 Expression::Intersect(mut i) => {
1069 i.left = self.qualify_derived_table_outputs(i.left);
1070 Expression::Intersect(i)
1071 }
1072 Expression::Except(mut e) => {
1073 e.left = self.qualify_derived_table_outputs(e.left);
1074 Expression::Except(e)
1075 }
1076 Expression::Subquery(mut s) => {
1078 s.this = self.qualify_derived_table_outputs(s.this);
1079 Expression::Subquery(s)
1080 }
1081 other => other,
1083 }
1084 }
1085
1086 fn maybe_alias_expression(&self, expr: Expression) -> Expression {
1091 match &expr {
1092 Expression::Alias(_) => expr,
1094 Expression::Aliases(_) => expr,
1096 Expression::Star(_) => expr,
1098 _ => {
1103 if let Some(output_name) = self.get_output_name(&expr) {
1104 Expression::Alias(Box::new(Alias {
1105 this: expr,
1106 alias: Identifier {
1107 name: output_name,
1108 quoted: true, trailing_comments: Vec::new(),
1110 span: None,
1111 },
1112 column_aliases: Vec::new(),
1113 pre_alias_comments: Vec::new(),
1114 trailing_comments: Vec::new(),
1115 }))
1116 } else {
1117 expr
1119 }
1120 }
1121 }
1122 }
1123
1124 fn get_output_name(&self, expr: &Expression) -> Option<String> {
1128 match expr {
1129 Expression::Literal(lit) => match lit {
1131 Literal::Number(n) => Some(n.clone()),
1132 Literal::String(s) => Some(s.clone()),
1133 Literal::HexString(h) => Some(format!("0x{}", h)),
1134 Literal::HexNumber(h) => Some(format!("0x{}", h)),
1135 Literal::BitString(b) => Some(format!("b{}", b)),
1136 Literal::ByteString(b) => Some(format!("b'{}'", b)),
1137 Literal::NationalString(s) => Some(format!("N'{}'", s)),
1138 Literal::Date(d) => Some(d.clone()),
1139 Literal::Time(t) => Some(t.clone()),
1140 Literal::Timestamp(ts) => Some(ts.clone()),
1141 Literal::Datetime(dt) => Some(dt.clone()),
1142 Literal::TripleQuotedString(s, _) => Some(s.clone()),
1143 Literal::EscapeString(s) => Some(s.clone()),
1144 Literal::DollarString(s) => Some(s.clone()),
1145 Literal::RawString(s) => Some(s.clone()),
1146 },
1147 Expression::Column(col) => Some(col.name.name.clone()),
1149 Expression::Identifier(ident) => Some(ident.name.clone()),
1151 Expression::Boolean(b) => Some(if b.value { "1" } else { "0" }.to_string()),
1153 Expression::Null(_) => Some("NULL".to_string()),
1155 Expression::Function(f) => Some(f.name.clone()),
1157 Expression::AggregateFunction(f) => Some(f.name.clone()),
1159 _ => Some(format!("_col_{}", 0)),
1161 }
1162 }
1163
1164 fn uppercase_first_arg_if_identifier(mut args: Vec<Expression>) -> Vec<Expression> {
1166 use crate::expressions::Identifier;
1167 if !args.is_empty() {
1168 match &args[0] {
1169 Expression::Identifier(id) => {
1170 args[0] = Expression::Identifier(Identifier {
1171 name: id.name.to_uppercase(),
1172 quoted: id.quoted,
1173 trailing_comments: id.trailing_comments.clone(),
1174 span: None,
1175 });
1176 }
1177 Expression::Column(col) if col.table.is_none() => {
1178 args[0] = Expression::Identifier(Identifier {
1179 name: col.name.name.to_uppercase(),
1180 quoted: col.name.quoted,
1181 trailing_comments: col.name.trailing_comments.clone(),
1182 span: None,
1183 });
1184 }
1185 _ => {}
1186 }
1187 }
1188 args
1189 }
1190}
1191
1192#[cfg(test)]
1193mod tests {
1194 use super::*;
1195 use crate::dialects::Dialect;
1196
1197 fn transpile_to_tsql(sql: &str) -> String {
1198 let dialect = Dialect::get(DialectType::Generic);
1199 let result = dialect
1200 .transpile_to(sql, DialectType::TSQL)
1201 .expect("Transpile failed");
1202 result[0].clone()
1203 }
1204
1205 #[test]
1206 fn test_nvl_to_isnull() {
1207 let result = transpile_to_tsql("SELECT NVL(a, b)");
1208 assert!(
1209 result.contains("ISNULL"),
1210 "Expected ISNULL, got: {}",
1211 result
1212 );
1213 }
1214
1215 #[test]
1216 fn test_coalesce_to_isnull() {
1217 let result = transpile_to_tsql("SELECT COALESCE(a, b)");
1218 assert!(
1219 result.contains("ISNULL"),
1220 "Expected ISNULL, got: {}",
1221 result
1222 );
1223 }
1224
1225 #[test]
1226 fn test_basic_select() {
1227 let result = transpile_to_tsql("SELECT a, b FROM users WHERE id = 1");
1228 assert!(result.contains("SELECT"));
1229 assert!(result.contains("FROM users"));
1230 }
1231
1232 #[test]
1233 fn test_length_to_len() {
1234 let result = transpile_to_tsql("SELECT LENGTH(name)");
1235 assert!(result.contains("LEN"), "Expected LEN, got: {}", result);
1236 }
1237
1238 #[test]
1239 fn test_now_to_getdate() {
1240 let result = transpile_to_tsql("SELECT NOW()");
1241 assert!(
1242 result.contains("GETDATE"),
1243 "Expected GETDATE, got: {}",
1244 result
1245 );
1246 }
1247
1248 #[test]
1249 fn test_group_concat_to_string_agg() {
1250 let result = transpile_to_tsql("SELECT GROUP_CONCAT(name)");
1251 assert!(
1252 result.contains("STRING_AGG"),
1253 "Expected STRING_AGG, got: {}",
1254 result
1255 );
1256 }
1257
1258 #[test]
1259 fn test_listagg_to_string_agg() {
1260 let result = transpile_to_tsql("SELECT LISTAGG(name)");
1261 assert!(
1262 result.contains("STRING_AGG"),
1263 "Expected STRING_AGG, got: {}",
1264 result
1265 );
1266 }
1267
1268 #[test]
1269 fn test_ln_to_log() {
1270 let result = transpile_to_tsql("SELECT LN(x)");
1271 assert!(result.contains("LOG"), "Expected LOG, got: {}", result);
1272 }
1273
1274 #[test]
1275 fn test_stddev_to_stdev() {
1276 let result = transpile_to_tsql("SELECT STDDEV(x)");
1277 assert!(result.contains("STDEV"), "Expected STDEV, got: {}", result);
1278 }
1279
1280 #[test]
1281 fn test_bracket_identifiers() {
1282 let dialect = Dialect::get(DialectType::TSQL);
1284 let config = dialect.generator_config();
1285 assert_eq!(config.identifier_quote, '[');
1286 }
1287
1288 #[test]
1289 fn test_json_query_isnull_wrapper_simple() {
1290 let dialect = Dialect::get(DialectType::TSQL);
1292 let result = dialect
1293 .transpile_to(r#"JSON_QUERY(x, '$')"#, DialectType::TSQL)
1294 .expect("transpile failed");
1295 assert!(
1296 result[0].contains("ISNULL"),
1297 "JSON_QUERY should be wrapped with ISNULL: {}",
1298 result[0]
1299 );
1300 }
1301
1302 #[test]
1303 fn test_json_query_isnull_wrapper_nested() {
1304 std::thread::Builder::new()
1306 .stack_size(16 * 1024 * 1024)
1307 .spawn(|| {
1308 let dialect = Dialect::get(DialectType::TSQL);
1309 let result = dialect.transpile_to(
1310 r#"JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'))"#,
1311 DialectType::TSQL,
1312 ).expect("transpile failed");
1313 let expected = r#"ISNULL(JSON_QUERY(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'), JSON_VALUE(REPLACE(REPLACE(x, '''', '"'), '""', '"'), '$'))"#;
1314 assert_eq!(result[0], expected, "JSON_QUERY should be wrapped with ISNULL");
1315 })
1316 .expect("Failed to spawn test thread")
1317 .join()
1318 .expect("Test thread panicked");
1319 }
1320}