1use super::{DialectImpl, DialectType};
19use crate::error::Result;
20use crate::expressions::{
21 AggFunc, AggregateFunction, BinaryOp, BooleanLiteral, Case, Cast, CeilFunc, DataType,
22 DateTimeField, Expression, ExtractFunc, Function, Interval, IntervalUnit, IntervalUnitSpec,
23 Join, JoinKind, Literal, Paren, UnaryFunc, VarArgFunc,
24};
25#[cfg(feature = "generate")]
26use crate::generator::GeneratorConfig;
27use crate::tokens::TokenizerConfig;
28
29fn wrap_if_json_arrow(expr: Expression) -> Expression {
33 match &expr {
34 Expression::JsonExtract(f) if f.arrow_syntax => Expression::Paren(Box::new(Paren {
35 this: expr,
36 trailing_comments: Vec::new(),
37 })),
38 Expression::JsonExtractScalar(f) if f.arrow_syntax => Expression::Paren(Box::new(Paren {
39 this: expr,
40 trailing_comments: Vec::new(),
41 })),
42 _ => expr,
43 }
44}
45
46pub struct PostgresDialect;
48
49impl DialectImpl for PostgresDialect {
50 fn dialect_type(&self) -> DialectType {
51 DialectType::PostgreSQL
52 }
53
54 fn tokenizer_config(&self) -> TokenizerConfig {
55 use crate::tokens::TokenType;
56 let mut config = TokenizerConfig::default();
57 config.quotes.insert("$$".to_string(), "$$".to_string());
59 config.identifiers.insert('"', '"');
61 config.nested_comments = true;
63 config
66 .keywords
67 .insert("EXEC".to_string(), TokenType::Command);
68 for command in [
69 "BASE_BACKUP",
70 "CREATE_REPLICATION_SLOT",
71 "DROP_REPLICATION_SLOT",
72 "IDENTIFY_SYSTEM",
73 "READ_REPLICATION_SLOT",
74 "START_REPLICATION",
75 "TIMELINE_HISTORY",
76 ] {
77 config
78 .keywords
79 .insert(command.to_string(), TokenType::Command);
80 }
81 config
82 }
83
84 #[cfg(feature = "generate")]
85
86 fn generator_config(&self) -> GeneratorConfig {
87 use crate::generator::IdentifierQuoteStyle;
88 GeneratorConfig {
89 identifier_quote: '"',
90 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
91 dialect: Some(DialectType::PostgreSQL),
92 tz_to_with_time_zone: false,
94 single_string_interval: true,
96 tablesample_seed_keyword: "REPEATABLE",
98 nvl2_supported: false,
100 parameter_token: "$",
102 named_placeholder_token: "%",
104 supports_select_into: true,
106 index_using_no_space: true,
108 supports_unlogged_tables: true,
110 multi_arg_distinct: false,
112 quantified_no_paren_space: false,
114 supports_window_exclude: true,
116 normalize_window_frame_between: true,
118 copy_has_into_keyword: false,
120 array_size_dim_required: Some(true),
122 supports_between_flags: true,
124 join_hints: false,
126 table_hints: false,
127 query_hints: false,
128 locking_reads_supported: true,
130 rename_table_with_db: false,
132 can_implement_array_any: true,
134 array_concat_is_var_len: false,
136 supports_median: false,
138 json_type_required_for_extraction: true,
140 like_property_inside_schema: true,
142 ..Default::default()
143 }
144 }
145
146 #[cfg(feature = "transpile")]
147
148 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
149 match expr {
150 Expression::DataType(dt) => self.transform_data_type(dt),
155
156 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
161 original_name: None,
162 expressions: vec![f.this, f.expression],
163 inferred_type: None,
164 }))),
165
166 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
168 original_name: None,
169 expressions: vec![f.this, f.expression],
170 inferred_type: None,
171 }))),
172
173 Expression::Coalesce(mut f) => {
176 f.original_name = None;
177 Ok(Expression::Coalesce(f))
178 }
179
180 Expression::TryCast(c) => Ok(Expression::Cast(c)),
185
186 Expression::SafeCast(c) => Ok(Expression::Cast(c)),
188
189 Expression::Rand(r) => {
194 let _ = r.seed; Ok(Expression::Random(crate::expressions::Random))
197 }
198
199 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
204 "GEN_RANDOM_UUID".to_string(),
205 vec![],
206 )))),
207
208 Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
213 crate::expressions::UnnestFunc {
214 this: f.this,
215 expressions: Vec::new(),
216 with_ordinality: false,
217 alias: None,
218 offset_alias: None,
219 inferred_type: None,
220 },
221 ))),
222
223 Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
225 crate::expressions::UnnestFunc {
226 this: f.this,
227 expressions: Vec::new(),
228 with_ordinality: false,
229 alias: None,
230 offset_alias: None,
231 inferred_type: None,
232 },
233 ))),
234
235 Expression::ArrayConcat(f) => Ok(Expression::Function(Box::new(Function::new(
237 "ARRAY_CAT".to_string(),
238 f.expressions,
239 )))),
240
241 Expression::ArrayPrepend(f) => Ok(Expression::Function(Box::new(Function::new(
243 "ARRAY_PREPEND".to_string(),
244 vec![f.expression, f.this], )))),
246
247 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
249 "BIT_AND".to_string(),
250 vec![f.this],
251 )))),
252
253 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
255 "BIT_OR".to_string(),
256 vec![f.this],
257 )))),
258
259 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
261 "BIT_XOR".to_string(),
262 vec![f.this],
263 )))),
264
265 Expression::LogicalAnd(f) => {
270 Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
271 name: "BOOL_AND".to_string(),
272 args: vec![f.this],
273 distinct: f.distinct,
274 filter: f.filter,
275 order_by: f.order_by,
276 limit: f.limit,
277 ignore_nulls: f.ignore_nulls,
278 inferred_type: f.inferred_type,
279 })))
280 }
281
282 Expression::LogicalOr(f) => {
284 Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
285 name: "BOOL_OR".to_string(),
286 args: vec![f.this],
287 distinct: f.distinct,
288 filter: f.filter,
289 order_by: f.order_by,
290 limit: f.limit,
291 ignore_nulls: f.ignore_nulls,
292 inferred_type: f.inferred_type,
293 })))
294 }
295
296 Expression::Xor(f) => {
298 if let (Some(a), Some(b)) = (f.this, f.expression) {
299 Ok(Expression::Neq(Box::new(BinaryOp {
300 left: *a,
301 right: *b,
302 left_comments: Vec::new(),
303 operator_comments: Vec::new(),
304 trailing_comments: Vec::new(),
305 inferred_type: None,
306 })))
307 } else {
308 Ok(Expression::Boolean(BooleanLiteral { value: false }))
309 }
310 }
311
312 Expression::RegexpLike(f) => {
317 Ok(Expression::RegexpLike(f))
319 }
320
321 Expression::DateAdd(f) => {
326 let is_literal = matches!(&f.interval, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_) | Literal::String(_)));
327 let right_expr = if is_literal {
328 Expression::Interval(Box::new(Interval {
330 this: Some(f.interval),
331 unit: Some(IntervalUnitSpec::Simple {
332 unit: f.unit,
333 use_plural: false,
334 }),
335 }))
336 } else {
337 let unit_str = match f.unit {
339 IntervalUnit::Year => "YEAR",
340 IntervalUnit::Quarter => "QUARTER",
341 IntervalUnit::Month => "MONTH",
342 IntervalUnit::Week => "WEEK",
343 IntervalUnit::Day => "DAY",
344 IntervalUnit::Hour => "HOUR",
345 IntervalUnit::Minute => "MINUTE",
346 IntervalUnit::Second => "SECOND",
347 IntervalUnit::Millisecond => "MILLISECOND",
348 IntervalUnit::Microsecond => "MICROSECOND",
349 IntervalUnit::Nanosecond => "NANOSECOND",
350 };
351 let interval_one = Expression::Interval(Box::new(Interval {
352 this: Some(Expression::Literal(Box::new(Literal::String(format!(
353 "1 {unit_str}"
354 ))))),
355 unit: None,
356 }));
357 Expression::Mul(Box::new(BinaryOp {
358 left: interval_one,
359 right: f.interval,
360 left_comments: Vec::new(),
361 operator_comments: Vec::new(),
362 trailing_comments: Vec::new(),
363 inferred_type: None,
364 }))
365 };
366 Ok(Expression::Add(Box::new(BinaryOp {
367 left: f.this,
368 right: right_expr,
369 left_comments: Vec::new(),
370 operator_comments: Vec::new(),
371 trailing_comments: Vec::new(),
372 inferred_type: None,
373 })))
374 }
375
376 Expression::DateSub(f) => {
378 let interval_expr = Expression::Interval(Box::new(Interval {
379 this: Some(f.interval),
380 unit: Some(IntervalUnitSpec::Simple {
381 unit: f.unit,
382 use_plural: false,
383 }),
384 }));
385 Ok(Expression::Sub(Box::new(BinaryOp {
386 left: f.this,
387 right: interval_expr,
388 left_comments: Vec::new(),
389 operator_comments: Vec::new(),
390 trailing_comments: Vec::new(),
391 inferred_type: None,
392 })))
393 }
394
395 Expression::DateDiff(f) => {
397 let unit = f.unit.unwrap_or(IntervalUnit::Day);
400
401 let cast_ts = |e: Expression| -> Expression {
403 Expression::Cast(Box::new(Cast {
404 this: e,
405 to: DataType::Timestamp {
406 precision: None,
407 timezone: false,
408 },
409 trailing_comments: Vec::new(),
410 double_colon_syntax: false,
411 format: None,
412 default: None,
413 inferred_type: None,
414 }))
415 };
416
417 let cast_bigint = |e: Expression| -> Expression {
419 Expression::Cast(Box::new(Cast {
420 this: e,
421 to: DataType::BigInt { length: None },
422 trailing_comments: Vec::new(),
423 double_colon_syntax: false,
424 format: None,
425 default: None,
426 inferred_type: None,
427 }))
428 };
429
430 let end_expr = f.this;
432 let start = f.expression;
433
434 let ts_diff = || -> Expression {
436 Expression::Sub(Box::new(BinaryOp::new(
437 cast_ts(end_expr.clone()),
438 cast_ts(start.clone()),
439 )))
440 };
441
442 let age_call = || -> Expression {
444 Expression::Function(Box::new(Function::new(
445 "AGE".to_string(),
446 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
447 )))
448 };
449
450 let extract = |field: DateTimeField, from: Expression| -> Expression {
452 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
453 };
454
455 let num = |n: i64| -> Expression {
457 Expression::Literal(Box::new(Literal::Number(n.to_string())))
458 };
459
460 let epoch_field = DateTimeField::Custom("epoch".to_string());
461
462 let result = match unit {
463 IntervalUnit::Nanosecond => {
464 let epoch = extract(epoch_field.clone(), ts_diff());
465 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
466 epoch,
467 num(1000000000),
468 ))))
469 }
470 IntervalUnit::Microsecond => {
471 let epoch = extract(epoch_field, ts_diff());
472 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
473 epoch,
474 num(1000000),
475 ))))
476 }
477 IntervalUnit::Millisecond => {
478 let epoch = extract(epoch_field, ts_diff());
479 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
480 }
481 IntervalUnit::Second => {
482 let epoch = extract(epoch_field, ts_diff());
483 cast_bigint(epoch)
484 }
485 IntervalUnit::Minute => {
486 let epoch = extract(epoch_field, ts_diff());
487 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
488 }
489 IntervalUnit::Hour => {
490 let epoch = extract(epoch_field, ts_diff());
491 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
492 }
493 IntervalUnit::Day => {
494 let epoch = extract(epoch_field, ts_diff());
495 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
496 }
497 IntervalUnit::Week => {
498 let diff_parens = Expression::Paren(Box::new(Paren {
499 this: ts_diff(),
500 trailing_comments: Vec::new(),
501 }));
502 let days = extract(DateTimeField::Custom("days".to_string()), diff_parens);
503 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
504 }
505 IntervalUnit::Month => {
506 let year_part =
507 extract(DateTimeField::Custom("year".to_string()), age_call());
508 let month_part =
509 extract(DateTimeField::Custom("month".to_string()), age_call());
510 let year_months =
511 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
512 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
513 year_months,
514 month_part,
515 ))))
516 }
517 IntervalUnit::Quarter => {
518 let year_part =
519 extract(DateTimeField::Custom("year".to_string()), age_call());
520 let month_part =
521 extract(DateTimeField::Custom("month".to_string()), age_call());
522 let year_quarters =
523 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
524 let month_quarters =
525 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
526 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
527 year_quarters,
528 month_quarters,
529 ))))
530 }
531 IntervalUnit::Year => cast_bigint(extract(
532 DateTimeField::Custom("year".to_string()),
533 age_call(),
534 )),
535 };
536 Ok(result)
537 }
538
539 Expression::UnixToTime(f) => Ok(Expression::Function(Box::new(Function::new(
541 "TO_TIMESTAMP".to_string(),
542 vec![*f.this],
543 )))),
544
545 Expression::TimeToUnix(f) => Ok(Expression::Function(Box::new(Function::new(
547 "DATE_PART".to_string(),
548 vec![Expression::string("epoch"), f.this],
549 )))),
550
551 Expression::ToTimestamp(f) => {
553 let mut args = vec![f.this];
554 if let Some(fmt) = f.format {
555 args.push(fmt);
556 }
557 Ok(Expression::Function(Box::new(Function::new(
558 "TO_TIMESTAMP".to_string(),
559 args,
560 ))))
561 }
562
563 Expression::ToDate(f) => {
565 let mut args = vec![f.this];
566 if let Some(fmt) = f.format {
567 args.push(fmt);
568 }
569 Ok(Expression::Function(Box::new(Function::new(
570 "TO_DATE".to_string(),
571 args,
572 ))))
573 }
574
575 Expression::TimestampTrunc(f) => {
577 let unit_str = format!("{:?}", f.unit).to_lowercase();
579 let args = vec![Expression::string(&unit_str), f.this];
580 Ok(Expression::Function(Box::new(Function::new(
581 "DATE_TRUNC".to_string(),
582 args,
583 ))))
584 }
585
586 Expression::TimeFromParts(f) => {
588 let mut args = Vec::new();
589 if let Some(h) = f.hour {
590 args.push(*h);
591 }
592 if let Some(m) = f.min {
593 args.push(*m);
594 }
595 if let Some(s) = f.sec {
596 args.push(*s);
597 }
598 Ok(Expression::Function(Box::new(Function::new(
599 "MAKE_TIME".to_string(),
600 args,
601 ))))
602 }
603
604 Expression::MakeTimestamp(f) => {
606 let args = vec![f.year, f.month, f.day, f.hour, f.minute, f.second];
608 Ok(Expression::Function(Box::new(Function::new(
609 "MAKE_TIMESTAMP".to_string(),
610 args,
611 ))))
612 }
613
614 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
619
620 Expression::GroupConcat(f) => {
622 let mut args = vec![f.this.clone()];
623 if let Some(sep) = f.separator.clone() {
624 args.push(sep);
625 } else {
626 args.push(Expression::string(","));
627 }
628 Ok(Expression::Function(Box::new(Function::new(
629 "STRING_AGG".to_string(),
630 args,
631 ))))
632 }
633
634 Expression::Position(f) => {
636 Ok(Expression::Position(f))
639 }
640
641 Expression::CountIf(f) => {
646 let case_expr = Expression::Case(Box::new(Case {
647 operand: None,
648 whens: vec![(f.this.clone(), Expression::number(1))],
649 else_: Some(Expression::number(0)),
650 comments: Vec::new(),
651 inferred_type: None,
652 }));
653 Ok(Expression::Sum(Box::new(AggFunc {
654 ignore_nulls: None,
655 having_max: None,
656 this: case_expr,
657 distinct: f.distinct,
658 filter: f.filter,
659 order_by: Vec::new(),
660 name: None,
661 limit: None,
662 inferred_type: None,
663 })))
664 }
665
666 Expression::AnyValue(f) => Ok(Expression::AnyValue(f)),
668
669 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
671 "VAR_SAMP".to_string(),
672 vec![f.this],
673 )))),
674
675 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
677 "VAR_POP".to_string(),
678 vec![f.this],
679 )))),
680
681 Expression::JsonExtract(mut f) => {
687 f.arrow_syntax = Self::is_simple_json_path(&f.path);
690 Ok(Expression::JsonExtract(f))
691 }
692
693 Expression::JsonExtractScalar(mut f) => {
697 if !f.hash_arrow_syntax {
698 f.arrow_syntax = Self::is_simple_json_path(&f.path);
701 }
702 Ok(Expression::JsonExtractScalar(f))
703 }
704
705 Expression::JsonObjectAgg(f) => {
709 let args = vec![f.key, f.value];
711 Ok(Expression::Function(Box::new(Function::new(
712 "JSON_OBJECT_AGG".to_string(),
713 args,
714 ))))
715 }
716
717 Expression::JsonArrayAgg(f) => Ok(Expression::Function(Box::new(Function::new(
719 "JSON_AGG".to_string(),
720 vec![f.this],
721 )))),
722
723 Expression::JSONPathRoot(_) => Ok(Expression::Literal(Box::new(Literal::String(
725 String::new(),
726 )))),
727
728 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
733 "DIV".to_string(),
734 vec![f.this, f.expression],
735 )))),
736
737 Expression::Unicode(f) => Ok(Expression::Function(Box::new(Function::new(
739 "ASCII".to_string(),
740 vec![f.this],
741 )))),
742
743 Expression::LastDay(f) => {
745 let truncated = Expression::Function(Box::new(Function::new(
747 "DATE_TRUNC".to_string(),
748 vec![Expression::string("month"), f.this.clone()],
749 )));
750 let plus_month = Expression::Add(Box::new(BinaryOp {
751 left: truncated,
752 right: Expression::Interval(Box::new(Interval {
753 this: Some(Expression::string("1")),
754 unit: Some(IntervalUnitSpec::Simple {
755 unit: IntervalUnit::Month,
756 use_plural: false,
757 }),
758 })),
759 left_comments: Vec::new(),
760 operator_comments: Vec::new(),
761 trailing_comments: Vec::new(),
762 inferred_type: None,
763 }));
764 let minus_day = Expression::Sub(Box::new(BinaryOp {
765 left: plus_month,
766 right: Expression::Interval(Box::new(Interval {
767 this: Some(Expression::string("1")),
768 unit: Some(IntervalUnitSpec::Simple {
769 unit: IntervalUnit::Day,
770 use_plural: false,
771 }),
772 })),
773 left_comments: Vec::new(),
774 operator_comments: Vec::new(),
775 trailing_comments: Vec::new(),
776 inferred_type: None,
777 }));
778 Ok(Expression::Cast(Box::new(Cast {
779 this: minus_day,
780 to: DataType::Date,
781 trailing_comments: Vec::new(),
782 double_colon_syntax: true, format: None,
784 default: None,
785 inferred_type: None,
786 })))
787 }
788
789 Expression::GenerateSeries(f) => Ok(Expression::GenerateSeries(f)),
791
792 Expression::ExplodingGenerateSeries(f) => {
794 let mut args = vec![f.start, f.stop];
795 if let Some(step) = f.step {
796 args.push(step); }
798 Ok(Expression::Function(Box::new(Function::new(
799 "GENERATE_SERIES".to_string(),
800 args,
801 ))))
802 }
803
804 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function {
809 name: "CURRENT_TIMESTAMP".to_string(),
810 args: vec![],
811 distinct: false,
812 trailing_comments: vec![],
813 use_bracket_syntax: false,
814 no_parens: true,
815 quoted: false,
816 span: None,
817 inferred_type: None,
818 }))),
819
820 Expression::CurrentUser(_) => Ok(Expression::Function(Box::new(Function::new(
822 "CURRENT_USER".to_string(),
823 vec![],
824 )))),
825
826 Expression::CurrentDate(_) => Ok(Expression::Function(Box::new(Function {
828 name: "CURRENT_DATE".to_string(),
829 args: vec![],
830 distinct: false,
831 trailing_comments: vec![],
832 use_bracket_syntax: false,
833 no_parens: true,
834 quoted: false,
835 span: None,
836 inferred_type: None,
837 }))),
838
839 Expression::Join(join) if join.kind == JoinKind::CrossApply => {
844 Ok(Expression::Join(Box::new(Join {
845 this: join.this,
846 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
847 using: join.using,
848 kind: JoinKind::CrossApply,
849 use_inner_keyword: false,
850 use_outer_keyword: false,
851 deferred_condition: false,
852 join_hint: None,
853 match_condition: None,
854 pivots: join.pivots,
855 comments: join.comments,
856 nesting_group: 0,
857 directed: false,
858 })))
859 }
860
861 Expression::Join(join) if join.kind == JoinKind::OuterApply => {
863 Ok(Expression::Join(Box::new(Join {
864 this: join.this,
865 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
866 using: join.using,
867 kind: JoinKind::OuterApply,
868 use_inner_keyword: false,
869 use_outer_keyword: false,
870 deferred_condition: false,
871 join_hint: None,
872 match_condition: None,
873 pivots: join.pivots,
874 comments: join.comments,
875 nesting_group: 0,
876 directed: false,
877 })))
878 }
879
880 Expression::Function(f) => self.transform_function(*f),
884
885 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
887
888 Expression::Eq(op) => Ok(Expression::Eq(Box::new(BinaryOp {
895 left: wrap_if_json_arrow(op.left),
896 right: wrap_if_json_arrow(op.right),
897 ..*op
898 }))),
899 Expression::Neq(op) => Ok(Expression::Neq(Box::new(BinaryOp {
900 left: wrap_if_json_arrow(op.left),
901 right: wrap_if_json_arrow(op.right),
902 ..*op
903 }))),
904 Expression::Lt(op) => Ok(Expression::Lt(Box::new(BinaryOp {
905 left: wrap_if_json_arrow(op.left),
906 right: wrap_if_json_arrow(op.right),
907 ..*op
908 }))),
909 Expression::Lte(op) => Ok(Expression::Lte(Box::new(BinaryOp {
910 left: wrap_if_json_arrow(op.left),
911 right: wrap_if_json_arrow(op.right),
912 ..*op
913 }))),
914 Expression::Gt(op) => Ok(Expression::Gt(Box::new(BinaryOp {
915 left: wrap_if_json_arrow(op.left),
916 right: wrap_if_json_arrow(op.right),
917 ..*op
918 }))),
919 Expression::Gte(op) => Ok(Expression::Gte(Box::new(BinaryOp {
920 left: wrap_if_json_arrow(op.left),
921 right: wrap_if_json_arrow(op.right),
922 ..*op
923 }))),
924
925 Expression::In(mut i) => {
927 i.this = wrap_if_json_arrow(i.this);
928 Ok(Expression::In(i))
929 }
930
931 Expression::Not(mut n) => {
933 n.this = wrap_if_json_arrow(n.this);
934 Ok(Expression::Not(n))
935 }
936
937 Expression::Merge(m) => Ok(Expression::Merge(m)),
940
941 Expression::JSONExtract(je) if je.variant_extract.is_some() => {
943 let path = match *je.expression {
946 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
947 let Literal::String(s) = lit.as_ref() else {
948 unreachable!()
949 };
950 let cleaned = if s.starts_with("[\"") && s.ends_with("\"]") {
952 s[2..s.len() - 2].to_string()
953 } else {
954 s.clone()
955 };
956 Expression::Literal(Box::new(Literal::String(cleaned)))
957 }
958 other => other,
959 };
960 Ok(Expression::Function(Box::new(Function::new(
961 "JSON_EXTRACT_PATH".to_string(),
962 vec![*je.this, path],
963 ))))
964 }
965
966 Expression::Trim(t) if !t.sql_standard_syntax && t.characters.is_some() => {
968 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
969 this: t.this,
970 characters: t.characters,
971 position: t.position,
972 sql_standard_syntax: true,
973 position_explicit: t.position_explicit,
974 inferred_type: t.inferred_type,
975 })))
976 }
977
978 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::ByteString(_)) => {
980 let Literal::ByteString(s) = lit.as_ref() else {
981 unreachable!()
982 };
983 Ok(Expression::Cast(Box::new(Cast {
984 this: Expression::Literal(Box::new(Literal::EscapeString(s.clone()))),
985 to: DataType::VarBinary { length: None },
986 trailing_comments: Vec::new(),
987 double_colon_syntax: false,
988 format: None,
989 default: None,
990 inferred_type: None,
991 })))
992 }
993
994 _ => Ok(expr),
996 }
997 }
998}
999
1000#[cfg(feature = "transpile")]
1001impl PostgresDialect {
1002 fn is_simple_json_path(path: &Expression) -> bool {
1006 match path {
1007 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => true,
1009 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1011 let Literal::Number(n) = lit.as_ref() else {
1012 unreachable!()
1013 };
1014 !n.starts_with('-')
1016 }
1017 Expression::JSONPath(_) => true,
1019 _ => false,
1021 }
1022 }
1023
1024 fn transform_data_type(&self, dt: DataType) -> Result<Expression> {
1026 let transformed = match dt {
1027 DataType::TinyInt { .. } => DataType::SmallInt { length: None },
1029
1030 DataType::Float { real_spelling, .. } => DataType::Custom {
1032 name: if real_spelling {
1033 "REAL".to_string()
1034 } else {
1035 "DOUBLE PRECISION".to_string()
1036 },
1037 },
1038
1039 DataType::Double { .. } => DataType::Custom {
1041 name: "DOUBLE PRECISION".to_string(),
1042 },
1043
1044 DataType::Binary { .. } => dt,
1046
1047 DataType::VarBinary { .. } => dt,
1049
1050 DataType::Blob => DataType::Custom {
1052 name: "BYTEA".to_string(),
1053 },
1054
1055 DataType::Custom { ref name } => {
1057 let upper = name.to_uppercase();
1058 match upper.as_str() {
1059 "INT8" => DataType::BigInt { length: None },
1061 "FLOAT8" => DataType::Custom {
1063 name: "DOUBLE PRECISION".to_string(),
1064 },
1065 "FLOAT4" => DataType::Custom {
1067 name: "REAL".to_string(),
1068 },
1069 "INT4" => DataType::Int {
1071 length: None,
1072 integer_spelling: false,
1073 },
1074 "INT2" => DataType::SmallInt { length: None },
1076 _ => dt,
1077 }
1078 }
1079
1080 other => other,
1082 };
1083 Ok(Expression::DataType(transformed))
1084 }
1085
1086 fn transform_function(&self, f: Function) -> Result<Expression> {
1087 let name_upper = f.name.to_uppercase();
1088 match name_upper.as_str() {
1089 "IFNULL" 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 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1098 original_name: None,
1099 expressions: f.args,
1100 inferred_type: None,
1101 }))),
1102
1103 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1105 original_name: None,
1106 expressions: f.args,
1107 inferred_type: None,
1108 }))),
1109
1110 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1112 Function::new("STRING_AGG".to_string(), f.args),
1113 ))),
1114
1115 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1117 "SUBSTRING".to_string(),
1118 f.args,
1119 )))),
1120
1121 "RAND" => Ok(Expression::Random(crate::expressions::Random)),
1123
1124 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1126 this: f.args.into_iter().next().unwrap(),
1127 decimals: None,
1128 to: None,
1129 }))),
1130
1131 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1133 this: f.args.into_iter().next().unwrap(),
1134 original_name: None,
1135 inferred_type: None,
1136 }))),
1137
1138 "CHAR_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1140 this: f.args.into_iter().next().unwrap(),
1141 original_name: None,
1142 inferred_type: None,
1143 }))),
1144
1145 "CHARACTER_LENGTH" if f.args.len() == 1 => {
1147 Ok(Expression::Length(Box::new(UnaryFunc {
1148 this: f.args.into_iter().next().unwrap(),
1149 original_name: None,
1150 inferred_type: None,
1151 })))
1152 }
1153
1154 "CHARINDEX" if f.args.len() >= 2 => {
1157 let mut args = f.args;
1158 let substring = args.remove(0);
1159 let string = args.remove(0);
1160 Ok(Expression::Position(Box::new(
1161 crate::expressions::PositionFunc {
1162 substring,
1163 string,
1164 start: args.pop(),
1165 },
1166 )))
1167 }
1168
1169 "GETDATE" => Ok(Expression::CurrentTimestamp(
1171 crate::expressions::CurrentTimestamp {
1172 precision: None,
1173 sysdate: false,
1174 },
1175 )),
1176
1177 "SYSDATETIME" => Ok(Expression::CurrentTimestamp(
1179 crate::expressions::CurrentTimestamp {
1180 precision: None,
1181 sysdate: false,
1182 },
1183 )),
1184
1185 "NOW" => Ok(Expression::CurrentTimestamp(
1187 crate::expressions::CurrentTimestamp {
1188 precision: None,
1189 sysdate: false,
1190 },
1191 )),
1192
1193 "GEN_RANDOM_UUID" | "UUID_GENERATE_V4" | "UUIDV4" if f.args.is_empty() => {
1195 Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
1196 this: None,
1197 name: None,
1198 is_string: None,
1199 })))
1200 }
1201
1202 "NEWID" => Ok(Expression::Function(Box::new(Function::new(
1204 "GEN_RANDOM_UUID".to_string(),
1205 vec![],
1206 )))),
1207
1208 "UUID" if f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1210 "GEN_RANDOM_UUID".to_string(),
1211 vec![],
1212 )))),
1213
1214 "UNNEST" => Ok(Expression::Function(Box::new(f))),
1216
1217 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(f))),
1219
1220 "SHA256" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1222 "SHA256".to_string(),
1223 f.args,
1224 )))),
1225
1226 "SHA2" if f.args.len() == 2 => {
1228 let args = f.args;
1230 let data = args[0].clone();
1231 Ok(Expression::Function(Box::new(Function::new(
1233 "SHA256".to_string(),
1234 vec![data],
1235 ))))
1236 }
1237
1238 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
1240
1241 "EDITDISTANCE" if f.args.len() == 3 => Ok(Expression::Function(Box::new(
1243 Function::new("LEVENSHTEIN_LESS_EQUAL".to_string(), f.args),
1244 ))),
1245 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1246 Function::new("LEVENSHTEIN".to_string(), f.args),
1247 ))),
1248
1249 "TRIM" if f.args.len() == 2 => {
1251 let value = f.args[0].clone();
1252 let chars = f.args[1].clone();
1253 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
1254 this: value,
1255 characters: Some(chars),
1256 position: crate::expressions::TrimPosition::Both,
1257 sql_standard_syntax: true,
1258 position_explicit: false,
1259 inferred_type: f.inferred_type,
1260 })))
1261 }
1262
1263 "DATEDIFF" if f.args.len() >= 2 => {
1265 let mut args = f.args;
1266 if args.len() == 2 {
1267 let first = args.remove(0);
1269 let second = args.remove(0);
1270 Ok(Expression::Function(Box::new(Function::new(
1271 "AGE".to_string(),
1272 vec![first, second],
1273 ))))
1274 } else {
1275 let unit_expr = args.remove(0);
1277 let start = args.remove(0);
1278 let end_expr = args.remove(0);
1279
1280 let unit_name = match &unit_expr {
1282 Expression::Identifier(id) => id.name.to_uppercase(),
1283 Expression::Var(v) => v.this.to_uppercase(),
1284 Expression::Column(col) if col.table.is_none() => {
1285 col.name.name.to_uppercase()
1286 }
1287 _ => "DAY".to_string(),
1288 };
1289
1290 let cast_ts = |e: Expression| -> Expression {
1292 Expression::Cast(Box::new(Cast {
1293 this: e,
1294 to: DataType::Timestamp {
1295 precision: None,
1296 timezone: false,
1297 },
1298 trailing_comments: Vec::new(),
1299 double_colon_syntax: false,
1300 format: None,
1301 default: None,
1302 inferred_type: None,
1303 }))
1304 };
1305
1306 let cast_bigint = |e: Expression| -> Expression {
1308 Expression::Cast(Box::new(Cast {
1309 this: e,
1310 to: DataType::BigInt { length: None },
1311 trailing_comments: Vec::new(),
1312 double_colon_syntax: false,
1313 format: None,
1314 default: None,
1315 inferred_type: None,
1316 }))
1317 };
1318
1319 let end_ts = cast_ts(end_expr.clone());
1320 let start_ts = cast_ts(start.clone());
1321
1322 let ts_diff = || -> Expression {
1324 Expression::Sub(Box::new(BinaryOp::new(
1325 cast_ts(end_expr.clone()),
1326 cast_ts(start.clone()),
1327 )))
1328 };
1329
1330 let age_call = || -> Expression {
1332 Expression::Function(Box::new(Function::new(
1333 "AGE".to_string(),
1334 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
1335 )))
1336 };
1337
1338 let extract = |field: DateTimeField, from: Expression| -> Expression {
1340 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
1341 };
1342
1343 let num = |n: i64| -> Expression {
1345 Expression::Literal(Box::new(Literal::Number(n.to_string())))
1346 };
1347
1348 let epoch_field = DateTimeField::Custom("epoch".to_string());
1350
1351 let result = match unit_name.as_str() {
1352 "MICROSECOND" => {
1353 let epoch = extract(epoch_field, ts_diff());
1355 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
1356 epoch,
1357 num(1000000),
1358 ))))
1359 }
1360 "MILLISECOND" => {
1361 let epoch = extract(epoch_field, ts_diff());
1362 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
1363 }
1364 "SECOND" => {
1365 let epoch = extract(epoch_field, ts_diff());
1366 cast_bigint(epoch)
1367 }
1368 "MINUTE" => {
1369 let epoch = extract(epoch_field, ts_diff());
1370 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
1371 }
1372 "HOUR" => {
1373 let epoch = extract(epoch_field, ts_diff());
1374 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
1375 }
1376 "DAY" => {
1377 let epoch = extract(epoch_field, ts_diff());
1378 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
1379 }
1380 "WEEK" => {
1381 let diff_parens = Expression::Paren(Box::new(Paren {
1383 this: ts_diff(),
1384 trailing_comments: Vec::new(),
1385 }));
1386 let days =
1387 extract(DateTimeField::Custom("days".to_string()), diff_parens);
1388 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
1389 }
1390 "MONTH" => {
1391 let year_part =
1393 extract(DateTimeField::Custom("year".to_string()), age_call());
1394 let month_part =
1395 extract(DateTimeField::Custom("month".to_string()), age_call());
1396 let year_months =
1397 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
1398 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1399 year_months,
1400 month_part,
1401 ))))
1402 }
1403 "QUARTER" => {
1404 let year_part =
1406 extract(DateTimeField::Custom("year".to_string()), age_call());
1407 let month_part =
1408 extract(DateTimeField::Custom("month".to_string()), age_call());
1409 let year_quarters =
1410 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
1411 let month_quarters =
1412 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
1413 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1414 year_quarters,
1415 month_quarters,
1416 ))))
1417 }
1418 "YEAR" => {
1419 cast_bigint(extract(
1421 DateTimeField::Custom("year".to_string()),
1422 age_call(),
1423 ))
1424 }
1425 _ => {
1426 Expression::Function(Box::new(Function::new(
1428 "AGE".to_string(),
1429 vec![end_ts, start_ts],
1430 )))
1431 }
1432 };
1433 Ok(result)
1434 }
1435 }
1436
1437 "TIMESTAMPDIFF" if f.args.len() >= 3 => {
1439 let mut args = f.args;
1440 let _unit = args.remove(0); let start = args.remove(0);
1442 let end = args.remove(0);
1443 Ok(Expression::Function(Box::new(Function::new(
1444 "AGE".to_string(),
1445 vec![end, start],
1446 ))))
1447 }
1448
1449 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1451 "TO_TIMESTAMP".to_string(),
1452 f.args,
1453 )))),
1454
1455 "UNIX_TIMESTAMP" if f.args.len() == 1 => {
1457 let arg = f.args.into_iter().next().unwrap();
1458 Ok(Expression::Function(Box::new(Function::new(
1459 "DATE_PART".to_string(),
1460 vec![Expression::string("epoch"), arg],
1461 ))))
1462 }
1463
1464 "UNIX_TIMESTAMP" if f.args.is_empty() => {
1466 Ok(Expression::Function(Box::new(Function::new(
1467 "DATE_PART".to_string(),
1468 vec![
1469 Expression::string("epoch"),
1470 Expression::CurrentTimestamp(crate::expressions::CurrentTimestamp {
1471 precision: None,
1472 sysdate: false,
1473 }),
1474 ],
1475 ))))
1476 }
1477
1478 "DATEADD" if f.args.len() == 3 => {
1480 let mut args = f.args;
1483 let _unit = args.remove(0);
1484 let count = args.remove(0);
1485 let date = args.remove(0);
1486 Ok(Expression::Add(Box::new(BinaryOp {
1487 left: date,
1488 right: count,
1489 left_comments: Vec::new(),
1490 operator_comments: Vec::new(),
1491 trailing_comments: Vec::new(),
1492 inferred_type: None,
1493 })))
1494 }
1495
1496 "INSTR" if f.args.len() >= 2 => {
1498 let mut args = f.args;
1499 let string = args.remove(0);
1500 let substring = args.remove(0);
1501 Ok(Expression::Position(Box::new(
1502 crate::expressions::PositionFunc {
1503 substring,
1504 string,
1505 start: args.pop(),
1506 },
1507 )))
1508 }
1509
1510 "CONCAT_WS" => Ok(Expression::Function(Box::new(f))),
1512
1513 "REGEXP_REPLACE" if f.args.len() == 3 || f.args.len() == 4 => {
1516 Ok(Expression::Function(Box::new(f)))
1517 }
1518 "REGEXP_REPLACE" if f.args.len() == 6 => {
1521 let is_global = match &f.args[4] {
1522 Expression::Literal(lit)
1523 if matches!(lit.as_ref(), crate::expressions::Literal::Number(_)) =>
1524 {
1525 let crate::expressions::Literal::Number(n) = lit.as_ref() else {
1526 unreachable!()
1527 };
1528 n == "0"
1529 }
1530 _ => false,
1531 };
1532 if is_global {
1533 let subject = f.args[0].clone();
1534 let pattern = f.args[1].clone();
1535 let replacement = f.args[2].clone();
1536 let position = f.args[3].clone();
1537 let occurrence = f.args[4].clone();
1538 let params = &f.args[5];
1539 let mut flags = if let Expression::Literal(lit) = params {
1540 if let crate::expressions::Literal::String(s) = lit.as_ref() {
1541 s.clone()
1542 } else {
1543 String::new()
1544 }
1545 } else {
1546 String::new()
1547 };
1548 if !flags.contains('g') {
1549 flags.push('g');
1550 }
1551 Ok(Expression::Function(Box::new(Function::new(
1552 "REGEXP_REPLACE".to_string(),
1553 vec![
1554 subject,
1555 pattern,
1556 replacement,
1557 position,
1558 occurrence,
1559 Expression::Literal(Box::new(crate::expressions::Literal::String(
1560 flags,
1561 ))),
1562 ],
1563 ))))
1564 } else {
1565 Ok(Expression::Function(Box::new(f)))
1566 }
1567 }
1568 "REGEXP_REPLACE" => Ok(Expression::Function(Box::new(f))),
1570
1571 _ => Ok(Expression::Function(Box::new(f))),
1573 }
1574 }
1575
1576 fn transform_aggregate_function(
1577 &self,
1578 f: Box<crate::expressions::AggregateFunction>,
1579 ) -> Result<Expression> {
1580 let name_upper = f.name.to_uppercase();
1581 match name_upper.as_str() {
1582 "COUNT_IF" if !f.args.is_empty() => {
1584 let condition = f.args.into_iter().next().unwrap();
1585 let case_expr = Expression::Case(Box::new(Case {
1586 operand: None,
1587 whens: vec![(condition, Expression::number(1))],
1588 else_: Some(Expression::number(0)),
1589 comments: Vec::new(),
1590 inferred_type: None,
1591 }));
1592 Ok(Expression::Sum(Box::new(AggFunc {
1593 ignore_nulls: None,
1594 having_max: None,
1595 this: case_expr,
1596 distinct: f.distinct,
1597 filter: f.filter,
1598 order_by: Vec::new(),
1599 name: None,
1600 limit: None,
1601 inferred_type: None,
1602 })))
1603 }
1604
1605 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1607 Function::new("STRING_AGG".to_string(), f.args),
1608 ))),
1609
1610 "STDEV" if !f.args.is_empty() => Ok(Expression::Stddev(Box::new(AggFunc {
1612 ignore_nulls: None,
1613 having_max: None,
1614 this: f.args.into_iter().next().unwrap(),
1615 distinct: f.distinct,
1616 filter: f.filter,
1617 order_by: Vec::new(),
1618 name: None,
1619 limit: None,
1620 inferred_type: None,
1621 }))),
1622
1623 "STDEVP" if !f.args.is_empty() => Ok(Expression::StddevPop(Box::new(AggFunc {
1625 ignore_nulls: None,
1626 having_max: None,
1627 this: f.args.into_iter().next().unwrap(),
1628 distinct: f.distinct,
1629 filter: f.filter,
1630 order_by: Vec::new(),
1631 name: None,
1632 limit: None,
1633 inferred_type: None,
1634 }))),
1635
1636 "VAR" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1638 ignore_nulls: None,
1639 having_max: None,
1640 this: f.args.into_iter().next().unwrap(),
1641 distinct: f.distinct,
1642 filter: f.filter,
1643 order_by: Vec::new(),
1644 name: None,
1645 limit: None,
1646 inferred_type: None,
1647 }))),
1648
1649 "VARP" if !f.args.is_empty() => Ok(Expression::VarPop(Box::new(AggFunc {
1651 ignore_nulls: None,
1652 having_max: None,
1653 this: f.args.into_iter().next().unwrap(),
1654 distinct: f.distinct,
1655 filter: f.filter,
1656 order_by: Vec::new(),
1657 name: None,
1658 limit: None,
1659 inferred_type: None,
1660 }))),
1661
1662 "BIT_AND" => Ok(Expression::AggregateFunction(f)),
1664
1665 "BIT_OR" => Ok(Expression::AggregateFunction(f)),
1667
1668 "BIT_XOR" => Ok(Expression::AggregateFunction(f)),
1670
1671 "BOOL_AND" => Ok(Expression::AggregateFunction(f)),
1673
1674 "BOOL_OR" => Ok(Expression::AggregateFunction(f)),
1676
1677 "VARIANCE" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1679 ignore_nulls: None,
1680 having_max: None,
1681 this: f.args.into_iter().next().unwrap(),
1682 distinct: f.distinct,
1683 filter: f.filter,
1684 order_by: Vec::new(),
1685 name: None,
1686 limit: None,
1687 inferred_type: None,
1688 }))),
1689
1690 "LOGICAL_OR" if !f.args.is_empty() => {
1692 let mut new_agg = f.clone();
1693 new_agg.name = "BOOL_OR".to_string();
1694 Ok(Expression::AggregateFunction(new_agg))
1695 }
1696
1697 "LOGICAL_AND" if !f.args.is_empty() => {
1699 let mut new_agg = f.clone();
1700 new_agg.name = "BOOL_AND".to_string();
1701 Ok(Expression::AggregateFunction(new_agg))
1702 }
1703
1704 _ => Ok(Expression::AggregateFunction(f)),
1706 }
1707 }
1708}
1709
1710#[cfg(test)]
1711mod tests {
1712 use super::*;
1713 use crate::dialects::Dialect;
1714
1715 fn transpile_to_postgres(sql: &str) -> String {
1716 let dialect = Dialect::get(DialectType::Generic);
1717 let result = dialect
1718 .transpile(sql, DialectType::PostgreSQL)
1719 .expect("Transpile failed");
1720 result[0].clone()
1721 }
1722
1723 #[test]
1724 fn test_ifnull_to_coalesce() {
1725 let result = transpile_to_postgres("SELECT IFNULL(a, b)");
1726 assert!(
1727 result.contains("COALESCE"),
1728 "Expected COALESCE, got: {}",
1729 result
1730 );
1731 }
1732
1733 #[test]
1734 fn test_nvl_to_coalesce() {
1735 let result = transpile_to_postgres("SELECT NVL(a, b)");
1736 assert!(
1737 result.contains("COALESCE"),
1738 "Expected COALESCE, got: {}",
1739 result
1740 );
1741 }
1742
1743 #[test]
1744 fn test_rand_to_random() {
1745 let result = transpile_to_postgres("SELECT RAND()");
1746 assert!(
1747 result.contains("RANDOM"),
1748 "Expected RANDOM, got: {}",
1749 result
1750 );
1751 }
1752
1753 #[test]
1754 fn test_basic_select() {
1755 let result = transpile_to_postgres("SELECT a, b FROM users WHERE id = 1");
1756 assert!(result.contains("SELECT"));
1757 assert!(result.contains("FROM users"));
1758 }
1759
1760 #[test]
1761 fn test_len_to_length() {
1762 let result = transpile_to_postgres("SELECT LEN(name)");
1763 assert!(
1764 result.contains("LENGTH"),
1765 "Expected LENGTH, got: {}",
1766 result
1767 );
1768 }
1769
1770 #[test]
1771 fn test_getdate_to_current_timestamp() {
1772 let result = transpile_to_postgres("SELECT GETDATE()");
1773 assert!(
1774 result.contains("CURRENT_TIMESTAMP"),
1775 "Expected CURRENT_TIMESTAMP, got: {}",
1776 result
1777 );
1778 }
1779
1780 #[test]
1781 fn test_substr_to_substring() {
1782 let result = transpile_to_postgres("SELECT SUBSTR(name, 1, 3)");
1783 assert!(
1784 result.contains("SUBSTRING"),
1785 "Expected SUBSTRING, got: {}",
1786 result
1787 );
1788 }
1789
1790 #[test]
1791 fn test_group_concat_to_string_agg() {
1792 let result = transpile_to_postgres("SELECT GROUP_CONCAT(name)");
1793 assert!(
1794 result.contains("STRING_AGG"),
1795 "Expected STRING_AGG, got: {}",
1796 result
1797 );
1798 }
1799
1800 #[test]
1801 fn test_double_quote_identifiers() {
1802 let dialect = PostgresDialect;
1804 let config = dialect.generator_config();
1805 assert_eq!(config.identifier_quote, '"');
1806 }
1807
1808 #[test]
1809 fn test_char_length_to_length() {
1810 let result = transpile_to_postgres("SELECT CHAR_LENGTH(name)");
1811 assert!(
1812 result.contains("LENGTH"),
1813 "Expected LENGTH, got: {}",
1814 result
1815 );
1816 }
1817
1818 #[test]
1819 fn test_character_length_to_length() {
1820 let result = transpile_to_postgres("SELECT CHARACTER_LENGTH(name)");
1821 assert!(
1822 result.contains("LENGTH"),
1823 "Expected LENGTH, got: {}",
1824 result
1825 );
1826 }
1827
1828 fn identity_postgres(sql: &str) -> String {
1830 let dialect = Dialect::get(DialectType::PostgreSQL);
1831 let exprs = dialect.parse(sql).expect("Parse failed");
1832 let transformed = dialect
1833 .transform(exprs[0].clone())
1834 .expect("Transform failed");
1835 dialect.generate(&transformed).expect("Generate failed")
1836 }
1837
1838 #[test]
1839 fn test_json_extract_with_column_path() {
1840 let result = identity_postgres("json_data.data -> field_ids.field_id");
1842 assert!(
1843 result.contains("JSON_EXTRACT_PATH"),
1844 "Expected JSON_EXTRACT_PATH for column path, got: {}",
1845 result
1846 );
1847 }
1848
1849 #[test]
1850 fn test_json_extract_scalar_with_negative_index() {
1851 let result = identity_postgres("x::JSON -> 'duration' ->> -1");
1853 assert!(
1854 result.contains("JSON_EXTRACT_PATH_TEXT"),
1855 "Expected JSON_EXTRACT_PATH_TEXT for negative index, got: {}",
1856 result
1857 );
1858 assert!(
1860 result.contains("->"),
1861 "Expected -> for string literal path, got: {}",
1862 result
1863 );
1864 }
1865
1866 #[test]
1867 fn test_json_extract_with_string_literal() {
1868 let result = identity_postgres("data -> 'key'");
1870 assert!(
1871 result.contains("->"),
1872 "Expected -> for string literal path, got: {}",
1873 result
1874 );
1875 assert!(
1876 !result.contains("JSON_EXTRACT_PATH"),
1877 "Should NOT use function form for string literal, got: {}",
1878 result
1879 );
1880 }
1881}