1use super::{DialectImpl, DialectType};
19use crate::error::Result;
20use crate::expressions::{
21 AggFunc, BinaryOp, BooleanLiteral, Case, Cast, CeilFunc, DataType, DateTimeField,
22 Expression, ExtractFunc, Function, Interval, IntervalUnit, IntervalUnitSpec,
23 Join, JoinKind, Literal, Paren, UnaryFunc, VarArgFunc,
24};
25use crate::generator::GeneratorConfig;
26use crate::tokens::TokenizerConfig;
27
28fn wrap_if_json_arrow(expr: Expression) -> Expression {
32 match &expr {
33 Expression::JsonExtract(f) if f.arrow_syntax => {
34 Expression::Paren(Box::new(Paren {
35 this: expr,
36 trailing_comments: Vec::new(),
37 }))
38 }
39 Expression::JsonExtractScalar(f) if f.arrow_syntax => {
40 Expression::Paren(Box::new(Paren {
41 this: expr,
42 trailing_comments: Vec::new(),
43 }))
44 }
45 _ => expr,
46 }
47}
48
49pub struct PostgresDialect;
51
52impl DialectImpl for PostgresDialect {
53 fn dialect_type(&self) -> DialectType {
54 DialectType::PostgreSQL
55 }
56
57 fn tokenizer_config(&self) -> TokenizerConfig {
58 use crate::tokens::TokenType;
59 let mut config = TokenizerConfig::default();
60 config.quotes.insert("$$".to_string(), "$$".to_string());
62 config.identifiers.insert('"', '"');
64 config.nested_comments = true;
66 config.keywords.insert("EXEC".to_string(), TokenType::Command);
69 config
70 }
71
72 fn generator_config(&self) -> GeneratorConfig {
73 use crate::generator::IdentifierQuoteStyle;
74 GeneratorConfig {
75 identifier_quote: '"',
76 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
77 dialect: Some(DialectType::PostgreSQL),
78 tz_to_with_time_zone: false,
80 single_string_interval: true,
82 tablesample_seed_keyword: "REPEATABLE",
84 nvl2_supported: false,
86 parameter_token: "$",
88 named_placeholder_token: "%",
90 supports_select_into: true,
92 index_using_no_space: true,
94 supports_unlogged_tables: true,
96 multi_arg_distinct: false,
98 quantified_no_paren_space: true,
100 supports_window_exclude: true,
102 normalize_window_frame_between: true,
104 copy_has_into_keyword: false,
106 array_size_dim_required: Some(true),
108 supports_between_flags: true,
110 join_hints: false,
112 table_hints: false,
113 query_hints: false,
114 locking_reads_supported: true,
116 rename_table_with_db: false,
118 can_implement_array_any: true,
120 array_concat_is_var_len: false,
122 supports_median: false,
124 json_type_required_for_extraction: true,
126 like_property_inside_schema: true,
128 ..Default::default()
129 }
130 }
131
132 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
133 match expr {
134 Expression::DataType(dt) => self.transform_data_type(dt),
139
140 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
145 expressions: vec![f.this, f.expression],
146 }))),
147
148 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
150 expressions: vec![f.this, f.expression],
151 }))),
152
153 Expression::Coalesce(mut f) => {
156 f.original_name = None;
157 Ok(Expression::Coalesce(f))
158 }
159
160 Expression::TryCast(c) => Ok(Expression::Cast(c)),
165
166 Expression::SafeCast(c) => Ok(Expression::Cast(c)),
168
169 Expression::Rand(r) => {
174 let _ = r.seed; Ok(Expression::Random(crate::expressions::Random))
177 }
178
179 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
184 "GEN_RANDOM_UUID".to_string(),
185 vec![],
186 )))),
187
188 Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
193 crate::expressions::UnnestFunc {
194 this: f.this,
195 expressions: Vec::new(),
196 with_ordinality: false,
197 alias: None,
198 offset_alias: None,
199 },
200 ))),
201
202 Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
204 crate::expressions::UnnestFunc {
205 this: f.this,
206 expressions: Vec::new(),
207 with_ordinality: false,
208 alias: None,
209 offset_alias: None,
210 },
211 ))),
212
213 Expression::ArrayConcat(f) => Ok(Expression::Function(Box::new(Function::new(
215 "ARRAY_CAT".to_string(),
216 f.expressions,
217 )))),
218
219 Expression::ArrayPrepend(f) => Ok(Expression::Function(Box::new(Function::new(
221 "ARRAY_PREPEND".to_string(),
222 vec![f.expression, f.this], )))),
224
225 Expression::BitwiseXor(f) => {
230 Ok(Expression::Function(Box::new(Function::new(
232 "__PG_BITWISE_XOR__".to_string(),
233 vec![f.left, f.right],
234 ))))
235 }
236
237 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
239 "BIT_AND".to_string(),
240 vec![f.this],
241 )))),
242
243 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
245 "BIT_OR".to_string(),
246 vec![f.this],
247 )))),
248
249 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
251 "BIT_XOR".to_string(),
252 vec![f.this],
253 )))),
254
255 Expression::LogicalAnd(f) => Ok(Expression::Function(Box::new(Function::new(
260 "BOOL_AND".to_string(),
261 vec![f.this],
262 )))),
263
264 Expression::LogicalOr(f) => Ok(Expression::Function(Box::new(Function::new(
266 "BOOL_OR".to_string(),
267 vec![f.this],
268 )))),
269
270 Expression::Xor(f) => {
272 if let (Some(a), Some(b)) = (f.this, f.expression) {
273 Ok(Expression::Neq(Box::new(BinaryOp {
274 left: *a,
275 right: *b,
276 left_comments: Vec::new(),
277 operator_comments: Vec::new(),
278 trailing_comments: Vec::new(),
279 })))
280 } else {
281 Ok(Expression::Boolean(BooleanLiteral { value: false }))
282 }
283 }
284
285 Expression::ArrayContainedBy(op) => Ok(Expression::ArrayContainsAll(Box::new(BinaryOp {
291 left: op.right,
292 right: op.left,
293 left_comments: Vec::new(),
294 operator_comments: Vec::new(),
295 trailing_comments: Vec::new(),
296 }))),
297
298 Expression::RegexpLike(f) => {
303 Ok(Expression::RegexpLike(f))
305 }
306
307 Expression::DateAdd(f) => {
312 let interval_expr = Expression::Interval(Box::new(Interval {
313 this: Some(f.interval),
314 unit: Some(IntervalUnitSpec::Simple {
315 unit: f.unit,
316 use_plural: false,
317 }),
318 }));
319 Ok(Expression::Add(Box::new(BinaryOp {
320 left: f.this,
321 right: interval_expr,
322 left_comments: Vec::new(),
323 operator_comments: Vec::new(),
324 trailing_comments: Vec::new(),
325 })))
326 }
327
328 Expression::DateSub(f) => {
330 let interval_expr = Expression::Interval(Box::new(Interval {
331 this: Some(f.interval),
332 unit: Some(IntervalUnitSpec::Simple {
333 unit: f.unit,
334 use_plural: false,
335 }),
336 }));
337 Ok(Expression::Sub(Box::new(BinaryOp {
338 left: f.this,
339 right: interval_expr,
340 left_comments: Vec::new(),
341 operator_comments: Vec::new(),
342 trailing_comments: Vec::new(),
343 })))
344 }
345
346 Expression::DateDiff(f) => {
348 Ok(Expression::Function(Box::new(Function::new(
351 "AGE".to_string(),
352 vec![f.this, f.expression],
353 ))))
354 }
355
356 Expression::UnixToTime(f) => Ok(Expression::Function(Box::new(Function::new(
358 "TO_TIMESTAMP".to_string(),
359 vec![*f.this],
360 )))),
361
362 Expression::TimeToUnix(f) => Ok(Expression::Function(Box::new(Function::new(
364 "DATE_PART".to_string(),
365 vec![Expression::string("epoch"), f.this],
366 )))),
367
368 Expression::ToTimestamp(f) => {
370 let mut args = vec![f.this];
371 if let Some(fmt) = f.format {
372 args.push(fmt);
373 }
374 Ok(Expression::Function(Box::new(Function::new(
375 "TO_TIMESTAMP".to_string(),
376 args,
377 ))))
378 }
379
380 Expression::ToDate(f) => {
382 let mut args = vec![f.this];
383 if let Some(fmt) = f.format {
384 args.push(fmt);
385 }
386 Ok(Expression::Function(Box::new(Function::new(
387 "TO_DATE".to_string(),
388 args,
389 ))))
390 }
391
392 Expression::TimestampTrunc(f) => {
394 let unit_str = format!("{:?}", f.unit).to_lowercase();
396 let args = vec![Expression::string(&unit_str), f.this];
397 Ok(Expression::Function(Box::new(Function::new(
398 "DATE_TRUNC".to_string(),
399 args,
400 ))))
401 }
402
403 Expression::TimeFromParts(f) => {
405 let mut args = Vec::new();
406 if let Some(h) = f.hour {
407 args.push(*h);
408 }
409 if let Some(m) = f.min {
410 args.push(*m);
411 }
412 if let Some(s) = f.sec {
413 args.push(*s);
414 }
415 Ok(Expression::Function(Box::new(Function::new(
416 "MAKE_TIME".to_string(),
417 args,
418 ))))
419 }
420
421 Expression::MakeTimestamp(f) => {
423 let args = vec![f.year, f.month, f.day, f.hour, f.minute, f.second];
425 Ok(Expression::Function(Box::new(Function::new(
426 "MAKE_TIMESTAMP".to_string(),
427 args,
428 ))))
429 }
430
431 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
436
437 Expression::GroupConcat(f) => {
439 let mut args = vec![f.this.clone()];
440 if let Some(sep) = f.separator.clone() {
441 args.push(sep);
442 } else {
443 args.push(Expression::string(","));
444 }
445 Ok(Expression::Function(Box::new(Function::new(
446 "STRING_AGG".to_string(),
447 args,
448 ))))
449 }
450
451 Expression::Position(f) => {
453 Ok(Expression::Position(f))
456 }
457
458 Expression::CountIf(f) => {
463 let case_expr = Expression::Case(Box::new(Case {
464 operand: None,
465 whens: vec![(f.this.clone(), Expression::number(1))],
466 else_: Some(Expression::number(0)),
467 }));
468 Ok(Expression::Sum(Box::new(AggFunc { ignore_nulls: None, having_max: None,
469 this: case_expr,
470 distinct: f.distinct,
471 filter: f.filter,
472 order_by: Vec::new(),
473 name: None,
474 limit: None,
475 })))
476 }
477
478 Expression::AnyValue(f) => Ok(Expression::AnyValue(f)),
480
481 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
483 "VAR_SAMP".to_string(),
484 vec![f.this],
485 )))),
486
487 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
489 "VAR_POP".to_string(),
490 vec![f.this],
491 )))),
492
493 Expression::JsonExtract(mut f) => {
499 f.arrow_syntax = Self::is_simple_json_path(&f.path);
502 Ok(Expression::JsonExtract(f))
503 }
504
505 Expression::JsonExtractScalar(mut f) => {
509 if !f.hash_arrow_syntax {
510 f.arrow_syntax = Self::is_simple_json_path(&f.path);
513 }
514 Ok(Expression::JsonExtractScalar(f))
515 }
516
517 Expression::JsonObjectAgg(f) => {
521 let args = vec![f.key, f.value];
523 Ok(Expression::Function(Box::new(Function::new(
524 "JSON_OBJECT_AGG".to_string(),
525 args,
526 ))))
527 }
528
529 Expression::JsonArrayAgg(f) => Ok(Expression::Function(Box::new(Function::new(
531 "JSON_AGG".to_string(),
532 vec![f.this],
533 )))),
534
535 Expression::JSONPathRoot(_) => {
537 Ok(Expression::Literal(Literal::String(String::new())))
538 }
539
540 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
545 "DIV".to_string(),
546 vec![f.this, f.expression],
547 )))),
548
549 Expression::Unicode(f) => Ok(Expression::Function(Box::new(Function::new(
551 "ASCII".to_string(),
552 vec![f.this],
553 )))),
554
555 Expression::LastDay(f) => {
557 let truncated = Expression::Function(Box::new(Function::new(
559 "DATE_TRUNC".to_string(),
560 vec![Expression::string("month"), f.this.clone()],
561 )));
562 let plus_month = Expression::Add(Box::new(BinaryOp {
563 left: truncated,
564 right: Expression::Interval(Box::new(Interval {
565 this: Some(Expression::string("1")),
566 unit: Some(IntervalUnitSpec::Simple {
567 unit: IntervalUnit::Month,
568 use_plural: false,
569 }),
570 })),
571 left_comments: Vec::new(),
572 operator_comments: Vec::new(),
573 trailing_comments: Vec::new(),
574 }));
575 let minus_day = Expression::Sub(Box::new(BinaryOp {
576 left: plus_month,
577 right: Expression::Interval(Box::new(Interval {
578 this: Some(Expression::string("1")),
579 unit: Some(IntervalUnitSpec::Simple {
580 unit: IntervalUnit::Day,
581 use_plural: false,
582 }),
583 })),
584 left_comments: Vec::new(),
585 operator_comments: Vec::new(),
586 trailing_comments: Vec::new(),
587 }));
588 Ok(Expression::Cast(Box::new(Cast {
589 this: minus_day,
590 to: DataType::Date,
591 trailing_comments: Vec::new(),
592 double_colon_syntax: true, format: None,
594 default: None,
595 })))
596 }
597
598 Expression::GenerateSeries(f) => Ok(Expression::GenerateSeries(f)),
600
601 Expression::ExplodingGenerateSeries(f) => {
603 let mut args = vec![f.start, f.stop];
604 if let Some(step) = f.step {
605 args.push(step); }
607 Ok(Expression::Function(Box::new(Function::new(
608 "GENERATE_SERIES".to_string(),
609 args,
610 ))))
611 }
612
613 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function {
618 name: "CURRENT_TIMESTAMP".to_string(),
619 args: vec![],
620 distinct: false,
621 trailing_comments: vec![],
622 use_bracket_syntax: false,
623 no_parens: true,
624 quoted: false,
625 }))),
626
627 Expression::CurrentUser(_) => Ok(Expression::Function(Box::new(Function::new(
629 "CURRENT_USER".to_string(),
630 vec![],
631 )))),
632
633 Expression::CurrentDate(_) => Ok(Expression::Function(Box::new(Function {
635 name: "CURRENT_DATE".to_string(),
636 args: vec![],
637 distinct: false,
638 trailing_comments: vec![],
639 use_bracket_syntax: false,
640 no_parens: true,
641 quoted: false,
642 }))),
643
644 Expression::Join(join) if join.kind == JoinKind::CrossApply => {
649 Ok(Expression::Join(Box::new(Join {
650 this: join.this,
651 on: None,
652 using: join.using,
653 kind: JoinKind::Lateral,
654 use_inner_keyword: false,
655 use_outer_keyword: false,
656 deferred_condition: false,
657 join_hint: None,
658 match_condition: None,
659 pivots: join.pivots,
660 })))
661 }
662
663 Expression::Join(join) if join.kind == JoinKind::OuterApply => {
665 Ok(Expression::Join(Box::new(Join {
666 this: join.this,
667 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
668 using: join.using,
669 kind: JoinKind::LeftLateral,
670 use_inner_keyword: false,
671 use_outer_keyword: false,
672 deferred_condition: false,
673 join_hint: None,
674 match_condition: None,
675 pivots: join.pivots,
676 })))
677 }
678
679 Expression::Function(f) => self.transform_function(*f),
683
684 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
686
687 Expression::Eq(op) => Ok(Expression::Eq(Box::new(BinaryOp {
694 left: wrap_if_json_arrow(op.left),
695 right: wrap_if_json_arrow(op.right),
696 ..*op
697 }))),
698 Expression::Neq(op) => Ok(Expression::Neq(Box::new(BinaryOp {
699 left: wrap_if_json_arrow(op.left),
700 right: wrap_if_json_arrow(op.right),
701 ..*op
702 }))),
703 Expression::Lt(op) => Ok(Expression::Lt(Box::new(BinaryOp {
704 left: wrap_if_json_arrow(op.left),
705 right: wrap_if_json_arrow(op.right),
706 ..*op
707 }))),
708 Expression::Lte(op) => Ok(Expression::Lte(Box::new(BinaryOp {
709 left: wrap_if_json_arrow(op.left),
710 right: wrap_if_json_arrow(op.right),
711 ..*op
712 }))),
713 Expression::Gt(op) => Ok(Expression::Gt(Box::new(BinaryOp {
714 left: wrap_if_json_arrow(op.left),
715 right: wrap_if_json_arrow(op.right),
716 ..*op
717 }))),
718 Expression::Gte(op) => Ok(Expression::Gte(Box::new(BinaryOp {
719 left: wrap_if_json_arrow(op.left),
720 right: wrap_if_json_arrow(op.right),
721 ..*op
722 }))),
723
724 Expression::In(mut i) => {
726 i.this = wrap_if_json_arrow(i.this);
727 Ok(Expression::In(i))
728 }
729
730 Expression::Not(mut n) => {
732 n.this = wrap_if_json_arrow(n.this);
733 Ok(Expression::Not(n))
734 }
735
736 Expression::Merge(m) => Ok(Expression::Merge(m)),
739
740 Expression::JSONExtract(je) if je.variant_extract.is_some() => {
742 let path = match *je.expression {
745 Expression::Literal(Literal::String(s)) => {
746 let cleaned = if s.starts_with("[\"") && s.ends_with("\"]") {
748 s[2..s.len() - 2].to_string()
749 } else {
750 s
751 };
752 Expression::Literal(Literal::String(cleaned))
753 }
754 other => other,
755 };
756 Ok(Expression::Function(Box::new(Function::new(
757 "JSON_EXTRACT_PATH".to_string(),
758 vec![*je.this, path],
759 ))))
760 }
761
762 Expression::Trim(t) if !t.sql_standard_syntax && t.characters.is_some() => {
764 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
765 this: t.this,
766 characters: t.characters,
767 position: t.position,
768 sql_standard_syntax: true,
769 position_explicit: t.position_explicit,
770 })))
771 }
772
773 Expression::Literal(Literal::ByteString(s)) => {
775 Ok(Expression::Cast(Box::new(Cast {
776 this: Expression::Literal(Literal::EscapeString(s)),
777 to: DataType::VarBinary { length: None },
778 trailing_comments: Vec::new(),
779 double_colon_syntax: false,
780 format: None,
781 default: None,
782 })))
783 }
784
785 _ => Ok(expr),
787 }
788 }
789}
790
791impl PostgresDialect {
792 fn is_simple_json_path(path: &Expression) -> bool {
796 match path {
797 Expression::Literal(Literal::String(_)) => true,
799 Expression::Literal(Literal::Number(n)) => {
801 !n.starts_with('-')
803 }
804 Expression::JSONPath(_) => true,
806 _ => false,
808 }
809 }
810
811 fn transform_data_type(&self, dt: DataType) -> Result<Expression> {
813 let transformed = match dt {
814 DataType::TinyInt { .. } => DataType::SmallInt { length: None },
816
817 DataType::Float { .. } => DataType::Custom {
819 name: "DOUBLE PRECISION".to_string(),
820 },
821
822 DataType::Double { .. } => DataType::Custom {
824 name: "DOUBLE PRECISION".to_string(),
825 },
826
827 DataType::Binary { .. } => DataType::Custom {
829 name: "BYTEA".to_string(),
830 },
831
832 DataType::VarBinary { .. } => DataType::Custom {
834 name: "BYTEA".to_string(),
835 },
836
837 DataType::Blob => DataType::Custom {
839 name: "BYTEA".to_string(),
840 },
841
842 DataType::Custom { ref name } => {
844 let upper = name.to_uppercase();
845 match upper.as_str() {
846 "INT8" => DataType::BigInt { length: None },
848 "FLOAT8" => DataType::Custom {
850 name: "DOUBLE PRECISION".to_string(),
851 },
852 "FLOAT4" => DataType::Custom {
854 name: "REAL".to_string(),
855 },
856 "INT4" => DataType::Int { length: None, integer_spelling: false },
858 "INT2" => DataType::SmallInt { length: None },
860 _ => dt,
861 }
862 }
863
864 other => other,
866 };
867 Ok(Expression::DataType(transformed))
868 }
869
870 fn transform_function(&self, f: Function) -> Result<Expression> {
871 let name_upper = f.name.to_uppercase();
872 match name_upper.as_str() {
873 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
875 expressions: f.args,
876 }))),
877
878 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
880 expressions: f.args,
881 }))),
882
883 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
885 expressions: f.args,
886 }))),
887
888 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
890 Function::new("STRING_AGG".to_string(), f.args),
891 ))),
892
893 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
895 "SUBSTRING".to_string(),
896 f.args,
897 )))),
898
899 "RAND" => Ok(Expression::Random(crate::expressions::Random)),
901
902 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
904 this: f.args.into_iter().next().unwrap(),
905 decimals: None,
906 to: None,
907 }))),
908
909 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
911 this: f.args.into_iter().next().unwrap(),
912 original_name: None,
913 }))),
914
915 "CHAR_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
917 this: f.args.into_iter().next().unwrap(),
918 original_name: None,
919 }))),
920
921 "CHARACTER_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
923 this: f.args.into_iter().next().unwrap(),
924 original_name: None,
925 }))),
926
927 "CHARINDEX" if f.args.len() >= 2 => {
930 let mut args = f.args;
931 let substring = args.remove(0);
932 let string = args.remove(0);
933 Ok(Expression::Position(Box::new(
934 crate::expressions::PositionFunc {
935 substring,
936 string,
937 start: args.pop(),
938 },
939 )))
940 }
941
942 "GETDATE" => Ok(Expression::CurrentTimestamp(
944 crate::expressions::CurrentTimestamp { precision: None, sysdate: false },
945 )),
946
947 "SYSDATETIME" => Ok(Expression::CurrentTimestamp(
949 crate::expressions::CurrentTimestamp { precision: None, sysdate: false },
950 )),
951
952 "NOW" => Ok(Expression::CurrentTimestamp(
954 crate::expressions::CurrentTimestamp { precision: None, sysdate: false },
955 )),
956
957 "NEWID" => Ok(Expression::Function(Box::new(Function::new(
959 "GEN_RANDOM_UUID".to_string(),
960 vec![],
961 )))),
962
963 "UUID" => Ok(Expression::Function(Box::new(Function::new(
965 "GEN_RANDOM_UUID".to_string(),
966 vec![],
967 )))),
968
969 "UNNEST" => Ok(Expression::Function(Box::new(f))),
971
972 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(f))),
974
975 "SHA256" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
977 "SHA256".to_string(),
978 f.args,
979 )))),
980
981 "SHA2" if f.args.len() == 2 => {
983 let args = f.args;
985 let data = args[0].clone();
986 Ok(Expression::Function(Box::new(Function::new(
988 "SHA256".to_string(),
989 vec![data],
990 ))))
991 }
992
993 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
995
996 "EDITDISTANCE" if f.args.len() == 3 => Ok(Expression::Function(Box::new(Function::new(
998 "LEVENSHTEIN_LESS_EQUAL".to_string(),
999 f.args,
1000 )))),
1001 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
1002 "LEVENSHTEIN".to_string(),
1003 f.args,
1004 )))),
1005
1006 "TRIM" if f.args.len() == 2 => {
1008 let value = f.args[0].clone();
1009 let chars = f.args[1].clone();
1010 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
1011 this: value,
1012 characters: Some(chars),
1013 position: crate::expressions::TrimPosition::Both,
1014 sql_standard_syntax: true,
1015 position_explicit: false,
1016 })))
1017 }
1018
1019 "DATEDIFF" if f.args.len() >= 2 => {
1021 let mut args = f.args;
1022 if args.len() == 2 {
1023 let first = args.remove(0);
1025 let second = args.remove(0);
1026 Ok(Expression::Function(Box::new(Function::new(
1027 "AGE".to_string(),
1028 vec![first, second],
1029 ))))
1030 } else {
1031 let unit_expr = args.remove(0);
1033 let start = args.remove(0);
1034 let end_expr = args.remove(0);
1035
1036 let unit_name = match &unit_expr {
1038 Expression::Identifier(id) => id.name.to_uppercase(),
1039 Expression::Column(col) if col.table.is_none() => col.name.name.to_uppercase(),
1040 _ => "DAY".to_string(),
1041 };
1042
1043 let cast_ts = |e: Expression| -> Expression {
1045 Expression::Cast(Box::new(Cast {
1046 this: e,
1047 to: DataType::Timestamp { precision: None, timezone: false },
1048 trailing_comments: Vec::new(),
1049 double_colon_syntax: false,
1050 format: None,
1051 default: None,
1052 }))
1053 };
1054
1055 let cast_bigint = |e: Expression| -> Expression {
1057 Expression::Cast(Box::new(Cast {
1058 this: e,
1059 to: DataType::BigInt { length: None },
1060 trailing_comments: Vec::new(),
1061 double_colon_syntax: false,
1062 format: None,
1063 default: None,
1064 }))
1065 };
1066
1067 let end_ts = cast_ts(end_expr.clone());
1068 let start_ts = cast_ts(start.clone());
1069
1070 let ts_diff = || -> Expression {
1072 Expression::Sub(Box::new(BinaryOp::new(
1073 cast_ts(end_expr.clone()),
1074 cast_ts(start.clone()),
1075 )))
1076 };
1077
1078 let age_call = || -> Expression {
1080 Expression::Function(Box::new(Function::new(
1081 "AGE".to_string(),
1082 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
1083 )))
1084 };
1085
1086 let extract = |field: DateTimeField, from: Expression| -> Expression {
1088 Expression::Extract(Box::new(ExtractFunc {
1089 this: from,
1090 field,
1091 }))
1092 };
1093
1094 let num = |n: i64| -> Expression {
1096 Expression::Literal(Literal::Number(n.to_string()))
1097 };
1098
1099 let epoch_field = DateTimeField::Custom("epoch".to_string());
1101
1102 let result = match unit_name.as_str() {
1103 "MICROSECOND" => {
1104 let epoch = extract(epoch_field, ts_diff());
1106 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000000)))))
1107 }
1108 "MILLISECOND" => {
1109 let epoch = extract(epoch_field, ts_diff());
1110 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
1111 }
1112 "SECOND" => {
1113 let epoch = extract(epoch_field, ts_diff());
1114 cast_bigint(epoch)
1115 }
1116 "MINUTE" => {
1117 let epoch = extract(epoch_field, ts_diff());
1118 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
1119 }
1120 "HOUR" => {
1121 let epoch = extract(epoch_field, ts_diff());
1122 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
1123 }
1124 "DAY" => {
1125 let epoch = extract(epoch_field, ts_diff());
1126 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
1127 }
1128 "WEEK" => {
1129 let diff_parens = Expression::Paren(Box::new(Paren {
1131 this: ts_diff(),
1132 trailing_comments: Vec::new(),
1133 }));
1134 let days = extract(DateTimeField::Custom("days".to_string()), diff_parens);
1135 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
1136 }
1137 "MONTH" => {
1138 let year_part = extract(DateTimeField::Custom("year".to_string()), age_call());
1140 let month_part = extract(DateTimeField::Custom("month".to_string()), age_call());
1141 let year_months = Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
1142 cast_bigint(Expression::Add(Box::new(BinaryOp::new(year_months, month_part))))
1143 }
1144 "QUARTER" => {
1145 let year_part = extract(DateTimeField::Custom("year".to_string()), age_call());
1147 let month_part = extract(DateTimeField::Custom("month".to_string()), age_call());
1148 let year_quarters = Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
1149 let month_quarters = Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
1150 cast_bigint(Expression::Add(Box::new(BinaryOp::new(year_quarters, month_quarters))))
1151 }
1152 "YEAR" => {
1153 cast_bigint(extract(DateTimeField::Custom("year".to_string()), age_call()))
1155 }
1156 _ => {
1157 Expression::Function(Box::new(Function::new(
1159 "AGE".to_string(),
1160 vec![end_ts, start_ts],
1161 )))
1162 }
1163 };
1164 Ok(result)
1165 }
1166 }
1167
1168 "TIMESTAMPDIFF" if f.args.len() >= 3 => {
1170 let mut args = f.args;
1171 let _unit = args.remove(0); let start = args.remove(0);
1173 let end = args.remove(0);
1174 Ok(Expression::Function(Box::new(Function::new(
1175 "AGE".to_string(),
1176 vec![end, start],
1177 ))))
1178 }
1179
1180 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1182 "TO_TIMESTAMP".to_string(),
1183 f.args,
1184 )))),
1185
1186 "UNIX_TIMESTAMP" if f.args.len() == 1 => {
1188 let arg = f.args.into_iter().next().unwrap();
1189 Ok(Expression::Function(Box::new(Function::new(
1190 "DATE_PART".to_string(),
1191 vec![Expression::string("epoch"), arg],
1192 ))))
1193 }
1194
1195 "UNIX_TIMESTAMP" if f.args.is_empty() => {
1197 Ok(Expression::Function(Box::new(Function::new(
1198 "DATE_PART".to_string(),
1199 vec![
1200 Expression::string("epoch"),
1201 Expression::CurrentTimestamp(crate::expressions::CurrentTimestamp {
1202 precision: None,
1203 sysdate: false,
1204 }),
1205 ],
1206 ))))
1207 }
1208
1209 "DATEADD" if f.args.len() == 3 => {
1211 let mut args = f.args;
1214 let _unit = args.remove(0);
1215 let count = args.remove(0);
1216 let date = args.remove(0);
1217 Ok(Expression::Add(Box::new(BinaryOp {
1218 left: date,
1219 right: count,
1220 left_comments: Vec::new(),
1221 operator_comments: Vec::new(),
1222 trailing_comments: Vec::new(),
1223 })))
1224 }
1225
1226 "INSTR" if f.args.len() >= 2 => {
1228 let mut args = f.args;
1229 let string = args.remove(0);
1230 let substring = args.remove(0);
1231 Ok(Expression::Position(Box::new(
1232 crate::expressions::PositionFunc {
1233 substring,
1234 string,
1235 start: args.pop(),
1236 },
1237 )))
1238 }
1239
1240 "CONCAT_WS" => Ok(Expression::Function(Box::new(f))),
1242
1243 "REGEXP_REPLACE" if f.args.len() == 3 || f.args.len() == 4 => {
1246 Ok(Expression::Function(Box::new(f)))
1247 }
1248 "REGEXP_REPLACE" if f.args.len() == 6 => {
1251 let is_global = match &f.args[4] {
1252 Expression::Literal(crate::expressions::Literal::Number(n)) => n == "0",
1253 _ => false,
1254 };
1255 if is_global {
1256 let subject = f.args[0].clone();
1257 let pattern = f.args[1].clone();
1258 let replacement = f.args[2].clone();
1259 let position = f.args[3].clone();
1260 let occurrence = f.args[4].clone();
1261 let params = &f.args[5];
1262 let mut flags = if let Expression::Literal(crate::expressions::Literal::String(s)) = params {
1263 s.clone()
1264 } else {
1265 String::new()
1266 };
1267 if !flags.contains('g') {
1268 flags.push('g');
1269 }
1270 Ok(Expression::Function(Box::new(Function::new(
1271 "REGEXP_REPLACE".to_string(),
1272 vec![subject, pattern, replacement, position, occurrence, Expression::Literal(crate::expressions::Literal::String(flags))],
1273 ))))
1274 } else {
1275 Ok(Expression::Function(Box::new(f)))
1276 }
1277 }
1278 "REGEXP_REPLACE" => Ok(Expression::Function(Box::new(f))),
1280
1281 _ => Ok(Expression::Function(Box::new(f))),
1283 }
1284 }
1285
1286 fn transform_aggregate_function(
1287 &self,
1288 f: Box<crate::expressions::AggregateFunction>,
1289 ) -> Result<Expression> {
1290 let name_upper = f.name.to_uppercase();
1291 match name_upper.as_str() {
1292 "COUNT_IF" if !f.args.is_empty() => {
1294 let condition = f.args.into_iter().next().unwrap();
1295 let case_expr = Expression::Case(Box::new(Case {
1296 operand: None,
1297 whens: vec![(condition, Expression::number(1))],
1298 else_: Some(Expression::number(0)),
1299 }));
1300 Ok(Expression::Sum(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1301 this: case_expr,
1302 distinct: f.distinct,
1303 filter: f.filter,
1304 order_by: Vec::new(),
1305 name: None,
1306 limit: None,
1307 })))
1308 }
1309
1310 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1312 Function::new("STRING_AGG".to_string(), f.args),
1313 ))),
1314
1315 "STDEV" if !f.args.is_empty() => Ok(Expression::Stddev(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1317 this: f.args.into_iter().next().unwrap(),
1318 distinct: f.distinct,
1319 filter: f.filter,
1320 order_by: Vec::new(),
1321 name: None,
1322 limit: None,
1323 }))),
1324
1325 "STDEVP" if !f.args.is_empty() => Ok(Expression::StddevPop(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1327 this: f.args.into_iter().next().unwrap(),
1328 distinct: f.distinct,
1329 filter: f.filter,
1330 order_by: Vec::new(),
1331 name: None,
1332 limit: None,
1333 }))),
1334
1335 "VAR" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1337 this: f.args.into_iter().next().unwrap(),
1338 distinct: f.distinct,
1339 filter: f.filter,
1340 order_by: Vec::new(),
1341 name: None,
1342 limit: None,
1343 }))),
1344
1345 "VARP" if !f.args.is_empty() => Ok(Expression::VarPop(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1347 this: f.args.into_iter().next().unwrap(),
1348 distinct: f.distinct,
1349 filter: f.filter,
1350 order_by: Vec::new(),
1351 name: None,
1352 limit: None,
1353 }))),
1354
1355 "BIT_AND" => Ok(Expression::AggregateFunction(f)),
1357
1358 "BIT_OR" => Ok(Expression::AggregateFunction(f)),
1360
1361 "BIT_XOR" => Ok(Expression::AggregateFunction(f)),
1363
1364 "BOOL_AND" => Ok(Expression::AggregateFunction(f)),
1366
1367 "BOOL_OR" => Ok(Expression::AggregateFunction(f)),
1369
1370 "VARIANCE" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc { ignore_nulls: None, having_max: None,
1372 this: f.args.into_iter().next().unwrap(),
1373 distinct: f.distinct,
1374 filter: f.filter,
1375 order_by: Vec::new(),
1376 name: None,
1377 limit: None,
1378 }))),
1379
1380 "LOGICAL_OR" if !f.args.is_empty() => {
1382 let mut new_agg = f.clone();
1383 new_agg.name = "BOOL_OR".to_string();
1384 Ok(Expression::AggregateFunction(new_agg))
1385 }
1386
1387 "LOGICAL_AND" if !f.args.is_empty() => {
1389 let mut new_agg = f.clone();
1390 new_agg.name = "BOOL_AND".to_string();
1391 Ok(Expression::AggregateFunction(new_agg))
1392 }
1393
1394 _ => Ok(Expression::AggregateFunction(f)),
1396 }
1397 }
1398}
1399
1400#[cfg(test)]
1401mod tests {
1402 use super::*;
1403 use crate::dialects::Dialect;
1404
1405 fn transpile_to_postgres(sql: &str) -> String {
1406 let dialect = Dialect::get(DialectType::Generic);
1407 let result = dialect
1408 .transpile_to(sql, DialectType::PostgreSQL)
1409 .expect("Transpile failed");
1410 result[0].clone()
1411 }
1412
1413 #[test]
1414 fn test_ifnull_to_coalesce() {
1415 let result = transpile_to_postgres("SELECT IFNULL(a, b)");
1416 assert!(
1417 result.contains("COALESCE"),
1418 "Expected COALESCE, got: {}",
1419 result
1420 );
1421 }
1422
1423 #[test]
1424 fn test_nvl_to_coalesce() {
1425 let result = transpile_to_postgres("SELECT NVL(a, b)");
1426 assert!(
1427 result.contains("COALESCE"),
1428 "Expected COALESCE, got: {}",
1429 result
1430 );
1431 }
1432
1433 #[test]
1434 fn test_rand_to_random() {
1435 let result = transpile_to_postgres("SELECT RAND()");
1436 assert!(
1437 result.contains("RANDOM"),
1438 "Expected RANDOM, got: {}",
1439 result
1440 );
1441 }
1442
1443 #[test]
1444 fn test_basic_select() {
1445 let result = transpile_to_postgres("SELECT a, b FROM users WHERE id = 1");
1446 assert!(result.contains("SELECT"));
1447 assert!(result.contains("FROM users"));
1448 }
1449
1450 #[test]
1451 fn test_len_to_length() {
1452 let result = transpile_to_postgres("SELECT LEN(name)");
1453 assert!(
1454 result.contains("LENGTH"),
1455 "Expected LENGTH, got: {}",
1456 result
1457 );
1458 }
1459
1460 #[test]
1461 fn test_getdate_to_current_timestamp() {
1462 let result = transpile_to_postgres("SELECT GETDATE()");
1463 assert!(
1464 result.contains("CURRENT_TIMESTAMP"),
1465 "Expected CURRENT_TIMESTAMP, got: {}",
1466 result
1467 );
1468 }
1469
1470 #[test]
1471 fn test_substr_to_substring() {
1472 let result = transpile_to_postgres("SELECT SUBSTR(name, 1, 3)");
1473 assert!(
1474 result.contains("SUBSTRING"),
1475 "Expected SUBSTRING, got: {}",
1476 result
1477 );
1478 }
1479
1480 #[test]
1481 fn test_group_concat_to_string_agg() {
1482 let result = transpile_to_postgres("SELECT GROUP_CONCAT(name)");
1483 assert!(
1484 result.contains("STRING_AGG"),
1485 "Expected STRING_AGG, got: {}",
1486 result
1487 );
1488 }
1489
1490 #[test]
1491 fn test_double_quote_identifiers() {
1492 let dialect = PostgresDialect;
1494 let config = dialect.generator_config();
1495 assert_eq!(config.identifier_quote, '"');
1496 }
1497
1498 #[test]
1499 fn test_char_length_to_length() {
1500 let result = transpile_to_postgres("SELECT CHAR_LENGTH(name)");
1501 assert!(
1502 result.contains("LENGTH"),
1503 "Expected LENGTH, got: {}",
1504 result
1505 );
1506 }
1507
1508 #[test]
1509 fn test_character_length_to_length() {
1510 let result = transpile_to_postgres("SELECT CHARACTER_LENGTH(name)");
1511 assert!(
1512 result.contains("LENGTH"),
1513 "Expected LENGTH, got: {}",
1514 result
1515 );
1516 }
1517
1518 fn identity_postgres(sql: &str) -> String {
1520 let dialect = Dialect::get(DialectType::PostgreSQL);
1521 let exprs = dialect.parse(sql).expect("Parse failed");
1522 let transformed = dialect.transform(exprs[0].clone()).expect("Transform failed");
1523 dialect.generate(&transformed).expect("Generate failed")
1524 }
1525
1526 #[test]
1527 fn test_json_extract_with_column_path() {
1528 let result = identity_postgres("json_data.data -> field_ids.field_id");
1530 assert!(
1531 result.contains("JSON_EXTRACT_PATH"),
1532 "Expected JSON_EXTRACT_PATH for column path, got: {}",
1533 result
1534 );
1535 }
1536
1537 #[test]
1538 fn test_json_extract_scalar_with_negative_index() {
1539 let result = identity_postgres("x::JSON -> 'duration' ->> -1");
1541 assert!(
1542 result.contains("JSON_EXTRACT_PATH_TEXT"),
1543 "Expected JSON_EXTRACT_PATH_TEXT for negative index, got: {}",
1544 result
1545 );
1546 assert!(
1548 result.contains("->"),
1549 "Expected -> for string literal path, got: {}",
1550 result
1551 );
1552 }
1553
1554 #[test]
1555 fn test_json_extract_with_string_literal() {
1556 let result = identity_postgres("data -> 'key'");
1558 assert!(
1559 result.contains("->"),
1560 "Expected -> for string literal path, got: {}",
1561 result
1562 );
1563 assert!(
1564 !result.contains("JSON_EXTRACT_PATH"),
1565 "Should NOT use function form for string literal, got: {}",
1566 result
1567 );
1568 }
1569}