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