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 })))
975 }
976
977 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::ByteString(_)) => {
979 let Literal::ByteString(s) = lit.as_ref() else {
980 unreachable!()
981 };
982 Ok(Expression::Cast(Box::new(Cast {
983 this: Expression::Literal(Box::new(Literal::EscapeString(s.clone()))),
984 to: DataType::VarBinary { length: None },
985 trailing_comments: Vec::new(),
986 double_colon_syntax: false,
987 format: None,
988 default: None,
989 inferred_type: None,
990 })))
991 }
992
993 _ => Ok(expr),
995 }
996 }
997}
998
999#[cfg(feature = "transpile")]
1000impl PostgresDialect {
1001 fn is_simple_json_path(path: &Expression) -> bool {
1005 match path {
1006 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => true,
1008 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1010 let Literal::Number(n) = lit.as_ref() else {
1011 unreachable!()
1012 };
1013 !n.starts_with('-')
1015 }
1016 Expression::JSONPath(_) => true,
1018 _ => false,
1020 }
1021 }
1022
1023 fn transform_data_type(&self, dt: DataType) -> Result<Expression> {
1025 let transformed = match dt {
1026 DataType::TinyInt { .. } => DataType::SmallInt { length: None },
1028
1029 DataType::Float { real_spelling, .. } => DataType::Custom {
1031 name: if real_spelling {
1032 "REAL".to_string()
1033 } else {
1034 "DOUBLE PRECISION".to_string()
1035 },
1036 },
1037
1038 DataType::Double { .. } => DataType::Custom {
1040 name: "DOUBLE PRECISION".to_string(),
1041 },
1042
1043 DataType::Binary { .. } => dt,
1045
1046 DataType::VarBinary { .. } => dt,
1048
1049 DataType::Blob => DataType::Custom {
1051 name: "BYTEA".to_string(),
1052 },
1053
1054 DataType::Custom { ref name } => {
1056 let upper = name.to_uppercase();
1057 match upper.as_str() {
1058 "INT8" => DataType::BigInt { length: None },
1060 "FLOAT8" => DataType::Custom {
1062 name: "DOUBLE PRECISION".to_string(),
1063 },
1064 "FLOAT4" => DataType::Custom {
1066 name: "REAL".to_string(),
1067 },
1068 "INT4" => DataType::Int {
1070 length: None,
1071 integer_spelling: false,
1072 },
1073 "INT2" => DataType::SmallInt { length: None },
1075 _ => dt,
1076 }
1077 }
1078
1079 other => other,
1081 };
1082 Ok(Expression::DataType(transformed))
1083 }
1084
1085 fn transform_function(&self, f: Function) -> Result<Expression> {
1086 let name_upper = f.name.to_uppercase();
1087 match name_upper.as_str() {
1088 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1090 original_name: None,
1091 expressions: f.args,
1092 inferred_type: None,
1093 }))),
1094
1095 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1097 original_name: None,
1098 expressions: f.args,
1099 inferred_type: None,
1100 }))),
1101
1102 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1104 original_name: None,
1105 expressions: f.args,
1106 inferred_type: None,
1107 }))),
1108
1109 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1111 Function::new("STRING_AGG".to_string(), f.args),
1112 ))),
1113
1114 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1116 "SUBSTRING".to_string(),
1117 f.args,
1118 )))),
1119
1120 "RAND" => Ok(Expression::Random(crate::expressions::Random)),
1122
1123 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1125 this: f.args.into_iter().next().unwrap(),
1126 decimals: None,
1127 to: None,
1128 }))),
1129
1130 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1132 this: f.args.into_iter().next().unwrap(),
1133 original_name: None,
1134 inferred_type: None,
1135 }))),
1136
1137 "CHAR_LENGTH" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc {
1139 this: f.args.into_iter().next().unwrap(),
1140 original_name: None,
1141 inferred_type: None,
1142 }))),
1143
1144 "CHARACTER_LENGTH" if f.args.len() == 1 => {
1146 Ok(Expression::Length(Box::new(UnaryFunc {
1147 this: f.args.into_iter().next().unwrap(),
1148 original_name: None,
1149 inferred_type: None,
1150 })))
1151 }
1152
1153 "CHARINDEX" if f.args.len() >= 2 => {
1156 let mut args = f.args;
1157 let substring = args.remove(0);
1158 let string = args.remove(0);
1159 Ok(Expression::Position(Box::new(
1160 crate::expressions::PositionFunc {
1161 substring,
1162 string,
1163 start: args.pop(),
1164 },
1165 )))
1166 }
1167
1168 "GETDATE" => Ok(Expression::CurrentTimestamp(
1170 crate::expressions::CurrentTimestamp {
1171 precision: None,
1172 sysdate: false,
1173 },
1174 )),
1175
1176 "SYSDATETIME" => Ok(Expression::CurrentTimestamp(
1178 crate::expressions::CurrentTimestamp {
1179 precision: None,
1180 sysdate: false,
1181 },
1182 )),
1183
1184 "NOW" => Ok(Expression::CurrentTimestamp(
1186 crate::expressions::CurrentTimestamp {
1187 precision: None,
1188 sysdate: false,
1189 },
1190 )),
1191
1192 "GEN_RANDOM_UUID" | "UUID_GENERATE_V4" | "UUIDV4" if f.args.is_empty() => {
1194 Ok(Expression::Uuid(Box::new(crate::expressions::Uuid {
1195 this: None,
1196 name: None,
1197 is_string: None,
1198 })))
1199 }
1200
1201 "NEWID" => Ok(Expression::Function(Box::new(Function::new(
1203 "GEN_RANDOM_UUID".to_string(),
1204 vec![],
1205 )))),
1206
1207 "UUID" if f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1209 "GEN_RANDOM_UUID".to_string(),
1210 vec![],
1211 )))),
1212
1213 "UNNEST" => Ok(Expression::Function(Box::new(f))),
1215
1216 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(f))),
1218
1219 "SHA256" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1221 "SHA256".to_string(),
1222 f.args,
1223 )))),
1224
1225 "SHA2" if f.args.len() == 2 => {
1227 let args = f.args;
1229 let data = args[0].clone();
1230 Ok(Expression::Function(Box::new(Function::new(
1232 "SHA256".to_string(),
1233 vec![data],
1234 ))))
1235 }
1236
1237 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(f))),
1239
1240 "EDITDISTANCE" if f.args.len() == 3 => Ok(Expression::Function(Box::new(
1242 Function::new("LEVENSHTEIN_LESS_EQUAL".to_string(), f.args),
1243 ))),
1244 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1245 Function::new("LEVENSHTEIN".to_string(), f.args),
1246 ))),
1247
1248 "TRIM" if f.args.len() == 2 => {
1250 let value = f.args[0].clone();
1251 let chars = f.args[1].clone();
1252 Ok(Expression::Trim(Box::new(crate::expressions::TrimFunc {
1253 this: value,
1254 characters: Some(chars),
1255 position: crate::expressions::TrimPosition::Both,
1256 sql_standard_syntax: true,
1257 position_explicit: false,
1258 })))
1259 }
1260
1261 "DATEDIFF" if f.args.len() >= 2 => {
1263 let mut args = f.args;
1264 if args.len() == 2 {
1265 let first = args.remove(0);
1267 let second = args.remove(0);
1268 Ok(Expression::Function(Box::new(Function::new(
1269 "AGE".to_string(),
1270 vec![first, second],
1271 ))))
1272 } else {
1273 let unit_expr = args.remove(0);
1275 let start = args.remove(0);
1276 let end_expr = args.remove(0);
1277
1278 let unit_name = match &unit_expr {
1280 Expression::Identifier(id) => id.name.to_uppercase(),
1281 Expression::Var(v) => v.this.to_uppercase(),
1282 Expression::Column(col) if col.table.is_none() => {
1283 col.name.name.to_uppercase()
1284 }
1285 _ => "DAY".to_string(),
1286 };
1287
1288 let cast_ts = |e: Expression| -> Expression {
1290 Expression::Cast(Box::new(Cast {
1291 this: e,
1292 to: DataType::Timestamp {
1293 precision: None,
1294 timezone: false,
1295 },
1296 trailing_comments: Vec::new(),
1297 double_colon_syntax: false,
1298 format: None,
1299 default: None,
1300 inferred_type: None,
1301 }))
1302 };
1303
1304 let cast_bigint = |e: Expression| -> Expression {
1306 Expression::Cast(Box::new(Cast {
1307 this: e,
1308 to: DataType::BigInt { length: None },
1309 trailing_comments: Vec::new(),
1310 double_colon_syntax: false,
1311 format: None,
1312 default: None,
1313 inferred_type: None,
1314 }))
1315 };
1316
1317 let end_ts = cast_ts(end_expr.clone());
1318 let start_ts = cast_ts(start.clone());
1319
1320 let ts_diff = || -> Expression {
1322 Expression::Sub(Box::new(BinaryOp::new(
1323 cast_ts(end_expr.clone()),
1324 cast_ts(start.clone()),
1325 )))
1326 };
1327
1328 let age_call = || -> Expression {
1330 Expression::Function(Box::new(Function::new(
1331 "AGE".to_string(),
1332 vec![cast_ts(end_expr.clone()), cast_ts(start.clone())],
1333 )))
1334 };
1335
1336 let extract = |field: DateTimeField, from: Expression| -> Expression {
1338 Expression::Extract(Box::new(ExtractFunc { this: from, field }))
1339 };
1340
1341 let num = |n: i64| -> Expression {
1343 Expression::Literal(Box::new(Literal::Number(n.to_string())))
1344 };
1345
1346 let epoch_field = DateTimeField::Custom("epoch".to_string());
1348
1349 let result = match unit_name.as_str() {
1350 "MICROSECOND" => {
1351 let epoch = extract(epoch_field, ts_diff());
1353 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(
1354 epoch,
1355 num(1000000),
1356 ))))
1357 }
1358 "MILLISECOND" => {
1359 let epoch = extract(epoch_field, ts_diff());
1360 cast_bigint(Expression::Mul(Box::new(BinaryOp::new(epoch, num(1000)))))
1361 }
1362 "SECOND" => {
1363 let epoch = extract(epoch_field, ts_diff());
1364 cast_bigint(epoch)
1365 }
1366 "MINUTE" => {
1367 let epoch = extract(epoch_field, ts_diff());
1368 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(60)))))
1369 }
1370 "HOUR" => {
1371 let epoch = extract(epoch_field, ts_diff());
1372 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(3600)))))
1373 }
1374 "DAY" => {
1375 let epoch = extract(epoch_field, ts_diff());
1376 cast_bigint(Expression::Div(Box::new(BinaryOp::new(epoch, num(86400)))))
1377 }
1378 "WEEK" => {
1379 let diff_parens = Expression::Paren(Box::new(Paren {
1381 this: ts_diff(),
1382 trailing_comments: Vec::new(),
1383 }));
1384 let days =
1385 extract(DateTimeField::Custom("days".to_string()), diff_parens);
1386 cast_bigint(Expression::Div(Box::new(BinaryOp::new(days, num(7)))))
1387 }
1388 "MONTH" => {
1389 let year_part =
1391 extract(DateTimeField::Custom("year".to_string()), age_call());
1392 let month_part =
1393 extract(DateTimeField::Custom("month".to_string()), age_call());
1394 let year_months =
1395 Expression::Mul(Box::new(BinaryOp::new(year_part, num(12))));
1396 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1397 year_months,
1398 month_part,
1399 ))))
1400 }
1401 "QUARTER" => {
1402 let year_part =
1404 extract(DateTimeField::Custom("year".to_string()), age_call());
1405 let month_part =
1406 extract(DateTimeField::Custom("month".to_string()), age_call());
1407 let year_quarters =
1408 Expression::Mul(Box::new(BinaryOp::new(year_part, num(4))));
1409 let month_quarters =
1410 Expression::Div(Box::new(BinaryOp::new(month_part, num(3))));
1411 cast_bigint(Expression::Add(Box::new(BinaryOp::new(
1412 year_quarters,
1413 month_quarters,
1414 ))))
1415 }
1416 "YEAR" => {
1417 cast_bigint(extract(
1419 DateTimeField::Custom("year".to_string()),
1420 age_call(),
1421 ))
1422 }
1423 _ => {
1424 Expression::Function(Box::new(Function::new(
1426 "AGE".to_string(),
1427 vec![end_ts, start_ts],
1428 )))
1429 }
1430 };
1431 Ok(result)
1432 }
1433 }
1434
1435 "TIMESTAMPDIFF" if f.args.len() >= 3 => {
1437 let mut args = f.args;
1438 let _unit = args.remove(0); let start = args.remove(0);
1440 let end = args.remove(0);
1441 Ok(Expression::Function(Box::new(Function::new(
1442 "AGE".to_string(),
1443 vec![end, start],
1444 ))))
1445 }
1446
1447 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1449 "TO_TIMESTAMP".to_string(),
1450 f.args,
1451 )))),
1452
1453 "UNIX_TIMESTAMP" if f.args.len() == 1 => {
1455 let arg = f.args.into_iter().next().unwrap();
1456 Ok(Expression::Function(Box::new(Function::new(
1457 "DATE_PART".to_string(),
1458 vec![Expression::string("epoch"), arg],
1459 ))))
1460 }
1461
1462 "UNIX_TIMESTAMP" if f.args.is_empty() => {
1464 Ok(Expression::Function(Box::new(Function::new(
1465 "DATE_PART".to_string(),
1466 vec![
1467 Expression::string("epoch"),
1468 Expression::CurrentTimestamp(crate::expressions::CurrentTimestamp {
1469 precision: None,
1470 sysdate: false,
1471 }),
1472 ],
1473 ))))
1474 }
1475
1476 "DATEADD" if f.args.len() == 3 => {
1478 let mut args = f.args;
1481 let _unit = args.remove(0);
1482 let count = args.remove(0);
1483 let date = args.remove(0);
1484 Ok(Expression::Add(Box::new(BinaryOp {
1485 left: date,
1486 right: count,
1487 left_comments: Vec::new(),
1488 operator_comments: Vec::new(),
1489 trailing_comments: Vec::new(),
1490 inferred_type: None,
1491 })))
1492 }
1493
1494 "INSTR" if f.args.len() >= 2 => {
1496 let mut args = f.args;
1497 let string = args.remove(0);
1498 let substring = args.remove(0);
1499 Ok(Expression::Position(Box::new(
1500 crate::expressions::PositionFunc {
1501 substring,
1502 string,
1503 start: args.pop(),
1504 },
1505 )))
1506 }
1507
1508 "CONCAT_WS" => Ok(Expression::Function(Box::new(f))),
1510
1511 "REGEXP_REPLACE" if f.args.len() == 3 || f.args.len() == 4 => {
1514 Ok(Expression::Function(Box::new(f)))
1515 }
1516 "REGEXP_REPLACE" if f.args.len() == 6 => {
1519 let is_global = match &f.args[4] {
1520 Expression::Literal(lit)
1521 if matches!(lit.as_ref(), crate::expressions::Literal::Number(_)) =>
1522 {
1523 let crate::expressions::Literal::Number(n) = lit.as_ref() else {
1524 unreachable!()
1525 };
1526 n == "0"
1527 }
1528 _ => false,
1529 };
1530 if is_global {
1531 let subject = f.args[0].clone();
1532 let pattern = f.args[1].clone();
1533 let replacement = f.args[2].clone();
1534 let position = f.args[3].clone();
1535 let occurrence = f.args[4].clone();
1536 let params = &f.args[5];
1537 let mut flags = if let Expression::Literal(lit) = params {
1538 if let crate::expressions::Literal::String(s) = lit.as_ref() {
1539 s.clone()
1540 } else {
1541 String::new()
1542 }
1543 } else {
1544 String::new()
1545 };
1546 if !flags.contains('g') {
1547 flags.push('g');
1548 }
1549 Ok(Expression::Function(Box::new(Function::new(
1550 "REGEXP_REPLACE".to_string(),
1551 vec![
1552 subject,
1553 pattern,
1554 replacement,
1555 position,
1556 occurrence,
1557 Expression::Literal(Box::new(crate::expressions::Literal::String(
1558 flags,
1559 ))),
1560 ],
1561 ))))
1562 } else {
1563 Ok(Expression::Function(Box::new(f)))
1564 }
1565 }
1566 "REGEXP_REPLACE" => Ok(Expression::Function(Box::new(f))),
1568
1569 _ => Ok(Expression::Function(Box::new(f))),
1571 }
1572 }
1573
1574 fn transform_aggregate_function(
1575 &self,
1576 f: Box<crate::expressions::AggregateFunction>,
1577 ) -> Result<Expression> {
1578 let name_upper = f.name.to_uppercase();
1579 match name_upper.as_str() {
1580 "COUNT_IF" if !f.args.is_empty() => {
1582 let condition = f.args.into_iter().next().unwrap();
1583 let case_expr = Expression::Case(Box::new(Case {
1584 operand: None,
1585 whens: vec![(condition, Expression::number(1))],
1586 else_: Some(Expression::number(0)),
1587 comments: Vec::new(),
1588 inferred_type: None,
1589 }));
1590 Ok(Expression::Sum(Box::new(AggFunc {
1591 ignore_nulls: None,
1592 having_max: None,
1593 this: case_expr,
1594 distinct: f.distinct,
1595 filter: f.filter,
1596 order_by: Vec::new(),
1597 name: None,
1598 limit: None,
1599 inferred_type: None,
1600 })))
1601 }
1602
1603 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1605 Function::new("STRING_AGG".to_string(), f.args),
1606 ))),
1607
1608 "STDEV" if !f.args.is_empty() => Ok(Expression::Stddev(Box::new(AggFunc {
1610 ignore_nulls: None,
1611 having_max: None,
1612 this: f.args.into_iter().next().unwrap(),
1613 distinct: f.distinct,
1614 filter: f.filter,
1615 order_by: Vec::new(),
1616 name: None,
1617 limit: None,
1618 inferred_type: None,
1619 }))),
1620
1621 "STDEVP" if !f.args.is_empty() => Ok(Expression::StddevPop(Box::new(AggFunc {
1623 ignore_nulls: None,
1624 having_max: None,
1625 this: f.args.into_iter().next().unwrap(),
1626 distinct: f.distinct,
1627 filter: f.filter,
1628 order_by: Vec::new(),
1629 name: None,
1630 limit: None,
1631 inferred_type: None,
1632 }))),
1633
1634 "VAR" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1636 ignore_nulls: None,
1637 having_max: None,
1638 this: f.args.into_iter().next().unwrap(),
1639 distinct: f.distinct,
1640 filter: f.filter,
1641 order_by: Vec::new(),
1642 name: None,
1643 limit: None,
1644 inferred_type: None,
1645 }))),
1646
1647 "VARP" if !f.args.is_empty() => Ok(Expression::VarPop(Box::new(AggFunc {
1649 ignore_nulls: None,
1650 having_max: None,
1651 this: f.args.into_iter().next().unwrap(),
1652 distinct: f.distinct,
1653 filter: f.filter,
1654 order_by: Vec::new(),
1655 name: None,
1656 limit: None,
1657 inferred_type: None,
1658 }))),
1659
1660 "BIT_AND" => Ok(Expression::AggregateFunction(f)),
1662
1663 "BIT_OR" => Ok(Expression::AggregateFunction(f)),
1665
1666 "BIT_XOR" => Ok(Expression::AggregateFunction(f)),
1668
1669 "BOOL_AND" => Ok(Expression::AggregateFunction(f)),
1671
1672 "BOOL_OR" => Ok(Expression::AggregateFunction(f)),
1674
1675 "VARIANCE" if !f.args.is_empty() => Ok(Expression::VarSamp(Box::new(AggFunc {
1677 ignore_nulls: None,
1678 having_max: None,
1679 this: f.args.into_iter().next().unwrap(),
1680 distinct: f.distinct,
1681 filter: f.filter,
1682 order_by: Vec::new(),
1683 name: None,
1684 limit: None,
1685 inferred_type: None,
1686 }))),
1687
1688 "LOGICAL_OR" if !f.args.is_empty() => {
1690 let mut new_agg = f.clone();
1691 new_agg.name = "BOOL_OR".to_string();
1692 Ok(Expression::AggregateFunction(new_agg))
1693 }
1694
1695 "LOGICAL_AND" if !f.args.is_empty() => {
1697 let mut new_agg = f.clone();
1698 new_agg.name = "BOOL_AND".to_string();
1699 Ok(Expression::AggregateFunction(new_agg))
1700 }
1701
1702 _ => Ok(Expression::AggregateFunction(f)),
1704 }
1705 }
1706}
1707
1708#[cfg(test)]
1709mod tests {
1710 use super::*;
1711 use crate::dialects::Dialect;
1712
1713 fn transpile_to_postgres(sql: &str) -> String {
1714 let dialect = Dialect::get(DialectType::Generic);
1715 let result = dialect
1716 .transpile(sql, DialectType::PostgreSQL)
1717 .expect("Transpile failed");
1718 result[0].clone()
1719 }
1720
1721 #[test]
1722 fn test_ifnull_to_coalesce() {
1723 let result = transpile_to_postgres("SELECT IFNULL(a, b)");
1724 assert!(
1725 result.contains("COALESCE"),
1726 "Expected COALESCE, got: {}",
1727 result
1728 );
1729 }
1730
1731 #[test]
1732 fn test_nvl_to_coalesce() {
1733 let result = transpile_to_postgres("SELECT NVL(a, b)");
1734 assert!(
1735 result.contains("COALESCE"),
1736 "Expected COALESCE, got: {}",
1737 result
1738 );
1739 }
1740
1741 #[test]
1742 fn test_rand_to_random() {
1743 let result = transpile_to_postgres("SELECT RAND()");
1744 assert!(
1745 result.contains("RANDOM"),
1746 "Expected RANDOM, got: {}",
1747 result
1748 );
1749 }
1750
1751 #[test]
1752 fn test_basic_select() {
1753 let result = transpile_to_postgres("SELECT a, b FROM users WHERE id = 1");
1754 assert!(result.contains("SELECT"));
1755 assert!(result.contains("FROM users"));
1756 }
1757
1758 #[test]
1759 fn test_len_to_length() {
1760 let result = transpile_to_postgres("SELECT LEN(name)");
1761 assert!(
1762 result.contains("LENGTH"),
1763 "Expected LENGTH, got: {}",
1764 result
1765 );
1766 }
1767
1768 #[test]
1769 fn test_getdate_to_current_timestamp() {
1770 let result = transpile_to_postgres("SELECT GETDATE()");
1771 assert!(
1772 result.contains("CURRENT_TIMESTAMP"),
1773 "Expected CURRENT_TIMESTAMP, got: {}",
1774 result
1775 );
1776 }
1777
1778 #[test]
1779 fn test_substr_to_substring() {
1780 let result = transpile_to_postgres("SELECT SUBSTR(name, 1, 3)");
1781 assert!(
1782 result.contains("SUBSTRING"),
1783 "Expected SUBSTRING, got: {}",
1784 result
1785 );
1786 }
1787
1788 #[test]
1789 fn test_group_concat_to_string_agg() {
1790 let result = transpile_to_postgres("SELECT GROUP_CONCAT(name)");
1791 assert!(
1792 result.contains("STRING_AGG"),
1793 "Expected STRING_AGG, got: {}",
1794 result
1795 );
1796 }
1797
1798 #[test]
1799 fn test_double_quote_identifiers() {
1800 let dialect = PostgresDialect;
1802 let config = dialect.generator_config();
1803 assert_eq!(config.identifier_quote, '"');
1804 }
1805
1806 #[test]
1807 fn test_char_length_to_length() {
1808 let result = transpile_to_postgres("SELECT CHAR_LENGTH(name)");
1809 assert!(
1810 result.contains("LENGTH"),
1811 "Expected LENGTH, got: {}",
1812 result
1813 );
1814 }
1815
1816 #[test]
1817 fn test_character_length_to_length() {
1818 let result = transpile_to_postgres("SELECT CHARACTER_LENGTH(name)");
1819 assert!(
1820 result.contains("LENGTH"),
1821 "Expected LENGTH, got: {}",
1822 result
1823 );
1824 }
1825
1826 fn identity_postgres(sql: &str) -> String {
1828 let dialect = Dialect::get(DialectType::PostgreSQL);
1829 let exprs = dialect.parse(sql).expect("Parse failed");
1830 let transformed = dialect
1831 .transform(exprs[0].clone())
1832 .expect("Transform failed");
1833 dialect.generate(&transformed).expect("Generate failed")
1834 }
1835
1836 #[test]
1837 fn test_json_extract_with_column_path() {
1838 let result = identity_postgres("json_data.data -> field_ids.field_id");
1840 assert!(
1841 result.contains("JSON_EXTRACT_PATH"),
1842 "Expected JSON_EXTRACT_PATH for column path, got: {}",
1843 result
1844 );
1845 }
1846
1847 #[test]
1848 fn test_json_extract_scalar_with_negative_index() {
1849 let result = identity_postgres("x::JSON -> 'duration' ->> -1");
1851 assert!(
1852 result.contains("JSON_EXTRACT_PATH_TEXT"),
1853 "Expected JSON_EXTRACT_PATH_TEXT for negative index, got: {}",
1854 result
1855 );
1856 assert!(
1858 result.contains("->"),
1859 "Expected -> for string literal path, got: {}",
1860 result
1861 );
1862 }
1863
1864 #[test]
1865 fn test_json_extract_with_string_literal() {
1866 let result = identity_postgres("data -> 'key'");
1868 assert!(
1869 result.contains("->"),
1870 "Expected -> for string literal path, got: {}",
1871 result
1872 );
1873 assert!(
1874 !result.contains("JSON_EXTRACT_PATH"),
1875 "Should NOT use function form for string literal, got: {}",
1876 result
1877 );
1878 }
1879}