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 source_dialect: None,
272
273 name: "BOOL_AND".to_string(),
274 args: vec![f.this],
275 distinct: f.distinct,
276 filter: f.filter,
277 order_by: f.order_by,
278 limit: f.limit,
279 ignore_nulls: f.ignore_nulls,
280 inferred_type: f.inferred_type,
281 })))
282 }
283
284 Expression::LogicalOr(f) => {
286 Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
287 source_dialect: None,
288
289 name: "BOOL_OR".to_string(),
290 args: vec![f.this],
291 distinct: f.distinct,
292 filter: f.filter,
293 order_by: f.order_by,
294 limit: f.limit,
295 ignore_nulls: f.ignore_nulls,
296 inferred_type: f.inferred_type,
297 })))
298 }
299
300 Expression::Xor(f) => {
302 if let (Some(a), Some(b)) = (f.this, f.expression) {
303 Ok(Expression::Neq(Box::new(BinaryOp {
304 left: *a,
305 right: *b,
306 left_comments: Vec::new(),
307 operator_comments: Vec::new(),
308 trailing_comments: Vec::new(),
309 inferred_type: None,
310 })))
311 } else {
312 Ok(Expression::Boolean(BooleanLiteral { value: false }))
313 }
314 }
315
316 Expression::RegexpLike(f) => {
321 Ok(Expression::RegexpLike(f))
323 }
324
325 Expression::DateAdd(f) => {
330 let is_literal = matches!(&f.interval, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_) | Literal::String(_)));
331 let right_expr = if is_literal {
332 Expression::Interval(Box::new(Interval {
334 this: Some(f.interval),
335 unit: Some(IntervalUnitSpec::Simple {
336 unit: f.unit,
337 use_plural: false,
338 }),
339 }))
340 } else {
341 let unit_str = match f.unit {
343 IntervalUnit::Year => "YEAR",
344 IntervalUnit::Quarter => "QUARTER",
345 IntervalUnit::Month => "MONTH",
346 IntervalUnit::Week => "WEEK",
347 IntervalUnit::Day => "DAY",
348 IntervalUnit::Hour => "HOUR",
349 IntervalUnit::Minute => "MINUTE",
350 IntervalUnit::Second => "SECOND",
351 IntervalUnit::Millisecond => "MILLISECOND",
352 IntervalUnit::Microsecond => "MICROSECOND",
353 IntervalUnit::Nanosecond => "NANOSECOND",
354 };
355 let interval_one = Expression::Interval(Box::new(Interval {
356 this: Some(Expression::Literal(Box::new(Literal::String(format!(
357 "1 {unit_str}"
358 ))))),
359 unit: None,
360 }));
361 Expression::Mul(Box::new(BinaryOp {
362 left: interval_one,
363 right: f.interval,
364 left_comments: Vec::new(),
365 operator_comments: Vec::new(),
366 trailing_comments: Vec::new(),
367 inferred_type: None,
368 }))
369 };
370 Ok(Expression::Add(Box::new(BinaryOp {
371 left: f.this,
372 right: right_expr,
373 left_comments: Vec::new(),
374 operator_comments: Vec::new(),
375 trailing_comments: Vec::new(),
376 inferred_type: None,
377 })))
378 }
379
380 Expression::DateSub(f) => {
382 let interval_expr = Expression::Interval(Box::new(Interval {
383 this: Some(f.interval),
384 unit: Some(IntervalUnitSpec::Simple {
385 unit: f.unit,
386 use_plural: false,
387 }),
388 }));
389 Ok(Expression::Sub(Box::new(BinaryOp {
390 left: f.this,
391 right: interval_expr,
392 left_comments: Vec::new(),
393 operator_comments: Vec::new(),
394 trailing_comments: Vec::new(),
395 inferred_type: None,
396 })))
397 }
398
399 Expression::DateDiff(f) => {
401 let unit = f.unit.unwrap_or(IntervalUnit::Day);
404
405 let cast_ts = |e: Expression| -> Expression {
407 Expression::Cast(Box::new(Cast {
408 this: e,
409 to: DataType::Timestamp {
410 precision: None,
411 timezone: false,
412 },
413 trailing_comments: Vec::new(),
414 double_colon_syntax: false,
415 format: None,
416 default: None,
417 inferred_type: None,
418 }))
419 };
420
421 let cast_bigint = |e: Expression| -> Expression {
423 Expression::Cast(Box::new(Cast {
424 this: e,
425 to: DataType::BigInt { length: None },
426 trailing_comments: Vec::new(),
427 double_colon_syntax: false,
428 format: None,
429 default: None,
430 inferred_type: None,
431 }))
432 };
433
434 let end_expr = f.this;
436 let start = f.expression;
437
438 let ts_diff = || -> Expression {
440 Expression::Sub(Box::new(BinaryOp::new(
441 cast_ts(end_expr.clone()),
442 cast_ts(start.clone()),
443 )))
444 };
445
446 let age_call = || -> Expression {
448 Expression::Function(Box::new(Function::new(
449 "AGE".to_string(),
450 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
451 )))
452 };
453
454 let extract = |field: DateTimeField, from: Expression| -> Expression {
456 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
457 };
458
459 let num = |n: i64| -> Expression {
461 Expression::Literal(Box::new(Literal::Number(n.to_string())))
462 };
463
464 let epoch_field = DateTimeField::Custom("epoch".to_string());
465
466 let result = match unit {
467 IntervalUnit::Nanosecond => {
468 let epoch = extract(epoch_field.clone(), ts_diff());
469 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
470 epoch,
471 num(1000000000),
472 ))))
473 }
474 IntervalUnit::Microsecond => {
475 let epoch = extract(epoch_field, ts_diff());
476 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
477 epoch,
478 num(1000000),
479 ))))
480 }
481 IntervalUnit::Millisecond => {
482 let epoch = extract(epoch_field, ts_diff());
483 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
484 }
485 IntervalUnit::Second => {
486 let epoch = extract(epoch_field, ts_diff());
487 cast_bigint(epoch)
488 }
489 IntervalUnit::Minute => {
490 let epoch = extract(epoch_field, ts_diff());
491 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
492 }
493 IntervalUnit::Hour => {
494 let epoch = extract(epoch_field, ts_diff());
495 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
496 }
497 IntervalUnit::Day => {
498 let epoch = extract(epoch_field, ts_diff());
499 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
500 }
501 IntervalUnit::Week => {
502 let diff_parens = Expression::Paren(Box::new(Paren {
503 this: ts_diff(),
504 trailing_comments: Vec::new(),
505 }));
506 let days = extract(DateTimeField::Custom("days".to_string()), diff_parens);
507 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
508 }
509 IntervalUnit::Month => {
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_months =
515 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
516 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
517 year_months,
518 month_part,
519 ))))
520 }
521 IntervalUnit::Quarter => {
522 let year_part =
523 extract(DateTimeField::Custom("year".to_string()), age_call());
524 let month_part =
525 extract(DateTimeField::Custom("month".to_string()), age_call());
526 let year_quarters =
527 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
528 let month_quarters =
529 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
530 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
531 year_quarters,
532 month_quarters,
533 ))))
534 }
535 IntervalUnit::Year => cast_bigint(extract(
536 DateTimeField::Custom("year".to_string()),
537 age_call(),
538 )),
539 };
540 Ok(result)
541 }
542
543 Expression::UnixToTime(f) => Ok(Expression::Function(Box::new(Function::new(
545 "TO_TIMESTAMP".to_string(),
546 vec![*f.this],
547 )))),
548
549 Expression::TimeToUnix(f) => Ok(Expression::Function(Box::new(Function::new(
551 "DATE_PART".to_string(),
552 vec![Expression::string("epoch"), f.this],
553 )))),
554
555 Expression::ToTimestamp(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_TIMESTAMP".to_string(),
563 args,
564 ))))
565 }
566
567 Expression::ToDate(f) => {
569 let mut args = vec![f.this];
570 if let Some(fmt) = f.format {
571 args.push(fmt);
572 }
573 Ok(Expression::Function(Box::new(Function::new(
574 "TO_DATE".to_string(),
575 args,
576 ))))
577 }
578
579 Expression::TimestampTrunc(f) => {
581 let unit_str = format!("{:?}", f.unit).to_lowercase();
583 let args = vec![Expression::string(&unit_str), f.this];
584 Ok(Expression::Function(Box::new(Function::new(
585 "DATE_TRUNC".to_string(),
586 args,
587 ))))
588 }
589
590 Expression::TimeFromParts(f) => {
592 let mut args = Vec::new();
593 if let Some(h) = f.hour {
594 args.push(*h);
595 }
596 if let Some(m) = f.min {
597 args.push(*m);
598 }
599 if let Some(s) = f.sec {
600 args.push(*s);
601 }
602 Ok(Expression::Function(Box::new(Function::new(
603 "MAKE_TIME".to_string(),
604 args,
605 ))))
606 }
607
608 Expression::MakeTimestamp(f) => {
610 let args = vec![f.year, f.month, f.day, f.hour, f.minute, f.second];
612 Ok(Expression::Function(Box::new(Function::new(
613 "MAKE_TIMESTAMP".to_string(),
614 args,
615 ))))
616 }
617
618 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
623
624 Expression::GroupConcat(f) => {
626 let mut args = vec![f.this.clone()];
627 if let Some(sep) = f.separator.clone() {
628 args.push(sep);
629 } else {
630 args.push(Expression::string(","));
631 }
632 Ok(Expression::Function(Box::new(Function::new(
633 "STRING_AGG".to_string(),
634 args,
635 ))))
636 }
637
638 Expression::Position(f) => {
640 Ok(Expression::Position(f))
643 }
644
645 Expression::CountIf(f) => {
650 let case_expr = Expression::Case(Box::new(Case {
651 operand: None,
652 whens: vec![(f.this.clone(), Expression::number(1))],
653 else_: Some(Expression::number(0)),
654 comments: Vec::new(),
655 inferred_type: None,
656 }));
657 Ok(Expression::Sum(Box::new(AggFunc {
658 ignore_nulls: None,
659 having_max: None,
660 this: case_expr,
661 distinct: f.distinct,
662 filter: f.filter,
663 order_by: Vec::new(),
664 name: None,
665 limit: None,
666 inferred_type: None,
667 })))
668 }
669
670 Expression::AnyValue(f) => Ok(Expression::AnyValue(f)),
672
673 Expression::Variance(f) => Ok(Expression::Function(Box::new(Function::new(
675 "VAR_SAMP".to_string(),
676 vec![f.this],
677 )))),
678
679 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
681 "VAR_POP".to_string(),
682 vec![f.this],
683 )))),
684
685 Expression::JsonExtract(mut f) => {
691 f.arrow_syntax = Self::is_simple_json_path(&f.path);
694 Ok(Expression::JsonExtract(f))
695 }
696
697 Expression::JsonExtractScalar(mut f) => {
701 if !f.hash_arrow_syntax {
702 f.arrow_syntax = Self::is_simple_json_path(&f.path);
705 }
706 Ok(Expression::JsonExtractScalar(f))
707 }
708
709 Expression::JsonObjectAgg(f) => {
713 let args = vec![f.key, f.value];
715 Ok(Expression::Function(Box::new(Function::new(
716 "JSON_OBJECT_AGG".to_string(),
717 args,
718 ))))
719 }
720
721 Expression::JsonArrayAgg(f) => Ok(Expression::Function(Box::new(Function::new(
723 "JSON_AGG".to_string(),
724 vec![f.this],
725 )))),
726
727 Expression::JSONPathRoot(_) => Ok(Expression::Literal(Box::new(Literal::String(
729 String::new(),
730 )))),
731
732 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
737 "DIV".to_string(),
738 vec![f.this, f.expression],
739 )))),
740
741 Expression::Unicode(f) => Ok(Expression::Function(Box::new(Function::new(
743 "ASCII".to_string(),
744 vec![f.this],
745 )))),
746
747 Expression::LastDay(f) => {
749 let truncated = Expression::Function(Box::new(Function::new(
751 "DATE_TRUNC".to_string(),
752 vec![Expression::string("month"), f.this.clone()],
753 )));
754 let plus_month = Expression::Add(Box::new(BinaryOp {
755 left: truncated,
756 right: Expression::Interval(Box::new(Interval {
757 this: Some(Expression::string("1")),
758 unit: Some(IntervalUnitSpec::Simple {
759 unit: IntervalUnit::Month,
760 use_plural: false,
761 }),
762 })),
763 left_comments: Vec::new(),
764 operator_comments: Vec::new(),
765 trailing_comments: Vec::new(),
766 inferred_type: None,
767 }));
768 let minus_day = Expression::Sub(Box::new(BinaryOp {
769 left: plus_month,
770 right: Expression::Interval(Box::new(Interval {
771 this: Some(Expression::string("1")),
772 unit: Some(IntervalUnitSpec::Simple {
773 unit: IntervalUnit::Day,
774 use_plural: false,
775 }),
776 })),
777 left_comments: Vec::new(),
778 operator_comments: Vec::new(),
779 trailing_comments: Vec::new(),
780 inferred_type: None,
781 }));
782 Ok(Expression::Cast(Box::new(Cast {
783 this: minus_day,
784 to: DataType::Date,
785 trailing_comments: Vec::new(),
786 double_colon_syntax: true, format: None,
788 default: None,
789 inferred_type: None,
790 })))
791 }
792
793 Expression::GenerateSeries(f) => Ok(Expression::GenerateSeries(f)),
795
796 Expression::ExplodingGenerateSeries(f) => {
798 let mut args = vec![f.start, f.stop];
799 if let Some(step) = f.step {
800 args.push(step); }
802 Ok(Expression::Function(Box::new(Function::new(
803 "GENERATE_SERIES".to_string(),
804 args,
805 ))))
806 }
807
808 Expression::CurrentTimestamp(_) => Ok(Expression::Function(Box::new(Function {
813 on_error: None,
814 qualified_name: Vec::new(),
815 source_dialect: None,
816
817 name: "CURRENT_TIMESTAMP".to_string(),
818 args: vec![],
819 distinct: false,
820 trailing_comments: vec![],
821 use_bracket_syntax: false,
822 no_parens: true,
823 quoted: false,
824 span: None,
825 inferred_type: None,
826 }))),
827
828 Expression::CurrentUser(_) => Ok(Expression::Function(Box::new(Function::new(
830 "CURRENT_USER".to_string(),
831 vec![],
832 )))),
833
834 Expression::CurrentDate(_) => Ok(Expression::Function(Box::new(Function {
836 on_error: None,
837 qualified_name: Vec::new(),
838 source_dialect: None,
839
840 name: "CURRENT_DATE".to_string(),
841 args: vec![],
842 distinct: false,
843 trailing_comments: vec![],
844 use_bracket_syntax: false,
845 no_parens: true,
846 quoted: false,
847 span: None,
848 inferred_type: None,
849 }))),
850
851 Expression::Join(join) if join.kind == JoinKind::CrossApply => {
856 Ok(Expression::Join(Box::new(Join {
857 this: join.this,
858 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
859 using: join.using,
860 kind: JoinKind::CrossApply,
861 use_inner_keyword: false,
862 use_outer_keyword: false,
863 deferred_condition: false,
864 join_hint: None,
865 match_condition: None,
866 pivots: join.pivots,
867 comments: join.comments,
868 nesting_group: 0,
869 directed: false,
870 })))
871 }
872
873 Expression::Join(join) if join.kind == JoinKind::OuterApply => {
875 Ok(Expression::Join(Box::new(Join {
876 this: join.this,
877 on: Some(Expression::Boolean(BooleanLiteral { value: true })),
878 using: join.using,
879 kind: JoinKind::OuterApply,
880 use_inner_keyword: false,
881 use_outer_keyword: false,
882 deferred_condition: false,
883 join_hint: None,
884 match_condition: None,
885 pivots: join.pivots,
886 comments: join.comments,
887 nesting_group: 0,
888 directed: false,
889 })))
890 }
891
892 Expression::Function(f) => self.transform_function(*f),
896
897 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
899
900 Expression::Eq(op) => Ok(Expression::Eq(Box::new(BinaryOp {
907 left: wrap_if_json_arrow(op.left),
908 right: wrap_if_json_arrow(op.right),
909 ..*op
910 }))),
911 Expression::Neq(op) => Ok(Expression::Neq(Box::new(BinaryOp {
912 left: wrap_if_json_arrow(op.left),
913 right: wrap_if_json_arrow(op.right),
914 ..*op
915 }))),
916 Expression::Lt(op) => Ok(Expression::Lt(Box::new(BinaryOp {
917 left: wrap_if_json_arrow(op.left),
918 right: wrap_if_json_arrow(op.right),
919 ..*op
920 }))),
921 Expression::Lte(op) => Ok(Expression::Lte(Box::new(BinaryOp {
922 left: wrap_if_json_arrow(op.left),
923 right: wrap_if_json_arrow(op.right),
924 ..*op
925 }))),
926 Expression::Gt(op) => Ok(Expression::Gt(Box::new(BinaryOp {
927 left: wrap_if_json_arrow(op.left),
928 right: wrap_if_json_arrow(op.right),
929 ..*op
930 }))),
931 Expression::Gte(op) => Ok(Expression::Gte(Box::new(BinaryOp {
932 left: wrap_if_json_arrow(op.left),
933 right: wrap_if_json_arrow(op.right),
934 ..*op
935 }))),
936
937 Expression::In(mut i) => {
939 i.this = wrap_if_json_arrow(i.this);
940 Ok(Expression::In(i))
941 }
942
943 Expression::Not(mut n) => {
945 n.this = wrap_if_json_arrow(n.this);
946 Ok(Expression::Not(n))
947 }
948
949 Expression::Merge(m) => Ok(Expression::Merge(m)),
952
953 Expression::JSONExtract(je) if je.variant_extract.is_some() => {
955 let path = match *je.expression {
958 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
959 let Literal::String(s) = lit.as_ref() else {
960 unreachable!()
961 };
962 let cleaned = if s.starts_with("[\"") && s.ends_with("\"]") {
964 s[2..s.len() - 2].to_string()
965 } else {
966 s.clone()
967 };
968 Expression::Literal(Box::new(Literal::String(cleaned)))
969 }
970 other => other,
971 };
972 Ok(Expression::Function(Box::new(Function::new(
973 "JSON_EXTRACT_PATH".to_string(),
974 vec![*je.this, path],
975 ))))
976 }
977
978 Expression::Trim(t) if !t.sql_standard_syntax && t.characters.is_some() => {
980 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
981 this: t.this,
982 characters: t.characters,
983 position: t.position,
984 sql_standard_syntax: true,
985 position_explicit: t.position_explicit,
986 inferred_type: t.inferred_type,
987 })))
988 }
989
990 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::ByteString(_)) => {
992 let Literal::ByteString(s) = lit.as_ref() else {
993 unreachable!()
994 };
995 Ok(Expression::Cast(Box::new(Cast {
996 this: Expression::Literal(Box::new(Literal::EscapeString(s.clone()))),
997 to: DataType::VarBinary { length: None },
998 trailing_comments: Vec::new(),
999 double_colon_syntax: false,
1000 format: None,
1001 default: None,
1002 inferred_type: None,
1003 })))
1004 }
1005
1006 _ => Ok(expr),
1008 }
1009 }
1010}
1011
1012#[cfg(feature = "transpile")]
1013impl PostgresDialect {
1014 fn is_simple_json_path(path: &Expression) -> bool {
1018 match path {
1019 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => true,
1021 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1023 let Literal::Number(n) = lit.as_ref() else {
1024 unreachable!()
1025 };
1026 !n.starts_with('-')
1028 }
1029 Expression::JSONPath(_) => true,
1031 _ => false,
1033 }
1034 }
1035
1036 fn transform_data_type(&self, dt: DataType) -> Result<Expression> {
1038 let transformed = match dt {
1039 DataType::TinyInt { .. } => DataType::SmallInt { length: None },
1041
1042 DataType::Float { real_spelling, .. } => DataType::Custom {
1044 name: if real_spelling {
1045 "REAL".to_string()
1046 } else {
1047 "DOUBLE PRECISION".to_string()
1048 },
1049 },
1050
1051 DataType::Double { .. } => DataType::Custom {
1053 name: "DOUBLE PRECISION".to_string(),
1054 },
1055
1056 DataType::Binary { .. } => dt,
1058
1059 DataType::VarBinary { .. } => dt,
1061
1062 DataType::Blob => DataType::Custom {
1064 name: "BYTEA".to_string(),
1065 },
1066
1067 DataType::Custom { ref name } => {
1069 let upper = name.to_uppercase();
1070 match upper.as_str() {
1071 "INT8" => DataType::BigInt { length: None },
1073 "FLOAT8" => DataType::Custom {
1075 name: "DOUBLE PRECISION".to_string(),
1076 },
1077 "FLOAT4" => DataType::Custom {
1079 name: "REAL".to_string(),
1080 },
1081 "INT4" => DataType::Int {
1083 length: None,
1084 integer_spelling: false,
1085 },
1086 "INT2" => DataType::SmallInt { length: None },
1088 _ => dt,
1089 }
1090 }
1091
1092 other => other,
1094 };
1095 Ok(Expression::DataType(transformed))
1096 }
1097
1098 fn transform_function(&self, f: Function) -> Result<Expression> {
1099 let name_upper = f.name.to_uppercase();
1100 match name_upper.as_str() {
1101 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1103 original_name: None,
1104 expressions: f.args,
1105 inferred_type: None,
1106 }))),
1107
1108 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1110 original_name: None,
1111 expressions: f.args,
1112 inferred_type: None,
1113 }))),
1114
1115 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1117 original_name: None,
1118 expressions: f.args,
1119 inferred_type: None,
1120 }))),
1121
1122 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1124 Function::new("STRING_AGG".to_string(), f.args),
1125 ))),
1126
1127 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1129 "SUBSTRING".to_string(),
1130 f.args,
1131 )))),
1132
1133 "RAND" => Ok(Expression::Random(crate::expressions::Random)),
1135
1136 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1138 this: f.args.into_iter().next().unwrap(),
1139 decimals: None,
1140 to: None,
1141 }))),
1142
1143 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1145 this: f.args.into_iter().next().unwrap(),
1146 original_name: None,
1147 inferred_type: None,
1148 }))),
1149
1150 "CHAR_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1152 this: f.args.into_iter().next().unwrap(),
1153 original_name: None,
1154 inferred_type: None,
1155 }))),
1156
1157 "CHARACTER_LENGTH" if f.args.len() == 1 => {
1159 Ok(Expression::Length(Box::new(UnaryFunc {
1160 this: f.args.into_iter().next().unwrap(),
1161 original_name: None,
1162 inferred_type: None,
1163 })))
1164 }
1165
1166 "CHARINDEX" if f.args.len() >= 2 => {
1169 let mut args = f.args;
1170 let substring = args.remove(0);
1171 let string = args.remove(0);
1172 Ok(Expression::Position(Box::new(
1173 crate::expressions::PositionFunc {
1174 substring,
1175 string,
1176 start: args.pop(),
1177 },
1178 )))
1179 }
1180
1181 "GETDATE" => Ok(Expression::CurrentTimestamp(
1183 crate::expressions::CurrentTimestamp {
1184 precision: None,
1185 sysdate: false,
1186 },
1187 )),
1188
1189 "SYSDATETIME" => Ok(Expression::CurrentTimestamp(
1191 crate::expressions::CurrentTimestamp {
1192 precision: None,
1193 sysdate: false,
1194 },
1195 )),
1196
1197 "NOW" => Ok(Expression::CurrentTimestamp(
1199 crate::expressions::CurrentTimestamp {
1200 precision: None,
1201 sysdate: false,
1202 },
1203 )),
1204
1205 "GEN_RANDOM_UUID" | "UUID_GENERATE_V4" | "UUIDV4" if f.args.is_empty() => {
1207 Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
1208 this: None,
1209 name: None,
1210 is_string: None,
1211 })))
1212 }
1213
1214 "NEWID" => Ok(Expression::Function(Box::new(Function::new(
1216 "GEN_RANDOM_UUID".to_string(),
1217 vec![],
1218 )))),
1219
1220 "UUID" if f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1222 "GEN_RANDOM_UUID".to_string(),
1223 vec![],
1224 )))),
1225
1226 "UNNEST" => Ok(Expression::Function(Box::new(f))),
1228
1229 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(f))),
1231
1232 "SHA256" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1234 "SHA256".to_string(),
1235 f.args,
1236 )))),
1237
1238 "SHA2" if f.args.len() == 2 => {
1240 let args = f.args;
1242 let data = args[0].clone();
1243 Ok(Expression::Function(Box::new(Function::new(
1245 "SHA256".to_string(),
1246 vec![data],
1247 ))))
1248 }
1249
1250 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
1252
1253 "EDITDISTANCE" if f.args.len() == 3 => Ok(Expression::Function(Box::new(
1255 Function::new("LEVENSHTEIN_LESS_EQUAL".to_string(), f.args),
1256 ))),
1257 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1258 Function::new("LEVENSHTEIN".to_string(), f.args),
1259 ))),
1260
1261 "TRIM" if f.args.len() == 2 => {
1263 let value = f.args[0].clone();
1264 let chars = f.args[1].clone();
1265 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
1266 this: value,
1267 characters: Some(chars),
1268 position: crate::expressions::TrimPosition::Both,
1269 sql_standard_syntax: true,
1270 position_explicit: false,
1271 inferred_type: f.inferred_type,
1272 })))
1273 }
1274
1275 "DATEDIFF" if f.args.len() >= 2 => {
1277 let mut args = f.args;
1278 if args.len() == 2 {
1279 let first = args.remove(0);
1281 let second = args.remove(0);
1282 Ok(Expression::Function(Box::new(Function::new(
1283 "AGE".to_string(),
1284 vec![first, second],
1285 ))))
1286 } else {
1287 let unit_expr = args.remove(0);
1289 let start = args.remove(0);
1290 let end_expr = args.remove(0);
1291
1292 let unit_name = match &unit_expr {
1294 Expression::Identifier(id) => id.name.to_uppercase(),
1295 Expression::Var(v) => v.this.to_uppercase(),
1296 Expression::Column(col) if col.table.is_none() => {
1297 col.name.name.to_uppercase()
1298 }
1299 _ => "DAY".to_string(),
1300 };
1301
1302 let cast_ts = |e: Expression| -> Expression {
1304 Expression::Cast(Box::new(Cast {
1305 this: e,
1306 to: DataType::Timestamp {
1307 precision: None,
1308 timezone: false,
1309 },
1310 trailing_comments: Vec::new(),
1311 double_colon_syntax: false,
1312 format: None,
1313 default: None,
1314 inferred_type: None,
1315 }))
1316 };
1317
1318 let cast_bigint = |e: Expression| -> Expression {
1320 Expression::Cast(Box::new(Cast {
1321 this: e,
1322 to: DataType::BigInt { length: None },
1323 trailing_comments: Vec::new(),
1324 double_colon_syntax: false,
1325 format: None,
1326 default: None,
1327 inferred_type: None,
1328 }))
1329 };
1330
1331 let end_ts = cast_ts(end_expr.clone());
1332 let start_ts = cast_ts(start.clone());
1333
1334 let ts_diff = || -> Expression {
1336 Expression::Sub(Box::new(BinaryOp::new(
1337 cast_ts(end_expr.clone()),
1338 cast_ts(start.clone()),
1339 )))
1340 };
1341
1342 let age_call = || -> Expression {
1344 Expression::Function(Box::new(Function::new(
1345 "AGE".to_string(),
1346 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
1347 )))
1348 };
1349
1350 let extract = |field: DateTimeField, from: Expression| -> Expression {
1352 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
1353 };
1354
1355 let num = |n: i64| -> Expression {
1357 Expression::Literal(Box::new(Literal::Number(n.to_string())))
1358 };
1359
1360 let epoch_field = DateTimeField::Custom("epoch".to_string());
1362
1363 let result = match unit_name.as_str() {
1364 "MICROSECOND" => {
1365 let epoch = extract(epoch_field, ts_diff());
1367 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
1368 epoch,
1369 num(1000000),
1370 ))))
1371 }
1372 "MILLISECOND" => {
1373 let epoch = extract(epoch_field, ts_diff());
1374 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
1375 }
1376 "SECOND" => {
1377 let epoch = extract(epoch_field, ts_diff());
1378 cast_bigint(epoch)
1379 }
1380 "MINUTE" => {
1381 let epoch = extract(epoch_field, ts_diff());
1382 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
1383 }
1384 "HOUR" => {
1385 let epoch = extract(epoch_field, ts_diff());
1386 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
1387 }
1388 "DAY" => {
1389 let epoch = extract(epoch_field, ts_diff());
1390 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
1391 }
1392 "WEEK" => {
1393 let diff_parens = Expression::Paren(Box::new(Paren {
1395 this: ts_diff(),
1396 trailing_comments: Vec::new(),
1397 }));
1398 let days =
1399 extract(DateTimeField::Custom("days".to_string()), diff_parens);
1400 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
1401 }
1402 "MONTH" => {
1403 let year_part =
1405 extract(DateTimeField::Custom("year".to_string()), age_call());
1406 let month_part =
1407 extract(DateTimeField::Custom("month".to_string()), age_call());
1408 let year_months =
1409 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
1410 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1411 year_months,
1412 month_part,
1413 ))))
1414 }
1415 "QUARTER" => {
1416 let year_part =
1418 extract(DateTimeField::Custom("year".to_string()), age_call());
1419 let month_part =
1420 extract(DateTimeField::Custom("month".to_string()), age_call());
1421 let year_quarters =
1422 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
1423 let month_quarters =
1424 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
1425 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1426 year_quarters,
1427 month_quarters,
1428 ))))
1429 }
1430 "YEAR" => {
1431 cast_bigint(extract(
1433 DateTimeField::Custom("year".to_string()),
1434 age_call(),
1435 ))
1436 }
1437 _ => {
1438 Expression::Function(Box::new(Function::new(
1440 "AGE".to_string(),
1441 vec![end_ts, start_ts],
1442 )))
1443 }
1444 };
1445 Ok(result)
1446 }
1447 }
1448
1449 "TIMESTAMPDIFF" if f.args.len() >= 3 => {
1451 let mut args = f.args;
1452 let _unit = args.remove(0); let start = args.remove(0);
1454 let end = args.remove(0);
1455 Ok(Expression::Function(Box::new(Function::new(
1456 "AGE".to_string(),
1457 vec![end, start],
1458 ))))
1459 }
1460
1461 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1463 "TO_TIMESTAMP".to_string(),
1464 f.args,
1465 )))),
1466
1467 "UNIX_TIMESTAMP" if f.args.len() == 1 => {
1469 let arg = f.args.into_iter().next().unwrap();
1470 Ok(Expression::Function(Box::new(Function::new(
1471 "DATE_PART".to_string(),
1472 vec![Expression::string("epoch"), arg],
1473 ))))
1474 }
1475
1476 "UNIX_TIMESTAMP" if f.args.is_empty() => {
1478 Ok(Expression::Function(Box::new(Function::new(
1479 "DATE_PART".to_string(),
1480 vec![
1481 Expression::string("epoch"),
1482 Expression::CurrentTimestamp(crate::expressions::CurrentTimestamp {
1483 precision: None,
1484 sysdate: false,
1485 }),
1486 ],
1487 ))))
1488 }
1489
1490 "DATEADD" if f.args.len() == 3 => {
1492 let mut args = f.args;
1495 let _unit = args.remove(0);
1496 let count = args.remove(0);
1497 let date = args.remove(0);
1498 Ok(Expression::Add(Box::new(BinaryOp {
1499 left: date,
1500 right: count,
1501 left_comments: Vec::new(),
1502 operator_comments: Vec::new(),
1503 trailing_comments: Vec::new(),
1504 inferred_type: None,
1505 })))
1506 }
1507
1508 "INSTR" if f.args.len() >= 2 => {
1510 let mut args = f.args;
1511 let string = args.remove(0);
1512 let substring = args.remove(0);
1513 Ok(Expression::Position(Box::new(
1514 crate::expressions::PositionFunc {
1515 substring,
1516 string,
1517 start: args.pop(),
1518 },
1519 )))
1520 }
1521
1522 "CONCAT_WS" => Ok(Expression::Function(Box::new(f))),
1524
1525 "REGEXP_REPLACE" if f.args.len() == 3 || f.args.len() == 4 => {
1528 Ok(Expression::Function(Box::new(f)))
1529 }
1530 "REGEXP_REPLACE" if f.args.len() == 6 => {
1533 let is_global = match &f.args[4] {
1534 Expression::Literal(lit)
1535 if matches!(lit.as_ref(), crate::expressions::Literal::Number(_)) =>
1536 {
1537 let crate::expressions::Literal::Number(n) = lit.as_ref() else {
1538 unreachable!()
1539 };
1540 n == "0"
1541 }
1542 _ => false,
1543 };
1544 if is_global {
1545 let subject = f.args[0].clone();
1546 let pattern = f.args[1].clone();
1547 let replacement = f.args[2].clone();
1548 let position = f.args[3].clone();
1549 let occurrence = f.args[4].clone();
1550 let params = &f.args[5];
1551 let mut flags = if let Expression::Literal(lit) = params {
1552 if let crate::expressions::Literal::String(s) = lit.as_ref() {
1553 s.clone()
1554 } else {
1555 String::new()
1556 }
1557 } else {
1558 String::new()
1559 };
1560 if !flags.contains('g') {
1561 flags.push('g');
1562 }
1563 Ok(Expression::Function(Box::new(Function::new(
1564 "REGEXP_REPLACE".to_string(),
1565 vec![
1566 subject,
1567 pattern,
1568 replacement,
1569 position,
1570 occurrence,
1571 Expression::Literal(Box::new(crate::expressions::Literal::String(
1572 flags,
1573 ))),
1574 ],
1575 ))))
1576 } else {
1577 Ok(Expression::Function(Box::new(f)))
1578 }
1579 }
1580 "REGEXP_REPLACE" => Ok(Expression::Function(Box::new(f))),
1582
1583 _ => Ok(Expression::Function(Box::new(f))),
1585 }
1586 }
1587
1588 fn transform_aggregate_function(
1589 &self,
1590 f: Box<crate::expressions::AggregateFunction>,
1591 ) -> Result<Expression> {
1592 let name_upper = f.name.to_uppercase();
1593 match name_upper.as_str() {
1594 "COUNT_IF" if !f.args.is_empty() => {
1596 let condition = f.args.into_iter().next().unwrap();
1597 let case_expr = Expression::Case(Box::new(Case {
1598 operand: None,
1599 whens: vec![(condition, Expression::number(1))],
1600 else_: Some(Expression::number(0)),
1601 comments: Vec::new(),
1602 inferred_type: None,
1603 }));
1604 Ok(Expression::Sum(Box::new(AggFunc {
1605 ignore_nulls: None,
1606 having_max: None,
1607 this: case_expr,
1608 distinct: f.distinct,
1609 filter: f.filter,
1610 order_by: Vec::new(),
1611 name: None,
1612 limit: None,
1613 inferred_type: None,
1614 })))
1615 }
1616
1617 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1619 Function::new("STRING_AGG".to_string(), f.args),
1620 ))),
1621
1622 "STDEV" if !f.args.is_empty() => Ok(Expression::Stddev(Box::new(AggFunc {
1624 ignore_nulls: None,
1625 having_max: None,
1626 this: f.args.into_iter().next().unwrap(),
1627 distinct: f.distinct,
1628 filter: f.filter,
1629 order_by: Vec::new(),
1630 name: None,
1631 limit: None,
1632 inferred_type: None,
1633 }))),
1634
1635 "STDEVP" if !f.args.is_empty() => Ok(Expression::StddevPop(Box::new(AggFunc {
1637 ignore_nulls: None,
1638 having_max: None,
1639 this: f.args.into_iter().next().unwrap(),
1640 distinct: f.distinct,
1641 filter: f.filter,
1642 order_by: Vec::new(),
1643 name: None,
1644 limit: None,
1645 inferred_type: None,
1646 }))),
1647
1648 "VAR" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1650 ignore_nulls: None,
1651 having_max: None,
1652 this: f.args.into_iter().next().unwrap(),
1653 distinct: f.distinct,
1654 filter: f.filter,
1655 order_by: Vec::new(),
1656 name: None,
1657 limit: None,
1658 inferred_type: None,
1659 }))),
1660
1661 "VARP" if !f.args.is_empty() => Ok(Expression::VarPop(Box::new(AggFunc {
1663 ignore_nulls: None,
1664 having_max: None,
1665 this: f.args.into_iter().next().unwrap(),
1666 distinct: f.distinct,
1667 filter: f.filter,
1668 order_by: Vec::new(),
1669 name: None,
1670 limit: None,
1671 inferred_type: None,
1672 }))),
1673
1674 "BIT_AND" => Ok(Expression::AggregateFunction(f)),
1676
1677 "BIT_OR" => Ok(Expression::AggregateFunction(f)),
1679
1680 "BIT_XOR" => Ok(Expression::AggregateFunction(f)),
1682
1683 "BOOL_AND" => Ok(Expression::AggregateFunction(f)),
1685
1686 "BOOL_OR" => Ok(Expression::AggregateFunction(f)),
1688
1689 "VARIANCE" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1691 ignore_nulls: None,
1692 having_max: None,
1693 this: f.args.into_iter().next().unwrap(),
1694 distinct: f.distinct,
1695 filter: f.filter,
1696 order_by: Vec::new(),
1697 name: None,
1698 limit: None,
1699 inferred_type: None,
1700 }))),
1701
1702 "LOGICAL_OR" if !f.args.is_empty() => {
1704 let mut new_agg = f.clone();
1705 new_agg.name = "BOOL_OR".to_string();
1706 Ok(Expression::AggregateFunction(new_agg))
1707 }
1708
1709 "LOGICAL_AND" if !f.args.is_empty() => {
1711 let mut new_agg = f.clone();
1712 new_agg.name = "BOOL_AND".to_string();
1713 Ok(Expression::AggregateFunction(new_agg))
1714 }
1715
1716 _ => Ok(Expression::AggregateFunction(f)),
1718 }
1719 }
1720}
1721
1722#[cfg(test)]
1723mod tests {
1724 use super::*;
1725 use crate::dialects::Dialect;
1726
1727 fn transpile_to_postgres(sql: &str) -> String {
1728 let dialect = Dialect::get(DialectType::Generic);
1729 let result = dialect
1730 .transpile(sql, DialectType::PostgreSQL)
1731 .expect("Transpile failed");
1732 result[0].clone()
1733 }
1734
1735 #[test]
1736 fn test_ifnull_to_coalesce() {
1737 let result = transpile_to_postgres("SELECT IFNULL(a, b)");
1738 assert!(
1739 result.contains("COALESCE"),
1740 "Expected COALESCE, got: {}",
1741 result
1742 );
1743 }
1744
1745 #[test]
1746 fn test_nvl_to_coalesce() {
1747 let result = transpile_to_postgres("SELECT NVL(a, b)");
1748 assert!(
1749 result.contains("COALESCE"),
1750 "Expected COALESCE, got: {}",
1751 result
1752 );
1753 }
1754
1755 #[test]
1756 fn test_rand_to_random() {
1757 let result = transpile_to_postgres("SELECT RAND()");
1758 assert!(
1759 result.contains("RANDOM"),
1760 "Expected RANDOM, got: {}",
1761 result
1762 );
1763 }
1764
1765 #[test]
1766 fn test_basic_select() {
1767 let result = transpile_to_postgres("SELECT a, b FROM users WHERE id = 1");
1768 assert!(result.contains("SELECT"));
1769 assert!(result.contains("FROM users"));
1770 }
1771
1772 #[test]
1773 fn test_len_to_length() {
1774 let result = transpile_to_postgres("SELECT LEN(name)");
1775 assert!(
1776 result.contains("LENGTH"),
1777 "Expected LENGTH, got: {}",
1778 result
1779 );
1780 }
1781
1782 #[test]
1783 fn test_getdate_to_current_timestamp() {
1784 let result = transpile_to_postgres("SELECT GETDATE()");
1785 assert!(
1786 result.contains("CURRENT_TIMESTAMP"),
1787 "Expected CURRENT_TIMESTAMP, got: {}",
1788 result
1789 );
1790 }
1791
1792 #[test]
1793 fn test_substr_to_substring() {
1794 let result = transpile_to_postgres("SELECT SUBSTR(name, 1, 3)");
1795 assert!(
1796 result.contains("SUBSTRING"),
1797 "Expected SUBSTRING, got: {}",
1798 result
1799 );
1800 }
1801
1802 #[test]
1803 fn test_group_concat_to_string_agg() {
1804 let result = transpile_to_postgres("SELECT GROUP_CONCAT(name)");
1805 assert!(
1806 result.contains("STRING_AGG"),
1807 "Expected STRING_AGG, got: {}",
1808 result
1809 );
1810 }
1811
1812 #[test]
1813 fn test_double_quote_identifiers() {
1814 let dialect = PostgresDialect;
1816 let config = dialect.generator_config();
1817 assert_eq!(config.identifier_quote, '"');
1818 }
1819
1820 #[test]
1821 fn test_char_length_to_length() {
1822 let result = transpile_to_postgres("SELECT CHAR_LENGTH(name)");
1823 assert!(
1824 result.contains("LENGTH"),
1825 "Expected LENGTH, got: {}",
1826 result
1827 );
1828 }
1829
1830 #[test]
1831 fn test_character_length_to_length() {
1832 let result = transpile_to_postgres("SELECT CHARACTER_LENGTH(name)");
1833 assert!(
1834 result.contains("LENGTH"),
1835 "Expected LENGTH, got: {}",
1836 result
1837 );
1838 }
1839
1840 fn identity_postgres(sql: &str) -> String {
1842 let dialect = Dialect::get(DialectType::PostgreSQL);
1843 let exprs = dialect.parse(sql).expect("Parse failed");
1844 let transformed = dialect
1845 .transform(exprs[0].clone())
1846 .expect("Transform failed");
1847 dialect.generate(&transformed).expect("Generate failed")
1848 }
1849
1850 #[test]
1851 fn test_json_extract_with_column_path() {
1852 let result = identity_postgres("json_data.data -> field_ids.field_id");
1854 assert!(
1855 result.contains("JSON_EXTRACT_PATH"),
1856 "Expected JSON_EXTRACT_PATH for column path, got: {}",
1857 result
1858 );
1859 }
1860
1861 #[test]
1862 fn test_json_extract_scalar_with_negative_index() {
1863 let result = identity_postgres("x::JSON -> 'duration' ->> -1");
1865 assert!(
1866 result.contains("JSON_EXTRACT_PATH_TEXT"),
1867 "Expected JSON_EXTRACT_PATH_TEXT for negative index, got: {}",
1868 result
1869 );
1870 assert!(
1872 result.contains("->"),
1873 "Expected -> for string literal path, got: {}",
1874 result
1875 );
1876 }
1877
1878 #[test]
1879 fn test_json_extract_with_string_literal() {
1880 let result = identity_postgres("data -> 'key'");
1882 assert!(
1883 result.contains("->"),
1884 "Expected -> for string literal path, got: {}",
1885 result
1886 );
1887 assert!(
1888 !result.contains("JSON_EXTRACT_PATH"),
1889 "Should NOT use function form for string literal, got: {}",
1890 result
1891 );
1892 }
1893}