You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by al...@apache.org on 2023/06/19 14:27:54 UTC

[arrow-datafusion] branch main updated: fix: parser for negative intervals (#6698)

This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new d29ab93150 fix: parser for negative intervals (#6698)
d29ab93150 is described below

commit d29ab931506f23d8ef8fcd48ae523adfd3c07d6a
Author: Igor Izvekov <iz...@gmail.com>
AuthorDate: Mon Jun 19 17:27:49 2023 +0300

    fix: parser for negative intervals (#6698)
    
    * fix: parser for negative intervals
    
    * fix: use format
    
    * fix: merge main
---
 .../tests/sqllogictests/test_files/interval.slt    |  8 ++++----
 datafusion/sql/src/expr/mod.rs                     |  1 +
 datafusion/sql/src/expr/unary_op.rs                |  6 ++++++
 datafusion/sql/src/expr/value.rs                   | 12 ++++++++++-
 datafusion/sql/tests/sql_integration.rs            | 24 ++++++++++++++++++++++
 5 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/datafusion/core/tests/sqllogictests/test_files/interval.slt b/datafusion/core/tests/sqllogictests/test_files/interval.slt
index 911b28c84b..74ddfbf78d 100644
--- a/datafusion/core/tests/sqllogictests/test_files/interval.slt
+++ b/datafusion/core/tests/sqllogictests/test_files/interval.slt
@@ -190,25 +190,25 @@ select interval '1 year' - '1 month' - '1 day' - '1 hour' - '1 minute' - '1 seco
 query ?
 select -interval '5' - '1' - '2' year;
 ----
-0 years -24 mons 0 days 0 hours 0 mins 0.000000000 secs
+0 years -96 mons 0 days 0 hours 0 mins 0.000000000 secs
 
 # Interval with nested string literal negation
 query ?
 select -interval '1 month' + '1 day' + '1 hour';
 ----
-0 years -1 mons -1 days -1 hours 0 mins 0.000000000 secs
+0 years -1 mons 1 days 1 hours 0 mins 0.000000000 secs
 
 # Interval with nested string literal negation and leading field
 query ?
 select -interval '10' - '1' - '1' month;
 ----
-0 years -8 mons 0 days 0 hours 0 mins 0.000000000 secs
+0 years -12 mons 0 days 0 hours 0 mins 0.000000000 secs
 
 # Interval mega nested string literal negation
 query ?
 select -interval '1 year' - '1 month' - '1 day' - '1 hour' - '1 minute' - '1 second' - '1 millisecond' - '1 microsecond' - '1 nanosecond'
 ----
-0 years -11 mons 1 days 1 hours 1 mins 1.001001001 secs
+0 years -13 mons -1 days -1 hours -1 mins -1.001001001 secs
 
 # Interval string literal + date
 query D
diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index 4155484cea..5b490f3b06 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -162,6 +162,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
 
             SQLExpr::Array(arr) => self.sql_array_literal(arr.elem, schema),
             SQLExpr::Interval(interval)=> self.sql_interval_to_expr(
+                false,
                 interval,
                 schema,
                 planner_context,
diff --git a/datafusion/sql/src/expr/unary_op.rs b/datafusion/sql/src/expr/unary_op.rs
index dc7b5abd45..8b9312e84a 100644
--- a/datafusion/sql/src/expr/unary_op.rs
+++ b/datafusion/sql/src/expr/unary_op.rs
@@ -48,6 +48,12 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                                     "negative operator can be only applied to integer and float operands, got: {n}"))
                             })?)),
                     },
+                    SQLExpr::Interval(interval) => self.sql_interval_to_expr(
+                        true,
+                        interval,
+                        schema,
+                        planner_context,
+                    ),
                     // not a literal, apply negative operator on expression
                     _ => Ok(Expr::Negative(Box::new(self.sql_expr_to_logical_expr(expr, schema, planner_context)?))),
                 }
diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs
index 1477bad0df..5a64d36330 100644
--- a/datafusion/sql/src/expr/value.rs
+++ b/datafusion/sql/src/expr/value.rs
@@ -165,6 +165,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
     /// expression
     pub(super) fn sql_interval_to_expr(
         &self,
+        negative: bool,
         interval: Interval,
         schema: &DFSchema,
         planner_context: &mut PlannerContext,
@@ -194,7 +195,13 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
         let value = match *interval.value {
             SQLExpr::Value(
                 Value::SingleQuotedString(s) | Value::DoubleQuotedString(s),
-            ) => s,
+            ) => {
+                if negative {
+                    format!("-{s}")
+                } else {
+                    s
+                }
+            }
             // Support expressions like `interval '1 month' + date/timestamp`.
             // Such expressions are parsed like this by sqlparser-rs
             //
@@ -224,6 +231,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                 match (interval.leading_field, left.as_ref(), right.as_ref()) {
                     (_, _, SQLExpr::Value(_)) => {
                         let left_expr = self.sql_interval_to_expr(
+                            negative,
                             Interval {
                                 value: left,
                                 leading_field: interval.leading_field,
@@ -235,6 +243,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                             planner_context,
                         )?;
                         let right_expr = self.sql_interval_to_expr(
+                            false,
                             Interval {
                                 value: right,
                                 leading_field: interval.leading_field,
@@ -258,6 +267,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                     // is not a value.
                     (None, _, _) => {
                         let left_expr = self.sql_interval_to_expr(
+                            negative,
                             Interval {
                                 value: left,
                                 leading_field: None,
diff --git a/datafusion/sql/tests/sql_integration.rs b/datafusion/sql/tests/sql_integration.rs
index 77706ea399..86cc736719 100644
--- a/datafusion/sql/tests/sql_integration.rs
+++ b/datafusion/sql/tests/sql_integration.rs
@@ -2740,6 +2740,30 @@ fn cte_use_same_name_multiple_times() {
     assert_eq!(result.to_string(), expected);
 }
 
+#[test]
+fn negative_interval_plus_interval_in_projection() {
+    let sql = "select -interval '2 days' + interval '5 days';";
+    let expected =
+    "Projection: IntervalMonthDayNano(\"79228162477370849446124847104\") + IntervalMonthDayNano(\"92233720368547758080\")\n  EmptyRelation";
+    quick_test(sql, expected);
+}
+
+#[test]
+fn complex_interval_expression_in_projection() {
+    let sql = "select -interval '2 days' + interval '5 days'+ (-interval '3 days' + interval '5 days');";
+    let expected =
+    "Projection: IntervalMonthDayNano(\"79228162477370849446124847104\") + IntervalMonthDayNano(\"92233720368547758080\") + IntervalMonthDayNano(\"79228162458924105372415295488\") + IntervalMonthDayNano(\"92233720368547758080\")\n  EmptyRelation";
+    quick_test(sql, expected);
+}
+
+#[test]
+fn negative_sum_intervals_in_projection() {
+    let sql = "select -((interval '2 days' + interval '5 days') + -(interval '4 days' + interval '7 days'));";
+    let expected =
+    "Projection: (- IntervalMonthDayNano(\"36893488147419103232\") + IntervalMonthDayNano(\"92233720368547758080\") + (- IntervalMonthDayNano(\"73786976294838206464\") + IntervalMonthDayNano(\"129127208515966861312\")))\n  EmptyRelation";
+    quick_test(sql, expected);
+}
+
 #[test]
 fn date_plus_interval_in_projection() {
     let sql = "select t_date32 + interval '5 days' FROM test";