1use super::{DialectImpl, DialectType};
13use crate::error::Result;
14use crate::expressions::{
15 AggFunc, BinaryOp, Cast, CeilFunc, DataType, Expression, Function, IntervalUnit, ListAggFunc,
16 Literal, UnaryFunc, VarArgFunc,
17};
18#[cfg(feature = "generate")]
19use crate::generator::GeneratorConfig;
20use crate::tokens::TokenizerConfig;
21
22fn interval_unit_to_str(unit: &IntervalUnit) -> String {
24 match unit {
25 IntervalUnit::Year => "YEAR".to_string(),
26 IntervalUnit::Quarter => "QUARTER".to_string(),
27 IntervalUnit::Month => "MONTH".to_string(),
28 IntervalUnit::Week => "WEEK".to_string(),
29 IntervalUnit::Day => "DAY".to_string(),
30 IntervalUnit::Hour => "HOUR".to_string(),
31 IntervalUnit::Minute => "MINUTE".to_string(),
32 IntervalUnit::Second => "SECOND".to_string(),
33 IntervalUnit::Millisecond => "MILLISECOND".to_string(),
34 IntervalUnit::Microsecond => "MICROSECOND".to_string(),
35 IntervalUnit::Nanosecond => "NANOSECOND".to_string(),
36 }
37}
38
39pub struct SnowflakeDialect;
41
42impl DialectImpl for SnowflakeDialect {
43 fn dialect_type(&self) -> DialectType {
44 DialectType::Snowflake
45 }
46
47 fn tokenizer_config(&self) -> TokenizerConfig {
48 let mut config = TokenizerConfig::default();
49 config.identifiers.insert('"', '"');
51 config.quotes.insert("$$".to_string(), "$$".to_string());
53 config.string_escapes.push('\\');
55 config.escape_follow_chars = vec![
58 '\'', '"', '\\', 'b', 'f', 'n', 'r', 't', '0', '1', '2', '3', '4', '5', '6', '7', 'x',
59 'u',
60 ];
61 config.nested_comments = false;
63 config.comments.insert("//".to_string(), None);
65 config
66 }
67
68 #[cfg(feature = "generate")]
69
70 fn generator_config(&self) -> GeneratorConfig {
71 use crate::generator::IdentifierQuoteStyle;
72 GeneratorConfig {
73 identifier_quote: '"',
74 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
75 dialect: Some(DialectType::Snowflake),
76 parameter_token: "$",
78 matched_by_source: false,
79 single_string_interval: true,
80 join_hints: false,
81 table_hints: false,
82 query_hints: false,
83 aggregate_filter_supported: false,
84 supports_table_copy: false,
85 collate_is_func: true,
86 limit_only_literals: true,
87 json_key_value_pair_sep: ",",
88 insert_overwrite: " OVERWRITE INTO",
89 struct_delimiter: ("(", ")"),
90 copy_params_are_wrapped: false,
91 copy_params_eq_required: true,
92 star_except: "EXCLUDE",
93 supports_exploding_projections: false,
94 array_concat_is_var_len: false,
95 supports_convert_timezone: true,
96 except_intersect_support_all_clause: false,
97 supports_median: true,
98 array_size_name: "ARRAY_SIZE",
99 supports_decode_case: true,
100 is_bool_allowed: false,
101 try_supported: true,
103 nvl2_supported: true,
105 unnest_with_ordinality: false,
107 quantified_no_paren_space: false,
109 array_bracket_only: true,
111 ..Default::default()
112 }
113 }
114
115 #[cfg(feature = "transpile")]
116
117 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
118 match expr {
119 Expression::DataType(dt) => self.transform_data_type(dt),
121
122 Expression::In(in_expr) if in_expr.not && in_expr.query.is_some() => {
126 let inner = in_expr.query.unwrap();
128 let subquery = Expression::Subquery(Box::new(crate::expressions::Subquery {
130 this: inner,
131 alias: None,
132 column_aliases: Vec::new(),
133 alias_explicit_as: false,
134 alias_keyword: None,
135 order_by: None,
136 limit: None,
137 offset: None,
138 distribute_by: None,
139 sort_by: None,
140 cluster_by: None,
141 lateral: false,
142 modifiers_inside: false,
143 trailing_comments: Vec::new(),
144 inferred_type: None,
145 }));
146 Ok(Expression::All(Box::new(
147 crate::expressions::QuantifiedExpr {
148 this: in_expr.this,
149 subquery,
150 op: Some(crate::expressions::QuantifiedOp::Neq),
151 },
152 )))
153 }
154
155 Expression::In(in_expr) if in_expr.not => {
157 let in_without_not = crate::expressions::In {
159 this: in_expr.this,
160 expressions: in_expr.expressions,
161 query: in_expr.query,
162 not: false,
163 global: in_expr.global,
164 unnest: in_expr.unnest,
165 is_field: in_expr.is_field,
166 };
167 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
168 this: Expression::In(Box::new(in_without_not)),
169 inferred_type: None,
170 })))
171 }
172
173 Expression::Interval(interval) => self.transform_interval(*interval),
176
177 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
180 original_name: None,
181 expressions: vec![f.this, f.expression],
182 inferred_type: None,
183 }))),
184
185 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
187 original_name: None,
188 expressions: vec![f.this, f.expression],
189 inferred_type: None,
190 }))),
191
192 Expression::Coalesce(mut f) => {
194 f.original_name = None;
195 Ok(Expression::Coalesce(f))
196 }
197
198 Expression::GroupConcat(f) => Ok(Expression::ListAgg(Box::new(ListAggFunc {
200 this: f.this,
201 separator: f.separator,
202 on_overflow: None,
203 order_by: f.order_by,
204 distinct: f.distinct,
205 filter: f.filter,
206 inferred_type: None,
207 }))),
208
209 Expression::Cast(c) => {
213 use crate::expressions::DataType;
214 let transformed_this = self.transform_expr(c.this)?;
216 match &c.to {
217 DataType::Geography { .. } => Ok(Expression::Function(Box::new(
218 Function::new("TO_GEOGRAPHY".to_string(), vec![transformed_this]),
219 ))),
220 DataType::Geometry { .. } => Ok(Expression::Function(Box::new(Function::new(
221 "TO_GEOMETRY".to_string(),
222 vec![transformed_this],
223 )))),
224 _ => {
225 let transformed_dt = match self.transform_data_type(c.to.clone())? {
227 Expression::DataType(dt) => dt,
228 _ => c.to.clone(),
229 };
230 Ok(Expression::Cast(Box::new(Cast {
231 this: transformed_this,
232 to: transformed_dt,
233 double_colon_syntax: false, trailing_comments: c.trailing_comments,
235 format: c.format,
236 default: c.default,
237 inferred_type: None,
238 })))
239 }
240 }
241 }
242
243 Expression::TryCast(c) => {
246 let transformed_this = self.transform_expr(c.this)?;
247 Ok(Expression::TryCast(Box::new(Cast {
248 this: transformed_this,
249 to: c.to,
250 double_colon_syntax: false, trailing_comments: c.trailing_comments,
252 format: c.format,
253 default: c.default,
254 inferred_type: None,
255 })))
256 }
257
258 Expression::SafeCast(c) => {
261 let to = match c.to {
262 DataType::Timestamp { .. } => DataType::Custom {
263 name: "TIMESTAMPTZ".to_string(),
264 },
265 DataType::Custom { name } if name.eq_ignore_ascii_case("TIMESTAMP") => {
266 DataType::Custom {
267 name: "TIMESTAMPTZ".to_string(),
268 }
269 }
270 other => other,
271 };
272 let transformed_this = self.transform_expr(c.this)?;
273 Ok(Expression::Cast(Box::new(Cast {
274 this: transformed_this,
275 to,
276 double_colon_syntax: c.double_colon_syntax,
277 trailing_comments: c.trailing_comments,
278 format: c.format,
279 default: c.default,
280 inferred_type: None,
281 })))
282 }
283
284 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Timestamp(_)) => {
287 let Literal::Timestamp(s) = lit.as_ref() else {
288 unreachable!()
289 };
290 Ok(Expression::Cast(Box::new(Cast {
291 this: Expression::Literal(Box::new(Literal::String(s.clone()))),
292 to: DataType::Timestamp {
293 precision: None,
294 timezone: false,
295 },
296 double_colon_syntax: false,
297 trailing_comments: Vec::new(),
298 format: None,
299 default: None,
300 inferred_type: None,
301 })))
302 }
303
304 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Date(_)) => {
306 let Literal::Date(s) = lit.as_ref() else {
307 unreachable!()
308 };
309 Ok(Expression::Cast(Box::new(Cast {
310 this: Expression::Literal(Box::new(Literal::String(s.clone()))),
311 to: DataType::Date,
312 double_colon_syntax: false,
313 trailing_comments: Vec::new(),
314 format: None,
315 default: None,
316 inferred_type: None,
317 })))
318 }
319
320 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Time(_)) => {
322 let Literal::Time(s) = lit.as_ref() else {
323 unreachable!()
324 };
325 Ok(Expression::Cast(Box::new(Cast {
326 this: Expression::Literal(Box::new(Literal::String(s.clone()))),
327 to: DataType::Time {
328 precision: None,
329 timezone: false,
330 },
331 double_colon_syntax: false,
332 trailing_comments: Vec::new(),
333 format: None,
334 default: None,
335 inferred_type: None,
336 })))
337 }
338
339 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Datetime(_)) => {
341 let Literal::Datetime(s) = lit.as_ref() else {
342 unreachable!()
343 };
344 Ok(Expression::Cast(Box::new(Cast {
345 this: Expression::Literal(Box::new(Literal::String(s.clone()))),
346 to: DataType::Custom {
347 name: "DATETIME".to_string(),
348 },
349 double_colon_syntax: false,
350 trailing_comments: Vec::new(),
351 format: None,
352 default: None,
353 inferred_type: None,
354 })))
355 }
356
357 Expression::ILike(op) => Ok(Expression::ILike(op)),
360
361 Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
364 "FLATTEN".to_string(),
365 vec![f.this],
366 )))),
367
368 Expression::ExplodeOuter(f) => Ok(Expression::Function(Box::new(Function::new(
370 "FLATTEN".to_string(),
371 vec![f.this],
372 )))),
373
374 Expression::Unnest(f) => {
376 let input_arg =
378 Expression::NamedArgument(Box::new(crate::expressions::NamedArgument {
379 name: crate::expressions::Identifier::new("INPUT"),
380 value: f.this,
381 separator: crate::expressions::NamedArgSeparator::DArrow,
382 }));
383
384 let flatten = Expression::Function(Box::new(Function::new(
386 "FLATTEN".to_string(),
387 vec![input_arg],
388 )));
389
390 let table_func =
392 Expression::TableFromRows(Box::new(crate::expressions::TableFromRows {
393 this: Box::new(flatten),
394 alias: None,
395 joins: vec![],
396 pivots: None,
397 sample: None,
398 }));
399
400 Ok(Expression::Alias(Box::new(crate::expressions::Alias {
402 this: table_func,
403 alias: crate::expressions::Identifier::new("_t0"),
404 column_aliases: vec![
405 crate::expressions::Identifier::new("seq"),
406 crate::expressions::Identifier::new("key"),
407 crate::expressions::Identifier::new("path"),
408 crate::expressions::Identifier::new("index"),
409 crate::expressions::Identifier::new("value"),
410 crate::expressions::Identifier::new("this"),
411 ],
412 alias_explicit_as: false,
413 alias_keyword: None,
414 pre_alias_comments: vec![],
415 trailing_comments: vec![],
416 inferred_type: None,
417 })))
418 }
419
420 Expression::ArrayFunc(arr) => {
424 if arr.bracket_notation {
425 Ok(Expression::ArrayFunc(arr))
427 } else {
428 Ok(Expression::Function(Box::new(Function::new(
430 "ARRAY_CONSTRUCT".to_string(),
431 arr.expressions,
432 ))))
433 }
434 }
435
436 Expression::ArrayConcat(f) => Ok(Expression::Function(Box::new(Function::new(
438 "ARRAY_CAT".to_string(),
439 f.expressions,
440 )))),
441
442 Expression::ArrayConcatAgg(f) => Ok(Expression::Function(Box::new(Function::new(
444 "ARRAY_FLATTEN".to_string(),
445 vec![f.this],
446 )))),
447
448 Expression::ArrayContains(f) => Ok(Expression::Function(Box::new(Function::new(
450 "ARRAY_CONTAINS".to_string(),
451 vec![f.this, f.expression],
452 )))),
453
454 Expression::ArrayIntersect(f) => Ok(Expression::Function(Box::new(Function::new(
456 "ARRAY_INTERSECTION".to_string(),
457 f.expressions,
458 )))),
459
460 Expression::ArraySort(f) => Ok(Expression::Function(Box::new(Function::new(
462 "ARRAY_SORT".to_string(),
463 vec![f.this],
464 )))),
465
466 Expression::StringToArray(f) => {
468 let mut args = vec![*f.this];
469 if let Some(expr) = f.expression {
470 args.push(*expr);
471 }
472 Ok(Expression::Function(Box::new(Function::new(
473 "STRTOK_TO_ARRAY".to_string(),
474 args,
475 ))))
476 }
477
478 Expression::BitwiseOr(f) => Ok(Expression::Function(Box::new(Function::new(
481 "BITOR".to_string(),
482 vec![f.left, f.right],
483 )))),
484
485 Expression::BitwiseXor(f) => Ok(Expression::Function(Box::new(Function::new(
487 "BITXOR".to_string(),
488 vec![f.left, f.right],
489 )))),
490
491 Expression::BitwiseAnd(f) => Ok(Expression::Function(Box::new(Function::new(
493 "BITAND".to_string(),
494 vec![f.left, f.right],
495 )))),
496
497 Expression::BitwiseNot(f) => Ok(Expression::Function(Box::new(Function::new(
499 "BITNOT".to_string(),
500 vec![f.this],
501 )))),
502
503 Expression::BitwiseLeftShift(f) => Ok(Expression::Function(Box::new(Function::new(
505 "BITSHIFTLEFT".to_string(),
506 vec![f.left, f.right],
507 )))),
508
509 Expression::BitwiseRightShift(f) => Ok(Expression::Function(Box::new(Function::new(
511 "BITSHIFTRIGHT".to_string(),
512 vec![f.left, f.right],
513 )))),
514
515 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
517 "BITAND_AGG".to_string(),
518 vec![f.this],
519 )))),
520
521 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
523 "BITOR_AGG".to_string(),
524 vec![f.this],
525 )))),
526
527 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
529 "BITXOR_AGG".to_string(),
530 vec![f.this],
531 )))),
532
533 Expression::LogicalAnd(f) => Ok(Expression::Function(Box::new(Function::new(
536 "BOOLAND_AGG".to_string(),
537 vec![f.this],
538 )))),
539
540 Expression::LogicalOr(f) => Ok(Expression::Function(Box::new(Function::new(
542 "BOOLOR_AGG".to_string(),
543 vec![f.this],
544 )))),
545
546 Expression::Booland(f) => Ok(Expression::Function(Box::new(Function::new(
548 "BOOLAND".to_string(),
549 vec![*f.this, *f.expression],
550 )))),
551
552 Expression::Boolor(f) => Ok(Expression::Function(Box::new(Function::new(
554 "BOOLOR".to_string(),
555 vec![*f.this, *f.expression],
556 )))),
557
558 Expression::Xor(f) => {
560 let mut args = Vec::new();
561 if let Some(this) = f.this {
562 args.push(*this);
563 }
564 if let Some(expr) = f.expression {
565 args.push(*expr);
566 }
567 Ok(Expression::Function(Box::new(Function::new(
568 "BOOLXOR".to_string(),
569 args,
570 ))))
571 }
572
573 Expression::DayOfMonth(f) => Ok(Expression::Function(Box::new(Function::new(
576 "DAYOFMONTH".to_string(),
577 vec![f.this],
578 )))),
579
580 Expression::DayOfWeek(f) => Ok(Expression::Function(Box::new(Function::new(
582 "DAYOFWEEK".to_string(),
583 vec![f.this],
584 )))),
585
586 Expression::DayOfWeekIso(f) => Ok(Expression::Function(Box::new(Function::new(
588 "DAYOFWEEKISO".to_string(),
589 vec![f.this],
590 )))),
591
592 Expression::DayOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
594 "DAYOFYEAR".to_string(),
595 vec![f.this],
596 )))),
597
598 Expression::WeekOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
600 "WEEK".to_string(),
601 vec![f.this],
602 )))),
603
604 Expression::YearOfWeek(f) => Ok(Expression::Function(Box::new(Function::new(
606 "YEAROFWEEK".to_string(),
607 vec![f.this],
608 )))),
609
610 Expression::YearOfWeekIso(f) => Ok(Expression::Function(Box::new(Function::new(
612 "YEAROFWEEKISO".to_string(),
613 vec![f.this],
614 )))),
615
616 Expression::ByteLength(f) => Ok(Expression::Function(Box::new(Function::new(
618 "OCTET_LENGTH".to_string(),
619 vec![f.this],
620 )))),
621
622 Expression::TimestampDiff(f) => {
624 let mut args = vec![];
625 if let Some(ref unit_str) = f.unit {
627 args.push(Expression::Identifier(crate::expressions::Identifier::new(
628 unit_str.clone(),
629 )));
630 args.push(*f.this);
631 args.push(*f.expression);
632 } else {
633 args.push(*f.this);
634 args.push(*f.expression);
635 }
636 Ok(Expression::Function(Box::new(Function::new(
637 "TIMESTAMPDIFF".to_string(),
638 args,
639 ))))
640 }
641
642 Expression::TimestampAdd(f) => {
644 let mut args = vec![];
645 if let Some(ref unit_str) = f.unit {
646 args.push(Expression::Identifier(crate::expressions::Identifier::new(
647 unit_str.clone(),
648 )));
649 args.push(*f.this);
650 args.push(*f.expression);
651 } else {
652 args.push(*f.this);
653 args.push(*f.expression);
654 }
655 Ok(Expression::Function(Box::new(Function::new(
656 "TIMESTAMPADD".to_string(),
657 args,
658 ))))
659 }
660
661 Expression::ToArray(f) => Ok(Expression::Function(Box::new(Function::new(
663 "TO_ARRAY".to_string(),
664 vec![f.this],
665 )))),
666
667 Expression::DateAdd(f) => {
669 let unit_str = interval_unit_to_str(&f.unit);
670 let unit = Expression::Identifier(crate::expressions::Identifier {
671 name: unit_str,
672 quoted: false,
673 trailing_comments: Vec::new(),
674 span: None,
675 });
676 Ok(Expression::Function(Box::new(Function::new(
677 "DATEADD".to_string(),
678 vec![unit, f.interval, f.this],
679 ))))
680 }
681
682 Expression::DateSub(f) => {
684 let unit_str = interval_unit_to_str(&f.unit);
685 let unit = Expression::Identifier(crate::expressions::Identifier {
686 name: unit_str,
687 quoted: false,
688 trailing_comments: Vec::new(),
689 span: None,
690 });
691 let neg_expr = Expression::Mul(Box::new(crate::expressions::BinaryOp::new(
693 f.interval,
694 Expression::Neg(Box::new(crate::expressions::UnaryOp {
695 this: Expression::number(1),
696 inferred_type: None,
697 })),
698 )));
699 Ok(Expression::Function(Box::new(Function::new(
700 "DATEADD".to_string(),
701 vec![unit, neg_expr, f.this],
702 ))))
703 }
704
705 Expression::DateDiff(f) => {
707 let unit_str =
708 interval_unit_to_str(&f.unit.unwrap_or(crate::expressions::IntervalUnit::Day));
709 let unit = Expression::Identifier(crate::expressions::Identifier {
710 name: unit_str,
711 quoted: false,
712 trailing_comments: Vec::new(),
713 span: None,
714 });
715 Ok(Expression::Function(Box::new(Function::new(
716 "DATEDIFF".to_string(),
717 vec![unit, f.expression, f.this],
718 ))))
719 }
720
721 Expression::StringAgg(f) => {
724 let mut args = vec![f.this.clone()];
725 if let Some(separator) = &f.separator {
726 args.push(separator.clone());
727 }
728 Ok(Expression::Function(Box::new(Function::new(
729 "LISTAGG".to_string(),
730 args,
731 ))))
732 }
733
734 Expression::StartsWith(f) => Ok(Expression::Function(Box::new(Function::new(
736 "STARTSWITH".to_string(),
737 vec![f.this, f.expression],
738 )))),
739
740 Expression::EndsWith(f) => Ok(Expression::EndsWith(f)),
742
743 Expression::Stuff(f) => {
745 let mut args = vec![*f.this];
746 if let Some(start) = f.start {
747 args.push(*start);
748 }
749 if let Some(length) = f.length {
750 args.push(Expression::number(length));
751 }
752 args.push(*f.expression);
753 Ok(Expression::Function(Box::new(Function::new(
754 "INSERT".to_string(),
755 args,
756 ))))
757 }
758
759 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
762 "SHA1".to_string(),
763 vec![f.this],
764 )))),
765
766 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
768 "SHA1_BINARY".to_string(),
769 vec![f.this],
770 )))),
771
772 Expression::SHA2Digest(f) => Ok(Expression::Function(Box::new(Function::new(
774 "SHA2_BINARY".to_string(),
775 vec![*f.this],
776 )))),
777
778 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
780 "MD5_BINARY".to_string(),
781 vec![*f.this],
782 )))),
783
784 Expression::MD5NumberLower64(f) => Ok(Expression::Function(Box::new(Function::new(
786 "MD5_NUMBER_LOWER64".to_string(),
787 vec![f.this],
788 )))),
789
790 Expression::MD5NumberUpper64(f) => Ok(Expression::Function(Box::new(Function::new(
792 "MD5_NUMBER_UPPER64".to_string(),
793 vec![f.this],
794 )))),
795
796 Expression::CosineDistance(f) => Ok(Expression::Function(Box::new(Function::new(
799 "VECTOR_COSINE_SIMILARITY".to_string(),
800 vec![*f.this, *f.expression],
801 )))),
802
803 Expression::DotProduct(f) => Ok(Expression::Function(Box::new(Function::new(
805 "VECTOR_INNER_PRODUCT".to_string(),
806 vec![*f.this, *f.expression],
807 )))),
808
809 Expression::EuclideanDistance(f) => Ok(Expression::Function(Box::new(Function::new(
811 "VECTOR_L2_DISTANCE".to_string(),
812 vec![*f.this, *f.expression],
813 )))),
814
815 Expression::ManhattanDistance(f) => Ok(Expression::Function(Box::new(Function::new(
817 "VECTOR_L1_DISTANCE".to_string(),
818 vec![*f.this, *f.expression],
819 )))),
820
821 Expression::JSONFormat(f) => {
824 let mut args = Vec::new();
825 if let Some(this) = f.this {
826 args.push(*this);
827 }
828 Ok(Expression::Function(Box::new(Function::new(
829 "TO_JSON".to_string(),
830 args,
831 ))))
832 }
833
834 Expression::JSONKeys(f) => Ok(Expression::Function(Box::new(Function::new(
836 "OBJECT_KEYS".to_string(),
837 vec![*f.this],
838 )))),
839
840 Expression::GetExtract(f) => Ok(Expression::Function(Box::new(Function::new(
842 "GET".to_string(),
843 vec![*f.this, *f.expression],
844 )))),
845
846 Expression::StarMap(f) => Ok(Expression::Function(Box::new(Function::new(
848 "OBJECT_CONSTRUCT".to_string(),
849 vec![f.this, f.expression],
850 )))),
851
852 Expression::LowerHex(f) => Ok(Expression::Function(Box::new(Function::new(
854 "TO_CHAR".to_string(),
855 vec![f.this],
856 )))),
857
858 Expression::Skewness(f) => Ok(Expression::Function(Box::new(Function::new(
860 "SKEW".to_string(),
861 vec![f.this],
862 )))),
863
864 Expression::StPoint(f) => Ok(Expression::Function(Box::new(Function::new(
866 "ST_MAKEPOINT".to_string(),
867 vec![*f.this, *f.expression],
868 )))),
869
870 Expression::FromTimeZone(f) => Ok(Expression::Function(Box::new(Function::new(
872 "CONVERT_TIMEZONE".to_string(),
873 vec![*f.this],
874 )))),
875
876 Expression::Unhex(f) => Ok(Expression::Function(Box::new(Function::new(
879 "HEX_DECODE_BINARY".to_string(),
880 vec![*f.this],
881 )))),
882
883 Expression::UnixToTime(f) => {
885 let mut args = vec![*f.this];
886 if let Some(scale) = f.scale {
887 args.push(Expression::number(scale));
888 }
889 Ok(Expression::Function(Box::new(Function::new(
890 "TO_TIMESTAMP".to_string(),
891 args,
892 ))))
893 }
894
895 Expression::IfFunc(f) => Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
898 condition: f.condition,
899 true_value: f.true_value,
900 false_value: Some(
901 f.false_value
902 .unwrap_or(Expression::Null(crate::expressions::Null)),
903 ),
904 original_name: Some("IFF".to_string()),
905 inferred_type: None,
906 }))),
907
908 Expression::ApproxDistinct(f) => Ok(Expression::Function(Box::new(Function::new(
911 "APPROX_COUNT_DISTINCT".to_string(),
912 vec![f.this],
913 )))),
914
915 Expression::ArgMax(f) => Ok(Expression::Function(Box::new(Function::new(
917 "MAX_BY".to_string(),
918 vec![*f.this, *f.expression],
919 )))),
920
921 Expression::ArgMin(f) => Ok(Expression::Function(Box::new(Function::new(
923 "MIN_BY".to_string(),
924 vec![*f.this, *f.expression],
925 )))),
926
927 Expression::Random(_) => Ok(Expression::Random(crate::expressions::Random)),
930
931 Expression::Rand(r) => Ok(Expression::Rand(r)),
933
934 Expression::Uuid(u) => Ok(Expression::Uuid(u)),
937
938 Expression::Map(f) => Ok(Expression::Function(Box::new(Function::new(
941 "OBJECT_CONSTRUCT".to_string(),
942 f.keys
943 .into_iter()
944 .zip(f.values.into_iter())
945 .flat_map(|(k, v)| vec![k, v])
946 .collect(),
947 )))),
948
949 Expression::MapFunc(f) => Ok(Expression::Function(Box::new(Function::new(
951 "OBJECT_CONSTRUCT".to_string(),
952 f.keys
953 .into_iter()
954 .zip(f.values.into_iter())
955 .flat_map(|(k, v)| vec![k, v])
956 .collect(),
957 )))),
958
959 Expression::VarMap(f) => Ok(Expression::Function(Box::new(Function::new(
961 "OBJECT_CONSTRUCT".to_string(),
962 f.keys
963 .into_iter()
964 .zip(f.values.into_iter())
965 .flat_map(|(k, v)| vec![k, v])
966 .collect(),
967 )))),
968
969 Expression::JsonObject(f) => Ok(Expression::Function(Box::new(Function::new(
972 "OBJECT_CONSTRUCT_KEEP_NULL".to_string(),
973 f.pairs.into_iter().flat_map(|(k, v)| vec![k, v]).collect(),
974 )))),
975
976 Expression::JsonExtractScalar(f) => Ok(Expression::Function(Box::new(Function::new(
978 "JSON_EXTRACT_PATH_TEXT".to_string(),
979 vec![f.this, f.path],
980 )))),
981
982 Expression::Struct(f) => Ok(Expression::Function(Box::new(Function::new(
985 "OBJECT_CONSTRUCT".to_string(),
986 f.fields
987 .into_iter()
988 .flat_map(|(name, expr)| {
989 let key = match name {
990 Some(n) => Expression::string(n),
991 None => Expression::Null(crate::expressions::Null),
992 };
993 vec![key, expr]
994 })
995 .collect(),
996 )))),
997
998 Expression::JSONPathRoot(_) => Ok(Expression::Literal(Box::new(
1001 crate::expressions::Literal::String(String::new()),
1002 ))),
1003
1004 Expression::VarSamp(agg) => Ok(Expression::Variance(agg)),
1007
1008 Expression::VarPop(agg) => Ok(Expression::VarPop(agg)),
1011
1012 Expression::Extract(f) => {
1015 use crate::expressions::DateTimeField;
1016 let transformed_this = self.transform_expr(f.this)?;
1018 let field_name = match &f.field {
1019 DateTimeField::Year => "YEAR",
1020 DateTimeField::Month => "MONTH",
1021 DateTimeField::Day => "DAY",
1022 DateTimeField::Hour => "HOUR",
1023 DateTimeField::Minute => "MINUTE",
1024 DateTimeField::Second => "SECOND",
1025 DateTimeField::Millisecond => "MILLISECOND",
1026 DateTimeField::Microsecond => "MICROSECOND",
1027 DateTimeField::Week => "WEEK",
1028 DateTimeField::WeekWithModifier(m) => {
1029 return Ok(Expression::Function(Box::new(Function::new(
1030 "DATE_PART".to_string(),
1031 vec![
1032 Expression::Identifier(crate::expressions::Identifier {
1033 name: format!("WEEK({})", m),
1034 quoted: false,
1035 trailing_comments: Vec::new(),
1036 span: None,
1037 }),
1038 transformed_this,
1039 ],
1040 ))))
1041 }
1042 DateTimeField::DayOfWeek => "DAYOFWEEK",
1043 DateTimeField::DayOfYear => "DAYOFYEAR",
1044 DateTimeField::Quarter => "QUARTER",
1045 DateTimeField::Epoch => "EPOCH",
1046 DateTimeField::Timezone => "TIMEZONE",
1047 DateTimeField::TimezoneHour => "TIMEZONE_HOUR",
1048 DateTimeField::TimezoneMinute => "TIMEZONE_MINUTE",
1049 DateTimeField::Date => "DATE",
1050 DateTimeField::Time => "TIME",
1051 DateTimeField::Custom(s) => {
1052 match s.to_uppercase().as_str() {
1054 "DAYOFMONTH" => "DAY",
1055 "DOW" => "DAYOFWEEK",
1056 "DOY" => "DAYOFYEAR",
1057 "ISODOW" => "DAYOFWEEKISO",
1058 "EPOCH_SECOND" | "EPOCH_SECONDS" => "EPOCH_SECOND",
1059 "EPOCH_MILLISECOND" | "EPOCH_MILLISECONDS" => "EPOCH_MILLISECOND",
1060 "EPOCH_MICROSECOND" | "EPOCH_MICROSECONDS" => "EPOCH_MICROSECOND",
1061 "EPOCH_NANOSECOND" | "EPOCH_NANOSECONDS" => "EPOCH_NANOSECOND",
1062 _ => {
1063 return {
1064 let field_ident =
1065 Expression::Identifier(crate::expressions::Identifier {
1066 name: s.to_string(),
1067 quoted: false,
1068 trailing_comments: Vec::new(),
1069 span: None,
1070 });
1071 Ok(Expression::Function(Box::new(Function::new(
1072 "DATE_PART".to_string(),
1073 vec![field_ident, transformed_this],
1074 ))))
1075 }
1076 }
1077 }
1078 }
1079 };
1080 let field_ident = Expression::Identifier(crate::expressions::Identifier {
1081 name: field_name.to_string(),
1082 quoted: false,
1083 trailing_comments: Vec::new(),
1084 span: None,
1085 });
1086 Ok(Expression::Function(Box::new(Function::new(
1087 "DATE_PART".to_string(),
1088 vec![field_ident, transformed_this],
1089 ))))
1090 }
1091
1092 Expression::Function(f) => self.transform_function(*f),
1094
1095 Expression::Sum(mut agg) => {
1097 agg.this = self.transform_expr(agg.this)?;
1098 Ok(Expression::Sum(agg))
1099 }
1100
1101 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
1103
1104 Expression::NamedArgument(na) => {
1106 let transformed_value = self.transform_expr(na.value)?;
1107 Ok(Expression::NamedArgument(Box::new(
1108 crate::expressions::NamedArgument {
1109 name: na.name,
1110 value: transformed_value,
1111 separator: na.separator,
1112 },
1113 )))
1114 }
1115
1116 Expression::CreateTable(mut ct) => {
1118 for col in &mut ct.columns {
1119 if let Expression::DataType(new_dt) =
1120 self.transform_data_type(col.data_type.clone())?
1121 {
1122 col.data_type = new_dt;
1123 }
1124 if let Some(default_expr) = col.default.take() {
1126 col.default = Some(self.transform_expr(default_expr)?);
1127 }
1128 for constraint in &mut col.constraints {
1130 if let crate::expressions::ColumnConstraint::ComputedColumn(cc) = constraint
1131 {
1132 let transformed = self.transform_expr(*cc.expression.clone())?;
1133 cc.expression = Box::new(transformed);
1134 }
1135 }
1136 }
1137
1138 if ct.table_modifier.as_deref() == Some("EXTERNAL")
1141 && !ct.with_properties.is_empty()
1142 {
1143 for (key, value) in ct.with_properties.drain(..) {
1144 let formatted = Self::format_external_table_property(&key, &value);
1145 ct.properties
1146 .push(Expression::Raw(crate::expressions::Raw { sql: formatted }));
1147 }
1148 }
1149
1150 Ok(Expression::CreateTable(ct))
1151 }
1152
1153 Expression::AlterTable(mut at) => {
1155 for action in &mut at.actions {
1156 if let crate::expressions::AlterTableAction::AddColumn { column, .. } = action {
1157 if let Expression::DataType(new_dt) =
1158 self.transform_data_type(column.data_type.clone())?
1159 {
1160 column.data_type = new_dt;
1161 }
1162 }
1163 }
1164 Ok(Expression::AlterTable(at))
1165 }
1166
1167 Expression::Table(mut t) => {
1169 if let Some(when) = t.when.take() {
1170 let transformed_expr = self.transform_expr(*when.expression)?;
1172 t.when = Some(Box::new(crate::expressions::HistoricalData {
1173 this: when.this,
1174 kind: when.kind,
1175 expression: Box::new(transformed_expr),
1176 }));
1177 }
1178 Ok(Expression::Table(t))
1179 }
1180
1181 Expression::Subscript(s) => {
1183 let transformed_this = self.transform_expr(s.this)?;
1184 let transformed_index = self.transform_expr(s.index)?;
1185 Ok(Expression::Subscript(Box::new(
1186 crate::expressions::Subscript {
1187 this: transformed_this,
1188 index: transformed_index,
1189 },
1190 )))
1191 }
1192
1193 Expression::Paren(p) => {
1195 let transformed = self.transform_expr(p.this)?;
1196 Ok(Expression::Paren(Box::new(crate::expressions::Paren {
1197 this: transformed,
1198 trailing_comments: p.trailing_comments,
1199 })))
1200 }
1201
1202 Expression::Select(mut select) => {
1206 if let Some(ref mut order) = select.order_by {
1207 for ord in &mut order.expressions {
1208 if ord.nulls_first.is_none() {
1209 ord.nulls_first = Some(ord.desc);
1210 }
1211 }
1212 }
1213 Ok(Expression::Select(select))
1214 }
1215
1216 Expression::WindowFunction(mut wf) => {
1218 for ord in &mut wf.over.order_by {
1219 if ord.nulls_first.is_none() {
1220 ord.nulls_first = Some(ord.desc);
1221 }
1222 }
1223 Ok(Expression::WindowFunction(wf))
1224 }
1225
1226 Expression::Window(mut w) => {
1228 for ord in &mut w.order_by {
1229 if ord.nulls_first.is_none() {
1230 ord.nulls_first = Some(ord.desc);
1231 }
1232 }
1233 Ok(Expression::Window(w))
1234 }
1235
1236 Expression::Lateral(mut lat) => {
1238 let is_flatten = match lat.this.as_ref() {
1240 Expression::Function(f) => f.name.to_uppercase() == "FLATTEN",
1241 _ => false,
1242 };
1243 if is_flatten && lat.column_aliases.is_empty() {
1244 lat.column_aliases = vec![
1246 "SEQ".to_string(),
1247 "KEY".to_string(),
1248 "PATH".to_string(),
1249 "INDEX".to_string(),
1250 "VALUE".to_string(),
1251 "THIS".to_string(),
1252 ];
1253 if lat.alias.is_none() {
1255 lat.alias = Some("_flattened".to_string());
1256 }
1257 }
1258 Ok(Expression::Lateral(lat))
1259 }
1260
1261 _ => Ok(expr),
1263 }
1264 }
1265}
1266
1267#[cfg(feature = "transpile")]
1268impl SnowflakeDialect {
1269 fn format_external_table_property(key: &str, value: &str) -> String {
1272 let lower_key = key.to_lowercase();
1273 match lower_key.as_str() {
1274 "location" => format!("LOCATION={}", value),
1275 "file_format" => {
1276 let formatted_value = Self::format_file_format_value(value);
1278 format!("FILE_FORMAT={}", formatted_value)
1279 }
1280 _ => format!("{}={}", key, value),
1281 }
1282 }
1283
1284 fn format_file_format_value(value: &str) -> String {
1288 if !value.starts_with('(') {
1289 return value.to_string();
1290 }
1291 let inner = value[1..value.len() - 1].trim();
1293 let mut result = String::from("(");
1295 let mut parts: Vec<String> = Vec::new();
1296 let tokens: Vec<&str> = inner.split_whitespace().collect();
1298 let mut i = 0;
1299 while i < tokens.len() {
1300 let token = tokens[i];
1301 if i + 2 < tokens.len() && tokens[i + 1] == "=" {
1302 let val = Self::format_property_value(tokens[i + 2]);
1304 parts.push(format!("{}={}", token, val));
1305 i += 3;
1306 } else if token.contains('=') {
1307 let eq_pos = token.find('=').unwrap();
1309 let k = &token[..eq_pos];
1310 let v = Self::format_property_value(&token[eq_pos + 1..]);
1311 parts.push(format!("{}={}", k, v));
1312 i += 1;
1313 } else {
1314 parts.push(token.to_string());
1315 i += 1;
1316 }
1317 }
1318 result.push_str(&parts.join(" "));
1319 result.push(')');
1320 result
1321 }
1322
1323 fn format_property_value(value: &str) -> String {
1325 match value.to_lowercase().as_str() {
1326 "true" => "TRUE".to_string(),
1327 "false" => "FALSE".to_string(),
1328 _ => value.to_string(),
1329 }
1330 }
1331
1332 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
1334 use crate::expressions::DataType;
1335 let transformed = match dt {
1336 DataType::Text => DataType::VarChar {
1338 length: None,
1339 parenthesized_length: false,
1340 },
1341 DataType::Struct { fields, .. } => {
1343 let _ = fields; DataType::Custom {
1346 name: "OBJECT".to_string(),
1347 }
1348 }
1349 DataType::Custom { name } => {
1351 let upper_name = name.to_uppercase();
1352 match upper_name.as_str() {
1353 "NVARCHAR" | "NCHAR" | "NATIONAL CHARACTER VARYING" | "NATIONAL CHAR" => {
1355 DataType::VarChar {
1356 length: None,
1357 parenthesized_length: false,
1358 }
1359 }
1360 "STRING" => DataType::VarChar {
1362 length: None,
1363 parenthesized_length: false,
1364 },
1365 "BIGDECIMAL" => DataType::Double {
1367 precision: None,
1368 scale: None,
1369 },
1370 "NESTED" => DataType::Custom {
1372 name: "OBJECT".to_string(),
1373 },
1374 "BYTEINT" => DataType::Int {
1376 length: None,
1377 integer_spelling: false,
1378 },
1379 "CHAR VARYING" | "CHARACTER VARYING" => DataType::VarChar {
1381 length: None,
1382 parenthesized_length: false,
1383 },
1384 "SQL_DOUBLE" => DataType::Double {
1386 precision: None,
1387 scale: None,
1388 },
1389 "SQL_VARCHAR" => DataType::VarChar {
1391 length: None,
1392 parenthesized_length: false,
1393 },
1394 "TIMESTAMP_NTZ" => DataType::Custom {
1396 name: "TIMESTAMPNTZ".to_string(),
1397 },
1398 "TIMESTAMP_LTZ" => DataType::Custom {
1400 name: "TIMESTAMPLTZ".to_string(),
1401 },
1402 "TIMESTAMP_TZ" => DataType::Custom {
1404 name: "TIMESTAMPTZ".to_string(),
1405 },
1406 "NCHAR VARYING" => DataType::VarChar {
1408 length: None,
1409 parenthesized_length: false,
1410 },
1411 "NUMBER" => DataType::Decimal {
1413 precision: Some(38),
1414 scale: Some(0),
1415 },
1416 _ if name.starts_with("NUMBER(") => {
1417 let inner = &name[7..name.len() - 1]; let parts: Vec<&str> = inner.split(',').map(|s| s.trim()).collect();
1421 let precision = parts.first().and_then(|p| p.parse::<u32>().ok());
1422 let scale = parts.get(1).and_then(|s| s.parse::<u32>().ok());
1423 DataType::Decimal { precision, scale }
1424 }
1425 _ => DataType::Custom { name },
1426 }
1427 }
1428 DataType::Decimal {
1430 precision: None,
1431 scale: None,
1432 } => DataType::Decimal {
1433 precision: Some(38),
1434 scale: Some(0),
1435 },
1436 DataType::Float { .. } => DataType::Double {
1438 precision: None,
1439 scale: None,
1440 },
1441 other => other,
1443 };
1444 Ok(Expression::DataType(transformed))
1445 }
1446
1447 fn map_date_part(abbr: &str) -> Option<&'static str> {
1449 match abbr.to_uppercase().as_str() {
1450 "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => Some("YEAR"),
1452 "MM" | "MON" | "MONS" | "MONTHS" => Some("MONTH"),
1454 "D" | "DD" | "DAYS" | "DAYOFMONTH" => Some("DAY"),
1456 "DAY OF WEEK" | "WEEKDAY" | "DOW" | "DW" => Some("DAYOFWEEK"),
1458 "WEEKDAY_ISO" | "DOW_ISO" | "DW_ISO" | "DAYOFWEEK_ISO" => Some("DAYOFWEEKISO"),
1459 "DAY OF YEAR" | "DOY" | "DY" => Some("DAYOFYEAR"),
1461 "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" => Some("WEEK"),
1463 "WEEK_ISO" | "WEEKOFYEARISO" | "WEEKOFYEAR_ISO" => Some("WEEKISO"),
1464 "Q" | "QTR" | "QTRS" | "QUARTERS" => Some("QUARTER"),
1466 "H" | "HH" | "HR" | "HOURS" | "HRS" => Some("HOUR"),
1468 "MI" | "MIN" | "MINUTES" | "MINS" => Some("MINUTE"),
1470 "S" | "SEC" | "SECONDS" | "SECS" => Some("SECOND"),
1472 "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
1474 | "MILLISECON" | "MILLISECONDS" => Some("MILLISECOND"),
1475 "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
1477 | "MICROSECONDS" => Some("MICROSECOND"),
1478 "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => Some("NANOSECOND"),
1480 "EPOCH_SECOND" | "EPOCH_SECONDS" => Some("EPOCH_SECOND"),
1482 "EPOCH_MILLISECOND" | "EPOCH_MILLISECONDS" => Some("EPOCH_MILLISECOND"),
1483 "EPOCH_MICROSECOND" | "EPOCH_MICROSECONDS" => Some("EPOCH_MICROSECOND"),
1484 "EPOCH_NANOSECOND" | "EPOCH_NANOSECONDS" => Some("EPOCH_NANOSECOND"),
1485 "TZH" => Some("TIMEZONE_HOUR"),
1487 "TZM" => Some("TIMEZONE_MINUTE"),
1488 "DEC" | "DECS" | "DECADES" => Some("DECADE"),
1490 "MIL" | "MILS" | "MILLENIA" => Some("MILLENNIUM"),
1492 "C" | "CENT" | "CENTS" | "CENTURIES" => Some("CENTURY"),
1494 _ => None,
1496 }
1497 }
1498
1499 fn transform_date_part_arg(&self, expr: Expression) -> Expression {
1501 match &expr {
1502 Expression::Literal(lit)
1504 if matches!(lit.as_ref(), crate::expressions::Literal::String(_)) =>
1505 {
1506 let crate::expressions::Literal::String(s) = lit.as_ref() else {
1507 unreachable!()
1508 };
1509 Expression::Identifier(crate::expressions::Identifier {
1510 name: s.clone(),
1511 quoted: false,
1512 trailing_comments: Vec::new(),
1513 span: None,
1514 })
1515 }
1516 Expression::Identifier(id) => {
1518 if let Some(canonical) = Self::map_date_part(&id.name) {
1519 Expression::Identifier(crate::expressions::Identifier {
1520 name: canonical.to_string(),
1521 quoted: false,
1522 trailing_comments: Vec::new(),
1523 span: None,
1524 })
1525 } else {
1526 expr
1528 }
1529 }
1530 Expression::Var(v) => {
1531 if let Some(canonical) = Self::map_date_part(&v.this) {
1532 Expression::Identifier(crate::expressions::Identifier {
1533 name: canonical.to_string(),
1534 quoted: false,
1535 trailing_comments: Vec::new(),
1536 span: None,
1537 })
1538 } else {
1539 expr
1540 }
1541 }
1542 Expression::Column(col) if col.table.is_none() => {
1544 if let Some(canonical) = Self::map_date_part(&col.name.name) {
1545 Expression::Identifier(crate::expressions::Identifier {
1546 name: canonical.to_string(),
1547 quoted: false,
1548 trailing_comments: Vec::new(),
1549 span: None,
1550 })
1551 } else {
1552 expr
1554 }
1555 }
1556 _ => expr,
1557 }
1558 }
1559
1560 fn transform_date_part_arg_identifiers_only(&self, expr: Expression) -> Expression {
1563 match &expr {
1564 Expression::Identifier(id) => {
1565 if let Some(canonical) = Self::map_date_part(&id.name) {
1566 Expression::Identifier(crate::expressions::Identifier {
1567 name: canonical.to_string(),
1568 quoted: false,
1569 trailing_comments: Vec::new(),
1570 span: None,
1571 })
1572 } else {
1573 expr
1574 }
1575 }
1576 Expression::Var(v) => {
1577 if let Some(canonical) = Self::map_date_part(&v.this) {
1578 Expression::Identifier(crate::expressions::Identifier {
1579 name: canonical.to_string(),
1580 quoted: false,
1581 trailing_comments: Vec::new(),
1582 span: None,
1583 })
1584 } else {
1585 expr
1586 }
1587 }
1588 Expression::Column(col) if col.table.is_none() => {
1589 if let Some(canonical) = Self::map_date_part(&col.name.name) {
1590 Expression::Identifier(crate::expressions::Identifier {
1591 name: canonical.to_string(),
1592 quoted: false,
1593 trailing_comments: Vec::new(),
1594 span: None,
1595 })
1596 } else {
1597 expr
1598 }
1599 }
1600 _ => expr,
1601 }
1602 }
1603
1604 fn transform_json_path(path: &str) -> String {
1608 fn is_safe_identifier(s: &str) -> bool {
1611 if s.is_empty() {
1612 return false;
1613 }
1614 let mut chars = s.chars();
1615 match chars.next() {
1616 Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
1617 _ => return false,
1618 }
1619 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
1620 }
1621
1622 if !path.contains('.') && !path.contains('[') && !path.contains(':') {
1625 if is_safe_identifier(path) {
1626 return path.to_string();
1627 } else {
1628 return format!("[\"{}\"]", path);
1630 }
1631 }
1632
1633 let result = path.replace(':', ".");
1636 result
1637 }
1638
1639 fn transform_interval(&self, interval: crate::expressions::Interval) -> Result<Expression> {
1641 use crate::expressions::{Interval, Literal};
1642
1643 fn expand_unit(abbr: &str) -> &'static str {
1645 match abbr.to_uppercase().as_str() {
1646 "D" => "DAY",
1647 "H" => "HOUR",
1648 "M" => "MINUTE",
1649 "MS" => "MILLISECOND",
1650 "NS" => "NANOSECOND",
1651 "Q" => "QUARTER",
1652 "S" => "SECOND",
1653 "US" => "MICROSECOND",
1654 "W" => "WEEK",
1655 "Y" => "YEAR",
1656 "WEEK" | "WEEKS" => "WEEK",
1658 "DAY" | "DAYS" => "DAY",
1659 "HOUR" | "HOURS" => "HOUR",
1660 "MINUTE" | "MINUTES" => "MINUTE",
1661 "SECOND" | "SECONDS" => "SECOND",
1662 "MONTH" | "MONTHS" => "MONTH",
1663 "YEAR" | "YEARS" => "YEAR",
1664 "QUARTER" | "QUARTERS" => "QUARTER",
1665 "MILLISECOND" | "MILLISECONDS" => "MILLISECOND",
1666 "MICROSECOND" | "MICROSECONDS" => "MICROSECOND",
1667 "NANOSECOND" | "NANOSECONDS" => "NANOSECOND",
1668 _ => "", }
1670 }
1671
1672 fn parse_interval_string(s: &str) -> Option<(&str, &str)> {
1674 let s = s.trim();
1675
1676 let mut num_end = 0;
1679 let mut chars = s.chars().peekable();
1680
1681 if chars.peek() == Some(&'-') {
1683 chars.next();
1684 num_end += 1;
1685 }
1686
1687 while let Some(&c) = chars.peek() {
1689 if c.is_ascii_digit() {
1690 chars.next();
1691 num_end += 1;
1692 } else {
1693 break;
1694 }
1695 }
1696
1697 if chars.peek() == Some(&'.') {
1699 chars.next();
1700 num_end += 1;
1701 while let Some(&c) = chars.peek() {
1702 if c.is_ascii_digit() {
1703 chars.next();
1704 num_end += 1;
1705 } else {
1706 break;
1707 }
1708 }
1709 }
1710
1711 if num_end == 0 || (num_end == 1 && s.starts_with('-')) {
1712 return None; }
1714
1715 let value = &s[..num_end];
1716 let rest = s[num_end..].trim();
1717
1718 if rest.is_empty() || !rest.chars().all(|c| c.is_ascii_alphabetic()) {
1720 return None;
1721 }
1722
1723 Some((value, rest))
1724 }
1725
1726 if let Some(Expression::Literal(ref lit)) = interval.this {
1728 if let Literal::String(ref s) = lit.as_ref() {
1729 if let Some((value, unit)) = parse_interval_string(s) {
1730 let expanded = expand_unit(unit);
1731 if !expanded.is_empty() {
1732 let new_value = format!("{} {}", value, expanded);
1734
1735 return Ok(Expression::Interval(Box::new(Interval {
1736 this: Some(Expression::Literal(Box::new(Literal::String(new_value)))),
1737 unit: None, })));
1739 }
1740 }
1741 }
1742 }
1743
1744 Ok(Expression::Interval(Box::new(interval)))
1746 }
1747
1748 fn transform_function(&self, f: Function) -> Result<Expression> {
1749 let transformed_args: Vec<Expression> = f
1751 .args
1752 .into_iter()
1753 .map(|arg| self.transform_expr(arg))
1754 .collect::<Result<Vec<_>>>()?;
1755
1756 let f = Function {
1757 name: f.name,
1758 args: transformed_args,
1759 distinct: f.distinct,
1760 trailing_comments: f.trailing_comments,
1761 use_bracket_syntax: f.use_bracket_syntax,
1762 no_parens: f.no_parens,
1763 quoted: f.quoted,
1764 span: None,
1765 inferred_type: None,
1766 };
1767
1768 let name_upper = f.name.to_uppercase();
1769 match name_upper.as_str() {
1770 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1772 original_name: None,
1773 expressions: f.args,
1774 inferred_type: None,
1775 }))),
1776
1777 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1779 original_name: None,
1780 expressions: f.args,
1781 inferred_type: None,
1782 }))),
1783
1784 "NVL2" => Ok(Expression::Function(Box::new(f))),
1786
1787 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1789 Function::new("LISTAGG".to_string(), f.args),
1790 ))),
1791
1792 "STRING_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1794 Function::new("LISTAGG".to_string(), f.args),
1795 ))),
1796
1797 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1799 "SUBSTRING".to_string(),
1800 f.args,
1801 )))),
1802
1803 "UNNEST" => Ok(Expression::Function(Box::new(Function::new(
1805 "FLATTEN".to_string(),
1806 f.args,
1807 )))),
1808
1809 "EXPLODE" => Ok(Expression::Function(Box::new(Function::new(
1811 "FLATTEN".to_string(),
1812 f.args,
1813 )))),
1814
1815 "CURRENT_DATE" => Ok(Expression::CurrentDate(crate::expressions::CurrentDate)),
1817
1818 "NOW" => Ok(Expression::Function(Box::new(Function {
1820 name: "CURRENT_TIMESTAMP".to_string(),
1821 args: f.args,
1822 distinct: false,
1823 trailing_comments: Vec::new(),
1824 use_bracket_syntax: false,
1825 no_parens: f.no_parens,
1826 quoted: false,
1827 span: None,
1828 inferred_type: None,
1829 }))),
1830
1831 "GETDATE" => Ok(Expression::Function(Box::new(Function {
1833 name: "CURRENT_TIMESTAMP".to_string(),
1834 args: f.args,
1835 distinct: false,
1836 trailing_comments: Vec::new(),
1837 use_bracket_syntax: false,
1838 no_parens: f.no_parens,
1839 quoted: false,
1840 span: None,
1841 inferred_type: None,
1842 }))),
1843
1844 "CURRENT_TIMESTAMP" if f.args.is_empty() => {
1848 Ok(Expression::Function(Box::new(Function {
1849 name: "CURRENT_TIMESTAMP".to_string(),
1850 args: Vec::new(),
1851 distinct: false,
1852 trailing_comments: Vec::new(),
1853 use_bracket_syntax: false,
1854 no_parens: false, quoted: false,
1856 span: None,
1857 inferred_type: None,
1858 })))
1859 }
1860
1861 "TO_DATE" => {
1865 if f.args.len() == 1 {
1866 if let Expression::Literal(lit) = &f.args[0] {
1867 if let crate::expressions::Literal::String(s) = lit.as_ref() {
1868 if s.contains('-') && s.len() >= 8 && s.len() <= 12 {
1870 return Ok(Expression::Cast(Box::new(Cast {
1871 this: f.args.into_iter().next().unwrap(),
1872 to: crate::expressions::DataType::Date,
1873 double_colon_syntax: false,
1874 trailing_comments: Vec::new(),
1875 format: None,
1876 default: None,
1877 inferred_type: None,
1878 })));
1879 }
1880 }
1881 }
1882 }
1883 let mut args = f.args;
1885 if args.len() >= 2 {
1886 args[1] = Self::normalize_format_arg(args[1].clone());
1887 }
1888 Ok(Expression::Function(Box::new(Function::new(
1889 "TO_DATE".to_string(),
1890 args,
1891 ))))
1892 }
1893
1894 "TO_TIME" => {
1896 if f.args.len() == 1 {
1897 if let Expression::Literal(lit) = &f.args[0] {
1898 if let crate::expressions::Literal::String(_) = lit.as_ref() {
1899 return Ok(Expression::Cast(Box::new(Cast {
1900 this: f.args.into_iter().next().unwrap(),
1901 to: crate::expressions::DataType::Time {
1902 precision: None,
1903 timezone: false,
1904 },
1905 double_colon_syntax: false,
1906 trailing_comments: Vec::new(),
1907 format: None,
1908 default: None,
1909 inferred_type: None,
1910 })));
1911 }
1912 }
1913 }
1914 let mut args = f.args;
1916 if args.len() >= 2 {
1917 args[1] = Self::normalize_format_arg(args[1].clone());
1918 }
1919 Ok(Expression::Function(Box::new(Function::new(
1920 "TO_TIME".to_string(),
1921 args,
1922 ))))
1923 }
1924
1925 "TO_TIMESTAMP" => {
1932 let args = f.args;
1933 if args.len() == 1 {
1934 let arg = &args[0];
1935 match arg {
1936 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(s) if Self::looks_like_datetime(s)) =>
1937 {
1938 let Literal::String(_) = lit.as_ref() else {
1939 unreachable!()
1940 };
1941 return Ok(Expression::Cast(Box::new(Cast {
1943 this: args.into_iter().next().unwrap(),
1944 to: DataType::Timestamp {
1945 precision: None,
1946 timezone: false,
1947 },
1948 double_colon_syntax: false,
1949 trailing_comments: vec![],
1950 format: None,
1951 default: None,
1952 inferred_type: None,
1953 })));
1954 }
1955 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(s) if Self::looks_like_epoch(s)) =>
1956 {
1957 let Literal::String(_) = lit.as_ref() else {
1958 unreachable!()
1959 };
1960 return Ok(Expression::UnixToTime(Box::new(
1962 crate::expressions::UnixToTime {
1963 this: Box::new(args.into_iter().next().unwrap()),
1964 scale: None,
1965 zone: None,
1966 hours: None,
1967 minutes: None,
1968 format: None,
1969 target_type: None,
1970 },
1971 )));
1972 }
1973 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1974 return Ok(Expression::UnixToTime(Box::new(
1976 crate::expressions::UnixToTime {
1977 this: Box::new(args.into_iter().next().unwrap()),
1978 scale: None,
1979 zone: None,
1980 hours: None,
1981 minutes: None,
1982 format: None,
1983 target_type: None,
1984 },
1985 )));
1986 }
1987 Expression::Neg(_) => {
1988 return Ok(Expression::UnixToTime(Box::new(
1990 crate::expressions::UnixToTime {
1991 this: Box::new(args.into_iter().next().unwrap()),
1992 scale: None,
1993 zone: None,
1994 hours: None,
1995 minutes: None,
1996 format: None,
1997 target_type: None,
1998 },
1999 )));
2000 }
2001 _ => {
2002 return Ok(Expression::Function(Box::new(Function::new(
2004 "TO_TIMESTAMP".to_string(),
2005 args,
2006 ))));
2007 }
2008 }
2009 } else if args.len() == 2 {
2010 let second_arg = &args[1];
2011 let is_int_scale = match second_arg {
2013 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
2014 let Literal::Number(n) = lit.as_ref() else {
2015 unreachable!()
2016 };
2017 n.parse::<i64>().is_ok()
2018 }
2019 _ => false,
2020 };
2021
2022 if is_int_scale {
2023 let mut args_iter = args.into_iter();
2025 let value = args_iter.next().unwrap();
2026 let scale_expr = args_iter.next().unwrap();
2027 let scale = if let Expression::Literal(lit) = &scale_expr {
2028 if let Literal::Number(n) = lit.as_ref() {
2029 n.parse::<i64>().ok()
2030 } else {
2031 None
2032 }
2033 } else {
2034 None
2035 };
2036 return Ok(Expression::UnixToTime(Box::new(
2037 crate::expressions::UnixToTime {
2038 this: Box::new(value),
2039 scale,
2040 zone: None,
2041 hours: None,
2042 minutes: None,
2043 format: None,
2044 target_type: None,
2045 },
2046 )));
2047 } else {
2048 let mut args_iter = args.into_iter();
2050 let value = args_iter.next().unwrap();
2051 let format_expr = args_iter.next().unwrap();
2052 let format_str = match &format_expr {
2053 Expression::Literal(lit)
2054 if matches!(lit.as_ref(), Literal::String(_)) =>
2055 {
2056 let Literal::String(s) = lit.as_ref() else {
2057 unreachable!()
2058 };
2059 s.clone()
2060 }
2061 _ => {
2062 return Ok(Expression::Function(Box::new(Function::new(
2064 "TO_TIMESTAMP".to_string(),
2065 vec![value, format_expr],
2066 ))));
2067 }
2068 };
2069 let normalized_format = Self::normalize_snowflake_format(&format_str);
2071 return Ok(Expression::StrToTime(Box::new(
2072 crate::expressions::StrToTime {
2073 this: Box::new(value),
2074 format: normalized_format,
2075 zone: None,
2076 safe: None,
2077 target_type: None,
2078 },
2079 )));
2080 }
2081 }
2082 Ok(Expression::Function(Box::new(Function::new(
2084 "TO_TIMESTAMP".to_string(),
2085 args,
2086 ))))
2087 }
2088
2089 "TO_CHAR" => Ok(Expression::Function(Box::new(f))),
2091
2092 "ROUND"
2095 if f.args
2096 .iter()
2097 .any(|a| matches!(a, Expression::NamedArgument(_))) =>
2098 {
2099 let mut expr_val = None;
2100 let mut scale_val = None;
2101 let mut rounding_mode_val = None;
2102 for arg in &f.args {
2103 if let Expression::NamedArgument(na) = arg {
2104 match na.name.name.to_uppercase().as_str() {
2105 "EXPR" => expr_val = Some(na.value.clone()),
2106 "SCALE" => scale_val = Some(na.value.clone()),
2107 "ROUNDING_MODE" => rounding_mode_val = Some(na.value.clone()),
2108 _ => {}
2109 }
2110 }
2111 }
2112 if let Some(expr) = expr_val {
2113 let mut args = vec![expr];
2114 if let Some(scale) = scale_val {
2115 args.push(scale);
2116 }
2117 if let Some(mode) = rounding_mode_val {
2118 args.push(mode);
2119 }
2120 Ok(Expression::Function(Box::new(Function::new(
2121 "ROUND".to_string(),
2122 args,
2123 ))))
2124 } else {
2125 Ok(Expression::Function(Box::new(f)))
2126 }
2127 }
2128
2129 "DATE_FORMAT" => {
2132 let mut args = f.args;
2133 if !args.is_empty() {
2135 if matches!(&args[0], Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)))
2136 {
2137 args[0] = Expression::Cast(Box::new(crate::expressions::Cast {
2138 this: args[0].clone(),
2139 to: DataType::Timestamp {
2140 precision: None,
2141 timezone: false,
2142 },
2143 trailing_comments: Vec::new(),
2144 double_colon_syntax: false,
2145 format: None,
2146 default: None,
2147 inferred_type: None,
2148 }));
2149 }
2150 }
2151 if args.len() >= 2 {
2153 if let Expression::Literal(ref lit) = args[1] {
2154 if let Literal::String(ref fmt) = lit.as_ref() {
2155 let sf_fmt = strftime_to_snowflake_format(fmt);
2156 args[1] = Expression::Literal(Box::new(Literal::String(sf_fmt)));
2157 }
2158 }
2159 }
2160 Ok(Expression::Function(Box::new(Function::new(
2161 "TO_CHAR".to_string(),
2162 args,
2163 ))))
2164 }
2165
2166 "ARRAY" => Ok(Expression::Function(Box::new(Function::new(
2168 "ARRAY_CONSTRUCT".to_string(),
2169 f.args,
2170 )))),
2171
2172 "STRUCT" => {
2175 let mut oc_args = Vec::new();
2176 for arg in f.args {
2177 match arg {
2178 Expression::Alias(a) => {
2179 oc_args.push(Expression::Literal(Box::new(
2181 crate::expressions::Literal::String(a.alias.name.clone()),
2182 )));
2183 oc_args.push(a.this);
2184 }
2185 other => {
2186 oc_args.push(other);
2188 }
2189 }
2190 }
2191 Ok(Expression::Function(Box::new(Function::new(
2192 "OBJECT_CONSTRUCT".to_string(),
2193 oc_args,
2194 ))))
2195 }
2196
2197 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
2199 "GET_PATH".to_string(),
2200 f.args,
2201 )))),
2202
2203 "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(Function::new(
2205 "JSON_EXTRACT_PATH_TEXT".to_string(),
2206 f.args,
2207 )))),
2208
2209 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc::new(
2211 f.args.into_iter().next().unwrap(),
2212 )))),
2213
2214 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
2216 this: f.args.into_iter().next().unwrap(),
2217 decimals: None,
2218 to: None,
2219 }))),
2220
2221 "CHARINDEX" => Ok(Expression::Function(Box::new(f))),
2223
2224 "SPLIT" => Ok(Expression::Function(Box::new(f))),
2226
2227 "ARRAY_AGG" => Ok(Expression::Function(Box::new(f))),
2229
2230 "JSON_PARSE" | "PARSE_JSON" => Ok(Expression::Function(Box::new(Function::new(
2232 "PARSE_JSON".to_string(),
2233 f.args,
2234 )))),
2235
2236 "RAND" => {
2238 let seed = f.args.first().cloned().map(Box::new);
2239 Ok(Expression::Rand(Box::new(crate::expressions::Rand {
2240 seed,
2241 lower: None,
2242 upper: None,
2243 })))
2244 }
2245
2246 "SHA" => Ok(Expression::Function(Box::new(Function::new(
2248 "SHA1".to_string(),
2249 f.args,
2250 )))),
2251
2252 "APPROX_DISTINCT" => Ok(Expression::Function(Box::new(Function::new(
2254 "APPROX_COUNT_DISTINCT".to_string(),
2255 f.args,
2256 )))),
2257
2258 "GEN_RANDOM_UUID" | "UUID" => {
2260 Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
2261 this: None,
2262 name: None,
2263 is_string: None,
2264 })))
2265 }
2266
2267 "NEWID" => Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
2269 this: None,
2270 name: None,
2271 is_string: None,
2272 }))),
2273
2274 "UUID_STRING" => {
2276 if f.args.is_empty() {
2277 Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
2278 this: None,
2279 name: None,
2280 is_string: None,
2281 })))
2282 } else {
2283 Ok(Expression::Function(Box::new(Function::new(
2284 "UUID_STRING".to_string(),
2285 f.args,
2286 ))))
2287 }
2288 }
2289
2290 "IF" if f.args.len() >= 2 => {
2292 let mut args = f.args;
2293 let condition = args.remove(0);
2294 let true_val = args.remove(0);
2295 let false_val = if !args.is_empty() {
2296 Some(args.remove(0))
2297 } else {
2298 None
2299 };
2300 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
2301 condition,
2302 true_value: true_val,
2303 false_value: Some(
2304 false_val.unwrap_or(Expression::Null(crate::expressions::Null)),
2305 ),
2306 original_name: Some("IFF".to_string()),
2307 inferred_type: None,
2308 })))
2309 }
2310
2311 "SQUARE" if f.args.len() == 1 => {
2313 let x = f.args.into_iter().next().unwrap();
2314 Ok(Expression::Power(Box::new(
2315 crate::expressions::BinaryFunc {
2316 original_name: None,
2317 this: x,
2318 expression: Expression::number(2),
2319 inferred_type: None,
2320 },
2321 )))
2322 }
2323
2324 "POW" if f.args.len() == 2 => {
2326 let mut args = f.args.into_iter();
2327 let x = args.next().unwrap();
2328 let y = args.next().unwrap();
2329 Ok(Expression::Power(Box::new(
2330 crate::expressions::BinaryFunc {
2331 original_name: None,
2332 this: x,
2333 expression: y,
2334 inferred_type: None,
2335 },
2336 )))
2337 }
2338
2339 "MOD" if f.args.len() == 2 => {
2341 let mut args = f.args.into_iter();
2342 let x = args.next().unwrap();
2343 let y = args.next().unwrap();
2344 Ok(Expression::Mod(Box::new(crate::expressions::BinaryOp {
2345 left: x,
2346 right: y,
2347 left_comments: Vec::new(),
2348 operator_comments: Vec::new(),
2349 trailing_comments: Vec::new(),
2350 inferred_type: None,
2351 })))
2352 }
2353
2354 "APPROXIMATE_JACCARD_INDEX" => Ok(Expression::Function(Box::new(Function::new(
2356 "APPROXIMATE_SIMILARITY".to_string(),
2357 f.args,
2358 )))),
2359
2360 "ARRAY_CONSTRUCT" => Ok(Expression::ArrayFunc(Box::new(
2362 crate::expressions::ArrayConstructor {
2363 expressions: f.args,
2364 bracket_notation: true,
2365 use_list_keyword: false,
2366 },
2367 ))),
2368
2369 "APPROX_TOP_K" if f.args.len() == 1 => {
2371 let mut args = f.args;
2372 args.push(Expression::number(1));
2373 Ok(Expression::Function(Box::new(Function::new(
2374 "APPROX_TOP_K".to_string(),
2375 args,
2376 ))))
2377 }
2378
2379 "TO_DECIMAL" | "TO_NUMERIC" => Ok(Expression::Function(Box::new(Function::new(
2381 "TO_NUMBER".to_string(),
2382 f.args,
2383 )))),
2384
2385 "TRY_TO_DECIMAL" | "TRY_TO_NUMERIC" => Ok(Expression::Function(Box::new(
2387 Function::new("TRY_TO_NUMBER".to_string(), f.args),
2388 ))),
2389
2390 "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2392 "STDDEV".to_string(),
2393 f.args,
2394 )))),
2395
2396 "STRTOK" if f.args.len() >= 1 => {
2398 let mut args = f.args;
2399 if args.len() == 1 {
2401 args.push(Expression::string(" ".to_string()));
2402 }
2403 if args.len() == 2 {
2405 args.push(Expression::number(1));
2406 }
2407 Ok(Expression::Function(Box::new(Function::new(
2408 "STRTOK".to_string(),
2409 args,
2410 ))))
2411 }
2412
2413 "STRTOK_TO_ARRAY" if f.args.len() == 1 => {
2414 let mut args = f.args;
2415 args.push(Expression::string(" ".to_string()));
2416 Ok(Expression::Function(Box::new(Function::new(
2417 "STRTOK_TO_ARRAY".to_string(),
2418 args,
2419 ))))
2420 }
2421
2422 "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
2424 "WEEK".to_string(),
2425 f.args,
2426 )))),
2427
2428 "LIKE" if f.args.len() >= 2 => {
2430 let mut args = f.args.into_iter();
2431 let left = args.next().unwrap();
2432 let right = args.next().unwrap();
2433 let escape = args.next();
2434 Ok(Expression::Like(Box::new(crate::expressions::LikeOp {
2435 left,
2436 right,
2437 escape,
2438 quantifier: None,
2439 inferred_type: None,
2440 })))
2441 }
2442
2443 "ILIKE" if f.args.len() >= 2 => {
2445 let mut args = f.args.into_iter();
2446 let left = args.next().unwrap();
2447 let right = args.next().unwrap();
2448 let escape = args.next();
2449 Ok(Expression::ILike(Box::new(crate::expressions::LikeOp {
2450 left,
2451 right,
2452 escape,
2453 quantifier: None,
2454 inferred_type: None,
2455 })))
2456 }
2457
2458 "RLIKE" if f.args.len() >= 2 => {
2460 let mut args = f.args.into_iter();
2461 let left = args.next().unwrap();
2462 let pattern = args.next().unwrap();
2463 let flags = args.next();
2464 Ok(Expression::RegexpLike(Box::new(
2465 crate::expressions::RegexpFunc {
2466 this: left,
2467 pattern,
2468 flags,
2469 },
2470 )))
2471 }
2472
2473 "IFF" if f.args.len() >= 2 => {
2475 let mut args = f.args;
2476 let condition = args.remove(0);
2477 let true_value = args.remove(0);
2478 let false_value = if !args.is_empty() {
2479 Some(args.remove(0))
2480 } else {
2481 None
2482 };
2483 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
2484 condition,
2485 true_value,
2486 false_value,
2487 original_name: Some("IFF".to_string()),
2488 inferred_type: None,
2489 })))
2490 }
2491
2492 "TIMESTAMP_NTZ_FROM_PARTS" | "TIMESTAMPFROMPARTS" | "TIMESTAMPNTZFROMPARTS" => {
2494 Ok(Expression::Function(Box::new(Function::new(
2495 "TIMESTAMP_FROM_PARTS".to_string(),
2496 f.args,
2497 ))))
2498 }
2499
2500 "TIMESTAMPLTZFROMPARTS" => Ok(Expression::Function(Box::new(Function::new(
2502 "TIMESTAMP_LTZ_FROM_PARTS".to_string(),
2503 f.args,
2504 )))),
2505
2506 "TIMESTAMPTZFROMPARTS" => Ok(Expression::Function(Box::new(Function::new(
2508 "TIMESTAMP_TZ_FROM_PARTS".to_string(),
2509 f.args,
2510 )))),
2511
2512 "DATEADD" if f.args.len() >= 1 => {
2514 let mut args = f.args;
2515 args[0] = self.transform_date_part_arg(args[0].clone());
2516 Ok(Expression::Function(Box::new(Function::new(
2517 "DATEADD".to_string(),
2518 args,
2519 ))))
2520 }
2521
2522 "DATEDIFF" if f.args.len() >= 1 => {
2525 let mut args = f.args;
2526 args[0] = self.transform_date_part_arg(args[0].clone());
2527 for i in 1..args.len() {
2530 if let Expression::Function(ref func) = args[i] {
2531 if func.name == "_POLYGLOT_TO_DATE" {
2532 let inner_args = func.args.clone();
2533 args[i] = Expression::Function(Box::new(Function::new(
2534 "TO_DATE".to_string(),
2535 inner_args,
2536 )));
2537 }
2538 }
2539 }
2540 Ok(Expression::Function(Box::new(Function::new(
2541 "DATEDIFF".to_string(),
2542 args,
2543 ))))
2544 }
2545
2546 "TIMEDIFF" => Ok(Expression::Function(Box::new(Function::new(
2548 "DATEDIFF".to_string(),
2549 f.args,
2550 )))),
2551
2552 "TIMESTAMPDIFF" => Ok(Expression::Function(Box::new(Function::new(
2554 "DATEDIFF".to_string(),
2555 f.args,
2556 )))),
2557
2558 "TIMESTAMPADD" => Ok(Expression::Function(Box::new(Function::new(
2560 "DATEADD".to_string(),
2561 f.args,
2562 )))),
2563
2564 "TIMEADD" => Ok(Expression::Function(Box::new(f))),
2566
2567 "DATEFROMPARTS" => Ok(Expression::Function(Box::new(Function::new(
2569 "DATE_FROM_PARTS".to_string(),
2570 f.args,
2571 )))),
2572
2573 "TIMEFROMPARTS" => Ok(Expression::Function(Box::new(Function::new(
2575 "TIME_FROM_PARTS".to_string(),
2576 f.args,
2577 )))),
2578
2579 "DAYOFWEEK" => Ok(Expression::Function(Box::new(f))),
2581
2582 "DAYOFMONTH" => Ok(Expression::Function(Box::new(f))),
2584
2585 "DAYOFYEAR" => Ok(Expression::Function(Box::new(f))),
2587
2588 "MONTHNAME" if f.args.len() == 1 => {
2591 let arg = f.args.into_iter().next().unwrap();
2592 Ok(Expression::Monthname(Box::new(
2593 crate::expressions::Monthname {
2594 this: Box::new(arg),
2595 abbreviated: Some(Box::new(Expression::Literal(Box::new(
2596 Literal::String("true".to_string()),
2597 )))),
2598 },
2599 )))
2600 }
2601
2602 "DAYNAME" if f.args.len() == 1 => {
2605 let arg = f.args.into_iter().next().unwrap();
2606 Ok(Expression::Dayname(Box::new(crate::expressions::Dayname {
2607 this: Box::new(arg),
2608 abbreviated: Some(Box::new(Expression::Literal(Box::new(Literal::String(
2609 "true".to_string(),
2610 ))))),
2611 })))
2612 }
2613
2614 "BOOLAND_AGG" | "BOOL_AND" | "LOGICAL_AND" if !f.args.is_empty() => {
2616 let arg = f.args.into_iter().next().unwrap();
2617 Ok(Expression::LogicalAnd(Box::new(AggFunc {
2618 this: arg,
2619 distinct: false,
2620 filter: None,
2621 order_by: Vec::new(),
2622 name: Some("BOOLAND_AGG".to_string()),
2623 ignore_nulls: None,
2624 having_max: None,
2625 limit: None,
2626 inferred_type: None,
2627 })))
2628 }
2629
2630 "BOOLOR_AGG" | "BOOL_OR" | "LOGICAL_OR" if !f.args.is_empty() => {
2632 let arg = f.args.into_iter().next().unwrap();
2633 Ok(Expression::LogicalOr(Box::new(AggFunc {
2634 this: arg,
2635 distinct: false,
2636 filter: None,
2637 order_by: Vec::new(),
2638 name: Some("BOOLOR_AGG".to_string()),
2639 ignore_nulls: None,
2640 having_max: None,
2641 limit: None,
2642 inferred_type: None,
2643 })))
2644 }
2645
2646 "SKEW" | "SKEWNESS" if !f.args.is_empty() => {
2648 let arg = f.args.into_iter().next().unwrap();
2649 Ok(Expression::Skewness(Box::new(AggFunc {
2650 this: arg,
2651 distinct: false,
2652 filter: None,
2653 order_by: Vec::new(),
2654 name: Some("SKEW".to_string()),
2655 ignore_nulls: None,
2656 having_max: None,
2657 limit: None,
2658 inferred_type: None,
2659 })))
2660 }
2661
2662 "VAR_SAMP" => Ok(Expression::Function(Box::new(Function::new(
2664 "VARIANCE".to_string(),
2665 f.args,
2666 )))),
2667
2668 "VAR_POP" => Ok(Expression::Function(Box::new(Function::new(
2670 "VARIANCE_POP".to_string(),
2671 f.args,
2672 )))),
2673
2674 "DATE" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2676 "TO_DATE".to_string(),
2677 f.args,
2678 )))),
2679 "DATE" if f.args.len() >= 2 => {
2683 let mut args = f.args;
2684 args[1] = Self::normalize_format_arg(args[1].clone());
2685 Ok(Expression::Function(Box::new(Function::new(
2686 "TO_DATE".to_string(),
2687 args,
2688 ))))
2689 }
2690 "_POLYGLOT_DATE" if f.args.len() >= 2 => {
2693 let mut args = f.args;
2694 args[1] = Self::normalize_format_arg(args[1].clone());
2695 Ok(Expression::Function(Box::new(Function::new(
2696 "DATE".to_string(),
2697 args,
2698 ))))
2699 }
2700
2701 "DESCRIBE" => Ok(Expression::Function(Box::new(f))),
2703
2704 "MD5_HEX" => Ok(Expression::Function(Box::new(Function::new(
2706 "MD5".to_string(),
2707 f.args,
2708 )))),
2709
2710 "SHA1_HEX" => Ok(Expression::Function(Box::new(Function::new(
2712 "SHA1".to_string(),
2713 f.args,
2714 )))),
2715
2716 "SHA2_HEX" => Ok(Expression::Function(Box::new(Function::new(
2718 "SHA2".to_string(),
2719 f.args,
2720 )))),
2721
2722 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(Function::new(
2724 "EDITDISTANCE".to_string(),
2725 f.args,
2726 )))),
2727
2728 "BIT_NOT" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2730 "BITNOT".to_string(),
2731 f.args,
2732 )))),
2733
2734 "BIT_AND" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(Function::new(
2736 "BITAND".to_string(),
2737 f.args,
2738 )))),
2739
2740 "BIT_OR" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(Function::new(
2742 "BITOR".to_string(),
2743 f.args,
2744 )))),
2745
2746 "BIT_XOR" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(Function::new(
2748 "BITXOR".to_string(),
2749 f.args,
2750 )))),
2751
2752 "BIT_SHIFTLEFT" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(
2754 Function::new("BITSHIFTLEFT".to_string(), f.args),
2755 ))),
2756
2757 "BIT_SHIFTRIGHT" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(
2759 Function::new("BITSHIFTRIGHT".to_string(), f.args),
2760 ))),
2761
2762 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function {
2764 name: "CURRENT_TIMESTAMP".to_string(),
2765 args: f.args,
2766 distinct: false,
2767 trailing_comments: Vec::new(),
2768 use_bracket_syntax: false,
2769 no_parens: f.no_parens,
2770 quoted: false,
2771 span: None,
2772 inferred_type: None,
2773 }))),
2774
2775 "LOCALTIMESTAMP" => Ok(Expression::Function(Box::new(Function {
2777 name: "CURRENT_TIMESTAMP".to_string(),
2778 args: f.args,
2779 distinct: false,
2780 trailing_comments: Vec::new(),
2781 use_bracket_syntax: false,
2782 no_parens: f.no_parens,
2783 quoted: false,
2784 span: None,
2785 inferred_type: None,
2786 }))),
2787
2788 "SPACE" if f.args.len() == 1 => {
2790 let arg = f.args.into_iter().next().unwrap();
2791 Ok(Expression::Function(Box::new(Function::new(
2792 "REPEAT".to_string(),
2793 vec![
2794 Expression::Literal(Box::new(Literal::String(" ".to_string()))),
2795 arg,
2796 ],
2797 ))))
2798 }
2799
2800 "CEILING" => Ok(Expression::Function(Box::new(Function::new(
2802 "CEIL".to_string(),
2803 f.args,
2804 )))),
2805
2806 "LOG" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
2808 "LN".to_string(),
2809 f.args,
2810 )))),
2811
2812 "REGEXP_SUBSTR_ALL" => Ok(Expression::Function(Box::new(f))),
2814
2815 "GET_PATH" if f.args.len() >= 2 => {
2819 let mut args = f.args;
2820 if let Expression::Literal(lit) = &args[1] {
2822 if let crate::expressions::Literal::String(path) = lit.as_ref() {
2823 let transformed = Self::transform_json_path(path);
2824 args[1] = Expression::Literal(Box::new(
2825 crate::expressions::Literal::String(transformed),
2826 ));
2827 }
2828 }
2829 Ok(Expression::Function(Box::new(Function::new(
2830 "GET_PATH".to_string(),
2831 args,
2832 ))))
2833 }
2834 "GET_PATH" => Ok(Expression::Function(Box::new(f))),
2835
2836 "FLATTEN" => Ok(Expression::Function(Box::new(f))),
2838
2839 "DATE_TRUNC" if f.args.len() >= 1 => {
2842 let mut args = f.args;
2843 let unit_name = match &args[0] {
2845 Expression::Identifier(id) => Some(id.name.as_str()),
2846 Expression::Var(v) => Some(v.this.as_str()),
2847 Expression::Column(col) if col.table.is_none() => Some(col.name.name.as_str()),
2848 _ => None,
2849 };
2850 if let Some(name) = unit_name {
2851 let canonical = Self::map_date_part(name).unwrap_or(name);
2852 args[0] = Expression::Literal(Box::new(crate::expressions::Literal::String(
2853 canonical.to_uppercase(),
2854 )));
2855 }
2856 Ok(Expression::Function(Box::new(Function::new(
2857 "DATE_TRUNC".to_string(),
2858 args,
2859 ))))
2860 }
2861
2862 "DATE_PART" if f.args.len() >= 1 => {
2868 let mut args = f.args;
2869 let from_typed_literal = args.len() >= 2
2870 && matches!(
2871 &args[1],
2872 Expression::Literal(lit) if matches!(lit.as_ref(),
2873 crate::expressions::Literal::Timestamp(_)
2874 | crate::expressions::Literal::Date(_)
2875 | crate::expressions::Literal::Time(_)
2876 | crate::expressions::Literal::Datetime(_)
2877 )
2878 );
2879 if from_typed_literal {
2880 args[0] = self.transform_date_part_arg(args[0].clone());
2881 } else {
2882 args[0] = self.transform_date_part_arg_identifiers_only(args[0].clone());
2885 }
2886 Ok(Expression::Function(Box::new(Function::new(
2887 "DATE_PART".to_string(),
2888 args,
2889 ))))
2890 }
2891
2892 "OBJECT_CONSTRUCT" => Ok(Expression::Function(Box::new(f))),
2894
2895 "OBJECT_CONSTRUCT_KEEP_NULL" => Ok(Expression::Function(Box::new(f))),
2897
2898 "DESC" => Ok(Expression::Function(Box::new(Function::new(
2900 "DESCRIBE".to_string(),
2901 f.args,
2902 )))),
2903
2904 "RLIKE" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(Function::new(
2906 "REGEXP_LIKE".to_string(),
2907 f.args,
2908 )))),
2909
2910 "TRANSFORM" => {
2915 let transformed_args: Vec<Expression> = f
2916 .args
2917 .into_iter()
2918 .map(|arg| {
2919 if let Expression::Lambda(lambda) = arg {
2920 self.transform_typed_lambda(*lambda)
2921 } else {
2922 arg
2923 }
2924 })
2925 .collect();
2926 Ok(Expression::Function(Box::new(Function::new(
2927 "TRANSFORM".to_string(),
2928 transformed_args,
2929 ))))
2930 }
2931
2932 "SEARCH" if f.args.len() >= 2 => {
2934 let mut args = f.args.into_iter();
2935 let this = Box::new(args.next().unwrap());
2936 let expression = Box::new(args.next().unwrap());
2937
2938 let mut analyzer: Option<Box<Expression>> = None;
2939 let mut search_mode: Option<Box<Expression>> = None;
2940
2941 for arg in args {
2943 if let Expression::NamedArgument(na) = &arg {
2944 let name_upper = na.name.name.to_uppercase();
2945 match name_upper.as_str() {
2946 "ANALYZER" => analyzer = Some(Box::new(arg)),
2947 "SEARCH_MODE" => search_mode = Some(Box::new(arg)),
2948 _ => {}
2949 }
2950 }
2951 }
2952
2953 Ok(Expression::Search(Box::new(crate::expressions::Search {
2954 this,
2955 expression,
2956 json_scope: None,
2957 analyzer,
2958 analyzer_options: None,
2959 search_mode,
2960 })))
2961 }
2962
2963 "CONVERT" if f.args.len() == 2 => {
2966 let value = f.args.get(0).cloned().unwrap();
2967 let type_arg = f.args.get(1).cloned().unwrap();
2968
2969 if let Expression::Column(col) = &type_arg {
2971 let type_name = col.name.name.to_uppercase();
2972 let data_type = match type_name.as_str() {
2973 "SQL_DOUBLE" => Some(DataType::Double {
2974 precision: None,
2975 scale: None,
2976 }),
2977 "SQL_VARCHAR" => Some(DataType::VarChar {
2978 length: None,
2979 parenthesized_length: false,
2980 }),
2981 "SQL_INTEGER" | "SQL_INT" => Some(DataType::Int {
2982 length: None,
2983 integer_spelling: false,
2984 }),
2985 "SQL_BIGINT" => Some(DataType::BigInt { length: None }),
2986 "SQL_SMALLINT" => Some(DataType::SmallInt { length: None }),
2987 "SQL_FLOAT" => Some(DataType::Float {
2988 precision: None,
2989 scale: None,
2990 real_spelling: false,
2991 }),
2992 "SQL_REAL" => Some(DataType::Float {
2993 precision: None,
2994 scale: None,
2995 real_spelling: true,
2996 }),
2997 "SQL_DECIMAL" => Some(DataType::Decimal {
2998 precision: None,
2999 scale: None,
3000 }),
3001 "SQL_DATE" => Some(DataType::Date),
3002 "SQL_TIME" => Some(DataType::Time {
3003 precision: None,
3004 timezone: false,
3005 }),
3006 "SQL_TIMESTAMP" => Some(DataType::Timestamp {
3007 precision: None,
3008 timezone: false,
3009 }),
3010 _ => None,
3011 };
3012
3013 if let Some(dt) = data_type {
3014 return Ok(Expression::Cast(Box::new(Cast {
3015 this: value,
3016 to: dt,
3017 double_colon_syntax: false,
3018 trailing_comments: vec![],
3019 format: None,
3020 default: None,
3021 inferred_type: None,
3022 })));
3023 }
3024 }
3025 Ok(Expression::Function(Box::new(f)))
3027 }
3028
3029 "TO_TIMESTAMP_TZ" => {
3032 if f.args.len() == 1 {
3033 if let Expression::Literal(lit) = &f.args[0] {
3034 if let crate::expressions::Literal::String(_) = lit.as_ref() {
3035 return Ok(Expression::Cast(Box::new(Cast {
3036 this: f.args.into_iter().next().unwrap(),
3037 to: DataType::Custom {
3038 name: "TIMESTAMPTZ".to_string(),
3039 },
3040 double_colon_syntax: false,
3041 trailing_comments: vec![],
3042 format: None,
3043 default: None,
3044 inferred_type: None,
3045 })));
3046 }
3047 }
3048 }
3049 Ok(Expression::Function(Box::new(f)))
3050 }
3051
3052 "TO_TIMESTAMP_NTZ" => {
3054 if f.args.len() == 1 {
3055 if let Expression::Literal(lit) = &f.args[0] {
3056 if let crate::expressions::Literal::String(_) = lit.as_ref() {
3057 return Ok(Expression::Cast(Box::new(Cast {
3058 this: f.args.into_iter().next().unwrap(),
3059 to: DataType::Custom {
3060 name: "TIMESTAMPNTZ".to_string(),
3061 },
3062 double_colon_syntax: false,
3063 trailing_comments: vec![],
3064 format: None,
3065 default: None,
3066 inferred_type: None,
3067 })));
3068 }
3069 }
3070 }
3071 Ok(Expression::Function(Box::new(f)))
3072 }
3073
3074 "TO_TIMESTAMP_LTZ" => {
3076 if f.args.len() == 1 {
3077 if let Expression::Literal(lit) = &f.args[0] {
3078 if let crate::expressions::Literal::String(_) = lit.as_ref() {
3079 return Ok(Expression::Cast(Box::new(Cast {
3080 this: f.args.into_iter().next().unwrap(),
3081 to: DataType::Custom {
3082 name: "TIMESTAMPLTZ".to_string(),
3083 },
3084 double_colon_syntax: false,
3085 trailing_comments: vec![],
3086 format: None,
3087 default: None,
3088 inferred_type: None,
3089 })));
3090 }
3091 }
3092 }
3093 Ok(Expression::Function(Box::new(f)))
3094 }
3095
3096 "UNIFORM" => Ok(Expression::Function(Box::new(f))),
3098
3099 "REPLACE" if f.args.len() == 2 => {
3101 let mut args = f.args;
3102 args.push(Expression::Literal(Box::new(
3103 crate::expressions::Literal::String(String::new()),
3104 )));
3105 Ok(Expression::Function(Box::new(Function::new(
3106 "REPLACE".to_string(),
3107 args,
3108 ))))
3109 }
3110
3111 "ARBITRARY" => Ok(Expression::Function(Box::new(Function::new(
3113 "ANY_VALUE".to_string(),
3114 f.args,
3115 )))),
3116
3117 "SAFE_DIVIDE" if f.args.len() == 2 => {
3119 let mut args = f.args;
3120 let x = args.remove(0);
3121 let y = args.remove(0);
3122 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
3123 condition: Expression::Neq(Box::new(BinaryOp {
3124 left: y.clone(),
3125 right: Expression::number(0),
3126 left_comments: Vec::new(),
3127 operator_comments: Vec::new(),
3128 trailing_comments: Vec::new(),
3129 inferred_type: None,
3130 })),
3131 true_value: Expression::Div(Box::new(BinaryOp {
3132 left: x,
3133 right: y,
3134 left_comments: Vec::new(),
3135 operator_comments: Vec::new(),
3136 trailing_comments: Vec::new(),
3137 inferred_type: None,
3138 })),
3139 false_value: Some(Expression::Null(crate::expressions::Null)),
3140 original_name: Some("IFF".to_string()),
3141 inferred_type: None,
3142 })))
3143 }
3144
3145 "TIMESTAMP" if f.args.len() == 1 => {
3147 let arg = f.args.into_iter().next().unwrap();
3148 Ok(Expression::Cast(Box::new(Cast {
3149 this: arg,
3150 to: DataType::Custom {
3151 name: "TIMESTAMPTZ".to_string(),
3152 },
3153 trailing_comments: Vec::new(),
3154 double_colon_syntax: false,
3155 format: None,
3156 default: None,
3157 inferred_type: None,
3158 })))
3159 }
3160
3161 "TIMESTAMP" if f.args.len() == 2 => {
3163 let mut args = f.args;
3164 let value = args.remove(0);
3165 let tz = args.remove(0);
3166 Ok(Expression::Function(Box::new(Function::new(
3167 "CONVERT_TIMEZONE".to_string(),
3168 vec![
3169 tz,
3170 Expression::Cast(Box::new(Cast {
3171 this: value,
3172 to: DataType::Timestamp {
3173 precision: None,
3174 timezone: false,
3175 },
3176 trailing_comments: Vec::new(),
3177 double_colon_syntax: false,
3178 format: None,
3179 default: None,
3180 inferred_type: None,
3181 })),
3182 ],
3183 ))))
3184 }
3185
3186 "TIME" if f.args.len() == 3 => Ok(Expression::Function(Box::new(Function::new(
3188 "TIME_FROM_PARTS".to_string(),
3189 f.args,
3190 )))),
3191
3192 "DIV0" if f.args.len() == 2 => {
3194 let mut args = f.args;
3195 let x = args.remove(0);
3196 let y = args.remove(0);
3197 let x_expr = Self::maybe_paren(x.clone());
3199 let y_expr = Self::maybe_paren(y.clone());
3200 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
3201 condition: Expression::And(Box::new(BinaryOp::new(
3202 Expression::Eq(Box::new(BinaryOp::new(
3203 y_expr.clone(),
3204 Expression::number(0),
3205 ))),
3206 Expression::Not(Box::new(crate::expressions::UnaryOp {
3207 this: Expression::IsNull(Box::new(crate::expressions::IsNull {
3208 this: x_expr.clone(),
3209 not: false,
3210 postfix_form: false,
3211 })),
3212 inferred_type: None,
3213 })),
3214 ))),
3215 true_value: Expression::number(0),
3216 false_value: Some(Expression::Div(Box::new(BinaryOp::new(x_expr, y_expr)))),
3217 original_name: Some("IFF".to_string()),
3218 inferred_type: None,
3219 })))
3220 }
3221
3222 "DIV0NULL" if f.args.len() == 2 => {
3224 let mut args = f.args;
3225 let x = args.remove(0);
3226 let y = args.remove(0);
3227 let x_expr = Self::maybe_paren(x.clone());
3228 let y_expr = Self::maybe_paren(y.clone());
3229 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
3230 condition: Expression::Or(Box::new(BinaryOp::new(
3231 Expression::Eq(Box::new(BinaryOp::new(
3232 y_expr.clone(),
3233 Expression::number(0),
3234 ))),
3235 Expression::IsNull(Box::new(crate::expressions::IsNull {
3236 this: y_expr.clone(),
3237 not: false,
3238 postfix_form: false,
3239 })),
3240 ))),
3241 true_value: Expression::number(0),
3242 false_value: Some(Expression::Div(Box::new(BinaryOp::new(x_expr, y_expr)))),
3243 original_name: Some("IFF".to_string()),
3244 inferred_type: None,
3245 })))
3246 }
3247
3248 "ZEROIFNULL" if f.args.len() == 1 => {
3250 let x = f.args.into_iter().next().unwrap();
3251 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
3252 condition: Expression::IsNull(Box::new(crate::expressions::IsNull {
3253 this: x.clone(),
3254 not: false,
3255 postfix_form: false,
3256 })),
3257 true_value: Expression::number(0),
3258 false_value: Some(x),
3259 original_name: Some("IFF".to_string()),
3260 inferred_type: None,
3261 })))
3262 }
3263
3264 "NULLIFZERO" if f.args.len() == 1 => {
3266 let x = f.args.into_iter().next().unwrap();
3267 Ok(Expression::IfFunc(Box::new(crate::expressions::IfFunc {
3268 condition: Expression::Eq(Box::new(BinaryOp::new(
3269 x.clone(),
3270 Expression::number(0),
3271 ))),
3272 true_value: Expression::Null(crate::expressions::Null),
3273 false_value: Some(x),
3274 original_name: Some("IFF".to_string()),
3275 inferred_type: None,
3276 })))
3277 }
3278
3279 "TRY_TO_TIME" => {
3281 if f.args.len() == 1 {
3282 if let Expression::Literal(lit) = &f.args[0] {
3283 if let crate::expressions::Literal::String(_) = lit.as_ref() {
3284 return Ok(Expression::TryCast(Box::new(Cast {
3285 this: f.args.into_iter().next().unwrap(),
3286 to: crate::expressions::DataType::Time {
3287 precision: None,
3288 timezone: false,
3289 },
3290 double_colon_syntax: false,
3291 trailing_comments: Vec::new(),
3292 format: None,
3293 default: None,
3294 inferred_type: None,
3295 })));
3296 }
3297 }
3298 }
3299 let mut args = f.args;
3301 if args.len() >= 2 {
3302 args[1] = Self::normalize_format_arg(args[1].clone());
3303 }
3304 Ok(Expression::Function(Box::new(Function::new(
3305 "TRY_TO_TIME".to_string(),
3306 args,
3307 ))))
3308 }
3309
3310 "TRY_TO_TIMESTAMP" => {
3313 if f.args.len() == 1 {
3314 if let Expression::Literal(lit) = &f.args[0] {
3315 if let crate::expressions::Literal::String(s) = lit.as_ref() {
3316 if !Self::looks_like_epoch(s) {
3317 return Ok(Expression::TryCast(Box::new(Cast {
3318 this: f.args.into_iter().next().unwrap(),
3319 to: DataType::Timestamp {
3320 precision: None,
3321 timezone: false,
3322 },
3323 double_colon_syntax: false,
3324 trailing_comments: Vec::new(),
3325 format: None,
3326 default: None,
3327 inferred_type: None,
3328 })));
3329 }
3330 }
3331 }
3332 }
3333 let mut args = f.args;
3335 if args.len() >= 2 {
3336 args[1] = Self::normalize_format_arg(args[1].clone());
3337 }
3338 Ok(Expression::Function(Box::new(Function::new(
3339 "TRY_TO_TIMESTAMP".to_string(),
3340 args,
3341 ))))
3342 }
3343
3344 "TRY_TO_DATE" => {
3346 if f.args.len() == 1 {
3347 if let Expression::Literal(lit) = &f.args[0] {
3348 if let crate::expressions::Literal::String(s) = lit.as_ref() {
3349 if s.contains('-') && s.len() >= 8 && s.len() <= 12 {
3351 return Ok(Expression::TryCast(Box::new(Cast {
3352 this: f.args.into_iter().next().unwrap(),
3353 to: crate::expressions::DataType::Date,
3354 double_colon_syntax: false,
3355 trailing_comments: Vec::new(),
3356 format: None,
3357 default: None,
3358 inferred_type: None,
3359 })));
3360 }
3361 }
3362 }
3363 }
3364 let mut args = f.args;
3366 if args.len() >= 2 {
3367 args[1] = Self::normalize_format_arg(args[1].clone());
3368 }
3369 Ok(Expression::Function(Box::new(Function::new(
3370 "TRY_TO_DATE".to_string(),
3371 args,
3372 ))))
3373 }
3374
3375 "TRY_TO_DOUBLE" => Ok(Expression::Function(Box::new(f))),
3377
3378 "REGEXP_REPLACE" if f.args.len() == 2 => {
3380 let mut args = f.args;
3381 args.push(Expression::Literal(Box::new(
3382 crate::expressions::Literal::String(String::new()),
3383 )));
3384 Ok(Expression::Function(Box::new(Function::new(
3385 "REGEXP_REPLACE".to_string(),
3386 args,
3387 ))))
3388 }
3389
3390 "LAST_DAY" if f.args.len() == 2 => {
3392 let mut args = f.args;
3393 let date = args.remove(0);
3394 let unit = args.remove(0);
3395 let unit_str = match &unit {
3396 Expression::Column(c) => c.name.name.to_uppercase(),
3397 Expression::Identifier(i) => i.name.to_uppercase(),
3398 _ => String::new(),
3399 };
3400 if unit_str == "MONTH" {
3401 Ok(Expression::Function(Box::new(Function::new(
3402 "LAST_DAY".to_string(),
3403 vec![date],
3404 ))))
3405 } else {
3406 Ok(Expression::Function(Box::new(Function::new(
3407 "LAST_DAY".to_string(),
3408 vec![date, unit],
3409 ))))
3410 }
3411 }
3412
3413 "EXTRACT" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
3415 "DATE_PART".to_string(),
3416 f.args,
3417 )))),
3418
3419 "ENDS_WITH" | "ENDSWITH" if f.args.len() == 2 => {
3421 let mut args = f.args;
3422 let this = args.remove(0);
3423 let expr = args.remove(0);
3424 Ok(Expression::EndsWith(Box::new(
3425 crate::expressions::BinaryFunc {
3426 original_name: None,
3427 this,
3428 expression: expr,
3429 inferred_type: None,
3430 },
3431 )))
3432 }
3433
3434 _ => Ok(Expression::Function(Box::new(f))),
3436 }
3437 }
3438
3439 fn looks_like_datetime(s: &str) -> bool {
3441 s.contains('-') || s.contains(':') || s.contains(' ') || s.contains('/')
3444 }
3445
3446 fn looks_like_epoch(s: &str) -> bool {
3448 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit() || c == '.')
3449 }
3450
3451 fn maybe_paren(expr: Expression) -> Expression {
3453 match &expr {
3454 Expression::Sub(_) | Expression::Add(_) | Expression::Mul(_) | Expression::Div(_) => {
3455 Expression::Paren(Box::new(crate::expressions::Paren {
3456 this: expr,
3457 trailing_comments: Vec::new(),
3458 }))
3459 }
3460 _ => expr,
3461 }
3462 }
3463
3464 fn normalize_snowflake_format(format: &str) -> String {
3468 let mut result = String::new();
3469 let chars: Vec<char> = format.chars().collect();
3470 let mut i = 0;
3471 while i < chars.len() {
3472 if chars[i] == '"' {
3474 i += 1;
3475 while i < chars.len() && chars[i] != '"' {
3476 result.push(chars[i]);
3477 i += 1;
3478 }
3479 if i < chars.len() {
3480 i += 1; }
3482 continue;
3483 }
3484
3485 let remaining = &format[i..];
3486 let remaining_upper = remaining.to_uppercase();
3487
3488 if remaining_upper.starts_with("YYYY") {
3490 result.push_str("yyyy");
3491 i += 4;
3492 } else if remaining_upper.starts_with("YY") {
3493 result.push_str("yy");
3494 i += 2;
3495 } else if remaining_upper.starts_with("MMMM") {
3496 result.push_str("mmmm");
3497 i += 4;
3498 } else if remaining_upper.starts_with("MON") {
3499 result.push_str("mon");
3500 i += 3;
3501 } else if remaining_upper.starts_with("MM") {
3502 result.push_str("mm");
3503 i += 2;
3504 } else if remaining_upper.starts_with("DD") {
3505 result.push_str("DD");
3506 i += 2;
3507 } else if remaining_upper.starts_with("DY") {
3508 result.push_str("dy");
3509 i += 2;
3510 } else if remaining_upper.starts_with("HH24") {
3511 result.push_str("hh24");
3512 i += 4;
3513 } else if remaining_upper.starts_with("HH12") {
3514 result.push_str("hh12");
3515 i += 4;
3516 } else if remaining_upper.starts_with("HH") {
3517 result.push_str("hh");
3518 i += 2;
3519 } else if remaining_upper.starts_with("MISS") {
3520 result.push_str("miss");
3522 i += 4;
3523 } else if remaining_upper.starts_with("MI") {
3524 result.push_str("mi");
3525 i += 2;
3526 } else if remaining_upper.starts_with("SS") {
3527 result.push_str("ss");
3528 i += 2;
3529 } else if remaining_upper.starts_with("FF") {
3530 let ff_len = 2;
3532 let digit = if i + ff_len < chars.len() && chars[i + ff_len].is_ascii_digit() {
3533 let d = chars[i + ff_len];
3534 Some(d)
3535 } else {
3536 None
3537 };
3538 if let Some(d) = digit {
3539 result.push_str("ff");
3540 result.push(d);
3541 i += 3;
3542 } else {
3543 result.push_str("ff9");
3545 i += 2;
3546 }
3547 } else if remaining_upper.starts_with("AM") || remaining_upper.starts_with("PM") {
3548 result.push_str("pm");
3549 i += 2;
3550 } else if remaining_upper.starts_with("TZH") {
3551 result.push_str("tzh");
3552 i += 3;
3553 } else if remaining_upper.starts_with("TZM") {
3554 result.push_str("tzm");
3555 i += 3;
3556 } else {
3557 result.push(chars[i]);
3559 i += 1;
3560 }
3561 }
3562 result
3563 }
3564
3565 fn normalize_format_arg(expr: Expression) -> Expression {
3567 if let Expression::Literal(lit) = &expr {
3568 if let crate::expressions::Literal::String(s) = lit.as_ref() {
3569 let normalized = Self::normalize_snowflake_format(s);
3570 Expression::Literal(Box::new(crate::expressions::Literal::String(normalized)))
3571 } else {
3572 expr.clone()
3573 }
3574 } else {
3575 expr
3576 }
3577 }
3578
3579 fn transform_typed_lambda(&self, lambda: crate::expressions::LambdaExpr) -> Expression {
3582 use crate::expressions::{DataType, LambdaExpr};
3583 use std::collections::HashMap;
3584
3585 let mut param_types: HashMap<String, DataType> = HashMap::new();
3587 for (i, param) in lambda.parameters.iter().enumerate() {
3588 if let Some(Some(dt)) = lambda.parameter_types.get(i) {
3589 param_types.insert(param.name.to_uppercase(), dt.clone());
3590 }
3591 }
3592
3593 if param_types.is_empty() {
3595 return Expression::Lambda(Box::new(lambda));
3596 }
3597
3598 let transformed_body = self.replace_lambda_params_with_cast(lambda.body, ¶m_types);
3600
3601 Expression::Lambda(Box::new(LambdaExpr {
3603 parameters: lambda.parameters,
3604 body: transformed_body,
3605 colon: lambda.colon,
3606 parameter_types: Vec::new(), }))
3608 }
3609
3610 fn replace_lambda_params_with_cast(
3612 &self,
3613 expr: Expression,
3614 param_types: &std::collections::HashMap<String, crate::expressions::DataType>,
3615 ) -> Expression {
3616 use crate::expressions::{BinaryOp, Cast, Paren};
3617
3618 match expr {
3619 Expression::Column(col) if col.table.is_none() => {
3621 let name_upper = col.name.name.to_uppercase();
3622 if let Some(dt) = param_types.get(&name_upper) {
3623 Expression::Cast(Box::new(Cast {
3625 this: Expression::Column(col),
3626 to: dt.clone(),
3627 double_colon_syntax: false,
3628 trailing_comments: Vec::new(),
3629 format: None,
3630 default: None,
3631 inferred_type: None,
3632 }))
3633 } else {
3634 Expression::Column(col)
3635 }
3636 }
3637
3638 Expression::Identifier(id) => {
3640 let name_upper = id.name.to_uppercase();
3641 if let Some(dt) = param_types.get(&name_upper) {
3642 Expression::Cast(Box::new(Cast {
3644 this: Expression::Identifier(id),
3645 to: dt.clone(),
3646 double_colon_syntax: false,
3647 trailing_comments: Vec::new(),
3648 format: None,
3649 default: None,
3650 inferred_type: None,
3651 }))
3652 } else {
3653 Expression::Identifier(id)
3654 }
3655 }
3656
3657 Expression::Add(op) => Expression::Add(Box::new(BinaryOp::new(
3659 self.replace_lambda_params_with_cast(op.left, param_types),
3660 self.replace_lambda_params_with_cast(op.right, param_types),
3661 ))),
3662 Expression::Sub(op) => Expression::Sub(Box::new(BinaryOp::new(
3663 self.replace_lambda_params_with_cast(op.left, param_types),
3664 self.replace_lambda_params_with_cast(op.right, param_types),
3665 ))),
3666 Expression::Mul(op) => Expression::Mul(Box::new(BinaryOp::new(
3667 self.replace_lambda_params_with_cast(op.left, param_types),
3668 self.replace_lambda_params_with_cast(op.right, param_types),
3669 ))),
3670 Expression::Div(op) => Expression::Div(Box::new(BinaryOp::new(
3671 self.replace_lambda_params_with_cast(op.left, param_types),
3672 self.replace_lambda_params_with_cast(op.right, param_types),
3673 ))),
3674 Expression::Mod(op) => Expression::Mod(Box::new(BinaryOp::new(
3675 self.replace_lambda_params_with_cast(op.left, param_types),
3676 self.replace_lambda_params_with_cast(op.right, param_types),
3677 ))),
3678
3679 Expression::Paren(p) => Expression::Paren(Box::new(Paren {
3681 this: self.replace_lambda_params_with_cast(p.this, param_types),
3682 trailing_comments: p.trailing_comments,
3683 })),
3684
3685 Expression::Function(mut f) => {
3687 f.args = f
3688 .args
3689 .into_iter()
3690 .map(|arg| self.replace_lambda_params_with_cast(arg, param_types))
3691 .collect();
3692 Expression::Function(f)
3693 }
3694
3695 Expression::Eq(op) => Expression::Eq(Box::new(BinaryOp::new(
3697 self.replace_lambda_params_with_cast(op.left, param_types),
3698 self.replace_lambda_params_with_cast(op.right, param_types),
3699 ))),
3700 Expression::Neq(op) => Expression::Neq(Box::new(BinaryOp::new(
3701 self.replace_lambda_params_with_cast(op.left, param_types),
3702 self.replace_lambda_params_with_cast(op.right, param_types),
3703 ))),
3704 Expression::Lt(op) => Expression::Lt(Box::new(BinaryOp::new(
3705 self.replace_lambda_params_with_cast(op.left, param_types),
3706 self.replace_lambda_params_with_cast(op.right, param_types),
3707 ))),
3708 Expression::Lte(op) => Expression::Lte(Box::new(BinaryOp::new(
3709 self.replace_lambda_params_with_cast(op.left, param_types),
3710 self.replace_lambda_params_with_cast(op.right, param_types),
3711 ))),
3712 Expression::Gt(op) => Expression::Gt(Box::new(BinaryOp::new(
3713 self.replace_lambda_params_with_cast(op.left, param_types),
3714 self.replace_lambda_params_with_cast(op.right, param_types),
3715 ))),
3716 Expression::Gte(op) => Expression::Gte(Box::new(BinaryOp::new(
3717 self.replace_lambda_params_with_cast(op.left, param_types),
3718 self.replace_lambda_params_with_cast(op.right, param_types),
3719 ))),
3720
3721 Expression::And(op) => Expression::And(Box::new(BinaryOp::new(
3723 self.replace_lambda_params_with_cast(op.left, param_types),
3724 self.replace_lambda_params_with_cast(op.right, param_types),
3725 ))),
3726 Expression::Or(op) => Expression::Or(Box::new(BinaryOp::new(
3727 self.replace_lambda_params_with_cast(op.left, param_types),
3728 self.replace_lambda_params_with_cast(op.right, param_types),
3729 ))),
3730
3731 other => other,
3733 }
3734 }
3735
3736 fn transform_aggregate_function(
3737 &self,
3738 f: Box<crate::expressions::AggregateFunction>,
3739 ) -> Result<Expression> {
3740 let name_upper = f.name.to_uppercase();
3741 match name_upper.as_str() {
3742 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3744 Function::new("LISTAGG".to_string(), f.args),
3745 ))),
3746
3747 "STRING_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3749 Function::new("LISTAGG".to_string(), f.args),
3750 ))),
3751
3752 "APPROX_DISTINCT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
3754 Function::new("APPROX_COUNT_DISTINCT".to_string(), f.args),
3755 ))),
3756
3757 "BIT_AND" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3759 "BITAND_AGG".to_string(),
3760 f.args,
3761 )))),
3762
3763 "BIT_OR" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3765 "BITOR_AGG".to_string(),
3766 f.args,
3767 )))),
3768
3769 "BIT_XOR" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
3771 "BITXOR_AGG".to_string(),
3772 f.args,
3773 )))),
3774
3775 "BOOL_AND" | "LOGICAL_AND" | "BOOLAND_AGG" if !f.args.is_empty() => {
3777 let arg = f.args.into_iter().next().unwrap();
3778 Ok(Expression::LogicalAnd(Box::new(AggFunc {
3779 this: arg,
3780 distinct: f.distinct,
3781 filter: f.filter,
3782 order_by: Vec::new(),
3783 name: Some("BOOLAND_AGG".to_string()),
3784 ignore_nulls: None,
3785 having_max: None,
3786 limit: None,
3787 inferred_type: None,
3788 })))
3789 }
3790
3791 "BOOL_OR" | "LOGICAL_OR" | "BOOLOR_AGG" if !f.args.is_empty() => {
3793 let arg = f.args.into_iter().next().unwrap();
3794 Ok(Expression::LogicalOr(Box::new(AggFunc {
3795 this: arg,
3796 distinct: f.distinct,
3797 filter: f.filter,
3798 order_by: Vec::new(),
3799 name: Some("BOOLOR_AGG".to_string()),
3800 ignore_nulls: None,
3801 having_max: None,
3802 limit: None,
3803 inferred_type: None,
3804 })))
3805 }
3806
3807 "APPROX_TOP_K" if f.args.len() == 1 => {
3809 let mut args = f.args;
3810 args.push(Expression::number(1));
3811 Ok(Expression::AggregateFunction(Box::new(
3812 crate::expressions::AggregateFunction {
3813 name: "APPROX_TOP_K".to_string(),
3814 args,
3815 distinct: f.distinct,
3816 filter: f.filter,
3817 order_by: Vec::new(),
3818 limit: None,
3819 ignore_nulls: None,
3820 inferred_type: None,
3821 },
3822 )))
3823 }
3824
3825 "SKEW" | "SKEWNESS" if !f.args.is_empty() => {
3827 let arg = f.args.into_iter().next().unwrap();
3828 Ok(Expression::Skewness(Box::new(AggFunc {
3829 this: arg,
3830 distinct: f.distinct,
3831 filter: f.filter,
3832 order_by: Vec::new(),
3833 name: Some("SKEW".to_string()),
3834 ignore_nulls: None,
3835 having_max: None,
3836 limit: None,
3837 inferred_type: None,
3838 })))
3839 }
3840
3841 _ => Ok(Expression::AggregateFunction(f)),
3843 }
3844 }
3845}
3846
3847fn strftime_to_snowflake_format(fmt: &str) -> String {
3849 let mut result = String::new();
3850 let chars: Vec<char> = fmt.chars().collect();
3851 let mut i = 0;
3852 while i < chars.len() {
3853 if chars[i] == '%' && i + 1 < chars.len() {
3854 match chars[i + 1] {
3855 'Y' => {
3856 result.push_str("yyyy");
3857 i += 2;
3858 }
3859 'y' => {
3860 result.push_str("yy");
3861 i += 2;
3862 }
3863 'm' => {
3864 result.push_str("mm");
3865 i += 2;
3866 }
3867 'd' => {
3868 result.push_str("DD");
3869 i += 2;
3870 }
3871 'H' => {
3872 result.push_str("hh24");
3873 i += 2;
3874 }
3875 'M' => {
3876 result.push_str("mmmm");
3877 i += 2;
3878 } 'i' => {
3880 result.push_str("mi");
3881 i += 2;
3882 }
3883 'S' | 's' => {
3884 result.push_str("ss");
3885 i += 2;
3886 }
3887 'f' => {
3888 result.push_str("ff");
3889 i += 2;
3890 }
3891 'w' => {
3892 result.push_str("dy");
3893 i += 2;
3894 } 'a' => {
3896 result.push_str("DY");
3897 i += 2;
3898 } 'b' => {
3900 result.push_str("mon");
3901 i += 2;
3902 } 'T' => {
3904 result.push_str("hh24:mi:ss");
3905 i += 2;
3906 } _ => {
3908 result.push(chars[i]);
3909 result.push(chars[i + 1]);
3910 i += 2;
3911 }
3912 }
3913 } else {
3914 result.push(chars[i]);
3915 i += 1;
3916 }
3917 }
3918 result
3919}
3920
3921#[cfg(test)]
3922mod tests {
3923 use super::*;
3924 use crate::dialects::Dialect;
3925
3926 fn transpile_to_snowflake(sql: &str) -> String {
3927 let dialect = Dialect::get(DialectType::Generic);
3928 let result = dialect
3929 .transpile(sql, DialectType::Snowflake)
3930 .expect("Transpile failed");
3931 result[0].clone()
3932 }
3933
3934 #[test]
3935 fn test_ifnull_to_coalesce() {
3936 let result = transpile_to_snowflake("SELECT IFNULL(a, b)");
3937 assert!(
3938 result.contains("COALESCE"),
3939 "Expected COALESCE, got: {}",
3940 result
3941 );
3942 }
3943
3944 #[test]
3945 fn test_basic_select() {
3946 let result = transpile_to_snowflake("SELECT a, b FROM users WHERE id = 1");
3947 assert!(result.contains("SELECT"));
3948 assert!(result.contains("FROM users"));
3949 }
3950
3951 #[test]
3952 fn test_snowflake_scripting_cursor_declare_block_roundtrip() {
3953 let sql = "DECLARE
3954 emp CURSOR FOR SELECT salary FROM employees;
3955BEGIN
3956 RETURN 1;
3957END";
3958
3959 let dialect = Dialect::get(DialectType::Snowflake);
3960 let ast = dialect.parse(sql).expect("Parse failed");
3961 let output = dialect.generate(&ast[0]).expect("Generate failed");
3962
3963 assert_eq!(output, sql);
3964 }
3965
3966 #[test]
3967 fn test_snowflake_scripting_cursor_return_table_roundtrip() {
3968 let sql = "DECLARE
3969 c1 CURSOR FOR SELECT * FROM invoices;
3970BEGIN
3971 OPEN c1;
3972 RETURN TABLE(RESULTSET_FROM_CURSOR(c1));
3973END";
3974
3975 let dialect = Dialect::get(DialectType::Snowflake);
3976 let ast = dialect.parse(sql).expect("Parse failed");
3977 let output = dialect.generate(&ast[0]).expect("Generate failed");
3978
3979 assert_eq!(output, sql);
3980 }
3981
3982 #[test]
3983 fn test_group_concat_to_listagg() {
3984 let result = transpile_to_snowflake("SELECT GROUP_CONCAT(name)");
3985 assert!(
3986 result.contains("LISTAGG"),
3987 "Expected LISTAGG, got: {}",
3988 result
3989 );
3990 }
3991
3992 #[test]
3993 fn test_string_agg_to_listagg() {
3994 let result = transpile_to_snowflake("SELECT STRING_AGG(name)");
3995 assert!(
3996 result.contains("LISTAGG"),
3997 "Expected LISTAGG, got: {}",
3998 result
3999 );
4000 }
4001
4002 #[test]
4003 fn test_array_to_array_construct() {
4004 let result = transpile_to_snowflake("SELECT ARRAY(1, 2, 3)");
4005 assert!(
4007 result.contains("[1, 2, 3]"),
4008 "Expected [1, 2, 3], got: {}",
4009 result
4010 );
4011 }
4012
4013 #[test]
4014 fn test_double_quote_identifiers() {
4015 let dialect = SnowflakeDialect;
4017 let config = dialect.generator_config();
4018 assert_eq!(config.identifier_quote, '"');
4019 }
4020}