1use super::{DialectImpl, DialectType};
11use crate::error::Result;
12use crate::expressions::{AggregateFunction, Cast, DataType, Expression, Function, JSONExtract, Literal, UnaryFunc, VarArgFunc};
13use crate::generator::GeneratorConfig;
14use crate::tokens::TokenizerConfig;
15
16pub struct DatabricksDialect;
18
19impl DialectImpl for DatabricksDialect {
20 fn dialect_type(&self) -> DialectType {
21 DialectType::Databricks
22 }
23
24 fn tokenizer_config(&self) -> TokenizerConfig {
25 let mut config = TokenizerConfig::default();
26 config.identifiers.clear();
28 config.identifiers.insert('`', '`');
29 config.quotes.insert("\"".to_string(), "\"".to_string());
31 config.string_escapes.push('\\');
33 config.keywords.insert("DIV".to_string(), crate::tokens::TokenType::Div);
35 config.numeric_literals.insert("L".to_string(), "BIGINT".to_string());
37 config.numeric_literals.insert("S".to_string(), "SMALLINT".to_string());
38 config.numeric_literals.insert("Y".to_string(), "TINYINT".to_string());
39 config.numeric_literals.insert("D".to_string(), "DOUBLE".to_string());
40 config.numeric_literals.insert("F".to_string(), "FLOAT".to_string());
41 config.numeric_literals.insert("BD".to_string(), "DECIMAL".to_string());
42 config.identifiers_can_start_with_digit = true;
44 config.string_escapes_allowed_in_raw_strings = false;
47 config
48 }
49
50 fn generator_config(&self) -> GeneratorConfig {
51 use crate::generator::IdentifierQuoteStyle;
52 GeneratorConfig {
53 identifier_quote: '`',
54 identifier_quote_style: IdentifierQuoteStyle::BACKTICK,
55 dialect: Some(DialectType::Databricks),
56 struct_field_sep: ": ",
57 create_function_return_as: false,
58 tablesample_seed_keyword: "REPEATABLE",
59 identifiers_can_start_with_digit: true,
60 schema_comment_with_eq: false,
62 ..Default::default()
63 }
64 }
65
66 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
67 match expr {
68 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
70 expressions: vec![f.this, f.expression],
71 }))),
72
73 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
75 expressions: vec![f.this, f.expression],
76 }))),
77
78 Expression::TryCast(c) => Ok(Expression::TryCast(c)),
80
81 Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
83
84 Expression::ILike(op) => Ok(Expression::ILike(op)),
86
87 Expression::Unnest(f) => Ok(Expression::Explode(Box::new(UnaryFunc::new(f.this)))),
89
90 Expression::Explode(f) => Ok(Expression::Explode(f)),
92
93 Expression::ExplodeOuter(f) => Ok(Expression::ExplodeOuter(f)),
95
96 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
98 seed: None, lower: None, upper: None,
99 }))),
100
101 Expression::Rand(r) => Ok(Expression::Rand(r)),
103
104 Expression::Concat(op) => Ok(Expression::Function(Box::new(Function::new(
106 "CONCAT".to_string(),
107 vec![op.left, op.right],
108 )))),
109
110 Expression::RegexpLike(op) => Ok(Expression::RegexpLike(op)),
112
113 Expression::Cast(c) => self.transform_cast(*c),
118
119 Expression::Function(f) => self.transform_function(*f),
121
122 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
124
125 Expression::DateSub(f) => {
127 let val = match f.interval {
129 Expression::Literal(crate::expressions::Literal::String(s)) if s.parse::<i64>().is_ok() => {
130 Expression::Literal(crate::expressions::Literal::Number(s))
131 }
132 other => other,
133 };
134 let neg_val = Expression::Neg(Box::new(crate::expressions::UnaryOp { this: val }));
135 Ok(Expression::Function(Box::new(Function::new(
136 "DATE_ADD".to_string(),
137 vec![f.this, neg_val],
138 ))))
139 }
140
141 _ => Ok(expr),
143 }
144 }
145}
146
147impl DatabricksDialect {
148 fn transform_function(&self, f: Function) -> Result<Expression> {
149 let name_upper = f.name.to_uppercase();
150 match name_upper.as_str() {
151 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
153 expressions: f.args,
154 }))),
155
156 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
158 expressions: f.args,
159 }))),
160
161 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
163 expressions: f.args,
164 }))),
165
166 "ROW" => Ok(Expression::Function(Box::new(Function::new(
168 "STRUCT".to_string(),
169 f.args,
170 )))),
171
172 "GETDATE" => Ok(Expression::CurrentTimestamp(
174 crate::expressions::CurrentTimestamp { precision: None, sysdate: false },
175 )),
176
177 "NOW" => Ok(Expression::CurrentTimestamp(
179 crate::expressions::CurrentTimestamp { precision: None, sysdate: false },
180 )),
181
182 "CURDATE" => Ok(Expression::CurrentDate(crate::expressions::CurrentDate)),
184
185 "CURRENT_DATE" if f.args.is_empty() => {
187 Ok(Expression::CurrentDate(crate::expressions::CurrentDate))
188 }
189
190 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
192 seed: None, lower: None, upper: None,
193 }))),
194
195 "GROUP_CONCAT" if !f.args.is_empty() => {
197 let mut args = f.args;
198 let first = args.remove(0);
199 let separator = args.pop();
200 let collect_list = Expression::Function(Box::new(Function::new(
201 "COLLECT_LIST".to_string(),
202 vec![first],
203 )));
204 if let Some(sep) = separator {
205 Ok(Expression::Function(Box::new(Function::new(
206 "ARRAY_JOIN".to_string(),
207 vec![collect_list, sep],
208 ))))
209 } else {
210 Ok(Expression::Function(Box::new(Function::new(
211 "ARRAY_JOIN".to_string(),
212 vec![collect_list],
213 ))))
214 }
215 }
216
217 "STRING_AGG" if !f.args.is_empty() => {
219 let mut args = f.args;
220 let first = args.remove(0);
221 let separator = args.pop();
222 let collect_list = Expression::Function(Box::new(Function::new(
223 "COLLECT_LIST".to_string(),
224 vec![first],
225 )));
226 if let Some(sep) = separator {
227 Ok(Expression::Function(Box::new(Function::new(
228 "ARRAY_JOIN".to_string(),
229 vec![collect_list, sep],
230 ))))
231 } else {
232 Ok(Expression::Function(Box::new(Function::new(
233 "ARRAY_JOIN".to_string(),
234 vec![collect_list],
235 ))))
236 }
237 }
238
239 "LISTAGG" if !f.args.is_empty() => {
241 let mut args = f.args;
242 let first = args.remove(0);
243 let separator = args.pop();
244 let collect_list = Expression::Function(Box::new(Function::new(
245 "COLLECT_LIST".to_string(),
246 vec![first],
247 )));
248 if let Some(sep) = separator {
249 Ok(Expression::Function(Box::new(Function::new(
250 "ARRAY_JOIN".to_string(),
251 vec![collect_list, sep],
252 ))))
253 } else {
254 Ok(Expression::Function(Box::new(Function::new(
255 "ARRAY_JOIN".to_string(),
256 vec![collect_list],
257 ))))
258 }
259 }
260
261 "ARRAY_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
263 Function::new("COLLECT_LIST".to_string(), f.args),
264 ))),
265
266 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
268 "SUBSTRING".to_string(),
269 f.args,
270 )))),
271
272 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc::new(
274 f.args.into_iter().next().unwrap(),
275 )))),
276
277 "CHARINDEX" if f.args.len() >= 2 => {
279 let mut args = f.args;
280 let substring = args.remove(0);
281 let string = args.remove(0);
282 Ok(Expression::Function(Box::new(Function::new(
284 "LOCATE".to_string(),
285 vec![substring, string],
286 ))))
287 }
288
289 "POSITION" if f.args.len() == 2 => {
291 let args = f.args;
292 Ok(Expression::Function(Box::new(Function::new(
293 "LOCATE".to_string(),
294 args,
295 ))))
296 }
297
298 "STRPOS" if f.args.len() == 2 => {
300 let args = f.args;
301 let string = args[0].clone();
302 let substring = args[1].clone();
303 Ok(Expression::Function(Box::new(Function::new(
305 "LOCATE".to_string(),
306 vec![substring, string],
307 ))))
308 }
309
310 "INSTR" => Ok(Expression::Function(Box::new(f))),
312
313 "LOCATE" => Ok(Expression::Function(Box::new(f))),
315
316 "ARRAY_LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
318 Function::new("SIZE".to_string(), f.args),
319 ))),
320
321 "CARDINALITY" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
323 Function::new("SIZE".to_string(), f.args),
324 ))),
325
326 "SIZE" => Ok(Expression::Function(Box::new(f))),
328
329 "ARRAY_CONTAINS" => Ok(Expression::Function(Box::new(f))),
331
332 "CONTAINS" if f.args.len() == 2 => {
335 let is_string_contains = matches!(&f.args[0], Expression::Lower(_)) && matches!(&f.args[1], Expression::Lower(_));
337 if is_string_contains {
338 Ok(Expression::Function(Box::new(f)))
339 } else {
340 Ok(Expression::Function(Box::new(Function::new(
341 "ARRAY_CONTAINS".to_string(),
342 f.args,
343 ))))
344 }
345 }
346
347 "TO_DATE" => Ok(Expression::Function(Box::new(f))),
349
350 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
352
353 "DATE_FORMAT" => Ok(Expression::Function(Box::new(f))),
355
356 "STRFTIME" if f.args.len() >= 2 => {
358 let mut args = f.args;
359 let format = args.remove(0);
360 let date = args.remove(0);
361 Ok(Expression::Function(Box::new(Function::new(
362 "DATE_FORMAT".to_string(),
363 vec![date, format],
364 ))))
365 }
366
367 "TO_CHAR" => Ok(Expression::Function(Box::new(f))),
369
370 "DATE_TRUNC" => Ok(Expression::Function(Box::new(f))),
372
373 "DATEADD" => {
375 let transformed_args = self.uppercase_first_arg_if_identifier(f.args);
376 Ok(Expression::Function(Box::new(Function::new(
377 "DATEADD".to_string(),
378 transformed_args,
379 ))))
380 }
381
382 "DATE_ADD" => {
387 if f.args.len() == 2 {
388 let is_simple_number = matches!(&f.args[1],
389 Expression::Literal(crate::expressions::Literal::Number(_))
390 | Expression::Neg(_)
391 );
392 if is_simple_number {
393 Ok(Expression::Function(Box::new(Function::new(
395 "DATE_ADD".to_string(),
396 f.args,
397 ))))
398 } else {
399 let mut args = f.args;
400 let date = args.remove(0);
401 let interval = args.remove(0);
402 let unit = Expression::Identifier(crate::expressions::Identifier {
403 name: "DAY".to_string(),
404 quoted: false,
405 trailing_comments: Vec::new(),
406 });
407 Ok(Expression::Function(Box::new(Function::new(
408 "DATEADD".to_string(),
409 vec![unit, interval, date],
410 ))))
411 }
412 } else {
413 let transformed_args = self.uppercase_first_arg_if_identifier(f.args);
414 Ok(Expression::Function(Box::new(Function::new(
415 "DATE_ADD".to_string(),
416 transformed_args,
417 ))))
418 }
419 }
420
421 "DATEDIFF" => {
425 if f.args.len() == 2 {
426 let mut args = f.args;
427 let end_date = args.remove(0);
428 let start_date = args.remove(0);
429 let unit = Expression::Identifier(crate::expressions::Identifier {
430 name: "DAY".to_string(),
431 quoted: false,
432 trailing_comments: Vec::new(),
433 });
434 Ok(Expression::Function(Box::new(Function::new(
435 "DATEDIFF".to_string(),
436 vec![unit, start_date, end_date],
437 ))))
438 } else {
439 let transformed_args = self.uppercase_first_arg_if_identifier(f.args);
440 Ok(Expression::Function(Box::new(Function::new(
441 "DATEDIFF".to_string(),
442 transformed_args,
443 ))))
444 }
445 }
446
447 "DATE_DIFF" => {
449 let transformed_args = self.uppercase_first_arg_if_identifier(f.args);
450 Ok(Expression::Function(Box::new(Function::new(
451 "DATEDIFF".to_string(),
452 transformed_args,
453 ))))
454 }
455
456 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(f))),
458
459 "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(f))),
461
462 "GET_JSON_OBJECT" if f.args.len() == 2 => {
465 let mut args = f.args;
466 let col = args.remove(0);
467 let path_arg = args.remove(0);
468
469 let path_expr = match &path_arg {
471 Expression::Literal(crate::expressions::Literal::String(s)) => {
472 let stripped = if s.starts_with("$.") {
474 &s[2..]
475 } else if s.starts_with("$") {
476 &s[1..]
477 } else {
478 s.as_str()
479 };
480 Expression::Literal(crate::expressions::Literal::String(stripped.to_string()))
481 }
482 _ => path_arg,
483 };
484
485 Ok(Expression::JSONExtract(Box::new(JSONExtract {
486 this: Box::new(col),
487 expression: Box::new(path_expr),
488 only_json_types: None,
489 expressions: Vec::new(),
490 variant_extract: Some(Box::new(Expression::true_())),
491 json_query: None,
492 option: None,
493 quote: None,
494 on_condition: None,
495 requires_json: None,
496 })))
497 }
498
499 "FROM_JSON" => Ok(Expression::Function(Box::new(f))),
501
502 "PARSE_JSON" => Ok(Expression::Function(Box::new(f))),
504
505 "COLLECT_LIST" => Ok(Expression::Function(Box::new(f))),
507
508 "COLLECT_SET" => Ok(Expression::Function(Box::new(f))),
510
511 "RLIKE" => Ok(Expression::Function(Box::new(f))),
513
514 "REGEXP" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
516 "RLIKE".to_string(),
517 f.args,
518 )))),
519
520 "REGEXP_LIKE" => Ok(Expression::Function(Box::new(f))),
522
523 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
525
526 "GENERATE_SERIES" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
528 Function::new("SEQUENCE".to_string(), f.args),
529 ))),
530
531 "SEQUENCE" => Ok(Expression::Function(Box::new(f))),
533
534 "FLATTEN" => Ok(Expression::Function(Box::new(f))),
536
537 "ARRAY_SORT" => Ok(Expression::Function(Box::new(f))),
539
540 "ARRAY_DISTINCT" => Ok(Expression::Function(Box::new(f))),
542
543 "TRANSFORM" => Ok(Expression::Function(Box::new(f))),
545
546 "FILTER" => Ok(Expression::Function(Box::new(f))),
548
549 "FROM_UTC_TIMESTAMP" if f.args.len() >= 2 => {
551 let mut args = f.args;
552 let first_arg = args.remove(0);
553
554 let wrapped_arg = if self.is_cast_to_timestamp(&first_arg) {
556 first_arg
557 } else {
558 Expression::Cast(Box::new(Cast {
560 this: first_arg,
561 to: DataType::Timestamp { precision: None, timezone: false },
562 trailing_comments: Vec::new(),
563 double_colon_syntax: false,
564 format: None,
565 default: None,
566 }))
567 };
568
569 let mut new_args = vec![wrapped_arg];
570 new_args.extend(args);
571
572 Ok(Expression::Function(Box::new(Function::new(
573 "FROM_UTC_TIMESTAMP".to_string(),
574 new_args,
575 ))))
576 }
577
578 "UNIFORM" if f.args.len() == 3 => {
580 let mut args = f.args;
581 let low = args.remove(0);
582 let high = args.remove(0);
583 let gen = args.remove(0);
584 match gen {
585 Expression::Function(func) if func.name.to_uppercase() == "RANDOM" => {
586 if func.args.len() == 1 {
587 let seed = func.args.into_iter().next().unwrap();
589 Ok(Expression::Function(Box::new(Function::new(
590 "UNIFORM".to_string(),
591 vec![low, high, seed],
592 ))))
593 } else {
594 Ok(Expression::Function(Box::new(Function::new(
596 "UNIFORM".to_string(),
597 vec![low, high],
598 ))))
599 }
600 }
601 Expression::Rand(r) => {
602 if let Some(seed) = r.seed {
603 Ok(Expression::Function(Box::new(Function::new(
604 "UNIFORM".to_string(),
605 vec![low, high, *seed],
606 ))))
607 } else {
608 Ok(Expression::Function(Box::new(Function::new(
609 "UNIFORM".to_string(),
610 vec![low, high],
611 ))))
612 }
613 }
614 _ => {
615 Ok(Expression::Function(Box::new(Function::new(
616 "UNIFORM".to_string(),
617 vec![low, high, gen],
618 ))))
619 }
620 }
621 }
622
623 "REGEXP_SUBSTR" if f.args.len() >= 2 => {
625 let subject = f.args[0].clone();
626 let pattern = f.args[1].clone();
627 Ok(Expression::Function(Box::new(Function::new(
628 "REGEXP_EXTRACT".to_string(),
629 vec![subject, pattern],
630 ))))
631 }
632
633 _ => Ok(Expression::Function(Box::new(f))),
635 }
636 }
637
638 fn transform_aggregate_function(
639 &self,
640 f: Box<crate::expressions::AggregateFunction>,
641 ) -> Result<Expression> {
642 let name_upper = f.name.to_uppercase();
643 match name_upper.as_str() {
644 "COUNT_IF" => Ok(Expression::AggregateFunction(f)),
646
647 "ANY_VALUE" => Ok(Expression::AggregateFunction(f)),
649
650 "GROUP_CONCAT" if !f.args.is_empty() => {
652 let mut args = f.args;
653 let first = args.remove(0);
654 let separator = args.pop();
655 let collect_list = Expression::Function(Box::new(Function::new(
656 "COLLECT_LIST".to_string(),
657 vec![first],
658 )));
659 if let Some(sep) = separator {
660 Ok(Expression::Function(Box::new(Function::new(
661 "ARRAY_JOIN".to_string(),
662 vec![collect_list, sep],
663 ))))
664 } else {
665 Ok(Expression::Function(Box::new(Function::new(
666 "ARRAY_JOIN".to_string(),
667 vec![collect_list],
668 ))))
669 }
670 }
671
672 "STRING_AGG" if !f.args.is_empty() => {
674 let mut args = f.args;
675 let first = args.remove(0);
676 let separator = args.pop();
677 let collect_list = Expression::Function(Box::new(Function::new(
678 "COLLECT_LIST".to_string(),
679 vec![first],
680 )));
681 if let Some(sep) = separator {
682 Ok(Expression::Function(Box::new(Function::new(
683 "ARRAY_JOIN".to_string(),
684 vec![collect_list, sep],
685 ))))
686 } else {
687 Ok(Expression::Function(Box::new(Function::new(
688 "ARRAY_JOIN".to_string(),
689 vec![collect_list],
690 ))))
691 }
692 }
693
694 "LISTAGG" if !f.args.is_empty() => {
696 let mut args = f.args;
697 let first = args.remove(0);
698 let separator = args.pop();
699 let collect_list = Expression::Function(Box::new(Function::new(
700 "COLLECT_LIST".to_string(),
701 vec![first],
702 )));
703 if let Some(sep) = separator {
704 Ok(Expression::Function(Box::new(Function::new(
705 "ARRAY_JOIN".to_string(),
706 vec![collect_list, sep],
707 ))))
708 } else {
709 Ok(Expression::Function(Box::new(Function::new(
710 "ARRAY_JOIN".to_string(),
711 vec![collect_list],
712 ))))
713 }
714 }
715
716 "ARRAY_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
718 Function::new("COLLECT_LIST".to_string(), f.args),
719 ))),
720
721 "STDDEV" => Ok(Expression::AggregateFunction(f)),
723
724 "VARIANCE" => Ok(Expression::AggregateFunction(f)),
726
727 "APPROX_COUNT_DISTINCT" => Ok(Expression::AggregateFunction(f)),
729
730 "APPROX_DISTINCT" if !f.args.is_empty() => Ok(Expression::AggregateFunction(Box::new(
732 AggregateFunction {
733 name: "APPROX_COUNT_DISTINCT".to_string(),
734 args: f.args,
735 distinct: f.distinct,
736 filter: f.filter,
737 order_by: Vec::new(),
738 limit: None,
739 ignore_nulls: None,
740 },
741 ))),
742
743 _ => Ok(Expression::AggregateFunction(f)),
745 }
746 }
747
748 fn transform_cast(&self, c: Cast) -> Result<Expression> {
758 match &c.this {
760 Expression::Literal(Literal::Timestamp(value)) => {
762 let inner_cast = Expression::Cast(Box::new(Cast {
764 this: Expression::Literal(Literal::String(value.clone())),
765 to: c.to,
766 trailing_comments: Vec::new(),
767 double_colon_syntax: false,
768 format: None,
769 default: None,
770 }));
771 Ok(Expression::Cast(Box::new(Cast {
773 this: inner_cast,
774 to: DataType::Timestamp { precision: None, timezone: false },
775 trailing_comments: c.trailing_comments,
776 double_colon_syntax: false,
777 format: None,
778 default: None,
779 })))
780 }
781 Expression::Literal(Literal::Date(value)) => {
783 let inner_cast = Expression::Cast(Box::new(Cast {
784 this: Expression::Literal(Literal::String(value.clone())),
785 to: c.to,
786 trailing_comments: Vec::new(),
787 double_colon_syntax: false,
788 format: None,
789 default: None,
790 }));
791 Ok(Expression::Cast(Box::new(Cast {
792 this: inner_cast,
793 to: DataType::Date,
794 trailing_comments: c.trailing_comments,
795 double_colon_syntax: false,
796 format: None,
797 default: None,
798 })))
799 }
800 Expression::Literal(Literal::Time(value)) => {
802 let inner_cast = Expression::Cast(Box::new(Cast {
803 this: Expression::Literal(Literal::String(value.clone())),
804 to: c.to,
805 trailing_comments: Vec::new(),
806 double_colon_syntax: false,
807 format: None,
808 default: None,
809 }));
810 Ok(Expression::Cast(Box::new(Cast {
811 this: inner_cast,
812 to: DataType::Time { precision: None, timezone: false },
813 trailing_comments: c.trailing_comments,
814 double_colon_syntax: false,
815 format: None,
816 default: None,
817 })))
818 }
819 _ => Ok(Expression::Cast(Box::new(c))),
821 }
822 }
823
824 fn is_cast_to_timestamp(&self, expr: &Expression) -> bool {
826 if let Expression::Cast(cast) = expr {
827 matches!(cast.to, DataType::Timestamp { .. })
828 } else {
829 false
830 }
831 }
832
833 fn uppercase_first_arg_if_identifier(&self, mut args: Vec<Expression>) -> Vec<Expression> {
835 use crate::expressions::Identifier;
836 if !args.is_empty() {
837 match &args[0] {
838 Expression::Identifier(id) => {
839 args[0] = Expression::Identifier(Identifier {
840 name: id.name.to_uppercase(),
841 quoted: id.quoted,
842 trailing_comments: id.trailing_comments.clone(),
843 });
844 }
845 Expression::Column(col) if col.table.is_none() => {
846 args[0] = Expression::Identifier(Identifier {
848 name: col.name.name.to_uppercase(),
849 quoted: col.name.quoted,
850 trailing_comments: col.name.trailing_comments.clone(),
851 });
852 }
853 _ => {}
854 }
855 }
856 args
857 }
858}
859
860#[cfg(test)]
861mod tests {
862 use super::*;
863 use crate::Dialect;
864
865 #[test]
866 fn test_timestamp_literal_cast() {
867 let sql = "SELECT TIMESTAMP '2025-04-29 18.47.18'::DATE";
870 let expected = "SELECT CAST(CAST('2025-04-29 18.47.18' AS DATE) AS TIMESTAMP)";
871
872 let d = Dialect::get(DialectType::Databricks);
873 let ast = d.parse(sql).expect("Parse failed");
874 let transformed = d.transform(ast[0].clone()).expect("Transform failed");
875 let output = d.generate(&transformed).expect("Generate failed");
876
877 assert_eq!(output, expected, "Timestamp literal cast transformation failed");
878 }
879
880 #[test]
881 fn test_from_utc_timestamp_wraps_column() {
882 let sql = "SELECT DATE_FORMAT(CAST(FROM_UTC_TIMESTAMP(foo, 'America/Los_Angeles') AS TIMESTAMP), 'yyyy-MM-dd HH:mm:ss') AS foo FROM t";
884 let expected = "SELECT DATE_FORMAT(CAST(FROM_UTC_TIMESTAMP(CAST(foo AS TIMESTAMP), 'America/Los_Angeles') AS TIMESTAMP), 'yyyy-MM-dd HH:mm:ss') AS foo FROM t";
885
886 let d = Dialect::get(DialectType::Databricks);
887 let ast = d.parse(sql).expect("Parse failed");
888 let transformed = d.transform(ast[0].clone()).expect("Transform failed");
889 let output = d.generate(&transformed).expect("Generate failed");
890
891 assert_eq!(output, expected, "FROM_UTC_TIMESTAMP transformation failed");
892 }
893
894 #[test]
895 fn test_from_utc_timestamp_keeps_existing_cast() {
896 let sql = "FROM_UTC_TIMESTAMP(x::TIMESTAMP, tz)";
899 let expected = "FROM_UTC_TIMESTAMP(CAST(x AS TIMESTAMP), tz)";
900
901 let d = Dialect::get(DialectType::Databricks);
902 let ast = d.parse(sql).expect("Parse failed");
903 let transformed = d.transform(ast[0].clone()).expect("Transform failed");
904 let output = d.generate(&transformed).expect("Generate failed");
905
906 assert_eq!(output, expected, "FROM_UTC_TIMESTAMP with existing CAST failed");
907 }
908}