From bccfd22db01794615e05f5f5e303f299650d4ac6 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 12 Sep 2024 17:34:00 +0100 Subject: [PATCH] fix interval units properly --- datafusion/sql/src/expr/value.rs | 59 ++++--------------- .../sqllogictest/test_files/interval.slt | 25 ++++++++ 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs index afcd182fa343..5df19cf357ed 100644 --- a/datafusion/sql/src/expr/value.rs +++ b/datafusion/sql/src/expr/value.rs @@ -16,7 +16,9 @@ // under the License. use crate::planner::{ContextProvider, PlannerContext, SqlToRel}; -use arrow::compute::kernels::cast_utils::parse_interval_month_day_nano; +use arrow::compute::kernels::cast_utils::{ + parse_interval_month_day_nano_config, IntervalParseConfig, IntervalUnit, +}; use arrow::datatypes::DECIMAL128_MAX_PRECISION; use arrow_schema::DataType; use datafusion_common::{ @@ -318,60 +320,19 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { } }; - let value = if has_units(&value) { - // If the interval already contains a unit - // `INTERVAL '5 month' rather than `INTERVAL '5' month` - // skip the other unit - value - } else { - // leading_field really means the unit if specified - // for example, "month" in `INTERVAL '5' month` - match interval.leading_field.as_ref() { - Some(leading_field) => { - format!("{value} {leading_field}") - } - None => { - // default to seconds for the units - // `INTERVAL '5' is parsed as '5 seconds' - format!("{value} seconds") - } - } + // leading_field really means the unit if specified + // for example, "month" in `INTERVAL '5' month` + let value = match interval.leading_field.as_ref() { + Some(leading_field) => format!("{value} {leading_field}"), + None => value, }; - let val = parse_interval_month_day_nano(&value)?; + let config = IntervalParseConfig::new(IntervalUnit::Second); + let val = parse_interval_month_day_nano_config(&value, config)?; Ok(lit(ScalarValue::IntervalMonthDayNano(Some(val)))) } } -// TODO make interval parsing better in arrow-rs / expose `IntervalType` -fn has_units(val: &str) -> bool { - let val = val.to_lowercase(); - val.ends_with("century") - || val.ends_with("centuries") - || val.ends_with("decade") - || val.ends_with("decades") - || val.ends_with("year") - || val.ends_with("years") - || val.ends_with("month") - || val.ends_with("months") - || val.ends_with("week") - || val.ends_with("weeks") - || val.ends_with("day") - || val.ends_with("days") - || val.ends_with("hour") - || val.ends_with("hours") - || val.ends_with("minute") - || val.ends_with("minutes") - || val.ends_with("second") - || val.ends_with("seconds") - || val.ends_with("millisecond") - || val.ends_with("milliseconds") - || val.ends_with("microsecond") - || val.ends_with("microseconds") - || val.ends_with("nanosecond") - || val.ends_with("nanoseconds") -} - /// Try to decode bytes from hex literal string. /// /// None will be returned if the input literal is hex-invalid. diff --git a/datafusion/sqllogictest/test_files/interval.slt b/datafusion/sqllogictest/test_files/interval.slt index 077f38d5d5bb..0d293b33b7bd 100644 --- a/datafusion/sqllogictest/test_files/interval.slt +++ b/datafusion/sqllogictest/test_files/interval.slt @@ -544,6 +544,31 @@ select i - d from t; query error DataFusion error: Error during planning: Cannot coerce arithmetic expression Interval\(MonthDayNano\) \- Timestamp\(Nanosecond, None\) to valid types select i - ts from t; +# interval unit abreiviation and plurals +query ? +select interval '1s' +---- +1.000000000 secs + +query ? +select '1s'::interval +---- +1.000000000 secs + +query ? +select interval'1sec' +---- +1.000000000 secs + +query ? +select interval '1ms' +---- +0.001000000 secs + +query ? +select interval '1 y' + interval '1 year' +---- +24 mons # interval (scalar) + date / timestamp (array) query D