1use super::{DialectImpl, DialectType};
19use crate::error::Result;
20use crate::expressions::{
21 AggFunc, BinaryOp, BooleanLiteral, Case, Cast, CeilFunc, DataType, DateTimeField, Expression,
22 ExtractFunc, Function, Interval, IntervalUnit, IntervalUnitSpec, Join, JoinKind, Literal,
23 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 => Expression::Paren(Box::new(Paren {
34 this: expr,
35 trailing_comments: Vec::new(),
36 })),
37 Expression::JsonExtractScalar(f) if f.arrow_syntax => Expression::Paren(Box::new(Paren {
38 this: expr,
39 trailing_comments: Vec::new(),
40 })),
41 _ => expr,
42 }
43}
44
45pub struct PostgresDialect;
47
48impl DialectImpl for PostgresDialect {
49 fn dialect_type(&self) -> DialectType {
50 DialectType::PostgreSQL
51 }
52
53 fn tokenizer_config(&self) -> TokenizerConfig {
54 use crate::tokens::TokenType;
55 let mut config = TokenizerConfig::default();
56 config.quotes.insert("$$".to_string(), "$$".to_string());
58 config.identifiers.insert('"', '"');
60 config.nested_comments = true;
62 config
65 .keywords
66 .insert("EXEC".to_string(), TokenType::Command);
67 config
68 }
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::PostgreSQL),
76 tz_to_with_time_zone: false,
78 single_string_interval: true,
80 tablesample_seed_keyword: "REPEATABLE",
82 nvl2_supported: false,
84 parameter_token: "$",
86 named_placeholder_token: "%",
88 supports_select_into: true,
90 index_using_no_space: true,
92 supports_unlogged_tables: true,
94 multi_arg_distinct: false,
96 quantified_no_paren_space: false,
98 supports_window_exclude: true,
100 normalize_window_frame_between: true,
102 copy_has_into_keyword: false,
104 array_size_dim_required: Some(true),
106 supports_between_flags: true,
108 join_hints: false,
110 table_hints: false,
111 query_hints: false,
112 locking_reads_supported: true,
114 rename_table_with_db: false,
116 can_implement_array_any: true,
118 array_concat_is_var_len: false,
120 supports_median: false,
122 json_type_required_for_extraction: true,
124 like_property_inside_schema: true,
126 ..Default::default()
127 }
128 }
129
130 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
131 match expr {
132 Expression::DataType(dt) => self.transform_data_type(dt),
137
138 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
143 original_name: None,
144 expressions: vec![f.this, f.expression],
145 inferred_type: None,
146 }))),
147
148 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
150 original_name: None,
151 expressions: vec![f.this, f.expression],
152 inferred_type: None,
153 }))),
154
155 Expression::Coalesce(mut f) => {
158 f.original_name = None;
159 Ok(Expression::Coalesce(f))
160 }
161
162 Expression::TryCast(c) => Ok(Expression::Cast(c)),
167
168 Expression::SafeCast(c) => Ok(Expression::Cast(c)),
170
171 Expression::Rand(r) => {
176 let _ = r.seed; Ok(Expression::Random(crate::expressions::Random))
179 }
180
181 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
186 "GEN_RANDOM_UUID".to_string(),
187 vec![],
188 )))),
189
190 Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
195 crate::expressions::UnnestFunc {
196 this: f.this,
197 expressions: Vec::new(),
198 with_ordinality: false,
199 alias: None,
200 offset_alias: None,
201 },
202 ))),
203
204 Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
206 crate::expressions::UnnestFunc {
207 this: f.this,
208 expressions: Vec::new(),
209 with_ordinality: false,
210 alias: None,
211 offset_alias: None,
212 },
213 ))),
214
215 Expression::ArrayConcat(f) => Ok(Expression::Function(Box::new(Function::new(
217 "ARRAY_CAT".to_string(),
218 f.expressions,
219 )))),
220
221 Expression::ArrayPrepend(f) => Ok(Expression::Function(Box::new(Function::new(
223 "ARRAY_PREPEND".to_string(),
224 vec![f.expression, f.this], )))),
226
227 Expression::BitwiseXor(f) => {
232 Ok(Expression::Function(Box::new(Function::new(
234 "__PG_BITWISE_XOR__".to_string(),
235 vec![f.left, f.right],
236 ))))
237 }
238
239 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
241 "BIT_AND".to_string(),
242 vec![f.this],
243 )))),
244
245 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
247 "BIT_OR".to_string(),
248 vec![f.this],
249 )))),
250
251 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
253 "BIT_XOR".to_string(),
254 vec![f.this],
255 )))),
256
257 Expression::LogicalAnd(f) => Ok(Expression::Function(Box::new(Function::new(
262 "BOOL_AND".to_string(),
263 vec![f.this],
264 )))),
265
266 Expression::LogicalOr(f) => Ok(Expression::Function(Box::new(Function::new(
268 "BOOL_OR".to_string(),
269 vec![f.this],
270 )))),
271
272 Expression::Xor(f) => {
274 if let (Some(a), Some(b)) = (f.this, f.expression) {
275 Ok(Expression::Neq(Box::new(BinaryOp {
276 left: *a,
277 right: *b,
278 left_comments: Vec::new(),
279 operator_comments: Vec::new(),
280 trailing_comments: Vec::new(),
281 inferred_type: None,
282 })))
283 } else {
284 Ok(Expression::Boolean(BooleanLiteral { value: false }))
285 }
286 }
287
288 Expression::ArrayContainedBy(op) => {
294 Ok(Expression::ArrayContainsAll(Box::new(BinaryOp {
295 left: op.right,
296 right: op.left,
297 left_comments: Vec::new(),
298 operator_comments: Vec::new(),
299 trailing_comments: Vec::new(),
300 inferred_type: None,
301 })))
302 }
303
304 Expression::RegexpLike(f) => {
309 Ok(Expression::RegexpLike(f))
311 }
312
313 Expression::DateAdd(f) => {
318 let is_literal = matches!(&f.interval, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_) | Literal::String(_)));
319 let right_expr = if is_literal {
320 Expression::Interval(Box::new(Interval {
322 this: Some(f.interval),
323 unit: Some(IntervalUnitSpec::Simple {
324 unit: f.unit,
325 use_plural: false,
326 }),
327 }))
328 } else {
329 let unit_str = match f.unit {
331 IntervalUnit::Year => "YEAR",
332 IntervalUnit::Quarter => "QUARTER",
333 IntervalUnit::Month => "MONTH",
334 IntervalUnit::Week => "WEEK",
335 IntervalUnit::Day => "DAY",
336 IntervalUnit::Hour => "HOUR",
337 IntervalUnit::Minute => "MINUTE",
338 IntervalUnit::Second => "SECOND",
339 IntervalUnit::Millisecond => "MILLISECOND",
340 IntervalUnit::Microsecond => "MICROSECOND",
341 IntervalUnit::Nanosecond => "NANOSECOND",
342 };
343 let interval_one = Expression::Interval(Box::new(Interval {
344 this: Some(Expression::Literal(Box::new(Literal::String(format!(
345 "1 {unit_str}"
346 ))))),
347 unit: None,
348 }));
349 Expression::Mul(Box::new(BinaryOp {
350 left: interval_one,
351 right: f.interval,
352 left_comments: Vec::new(),
353 operator_comments: Vec::new(),
354 trailing_comments: Vec::new(),
355 inferred_type: None,
356 }))
357 };
358 Ok(Expression::Add(Box::new(BinaryOp {
359 left: f.this,
360 right: right_expr,
361 left_comments: Vec::new(),
362 operator_comments: Vec::new(),
363 trailing_comments: Vec::new(),
364 inferred_type: None,
365 })))
366 }
367
368 Expression::DateSub(f) => {
370 let interval_expr = Expression::Interval(Box::new(Interval {
371 this: Some(f.interval),
372 unit: Some(IntervalUnitSpec::Simple {
373 unit: f.unit,
374 use_plural: false,
375 }),
376 }));
377 Ok(Expression::Sub(Box::new(BinaryOp {
378 left: f.this,
379 right: interval_expr,
380 left_comments: Vec::new(),
381 operator_comments: Vec::new(),
382 trailing_comments: Vec::new(),
383 inferred_type: None,
384 })))
385 }
386
387 Expression::DateDiff(f) => {
389 let unit = f.unit.unwrap_or(IntervalUnit::Day);
392
393 let cast_ts = |e: Expression| -> Expression {
395 Expression::Cast(Box::new(Cast {
396 this: e,
397 to: DataType::Timestamp {
398 precision: None,
399 timezone: false,
400 },
401 trailing_comments: Vec::new(),
402 double_colon_syntax: false,
403 format: None,
404 default: None,
405 inferred_type: None,
406 }))
407 };
408
409 let cast_bigint = |e: Expression| -> Expression {
411 Expression::Cast(Box::new(Cast {
412 this: e,
413 to: DataType::BigInt { length: None },
414 trailing_comments: Vec::new(),
415 double_colon_syntax: false,
416 format: None,
417 default: None,
418 inferred_type: None,
419 }))
420 };
421
422 let end_expr = f.this;
424 let start = f.expression;
425
426 let ts_diff = || -> Expression {
428 Expression::Sub(Box::new(BinaryOp::new(
429 cast_ts(end_expr.clone()),
430 cast_ts(start.clone()),
431 )))
432 };
433
434 let age_call = || -> Expression {
436 Expression::Function(Box::new(Function::new(
437 "AGE".to_string(),
438 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
439 )))
440 };
441
442 let extract = |field: DateTimeField, from: Expression| -> Expression {
444 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
445 };
446
447 let num = |n: i64| -> Expression {
449 Expression::Literal(Box::new(Literal::Number(n.to_string())))
450 };
451
452 let epoch_field = DateTimeField::Custom("epoch".to_string());
453
454 let result = match unit {
455 IntervalUnit::Nanosecond => {
456 let epoch = extract(epoch_field.clone(), ts_diff());
457 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
458 epoch,
459 num(1000000000),
460 ))))
461 }
462 IntervalUnit::Microsecond => {
463 let epoch = extract(epoch_field, ts_diff());
464 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
465 epoch,
466 num(1000000),
467 ))))
468 }
469 IntervalUnit::Millisecond => {
470 let epoch = extract(epoch_field, ts_diff());
471 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
472 }
473 IntervalUnit::Second => {
474 let epoch = extract(epoch_field, ts_diff());
475 cast_bigint(epoch)
476 }
477 IntervalUnit::Minute => {
478 let epoch = extract(epoch_field, ts_diff());
479 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
480 }
481 IntervalUnit::Hour => {
482 let epoch = extract(epoch_field, ts_diff());
483 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
484 }
485 IntervalUnit::Day => {
486 let epoch = extract(epoch_field, ts_diff());
487 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
488 }
489 IntervalUnit::Week => {
490 let diff_parens = Expression::Paren(Box::new(Paren {
491 this: ts_diff(),
492 trailing_comments: Vec::new(),
493 }));
494 let days = extract(DateTimeField::Custom("days".to_string()), diff_parens);
495 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
496 }
497 IntervalUnit::Month => {
498 let year_part =
499 extract(DateTimeField::Custom("year".to_string()), age_call());
500 let month_part =
501 extract(DateTimeField::Custom("month".to_string()), age_call());
502 let year_months =
503 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
504 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
505 year_months,
506 month_part,
507 ))))
508 }
509 IntervalUnit::Quarter => {
510 let year_part =
511 extract(DateTimeField::Custom("year".to_string()), age_call());
512 let month_part =
513 extract(DateTimeField::Custom("month".to_string()), age_call());
514 let year_quarters =
515 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
516 let month_quarters =
517 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
518 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
519 year_quarters,
520 month_quarters,
521 ))))
522 }
523 IntervalUnit::Year => cast_bigint(extract(
524 DateTimeField::Custom("year".to_string()),
525 age_call(),
526 )),
527 };
528 Ok(result)
529 }
530
531 Expression::UnixToTime(f) => Ok(Expression::Function(Box::new(Function::new(
533 "TO_TIMESTAMP".to_string(),
534 vec![*f.this],
535 )))),
536
537 Expression::TimeToUnix(f) => Ok(Expression::Function(Box::new(Function::new(
539 "DATE_PART".to_string(),
540 vec![Expression::string("epoch"), f.this],
541 )))),
542
543 Expression::ToTimestamp(f) => {
545 let mut args = vec![f.this];
546 if let Some(fmt) = f.format {
547 args.push(fmt);
548 }
549 Ok(Expression::Function(Box::new(Function::new(
550 "TO_TIMESTAMP".to_string(),
551 args,
552 ))))
553 }
554
555 Expression::ToDate(f) => {
557 let mut args = vec![f.this];
558 if let Some(fmt) = f.format {
559 args.push(fmt);
560 }
561 Ok(Expression::Function(Box::new(Function::new(
562 "TO_DATE".to_string(),
563 args,
564 ))))
565 }
566
567 Expression::TimestampTrunc(f) => {
569 let unit_str = format!("{:?}", f.unit).to_lowercase();
571 let args = vec![Expression::string(&unit_str), f.this];
572 Ok(Expression::Function(Box::new(Function::new(
573 "DATE_TRUNC".to_string(),
574 args,
575 ))))
576 }
577
578 Expression::TimeFromParts(f) => {
580 let mut args = Vec::new();
581 if let Some(h) = f.hour {
582 args.push(*h);
583 }
584 if let Some(m) = f.min {
585 args.push(*m);
586 }
587 if let Some(s) = f.sec {
588 args.push(*s);
589 }
590 Ok(Expression::Function(Box::new(Function::new(
591 "MAKE_TIME".to_string(),
592 args,
593 ))))
594 }
595
596 Expression::MakeTimestamp(f) => {
598 let args = vec![f.year, f.month, f.day, f.hour, f.minute, f.second];
600 Ok(Expression::Function(Box::new(Function::new(
601 "MAKE_TIMESTAMP".to_string(),
602 args,
603 ))))
604 }
605
606 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
611
612 Expression::GroupConcat(f) => {
614 let mut args = vec![f.this.clone()];
615 if let Some(sep) = f.separator.clone() {
616 args.push(sep);
617 } else {
618 args.push(Expression::string(","));
619 }
620 Ok(Expression::Function(Box::new(Function::new(
621 "STRING_AGG".to_string(),
622 args,
623 ))))
624 }
625
626 Expression::Position(f) => {
628 Ok(Expression::Position(f))
631 }
632
633 Expression::CountIf(f) => {
638 let case_expr = Expression::Case(Box::new(Case {
639 operand: None,
640 whens: vec![(f.this.clone(), Expression::number(1))],
641 else_: Some(Expression::number(0)),
642 comments: Vec::new(),
643 inferred_type: None,
644 }));
645 Ok(Expression::Sum(Box::new(AggFunc {
646 ignore_nulls: None,
647 having_max: None,
648 this: case_expr,
649 distinct: f.distinct,
650 filter: f.filter,
651 order_by: Vec::new(),
652 name: None,
653 limit: None,
654 inferred_type: None,
655 })))
656 }
657
658 Expression::AnyValue(f) => Ok(Expression::AnyValue(f)),
660
661 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
663 "VAR_SAMP".to_string(),
664 vec![f.this],
665 )))),
666
667 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
669 "VAR_POP".to_string(),
670 vec![f.this],
671 )))),
672
673 Expression::JsonExtract(mut f) => {
679 f.arrow_syntax = Self::is_simple_json_path(&f.path);
682 Ok(Expression::JsonExtract(f))
683 }
684
685 Expression::JsonExtractScalar(mut f) => {
689 if !f.hash_arrow_syntax {
690 f.arrow_syntax = Self::is_simple_json_path(&f.path);
693 }
694 Ok(Expression::JsonExtractScalar(f))
695 }
696
697 Expression::JsonObjectAgg(f) => {
701 let args = vec![f.key, f.value];
703 Ok(Expression::Function(Box::new(Function::new(
704 "JSON_OBJECT_AGG".to_string(),
705 args,
706 ))))
707 }
708
709 Expression::JsonArrayAgg(f) => Ok(Expression::Function(Box::new(Function::new(
711 "JSON_AGG".to_string(),
712 vec![f.this],
713 )))),
714
715 Expression::JSONPathRoot(_) => Ok(Expression::Literal(Box::new(Literal::String(
717 String::new(),
718 )))),
719
720 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
725 "DIV".to_string(),
726 vec![f.this, f.expression],
727 )))),
728
729 Expression::Unicode(f) => Ok(Expression::Function(Box::new(Function::new(
731 "ASCII".to_string(),
732 vec![f.this],
733 )))),
734
735 Expression::LastDay(f) => {
737 let truncated = Expression::Function(Box::new(Function::new(
739 "DATE_TRUNC".to_string(),
740 vec![Expression::string("month"), f.this.clone()],
741 )));
742 let plus_month = Expression::Add(Box::new(BinaryOp {
743 left: truncated,
744 right: Expression::Interval(Box::new(Interval {
745 this: Some(Expression::string("1")),
746 unit: Some(IntervalUnitSpec::Simple {
747 unit: IntervalUnit::Month,
748 use_plural: false,
749 }),
750 })),
751 left_comments: Vec::new(),
752 operator_comments: Vec::new(),
753 trailing_comments: Vec::new(),
754 inferred_type: None,
755 }));
756 let minus_day = Expression::Sub(Box::new(BinaryOp {
757 left: plus_month,
758 right: Expression::Interval(Box::new(Interval {
759 this: Some(Expression::string("1")),
760 unit: Some(IntervalUnitSpec::Simple {
761 unit: IntervalUnit::Day,
762 use_plural: false,
763 }),
764 })),
765 left_comments: Vec::new(),
766 operator_comments: Vec::new(),
767 trailing_comments: Vec::new(),
768 inferred_type: None,
769 }));
770 Ok(Expression::Cast(Box::new(Cast {
771 this: minus_day,
772 to: DataType::Date,
773 trailing_comments: Vec::new(),
774 double_colon_syntax: true, format: None,
776 default: None,
777 inferred_type: None,
778 })))
779 }
780
781 Expression::GenerateSeries(f) => Ok(Expression::GenerateSeries(f)),
783
784 Expression::ExplodingGenerateSeries(f) => {
786 let mut args = vec![f.start, f.stop];
787 if let Some(step) = f.step {
788 args.push(step); }
790 Ok(Expression::Function(Box::new(Function::new(
791 "GENERATE_SERIES".to_string(),
792 args,
793 ))))
794 }
795
796 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function {
801 name: "CURRENT_TIMESTAMP".to_string(),
802 args: vec![],
803 distinct: false,
804 trailing_comments: vec![],
805 use_bracket_syntax: false,
806 no_parens: true,
807 quoted: false,
808 span: None,
809 inferred_type: None,
810 }))),
811
812 Expression::CurrentUser(_) => Ok(Expression::Function(Box::new(Function::new(
814 "CURRENT_USER".to_string(),
815 vec![],
816 )))),
817
818 Expression::CurrentDate(_) => Ok(Expression::Function(Box::new(Function {
820 name: "CURRENT_DATE".to_string(),
821 args: vec![],
822 distinct: false,
823 trailing_comments: vec![],
824 use_bracket_syntax: false,
825 no_parens: true,
826 quoted: false,
827 span: None,
828 inferred_type: None,
829 }))),
830
831 Expression::Join(join) if join.kind == JoinKind::CrossApply => {
836 Ok(Expression::Join(Box::new(Join {
837 this: join.this,
838 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
839 using: join.using,
840 kind: JoinKind::CrossApply,
841 use_inner_keyword: false,
842 use_outer_keyword: false,
843 deferred_condition: false,
844 join_hint: None,
845 match_condition: None,
846 pivots: join.pivots,
847 comments: join.comments,
848 nesting_group: 0,
849 directed: false,
850 })))
851 }
852
853 Expression::Join(join) if join.kind == JoinKind::OuterApply => {
855 Ok(Expression::Join(Box::new(Join {
856 this: join.this,
857 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
858 using: join.using,
859 kind: JoinKind::OuterApply,
860 use_inner_keyword: false,
861 use_outer_keyword: false,
862 deferred_condition: false,
863 join_hint: None,
864 match_condition: None,
865 pivots: join.pivots,
866 comments: join.comments,
867 nesting_group: 0,
868 directed: false,
869 })))
870 }
871
872 Expression::Function(f) => self.transform_function(*f),
876
877 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
879
880 Expression::Eq(op) => Ok(Expression::Eq(Box::new(BinaryOp {
887 left: wrap_if_json_arrow(op.left),
888 right: wrap_if_json_arrow(op.right),
889 ..*op
890 }))),
891 Expression::Neq(op) => Ok(Expression::Neq(Box::new(BinaryOp {
892 left: wrap_if_json_arrow(op.left),
893 right: wrap_if_json_arrow(op.right),
894 ..*op
895 }))),
896 Expression::Lt(op) => Ok(Expression::Lt(Box::new(BinaryOp {
897 left: wrap_if_json_arrow(op.left),
898 right: wrap_if_json_arrow(op.right),
899 ..*op
900 }))),
901 Expression::Lte(op) => Ok(Expression::Lte(Box::new(BinaryOp {
902 left: wrap_if_json_arrow(op.left),
903 right: wrap_if_json_arrow(op.right),
904 ..*op
905 }))),
906 Expression::Gt(op) => Ok(Expression::Gt(Box::new(BinaryOp {
907 left: wrap_if_json_arrow(op.left),
908 right: wrap_if_json_arrow(op.right),
909 ..*op
910 }))),
911 Expression::Gte(op) => Ok(Expression::Gte(Box::new(BinaryOp {
912 left: wrap_if_json_arrow(op.left),
913 right: wrap_if_json_arrow(op.right),
914 ..*op
915 }))),
916
917 Expression::In(mut i) => {
919 i.this = wrap_if_json_arrow(i.this);
920 Ok(Expression::In(i))
921 }
922
923 Expression::Not(mut n) => {
925 n.this = wrap_if_json_arrow(n.this);
926 Ok(Expression::Not(n))
927 }
928
929 Expression::Merge(m) => Ok(Expression::Merge(m)),
932
933 Expression::JSONExtract(je) if je.variant_extract.is_some() => {
935 let path = match *je.expression {
938 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
939 let Literal::String(s) = lit.as_ref() else {
940 unreachable!()
941 };
942 let cleaned = if s.starts_with("[\"") && s.ends_with("\"]") {
944 s[2..s.len() - 2].to_string()
945 } else {
946 s.clone()
947 };
948 Expression::Literal(Box::new(Literal::String(cleaned)))
949 }
950 other => other,
951 };
952 Ok(Expression::Function(Box::new(Function::new(
953 "JSON_EXTRACT_PATH".to_string(),
954 vec![*je.this, path],
955 ))))
956 }
957
958 Expression::Trim(t) if !t.sql_standard_syntax && t.characters.is_some() => {
960 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
961 this: t.this,
962 characters: t.characters,
963 position: t.position,
964 sql_standard_syntax: true,
965 position_explicit: t.position_explicit,
966 })))
967 }
968
969 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::ByteString(_)) => {
971 let Literal::ByteString(s) = lit.as_ref() else {
972 unreachable!()
973 };
974 Ok(Expression::Cast(Box::new(Cast {
975 this: Expression::Literal(Box::new(Literal::EscapeString(s.clone()))),
976 to: DataType::VarBinary { length: None },
977 trailing_comments: Vec::new(),
978 double_colon_syntax: false,
979 format: None,
980 default: None,
981 inferred_type: None,
982 })))
983 }
984
985 _ => Ok(expr),
987 }
988 }
989}
990
991impl PostgresDialect {
992 fn is_simple_json_path(path: &Expression) -> bool {
996 match path {
997 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => true,
999 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1001 let Literal::Number(n) = lit.as_ref() else {
1002 unreachable!()
1003 };
1004 !n.starts_with('-')
1006 }
1007 Expression::JSONPath(_) => true,
1009 _ => false,
1011 }
1012 }
1013
1014 fn transform_data_type(&self, dt: DataType) -> Result<Expression> {
1016 let transformed = match dt {
1017 DataType::TinyInt { .. } => DataType::SmallInt { length: None },
1019
1020 DataType::Float { .. } => DataType::Custom {
1022 name: "DOUBLE PRECISION".to_string(),
1023 },
1024
1025 DataType::Double { .. } => DataType::Custom {
1027 name: "DOUBLE PRECISION".to_string(),
1028 },
1029
1030 DataType::Binary { .. } => dt,
1032
1033 DataType::VarBinary { .. } => dt,
1035
1036 DataType::Blob => DataType::Custom {
1038 name: "BYTEA".to_string(),
1039 },
1040
1041 DataType::Custom { ref name } => {
1043 let upper = name.to_uppercase();
1044 match upper.as_str() {
1045 "INT8" => DataType::BigInt { length: None },
1047 "FLOAT8" => DataType::Custom {
1049 name: "DOUBLE PRECISION".to_string(),
1050 },
1051 "FLOAT4" => DataType::Custom {
1053 name: "REAL".to_string(),
1054 },
1055 "INT4" => DataType::Int {
1057 length: None,
1058 integer_spelling: false,
1059 },
1060 "INT2" => DataType::SmallInt { length: None },
1062 _ => dt,
1063 }
1064 }
1065
1066 other => other,
1068 };
1069 Ok(Expression::DataType(transformed))
1070 }
1071
1072 fn transform_function(&self, f: Function) -> Result<Expression> {
1073 let name_upper = f.name.to_uppercase();
1074 match name_upper.as_str() {
1075 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1077 original_name: None,
1078 expressions: f.args,
1079 inferred_type: None,
1080 }))),
1081
1082 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1084 original_name: None,
1085 expressions: f.args,
1086 inferred_type: None,
1087 }))),
1088
1089 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1091 original_name: None,
1092 expressions: f.args,
1093 inferred_type: None,
1094 }))),
1095
1096 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1098 Function::new("STRING_AGG".to_string(), f.args),
1099 ))),
1100
1101 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1103 "SUBSTRING".to_string(),
1104 f.args,
1105 )))),
1106
1107 "RAND" => Ok(Expression::Random(crate::expressions::Random)),
1109
1110 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1112 this: f.args.into_iter().next().unwrap(),
1113 decimals: None,
1114 to: None,
1115 }))),
1116
1117 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1119 this: f.args.into_iter().next().unwrap(),
1120 original_name: None,
1121 inferred_type: None,
1122 }))),
1123
1124 "CHAR_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1126 this: f.args.into_iter().next().unwrap(),
1127 original_name: None,
1128 inferred_type: None,
1129 }))),
1130
1131 "CHARACTER_LENGTH" if f.args.len() == 1 => {
1133 Ok(Expression::Length(Box::new(UnaryFunc {
1134 this: f.args.into_iter().next().unwrap(),
1135 original_name: None,
1136 inferred_type: None,
1137 })))
1138 }
1139
1140 "CHARINDEX" if f.args.len() >= 2 => {
1143 let mut args = f.args;
1144 let substring = args.remove(0);
1145 let string = args.remove(0);
1146 Ok(Expression::Position(Box::new(
1147 crate::expressions::PositionFunc {
1148 substring,
1149 string,
1150 start: args.pop(),
1151 },
1152 )))
1153 }
1154
1155 "GETDATE" => Ok(Expression::CurrentTimestamp(
1157 crate::expressions::CurrentTimestamp {
1158 precision: None,
1159 sysdate: false,
1160 },
1161 )),
1162
1163 "SYSDATETIME" => Ok(Expression::CurrentTimestamp(
1165 crate::expressions::CurrentTimestamp {
1166 precision: None,
1167 sysdate: false,
1168 },
1169 )),
1170
1171 "NOW" => Ok(Expression::CurrentTimestamp(
1173 crate::expressions::CurrentTimestamp {
1174 precision: None,
1175 sysdate: false,
1176 },
1177 )),
1178
1179 "NEWID" => Ok(Expression::Function(Box::new(Function::new(
1181 "GEN_RANDOM_UUID".to_string(),
1182 vec![],
1183 )))),
1184
1185 "UUID" => Ok(Expression::Function(Box::new(Function::new(
1187 "GEN_RANDOM_UUID".to_string(),
1188 vec![],
1189 )))),
1190
1191 "UNNEST" => Ok(Expression::Function(Box::new(f))),
1193
1194 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(f))),
1196
1197 "SHA256" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1199 "SHA256".to_string(),
1200 f.args,
1201 )))),
1202
1203 "SHA2" if f.args.len() == 2 => {
1205 let args = f.args;
1207 let data = args[0].clone();
1208 Ok(Expression::Function(Box::new(Function::new(
1210 "SHA256".to_string(),
1211 vec![data],
1212 ))))
1213 }
1214
1215 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
1217
1218 "EDITDISTANCE" if f.args.len() == 3 => Ok(Expression::Function(Box::new(
1220 Function::new("LEVENSHTEIN_LESS_EQUAL".to_string(), f.args),
1221 ))),
1222 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1223 Function::new("LEVENSHTEIN".to_string(), f.args),
1224 ))),
1225
1226 "TRIM" if f.args.len() == 2 => {
1228 let value = f.args[0].clone();
1229 let chars = f.args[1].clone();
1230 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
1231 this: value,
1232 characters: Some(chars),
1233 position: crate::expressions::TrimPosition::Both,
1234 sql_standard_syntax: true,
1235 position_explicit: false,
1236 })))
1237 }
1238
1239 "DATEDIFF" if f.args.len() >= 2 => {
1241 let mut args = f.args;
1242 if args.len() == 2 {
1243 let first = args.remove(0);
1245 let second = args.remove(0);
1246 Ok(Expression::Function(Box::new(Function::new(
1247 "AGE".to_string(),
1248 vec![first, second],
1249 ))))
1250 } else {
1251 let unit_expr = args.remove(0);
1253 let start = args.remove(0);
1254 let end_expr = args.remove(0);
1255
1256 let unit_name = match &unit_expr {
1258 Expression::Identifier(id) => id.name.to_uppercase(),
1259 Expression::Var(v) => v.this.to_uppercase(),
1260 Expression::Column(col) if col.table.is_none() => {
1261 col.name.name.to_uppercase()
1262 }
1263 _ => "DAY".to_string(),
1264 };
1265
1266 let cast_ts = |e: Expression| -> Expression {
1268 Expression::Cast(Box::new(Cast {
1269 this: e,
1270 to: DataType::Timestamp {
1271 precision: None,
1272 timezone: false,
1273 },
1274 trailing_comments: Vec::new(),
1275 double_colon_syntax: false,
1276 format: None,
1277 default: None,
1278 inferred_type: None,
1279 }))
1280 };
1281
1282 let cast_bigint = |e: Expression| -> Expression {
1284 Expression::Cast(Box::new(Cast {
1285 this: e,
1286 to: DataType::BigInt { length: None },
1287 trailing_comments: Vec::new(),
1288 double_colon_syntax: false,
1289 format: None,
1290 default: None,
1291 inferred_type: None,
1292 }))
1293 };
1294
1295 let end_ts = cast_ts(end_expr.clone());
1296 let start_ts = cast_ts(start.clone());
1297
1298 let ts_diff = || -> Expression {
1300 Expression::Sub(Box::new(BinaryOp::new(
1301 cast_ts(end_expr.clone()),
1302 cast_ts(start.clone()),
1303 )))
1304 };
1305
1306 let age_call = || -> Expression {
1308 Expression::Function(Box::new(Function::new(
1309 "AGE".to_string(),
1310 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
1311 )))
1312 };
1313
1314 let extract = |field: DateTimeField, from: Expression| -> Expression {
1316 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
1317 };
1318
1319 let num = |n: i64| -> Expression {
1321 Expression::Literal(Box::new(Literal::Number(n.to_string())))
1322 };
1323
1324 let epoch_field = DateTimeField::Custom("epoch".to_string());
1326
1327 let result = match unit_name.as_str() {
1328 "MICROSECOND" => {
1329 let epoch = extract(epoch_field, ts_diff());
1331 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
1332 epoch,
1333 num(1000000),
1334 ))))
1335 }
1336 "MILLISECOND" => {
1337 let epoch = extract(epoch_field, ts_diff());
1338 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
1339 }
1340 "SECOND" => {
1341 let epoch = extract(epoch_field, ts_diff());
1342 cast_bigint(epoch)
1343 }
1344 "MINUTE" => {
1345 let epoch = extract(epoch_field, ts_diff());
1346 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
1347 }
1348 "HOUR" => {
1349 let epoch = extract(epoch_field, ts_diff());
1350 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
1351 }
1352 "DAY" => {
1353 let epoch = extract(epoch_field, ts_diff());
1354 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
1355 }
1356 "WEEK" => {
1357 let diff_parens = Expression::Paren(Box::new(Paren {
1359 this: ts_diff(),
1360 trailing_comments: Vec::new(),
1361 }));
1362 let days =
1363 extract(DateTimeField::Custom("days".to_string()), diff_parens);
1364 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
1365 }
1366 "MONTH" => {
1367 let year_part =
1369 extract(DateTimeField::Custom("year".to_string()), age_call());
1370 let month_part =
1371 extract(DateTimeField::Custom("month".to_string()), age_call());
1372 let year_months =
1373 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
1374 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1375 year_months,
1376 month_part,
1377 ))))
1378 }
1379 "QUARTER" => {
1380 let year_part =
1382 extract(DateTimeField::Custom("year".to_string()), age_call());
1383 let month_part =
1384 extract(DateTimeField::Custom("month".to_string()), age_call());
1385 let year_quarters =
1386 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
1387 let month_quarters =
1388 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
1389 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1390 year_quarters,
1391 month_quarters,
1392 ))))
1393 }
1394 "YEAR" => {
1395 cast_bigint(extract(
1397 DateTimeField::Custom("year".to_string()),
1398 age_call(),
1399 ))
1400 }
1401 _ => {
1402 Expression::Function(Box::new(Function::new(
1404 "AGE".to_string(),
1405 vec![end_ts, start_ts],
1406 )))
1407 }
1408 };
1409 Ok(result)
1410 }
1411 }
1412
1413 "TIMESTAMPDIFF" if f.args.len() >= 3 => {
1415 let mut args = f.args;
1416 let _unit = args.remove(0); let start = args.remove(0);
1418 let end = args.remove(0);
1419 Ok(Expression::Function(Box::new(Function::new(
1420 "AGE".to_string(),
1421 vec![end, start],
1422 ))))
1423 }
1424
1425 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1427 "TO_TIMESTAMP".to_string(),
1428 f.args,
1429 )))),
1430
1431 "UNIX_TIMESTAMP" if f.args.len() == 1 => {
1433 let arg = f.args.into_iter().next().unwrap();
1434 Ok(Expression::Function(Box::new(Function::new(
1435 "DATE_PART".to_string(),
1436 vec![Expression::string("epoch"), arg],
1437 ))))
1438 }
1439
1440 "UNIX_TIMESTAMP" if f.args.is_empty() => {
1442 Ok(Expression::Function(Box::new(Function::new(
1443 "DATE_PART".to_string(),
1444 vec![
1445 Expression::string("epoch"),
1446 Expression::CurrentTimestamp(crate::expressions::CurrentTimestamp {
1447 precision: None,
1448 sysdate: false,
1449 }),
1450 ],
1451 ))))
1452 }
1453
1454 "DATEADD" if f.args.len() == 3 => {
1456 let mut args = f.args;
1459 let _unit = args.remove(0);
1460 let count = args.remove(0);
1461 let date = args.remove(0);
1462 Ok(Expression::Add(Box::new(BinaryOp {
1463 left: date,
1464 right: count,
1465 left_comments: Vec::new(),
1466 operator_comments: Vec::new(),
1467 trailing_comments: Vec::new(),
1468 inferred_type: None,
1469 })))
1470 }
1471
1472 "INSTR" if f.args.len() >= 2 => {
1474 let mut args = f.args;
1475 let string = args.remove(0);
1476 let substring = args.remove(0);
1477 Ok(Expression::Position(Box::new(
1478 crate::expressions::PositionFunc {
1479 substring,
1480 string,
1481 start: args.pop(),
1482 },
1483 )))
1484 }
1485
1486 "CONCAT_WS" => Ok(Expression::Function(Box::new(f))),
1488
1489 "REGEXP_REPLACE" if f.args.len() == 3 || f.args.len() == 4 => {
1492 Ok(Expression::Function(Box::new(f)))
1493 }
1494 "REGEXP_REPLACE" if f.args.len() == 6 => {
1497 let is_global = match &f.args[4] {
1498 Expression::Literal(lit)
1499 if matches!(lit.as_ref(), crate::expressions::Literal::Number(_)) =>
1500 {
1501 let crate::expressions::Literal::Number(n) = lit.as_ref() else {
1502 unreachable!()
1503 };
1504 n == "0"
1505 }
1506 _ => false,
1507 };
1508 if is_global {
1509 let subject = f.args[0].clone();
1510 let pattern = f.args[1].clone();
1511 let replacement = f.args[2].clone();
1512 let position = f.args[3].clone();
1513 let occurrence = f.args[4].clone();
1514 let params = &f.args[5];
1515 let mut flags = if let Expression::Literal(lit) = params {
1516 if let crate::expressions::Literal::String(s) = lit.as_ref() {
1517 s.clone()
1518 } else {
1519 String::new()
1520 }
1521 } else {
1522 String::new()
1523 };
1524 if !flags.contains('g') {
1525 flags.push('g');
1526 }
1527 Ok(Expression::Function(Box::new(Function::new(
1528 "REGEXP_REPLACE".to_string(),
1529 vec![
1530 subject,
1531 pattern,
1532 replacement,
1533 position,
1534 occurrence,
1535 Expression::Literal(Box::new(crate::expressions::Literal::String(
1536 flags,
1537 ))),
1538 ],
1539 ))))
1540 } else {
1541 Ok(Expression::Function(Box::new(f)))
1542 }
1543 }
1544 "REGEXP_REPLACE" => Ok(Expression::Function(Box::new(f))),
1546
1547 _ => Ok(Expression::Function(Box::new(f))),
1549 }
1550 }
1551
1552 fn transform_aggregate_function(
1553 &self,
1554 f: Box<crate::expressions::AggregateFunction>,
1555 ) -> Result<Expression> {
1556 let name_upper = f.name.to_uppercase();
1557 match name_upper.as_str() {
1558 "COUNT_IF" if !f.args.is_empty() => {
1560 let condition = f.args.into_iter().next().unwrap();
1561 let case_expr = Expression::Case(Box::new(Case {
1562 operand: None,
1563 whens: vec![(condition, Expression::number(1))],
1564 else_: Some(Expression::number(0)),
1565 comments: Vec::new(),
1566 inferred_type: None,
1567 }));
1568 Ok(Expression::Sum(Box::new(AggFunc {
1569 ignore_nulls: None,
1570 having_max: None,
1571 this: case_expr,
1572 distinct: f.distinct,
1573 filter: f.filter,
1574 order_by: Vec::new(),
1575 name: None,
1576 limit: None,
1577 inferred_type: None,
1578 })))
1579 }
1580
1581 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1583 Function::new("STRING_AGG".to_string(), f.args),
1584 ))),
1585
1586 "STDEV" if !f.args.is_empty() => Ok(Expression::Stddev(Box::new(AggFunc {
1588 ignore_nulls: None,
1589 having_max: None,
1590 this: f.args.into_iter().next().unwrap(),
1591 distinct: f.distinct,
1592 filter: f.filter,
1593 order_by: Vec::new(),
1594 name: None,
1595 limit: None,
1596 inferred_type: None,
1597 }))),
1598
1599 "STDEVP" if !f.args.is_empty() => Ok(Expression::StddevPop(Box::new(AggFunc {
1601 ignore_nulls: None,
1602 having_max: None,
1603 this: f.args.into_iter().next().unwrap(),
1604 distinct: f.distinct,
1605 filter: f.filter,
1606 order_by: Vec::new(),
1607 name: None,
1608 limit: None,
1609 inferred_type: None,
1610 }))),
1611
1612 "VAR" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1614 ignore_nulls: None,
1615 having_max: None,
1616 this: f.args.into_iter().next().unwrap(),
1617 distinct: f.distinct,
1618 filter: f.filter,
1619 order_by: Vec::new(),
1620 name: None,
1621 limit: None,
1622 inferred_type: None,
1623 }))),
1624
1625 "VARP" if !f.args.is_empty() => Ok(Expression::VarPop(Box::new(AggFunc {
1627 ignore_nulls: None,
1628 having_max: None,
1629 this: f.args.into_iter().next().unwrap(),
1630 distinct: f.distinct,
1631 filter: f.filter,
1632 order_by: Vec::new(),
1633 name: None,
1634 limit: None,
1635 inferred_type: None,
1636 }))),
1637
1638 "BIT_AND" => Ok(Expression::AggregateFunction(f)),
1640
1641 "BIT_OR" => Ok(Expression::AggregateFunction(f)),
1643
1644 "BIT_XOR" => Ok(Expression::AggregateFunction(f)),
1646
1647 "BOOL_AND" => Ok(Expression::AggregateFunction(f)),
1649
1650 "BOOL_OR" => Ok(Expression::AggregateFunction(f)),
1652
1653 "VARIANCE" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1655 ignore_nulls: None,
1656 having_max: None,
1657 this: f.args.into_iter().next().unwrap(),
1658 distinct: f.distinct,
1659 filter: f.filter,
1660 order_by: Vec::new(),
1661 name: None,
1662 limit: None,
1663 inferred_type: None,
1664 }))),
1665
1666 "LOGICAL_OR" if !f.args.is_empty() => {
1668 let mut new_agg = f.clone();
1669 new_agg.name = "BOOL_OR".to_string();
1670 Ok(Expression::AggregateFunction(new_agg))
1671 }
1672
1673 "LOGICAL_AND" if !f.args.is_empty() => {
1675 let mut new_agg = f.clone();
1676 new_agg.name = "BOOL_AND".to_string();
1677 Ok(Expression::AggregateFunction(new_agg))
1678 }
1679
1680 _ => Ok(Expression::AggregateFunction(f)),
1682 }
1683 }
1684}
1685
1686#[cfg(test)]
1687mod tests {
1688 use super::*;
1689 use crate::dialects::Dialect;
1690
1691 fn transpile_to_postgres(sql: &str) -> String {
1692 let dialect = Dialect::get(DialectType::Generic);
1693 let result = dialect
1694 .transpile_to(sql, DialectType::PostgreSQL)
1695 .expect("Transpile failed");
1696 result[0].clone()
1697 }
1698
1699 #[test]
1700 fn test_ifnull_to_coalesce() {
1701 let result = transpile_to_postgres("SELECT IFNULL(a, b)");
1702 assert!(
1703 result.contains("COALESCE"),
1704 "Expected COALESCE, got: {}",
1705 result
1706 );
1707 }
1708
1709 #[test]
1710 fn test_nvl_to_coalesce() {
1711 let result = transpile_to_postgres("SELECT NVL(a, b)");
1712 assert!(
1713 result.contains("COALESCE"),
1714 "Expected COALESCE, got: {}",
1715 result
1716 );
1717 }
1718
1719 #[test]
1720 fn test_rand_to_random() {
1721 let result = transpile_to_postgres("SELECT RAND()");
1722 assert!(
1723 result.contains("RANDOM"),
1724 "Expected RANDOM, got: {}",
1725 result
1726 );
1727 }
1728
1729 #[test]
1730 fn test_basic_select() {
1731 let result = transpile_to_postgres("SELECT a, b FROM users WHERE id = 1");
1732 assert!(result.contains("SELECT"));
1733 assert!(result.contains("FROM users"));
1734 }
1735
1736 #[test]
1737 fn test_len_to_length() {
1738 let result = transpile_to_postgres("SELECT LEN(name)");
1739 assert!(
1740 result.contains("LENGTH"),
1741 "Expected LENGTH, got: {}",
1742 result
1743 );
1744 }
1745
1746 #[test]
1747 fn test_getdate_to_current_timestamp() {
1748 let result = transpile_to_postgres("SELECT GETDATE()");
1749 assert!(
1750 result.contains("CURRENT_TIMESTAMP"),
1751 "Expected CURRENT_TIMESTAMP, got: {}",
1752 result
1753 );
1754 }
1755
1756 #[test]
1757 fn test_substr_to_substring() {
1758 let result = transpile_to_postgres("SELECT SUBSTR(name, 1, 3)");
1759 assert!(
1760 result.contains("SUBSTRING"),
1761 "Expected SUBSTRING, got: {}",
1762 result
1763 );
1764 }
1765
1766 #[test]
1767 fn test_group_concat_to_string_agg() {
1768 let result = transpile_to_postgres("SELECT GROUP_CONCAT(name)");
1769 assert!(
1770 result.contains("STRING_AGG"),
1771 "Expected STRING_AGG, got: {}",
1772 result
1773 );
1774 }
1775
1776 #[test]
1777 fn test_double_quote_identifiers() {
1778 let dialect = PostgresDialect;
1780 let config = dialect.generator_config();
1781 assert_eq!(config.identifier_quote, '"');
1782 }
1783
1784 #[test]
1785 fn test_char_length_to_length() {
1786 let result = transpile_to_postgres("SELECT CHAR_LENGTH(name)");
1787 assert!(
1788 result.contains("LENGTH"),
1789 "Expected LENGTH, got: {}",
1790 result
1791 );
1792 }
1793
1794 #[test]
1795 fn test_character_length_to_length() {
1796 let result = transpile_to_postgres("SELECT CHARACTER_LENGTH(name)");
1797 assert!(
1798 result.contains("LENGTH"),
1799 "Expected LENGTH, got: {}",
1800 result
1801 );
1802 }
1803
1804 fn identity_postgres(sql: &str) -> String {
1806 let dialect = Dialect::get(DialectType::PostgreSQL);
1807 let exprs = dialect.parse(sql).expect("Parse failed");
1808 let transformed = dialect
1809 .transform(exprs[0].clone())
1810 .expect("Transform failed");
1811 dialect.generate(&transformed).expect("Generate failed")
1812 }
1813
1814 #[test]
1815 fn test_json_extract_with_column_path() {
1816 let result = identity_postgres("json_data.data -> field_ids.field_id");
1818 assert!(
1819 result.contains("JSON_EXTRACT_PATH"),
1820 "Expected JSON_EXTRACT_PATH for column path, got: {}",
1821 result
1822 );
1823 }
1824
1825 #[test]
1826 fn test_json_extract_scalar_with_negative_index() {
1827 let result = identity_postgres("x::JSON -> 'duration' ->> -1");
1829 assert!(
1830 result.contains("JSON_EXTRACT_PATH_TEXT"),
1831 "Expected JSON_EXTRACT_PATH_TEXT for negative index, got: {}",
1832 result
1833 );
1834 assert!(
1836 result.contains("->"),
1837 "Expected -> for string literal path, got: {}",
1838 result
1839 );
1840 }
1841
1842 #[test]
1843 fn test_json_extract_with_string_literal() {
1844 let result = identity_postgres("data -> 'key'");
1846 assert!(
1847 result.contains("->"),
1848 "Expected -> for string literal path, got: {}",
1849 result
1850 );
1851 assert!(
1852 !result.contains("JSON_EXTRACT_PATH"),
1853 "Should NOT use function form for string literal, got: {}",
1854 result
1855 );
1856 }
1857}