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/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 3f0fb4a9ed Upgrade sqlparser-rs to 0.51.0, support new interval logic
from `sqlparse-rs` (#12222)
3f0fb4a9ed is described below
commit 3f0fb4a9edb5bd20756f82c2cb6b2c8d137de80c
Author: Samuel Colvin <[email protected]>
AuthorDate: Sun Sep 15 13:00:44 2024 +0100
Upgrade sqlparser-rs to 0.51.0, support new interval logic from
`sqlparse-rs` (#12222)
* support new interval logic from sqlparse-rs
* uprev sqlparser-rs branch
* use sqlparser 51
* better extract logic and interval testing
* revert unnecessary changes
* revert unnecessary changes, more
* cleanup
* fix last failing test :fingerscrossed:
---
Cargo.toml | 2 +-
datafusion-cli/Cargo.lock | 4 +-
datafusion/functions/src/datetime/date_part.rs | 44 +++--
datafusion/sql/src/expr/mod.rs | 4 +-
datafusion/sql/src/expr/unary_op.rs | 2 +-
datafusion/sql/src/expr/value.rs | 195 ++++++++-------------
datafusion/sql/tests/cases/plan_to_sql.rs | 18 +-
datafusion/sqllogictest/test_files/expr.slt | 15 ++
datafusion/sqllogictest/test_files/interval.slt | 82 ++-------
.../sqllogictest/test_files/interval_mysql.slt | 71 ++++++++
10 files changed, 215 insertions(+), 222 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index d26734f08a..1332a05095 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -137,7 +137,7 @@ rand = "0.8"
regex = "1.8"
rstest = "0.22.0"
serde_json = "1"
-sqlparser = { version = "0.50.0", features = ["visitor"] }
+sqlparser = { version = "0.51.0", features = ["visitor"] }
tempfile = "3"
thiserror = "1.0.44"
tokio = { version = "1.36", features = ["macros", "rt", "sync"] }
diff --git a/datafusion-cli/Cargo.lock b/datafusion-cli/Cargo.lock
index cdf0c661c2..65ea0a756b 100644
--- a/datafusion-cli/Cargo.lock
+++ b/datafusion-cli/Cargo.lock
@@ -3543,9 +3543,9 @@ checksum =
"6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "sqlparser"
-version = "0.50.0"
+version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2e5b515a2bd5168426033e9efbfd05500114833916f1d5c268f938b4ee130ac"
+checksum = "5fe11944a61da0da3f592e19a45ebe5ab92dc14a779907ff1f08fbb797bfefc7"
dependencies = [
"log",
"sqlparser_derive",
diff --git a/datafusion/functions/src/datetime/date_part.rs
b/datafusion/functions/src/datetime/date_part.rs
index e24b11aeb7..8ee82d8726 100644
--- a/datafusion/functions/src/datetime/date_part.rs
+++ b/datafusion/functions/src/datetime/date_part.rs
@@ -16,9 +16,11 @@
// under the License.
use std::any::Any;
+use std::str::FromStr;
use std::sync::Arc;
use arrow::array::{Array, ArrayRef, Float64Array};
+use arrow::compute::kernels::cast_utils::IntervalUnit;
use arrow::compute::{binary, cast, date_part, DatePart};
use arrow::datatypes::DataType::{
Date32, Date64, Float64, Time32, Time64, Timestamp, Utf8, Utf8View,
@@ -161,22 +163,32 @@ impl ScalarUDFImpl for DatePartFunc {
return exec_err!("Date part '{part}' not supported");
}
- let arr = match part_trim.to_lowercase().as_str() {
- "year" => date_part_f64(array.as_ref(), DatePart::Year)?,
- "quarter" => date_part_f64(array.as_ref(), DatePart::Quarter)?,
- "month" => date_part_f64(array.as_ref(), DatePart::Month)?,
- "week" => date_part_f64(array.as_ref(), DatePart::Week)?,
- "day" => date_part_f64(array.as_ref(), DatePart::Day)?,
- "doy" => date_part_f64(array.as_ref(), DatePart::DayOfYear)?,
- "dow" => date_part_f64(array.as_ref(),
DatePart::DayOfWeekSunday0)?,
- "hour" => date_part_f64(array.as_ref(), DatePart::Hour)?,
- "minute" => date_part_f64(array.as_ref(), DatePart::Minute)?,
- "second" => seconds(array.as_ref(), Second)?,
- "millisecond" => seconds(array.as_ref(), Millisecond)?,
- "microsecond" => seconds(array.as_ref(), Microsecond)?,
- "nanosecond" => seconds(array.as_ref(), Nanosecond)?,
- "epoch" => epoch(array.as_ref())?,
- _ => return exec_err!("Date part '{part}' not supported"),
+ // using IntervalUnit here means we hand off all the work of
supporting plurals (like "seconds")
+ // and synonyms ( like "ms,msec,msecond,millisecond") to Arrow
+ let arr = if let Ok(interval_unit) = IntervalUnit::from_str(part_trim)
{
+ match interval_unit {
+ IntervalUnit::Year => date_part_f64(array.as_ref(),
DatePart::Year)?,
+ IntervalUnit::Month => date_part_f64(array.as_ref(),
DatePart::Month)?,
+ IntervalUnit::Week => date_part_f64(array.as_ref(),
DatePart::Week)?,
+ IntervalUnit::Day => date_part_f64(array.as_ref(),
DatePart::Day)?,
+ IntervalUnit::Hour => date_part_f64(array.as_ref(),
DatePart::Hour)?,
+ IntervalUnit::Minute => date_part_f64(array.as_ref(),
DatePart::Minute)?,
+ IntervalUnit::Second => seconds(array.as_ref(), Second)?,
+ IntervalUnit::Millisecond => seconds(array.as_ref(),
Millisecond)?,
+ IntervalUnit::Microsecond => seconds(array.as_ref(),
Microsecond)?,
+ IntervalUnit::Nanosecond => seconds(array.as_ref(),
Nanosecond)?,
+ // century and decade are not supported by `DatePart`,
although they are supported in postgres
+ _ => return exec_err!("Date part '{part}' not supported"),
+ }
+ } else {
+ // special cases that can be extracted (in postgres) but are not
interval units
+ match part_trim.to_lowercase().as_str() {
+ "qtr" | "quarter" => date_part_f64(array.as_ref(),
DatePart::Quarter)?,
+ "doy" => date_part_f64(array.as_ref(), DatePart::DayOfYear)?,
+ "dow" => date_part_f64(array.as_ref(),
DatePart::DayOfWeekSunday0)?,
+ "epoch" => epoch(array.as_ref())?,
+ _ => return exec_err!("Date part '{part}' not supported"),
+ }
};
Ok(if is_scalar {
diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index c79c6358be..34e119c45f 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -201,9 +201,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)
- }
+ SQLExpr::Interval(interval) => self.sql_interval_to_expr(false,
interval),
SQLExpr::Identifier(id) => {
self.sql_identifier_to_expr(id, schema, planner_context)
}
diff --git a/datafusion/sql/src/expr/unary_op.rs
b/datafusion/sql/src/expr/unary_op.rs
index 9fcee7a061..2a341fb7c4 100644
--- a/datafusion/sql/src/expr/unary_op.rs
+++ b/datafusion/sql/src/expr/unary_op.rs
@@ -43,7 +43,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
self.parse_sql_number(&n, true)
}
SQLExpr::Interval(interval) => {
- self.sql_interval_to_expr(true, interval, schema,
planner_context)
+ self.sql_interval_to_expr(true, interval)
}
// not a literal, apply negative operator on expression
_ =>
Ok(Expr::Negative(Box::new(self.sql_expr_to_logical_expr(
diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs
index afcd182fa3..64575645bc 100644
--- a/datafusion/sql/src/expr/value.rs
+++ b/datafusion/sql/src/expr/value.rs
@@ -26,7 +26,7 @@ use datafusion_expr::expr::{BinaryExpr, Placeholder};
use datafusion_expr::planner::PlannerResult;
use datafusion_expr::{lit, Expr, Operator};
use log::debug;
-use sqlparser::ast::{BinaryOperator, Expr as SQLExpr, Interval, Value};
+use sqlparser::ast::{BinaryOperator, Expr as SQLExpr, Interval, UnaryOperator,
Value};
use sqlparser::parser::ParserError::ParserError;
use std::borrow::Cow;
@@ -168,12 +168,11 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
/// Convert a SQL interval expression to a DataFusion logical plan
/// expression
+ #[allow(clippy::only_used_in_recursion)]
pub(super) fn sql_interval_to_expr(
&self,
negative: bool,
interval: Interval,
- schema: &DFSchema,
- planner_context: &mut PlannerContext,
) -> Result<Expr> {
if interval.leading_precision.is_some() {
return not_impl_err!(
@@ -196,127 +195,42 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
);
}
- // Only handle string exprs for now
- let value = match *interval.value {
- SQLExpr::Value(
- Value::SingleQuotedString(s) | Value::DoubleQuotedString(s),
- ) => {
- if negative {
- format!("-{s}")
- } else {
- s
- }
- }
- // Support expressions like `interval '1 month' + date/timestamp`.
- // Such expressions are parsed like this by sqlparser-rs
- //
- // Interval
- // BinaryOp
- // Value(StringLiteral)
- // Cast
- // Value(StringLiteral)
- //
- // This code rewrites them to the following:
- //
- // BinaryOp
- // Interval
- // Value(StringLiteral)
- // Cast
- // Value(StringLiteral)
- SQLExpr::BinaryOp { left, op, right } => {
- let df_op = match op {
- BinaryOperator::Plus => Operator::Plus,
- BinaryOperator::Minus => Operator::Minus,
- BinaryOperator::Eq => Operator::Eq,
- BinaryOperator::NotEq => Operator::NotEq,
- BinaryOperator::Gt => Operator::Gt,
- BinaryOperator::GtEq => Operator::GtEq,
- BinaryOperator::Lt => Operator::Lt,
- BinaryOperator::LtEq => Operator::LtEq,
- _ => {
- return not_impl_err!("Unsupported interval operator:
{op:?}");
- }
- };
- match (
- interval.leading_field.as_ref(),
- 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.clone(),
- leading_precision: None,
- last_field: None,
- fractional_seconds_precision: None,
- },
- schema,
- planner_context,
- )?;
- let right_expr = self.sql_interval_to_expr(
- false,
- Interval {
- value: right,
- leading_field: interval.leading_field,
- leading_precision: None,
- last_field: None,
- fractional_seconds_precision: None,
- },
- schema,
- planner_context,
- )?;
- return Ok(Expr::BinaryExpr(BinaryExpr::new(
- Box::new(left_expr),
- df_op,
- Box::new(right_expr),
- )));
- }
- // In this case, the left node is part of the interval
- // expr and the right node is an independent expr.
- //
- // Leading field is not supported when the right operand
- // is not a value.
- (None, _, _) => {
- let left_expr = self.sql_interval_to_expr(
- negative,
- Interval {
- value: left,
- leading_field: None,
- leading_precision: None,
- last_field: None,
- fractional_seconds_precision: None,
- },
- schema,
- planner_context,
- )?;
- let right_expr = self.sql_expr_to_logical_expr(
- *right,
- schema,
- planner_context,
- )?;
- return Ok(Expr::BinaryExpr(BinaryExpr::new(
- Box::new(left_expr),
- df_op,
- Box::new(right_expr),
- )));
- }
- _ => {
- let value = SQLExpr::BinaryOp { left, op, right };
- return not_impl_err!(
- "Unsupported interval argument. Expected string
literal, got: {value:?}"
- );
- }
+ if let SQLExpr::BinaryOp { left, op, right } = *interval.value {
+ let df_op = match op {
+ BinaryOperator::Plus => Operator::Plus,
+ BinaryOperator::Minus => Operator::Minus,
+ _ => {
+ return not_impl_err!("Unsupported interval operator:
{op:?}");
}
- }
- _ => {
- return not_impl_err!(
- "Unsupported interval argument. Expected string literal,
got: {:?}",
- interval.value
- );
- }
- };
+ };
+ let left_expr = self.sql_interval_to_expr(
+ negative,
+ Interval {
+ value: left,
+ leading_field: interval.leading_field.clone(),
+ leading_precision: None,
+ last_field: None,
+ fractional_seconds_precision: None,
+ },
+ )?;
+ let right_expr = self.sql_interval_to_expr(
+ false,
+ Interval {
+ value: right,
+ leading_field: interval.leading_field,
+ leading_precision: None,
+ last_field: None,
+ fractional_seconds_precision: None,
+ },
+ )?;
+ return Ok(Expr::BinaryExpr(BinaryExpr::new(
+ Box::new(left_expr),
+ df_op,
+ Box::new(right_expr),
+ )));
+ }
+
+ let value = interval_literal(*interval.value, negative)?;
let value = if has_units(&value) {
// If the interval already contains a unit
@@ -343,6 +257,41 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
}
+fn interval_literal(interval_value: SQLExpr, negative: bool) -> Result<String>
{
+ let s = match interval_value {
+ SQLExpr::Value(Value::SingleQuotedString(s) |
Value::DoubleQuotedString(s)) => s,
+ SQLExpr::Value(Value::Number(ref v, long)) => {
+ if long {
+ return not_impl_err!(
+ "Unsupported interval argument. Long number not supported:
{interval_value:?}"
+ );
+ } else {
+ v.to_string()
+ }
+ }
+ SQLExpr::UnaryOp { op, expr } => {
+ let negative = match op {
+ UnaryOperator::Minus => !negative,
+ UnaryOperator::Plus => negative,
+ _ => {
+ return not_impl_err!(
+ "Unsupported SQL unary operator in interval {op:?}"
+ );
+ }
+ };
+ interval_literal(*expr, negative)?
+ }
+ _ => {
+ return not_impl_err!("Unsupported interval argument. Expected
string literal or number, got: {interval_value:?}");
+ }
+ };
+ if negative {
+ Ok(format!("-{s}"))
+ } else {
+ Ok(s)
+ }
+}
+
// TODO make interval parsing better in arrow-rs / expose `IntervalType`
fn has_units(val: &str) -> bool {
let val = val.to_lowercase();
diff --git a/datafusion/sql/tests/cases/plan_to_sql.rs
b/datafusion/sql/tests/cases/plan_to_sql.rs
index fa95d05c32..bd338e440e 100644
--- a/datafusion/sql/tests/cases/plan_to_sql.rs
+++ b/datafusion/sql/tests/cases/plan_to_sql.rs
@@ -495,11 +495,17 @@ fn test_table_references_in_plan_to_sql() {
assert_eq!(format!("{}", sql), expected_sql)
}
- test("catalog.schema.table", "SELECT catalog.\"schema\".\"table\".id,
catalog.\"schema\".\"table\".\"value\" FROM catalog.\"schema\".\"table\"");
- test("schema.table", "SELECT \"schema\".\"table\".id,
\"schema\".\"table\".\"value\" FROM \"schema\".\"table\"");
+ test(
+ "catalog.schema.table",
+ r#"SELECT "catalog"."schema"."table".id,
"catalog"."schema"."table"."value" FROM "catalog"."schema"."table""#,
+ );
+ test(
+ "schema.table",
+ r#"SELECT "schema"."table".id, "schema"."table"."value" FROM
"schema"."table""#,
+ );
test(
"table",
- "SELECT \"table\".id, \"table\".\"value\" FROM \"table\"",
+ r#"SELECT "table".id, "table"."value" FROM "table""#,
);
}
@@ -521,10 +527,10 @@ fn test_table_scan_with_no_projection_in_plan_to_sql() {
test(
"catalog.schema.table",
- "SELECT * FROM catalog.\"schema\".\"table\"",
+ r#"SELECT * FROM "catalog"."schema"."table""#,
);
- test("schema.table", "SELECT * FROM \"schema\".\"table\"");
- test("table", "SELECT * FROM \"table\"");
+ test("schema.table", r#"SELECT * FROM "schema"."table""#);
+ test("table", r#"SELECT * FROM "table""#);
}
#[test]
diff --git a/datafusion/sqllogictest/test_files/expr.slt
b/datafusion/sqllogictest/test_files/expr.slt
index 002e8db213..a478e36172 100644
--- a/datafusion/sqllogictest/test_files/expr.slt
+++ b/datafusion/sqllogictest/test_files/expr.slt
@@ -1355,6 +1355,16 @@ SELECT date_part('second',
arrow_cast('23:32:50.123456789'::time, 'Time64(Nanose
----
50.123456789
+query R
+select extract(second from '2024-08-09T12:13:14')
+----
+14
+
+query R
+select extract(seconds from '2024-08-09T12:13:14')
+----
+14
+
query R
SELECT extract(second from arrow_cast('23:32:50.123456789'::time,
'Time64(Nanosecond)'))
----
@@ -1381,6 +1391,11 @@ SELECT extract(microsecond from
arrow_cast('23:32:50.123456789'::time, 'Time64(N
----
50123456.789000005
+query R
+SELECT extract(us from arrow_cast('23:32:50.123456789'::time,
'Time64(Nanosecond)'))
+----
+50123456.789000005
+
query R
SELECT date_part('nanosecond', arrow_cast('23:32:50.123456789'::time,
'Time64(Nanosecond)'))
----
diff --git a/datafusion/sqllogictest/test_files/interval.slt
b/datafusion/sqllogictest/test_files/interval.slt
index 077f38d5d5..c73e340f91 100644
--- a/datafusion/sqllogictest/test_files/interval.slt
+++ b/datafusion/sqllogictest/test_files/interval.slt
@@ -15,7 +15,6 @@
# specific language governing permissions and limitations
# under the License.
-
# Use `interval` SQL literal syntax
# the types should be the same:
https://github.com/apache/datafusion/issues/5801
query TT
@@ -206,117 +205,60 @@ select interval '5 YEAR 5 MONTH 5 DAY 5 HOUR 5 MINUTE 5
SECOND 5 MILLISECOND 5 M
----
65 mons 5 days 5 hours 5 mins 5.005005005 secs
-# Interval with string literal addition
-query ?
-select interval '1 month' + '1 month'
-----
-2 mons
-
-# Interval with string literal addition and leading field
-query ?
-select interval '1' + '1' month
-----
-2 mons
-
-# Interval with nested string literal addition
-query ?
-select interval '1 month' + '1 month' + '1 month'
-----
-3 mons
-
-# Interval with nested string literal addition and leading field
-query ?
-select interval '1' + '1' + '1' month
-----
-3 mons
-
-# Interval mega nested string literal addition
+# Interval mega nested literal addition
query ?
-select interval '1 year' + '1 month' + '1 day' + '1 hour' + '1 minute' + '1
second' + '1 millisecond' + '1 microsecond' + '1 nanosecond'
+select interval '1 year' + interval '1 month' + interval '1 day' + interval '1
hour' + interval '1 minute' + interval '1 second' + interval '1 millisecond' +
interval '1 microsecond' + interval '1 nanosecond'
----
13 mons 1 days 1 hours 1 mins 1.001001001 secs
# Interval with string literal subtraction
query ?
-select interval '1 month' - '1 day';
+select interval '1 month' - interval '1 day';
----
1 mons -1 days
-# Interval with string literal subtraction and leading field
-query ?
-select interval '5' - '1' - '2' year;
-----
-24 mons
-
# Interval with nested string literal subtraction
query ?
-select interval '1 month' - '1 day' - '1 hour';
+select interval '1 month' - interval '1 day' - interval '1 hour';
----
1 mons -1 days -1 hours
-# Interval with nested string literal subtraction and leading field
-query ?
-select interval '10' - '1' - '1' month;
-----
-8 mons
-
# Interval mega nested string literal subtraction
query ?
-select interval '1 year' - '1 month' - '1 day' - '1 hour' - '1 minute' - '1
second' - '1 millisecond' - '1 microsecond' - '1 nanosecond'
+select interval '1 year' - interval '1 month' - interval '1 day' - interval '1
hour' - interval '1 minute' - interval '1 second' - interval '1 millisecond' -
interval '1 microsecond' - interval '1 nanosecond'
----
11 mons -1 days -1 hours -1 mins -1.001001001 secs
-# Interval with string literal negation and leading field
-query ?
-select -interval '5' - '1' - '2' year;
-----
--96 mons
-
-# Interval with nested string literal negation
+# Interval with nested literal negation
query ?
-select -interval '1 month' + '1 day' + '1 hour';
+select -interval '1 month' + interval '1 day' + interval '1 hour';
----
-1 mons 1 days 1 hours
-# Interval with nested string literal negation and leading field
-query ?
-select -interval '10' - '1' - '1' month;
-----
--12 mons
-
-# Interval mega nested string literal negation
+# Interval mega nested literal negation
query ?
-select -interval '1 year' - '1 month' - '1 day' - '1 hour' - '1 minute' - '1
second' - '1 millisecond' - '1 microsecond' - '1 nanosecond'
+select -interval '1 year' - interval '1 month' - interval '1 day' - interval
'1 hour' - interval '1 minute' - interval '1 second' - interval '1 millisecond'
- interval '1 microsecond' - interval '1 nanosecond'
----
-13 mons -1 days -1 hours -1 mins -1.001001001 secs
# Interval string literal + date
query D
-select interval '1 month' + '1 day' + '2012-01-01'::date;
-----
-2012-02-02
-
-# Interval string literal parenthesized + date
-query D
-select ( interval '1 month' + '1 day' ) + '2012-01-01'::date;
+select interval 1 month + interval 1 day + '2012-01-01'::date;
----
2012-02-02
# Interval nested string literal + date
query D
-select interval '1 year' + '1 month' + '1 day' + '2012-01-01'::date
+select interval 1 year + interval 1 month + interval 1 day +
'2012-01-01'::date
----
2013-02-02
# Interval nested string literal subtraction + date
query D
-select interval '1 year' - '1 month' + '1 day' + '2012-01-01'::date
+select interval 1 year - interval 1 month + interval 1 day + '2012-01-01'::date
----
2012-12-02
-
-
-
# Use interval SQL type
query TT
select
diff --git a/datafusion/sqllogictest/test_files/interval_mysql.slt
b/datafusion/sqllogictest/test_files/interval_mysql.slt
new file mode 100644
index 0000000000..c05bb007e5
--- /dev/null
+++ b/datafusion/sqllogictest/test_files/interval_mysql.slt
@@ -0,0 +1,71 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Use `interval` SQL literal syntax with MySQL dialect
+
+# this should fail with the generic dialect
+query error DataFusion error: Error during planning: Cannot coerce arithmetic
expression Interval\(MonthDayNano\) \+ Utf8 to valid types
+select interval '1' + '1' month
+
+statement ok
+set datafusion.sql_parser.dialect = 'Mysql';
+
+# Interval with string literal addition and leading field
+query ?
+select interval '1' + '1' month
+----
+2 mons
+
+# Interval with nested string literal addition
+query ?
+select interval 1 + 1 + 1 month
+----
+3 mons
+
+# Interval with nested string literal addition and leading field
+query ?
+select interval '1' + '1' + '1' month
+----
+3 mons
+
+# Interval with string literal subtraction and leading field
+query ?
+select interval '5' - '1' - '2' year;
+----
+24 mons
+
+# Interval with nested string literal subtraction and leading field
+query ?
+select interval '10' - '1' - '1' month;
+----
+8 mons
+
+# Interval with string literal negation and leading field
+query ?
+select -interval '5' - '1' - '2' year;
+----
+-96 mons
+
+# Interval with nested string literal negation and leading field
+query ?
+select -interval '10' - '1' - '1' month;
+----
+-12 mons
+
+# revert to standard dialect
+statement ok
+set datafusion.sql_parser.dialect = 'Generic';
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]