From 4eba5c2ea3ad9ac474a7282a20c4cade96a442da Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Thu, 30 Mar 2023 16:41:11 -0400 Subject: [PATCH 1/3] Fix `interval` to use consistent units and arrow parser --- datafusion/common/src/lib.rs | 1 - datafusion/common/src/parsers.rs | 338 ------------------ datafusion/core/tests/sql/expr.rs | 72 ++-- datafusion/core/tests/sql/mod.rs | 1 + datafusion/core/tests/sql/timestamp.rs | 56 +-- .../sqllogictests/test_files/arrow_typeof.slt | 6 +- .../sqllogictests/test_files/interval.slt | 108 +++++- datafusion/optimizer/src/type_coercion.rs | 21 +- datafusion/sql/Cargo.toml | 1 + datafusion/sql/src/expr/value.rs | 57 ++- datafusion/sql/tests/integration_test.rs | 9 +- 11 files changed, 236 insertions(+), 434 deletions(-) diff --git a/datafusion/common/src/lib.rs b/datafusion/common/src/lib.rs index c0f7f9a4c81d..2b227fd1db12 100644 --- a/datafusion/common/src/lib.rs +++ b/datafusion/common/src/lib.rs @@ -39,7 +39,6 @@ pub use error::{ field_not_found, unqualified_field_not_found, DataFusionError, Result, SchemaError, SharedResult, }; -pub use parsers::parse_interval; pub use scalar::{ScalarType, ScalarValue}; pub use stats::{ColumnStatistics, Statistics}; pub use table_reference::{OwnedTableReference, ResolvedTableReference, TableReference}; diff --git a/datafusion/common/src/parsers.rs b/datafusion/common/src/parsers.rs index fbc663e1aa10..58f4db751c4c 100644 --- a/datafusion/common/src/parsers.rs +++ b/datafusion/common/src/parsers.rs @@ -18,13 +18,9 @@ //! Interval parsing logic use sqlparser::parser::ParserError; -use crate::{DataFusionError, Result, ScalarValue}; use std::result; use std::str::FromStr; -const SECONDS_PER_HOUR: f64 = 3_600_f64; -const NANOS_PER_SECOND: f64 = 1_000_000_000_f64; - /// Readable file compression type #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CompressionTypeVariant { @@ -76,337 +72,3 @@ impl CompressionTypeVariant { !matches!(self, &Self::UNCOMPRESSED) } } - -#[rustfmt::skip] -#[derive(Clone, Copy)] -#[repr(u16)] -enum IntervalType { - Century = 0b_0000_0000_0001, - Decade = 0b_0000_0000_0010, - Year = 0b_0000_0000_0100, - Month = 0b_0000_0000_1000, - Week = 0b_0000_0001_0000, - Day = 0b_0000_0010_0000, - Hour = 0b_0000_0100_0000, - Minute = 0b_0000_1000_0000, - Second = 0b_0001_0000_0000, - Millisecond = 0b_0010_0000_0000, - Microsecond = 0b_0100_0000_0000, - Nanosecond = 0b_1000_0000_0000, -} - -impl FromStr for IntervalType { - type Err = DataFusionError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "century" | "centuries" => Ok(Self::Century), - "decade" | "decades" => Ok(Self::Decade), - "year" | "years" => Ok(Self::Year), - "month" | "months" => Ok(Self::Month), - "week" | "weeks" => Ok(Self::Week), - "day" | "days" => Ok(Self::Day), - "hour" | "hours" => Ok(Self::Hour), - "minute" | "minutes" => Ok(Self::Minute), - "second" | "seconds" => Ok(Self::Second), - "millisecond" | "milliseconds" => Ok(Self::Millisecond), - "microsecond" | "microseconds" => Ok(Self::Microsecond), - "nanosecond" | "nanoseconds" => Ok(Self::Nanosecond), - _ => Err(DataFusionError::NotImplemented(format!( - "Unknown interval type: {s}" - ))), - } - } -} - -/// Parses a string with an interval like `'0.5 MONTH'` to an -/// appropriately typed [`ScalarValue`] -pub fn parse_interval(leading_field: &str, value: &str) -> Result { - // We are storing parts as integers, it's why we need to align parts fractional - // INTERVAL '0.5 MONTH' = 15 days, INTERVAL '1.5 MONTH' = 1 month 15 days - // INTERVAL '0.5 DAY' = 12 hours, INTERVAL '1.5 DAY' = 1 day 12 hours - let align_interval_parts = - |month_part: f64, mut day_part: f64, mut nanos_part: f64| -> (i64, i64, f64) { - // Convert fractional month to days, It's not supported by Arrow types, but anyway - day_part += (month_part - (month_part as i64) as f64) * 30_f64; - - // Convert fractional days to hours - nanos_part += (day_part - ((day_part as i64) as f64)) - * 24_f64 - * SECONDS_PER_HOUR - * NANOS_PER_SECOND; - - (month_part as i64, day_part as i64, nanos_part) - }; - - let mut used_interval_types = 0; - - let mut calculate_from_part = |interval_period_str: &str, - interval_type: &str| - -> Result<(i64, i64, f64)> { - // @todo It's better to use Decimal in order to protect rounding errors - // Wait https://github.com/apache/arrow/pull/9232 - let interval_period = match f64::from_str(interval_period_str) { - Ok(n) => n, - Err(_) => { - return Err(DataFusionError::NotImplemented(format!( - "Unsupported Interval Expression with value {value:?}" - ))); - } - }; - - if interval_period > (i64::MAX as f64) { - return Err(DataFusionError::NotImplemented(format!( - "Interval field value out of range: {value:?}" - ))); - } - - let it = IntervalType::from_str(interval_type).map_err(|_| { - DataFusionError::NotImplemented(format!( - "Invalid input syntax for type interval: {value:?}" - )) - })?; - - // Disallow duplicate interval types - if used_interval_types & (it as u16) != 0 { - return Err(DataFusionError::SQL(ParserError::ParserError(format!( - "Invalid input syntax for type interval: {value:?}. Repeated type '{interval_type}'" - )))); - } else { - used_interval_types |= it as u16; - } - - match it { - IntervalType::Century => { - Ok(align_interval_parts(interval_period * 1200_f64, 0.0, 0.0)) - } - IntervalType::Decade => { - Ok(align_interval_parts(interval_period * 120_f64, 0.0, 0.0)) - } - IntervalType::Year => { - Ok(align_interval_parts(interval_period * 12_f64, 0.0, 0.0)) - } - IntervalType::Month => Ok(align_interval_parts(interval_period, 0.0, 0.0)), - IntervalType::Week => { - Ok(align_interval_parts(0.0, interval_period * 7_f64, 0.0)) - } - IntervalType::Day => Ok(align_interval_parts(0.0, interval_period, 0.0)), - IntervalType::Hour => { - Ok((0, 0, interval_period * SECONDS_PER_HOUR * NANOS_PER_SECOND)) - } - IntervalType::Minute => { - Ok((0, 0, interval_period * 60_f64 * NANOS_PER_SECOND)) - } - IntervalType::Second => Ok((0, 0, interval_period * NANOS_PER_SECOND)), - IntervalType::Millisecond => Ok((0, 0, interval_period * 1_000_000f64)), - IntervalType::Microsecond => Ok((0, 0, interval_period * 1_000f64)), - IntervalType::Nanosecond => Ok((0, 0, interval_period)), - } - }; - - let mut result_month: i64 = 0; - let mut result_days: i64 = 0; - let mut result_nanos: i128 = 0; - - let mut parts = value.split_whitespace(); - - loop { - let interval_period_str = parts.next(); - if interval_period_str.is_none() { - break; - } - - let unit = parts.next().unwrap_or(leading_field); - - let (diff_month, diff_days, diff_nanos) = - calculate_from_part(interval_period_str.unwrap(), unit)?; - - result_month += diff_month; - - if result_month > (i32::MAX as i64) { - return Err(DataFusionError::NotImplemented(format!( - "Interval field value out of range: {value:?}" - ))); - } - - result_days += diff_days; - - if result_days > (i32::MAX as i64) { - return Err(DataFusionError::NotImplemented(format!( - "Interval field value out of range: {value:?}" - ))); - } - - result_nanos += diff_nanos as i128; - - if result_nanos > (i64::MAX as i128) { - return Err(DataFusionError::NotImplemented(format!( - "Interval field value out of range: {value:?}" - ))); - } - } - - // Interval is tricky thing - // 1 day is not 24 hours because timezones, 1 year != 365/364! 30 days != 1 month - // The true way to store and calculate intervals is to store it as it defined - // It's why we there are 3 different interval types in Arrow - - // If have a unit smaller than milliseconds then must use IntervalMonthDayNano - if (result_nanos % 1_000_000 != 0) - || (result_month != 0 && (result_days != 0 || result_nanos != 0)) - { - let result: i128 = ((result_month as i128) << 96) - // ensure discard high 32 bits of result_days before casting to i128 - | (((result_days & u32::MAX as i64) as i128) << 64) - // ensure discard high 64 bits of result_nanos - | (result_nanos & u64::MAX as i128); - - return Ok(ScalarValue::IntervalMonthDayNano(Some(result))); - } - - // Month interval - if result_month != 0 { - return Ok(ScalarValue::IntervalYearMonth(Some(result_month as i32))); - } - - // IntervalMonthDayNano uses nanos, but IntervalDayTime uses millis - let result: i64 = - // ensure discard high 32 bits of milliseconds - (result_days << 32) | ((result_nanos as i64 / 1_000_000) & (u32::MAX as i64)); - Ok(ScalarValue::IntervalDayTime(Some(result))) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::assert_contains; - - const MILLIS_PER_SECOND: f64 = 1_000_f64; - - #[test] - fn test_parse_ym() { - assert_eq!( - parse_interval("months", "1 month").unwrap(), - ScalarValue::new_interval_ym(0, 1) - ); - - assert_eq!( - parse_interval("months", "2 month").unwrap(), - ScalarValue::new_interval_ym(0, 2) - ); - - assert_eq!( - parse_interval("months", "3 year 1 month").unwrap(), - ScalarValue::new_interval_ym(3, 1) - ); - - assert_contains!( - parse_interval("months", "1 centurys 1 month") - .unwrap_err() - .to_string(), - r#"Invalid input syntax for type interval: "1 centurys 1 month""# - ); - - assert_eq!( - parse_interval("months", "3 year -1 month").unwrap(), - ScalarValue::new_interval_ym(3, -1) - ); - - assert_eq!( - parse_interval("months", "-3 year -1 month").unwrap(), - ScalarValue::new_interval_ym(-3, -1) - ); - - assert_eq!( - parse_interval("months", "-3 year 1 month").unwrap(), - ScalarValue::new_interval_ym(-3, 1) - ); - } - - #[test] - fn test_dt() { - assert_eq!( - parse_interval("months", "5 days").unwrap(), - ScalarValue::new_interval_dt(5, 0) - ); - - assert_eq!( - parse_interval("months", "7 days 3 hours").unwrap(), - ScalarValue::new_interval_dt( - 7, - (3.0 * SECONDS_PER_HOUR * MILLIS_PER_SECOND) as i32 - ) - ); - - assert_eq!( - parse_interval("months", "7 days 5 minutes").unwrap(), - ScalarValue::new_interval_dt(7, 5 * 60 * MILLIS_PER_SECOND as i32) - ); - - assert_eq!( - parse_interval("months", "7 days -5 minutes").unwrap(), - ScalarValue::new_interval_dt(7, -5 * 60 * MILLIS_PER_SECOND as i32) - ); - - assert_eq!( - parse_interval("months", "-7 days 5 hours").unwrap(), - ScalarValue::new_interval_dt(-7, 5 * 60 * 60 * MILLIS_PER_SECOND as i32) - ); - - assert_eq!( - parse_interval("months", "-7 days -5 hours -5 minutes -5 seconds").unwrap(), - ScalarValue::new_interval_dt( - -7, - -(5 * 60 * 60 + 5 * 60 + 5) * MILLIS_PER_SECOND as i32 - ) - ); - } - - #[test] - fn test_mdn() { - assert_eq!( - parse_interval("months", "1 year 25 millisecond").unwrap(), - ScalarValue::new_interval_mdn(12, 0, 25 * 1_000_000) - ); - - assert_eq!( - parse_interval("months", "1 year 1 day 0.000000001 seconds").unwrap(), - ScalarValue::new_interval_mdn(12, 1, 1) - ); - - assert_eq!( - parse_interval("months", "1 year 1 day 0.1 milliseconds").unwrap(), - ScalarValue::new_interval_mdn(12, 1, 1_00 * 1_000) - ); - - assert_eq!( - parse_interval("months", "1 year 1 day 1 microsecond").unwrap(), - ScalarValue::new_interval_mdn(12, 1, 1_000) - ); - - assert_eq!( - parse_interval("months", "1 year 1 day 5 nanoseconds").unwrap(), - ScalarValue::new_interval_mdn(12, 1, 5) - ); - - assert_eq!( - parse_interval("months", "1 month -1 second").unwrap(), - ScalarValue::new_interval_mdn(1, 0, -1_000_000_000) - ); - - assert_eq!( - parse_interval("months", "-1 year -1 month -1 week -1 day -1 hour -1 minute -1 second -1.11 millisecond").unwrap(), - ScalarValue::new_interval_mdn(-13, -8, -(60 * 60 + 60 + 1) * NANOS_PER_SECOND as i64 - 1_110_000) - ); - } - - #[test] - fn test_duplicate_interval_type() { - let err = parse_interval("months", "1 month 1 second 1 second") - .expect_err("parsing interval should have failed"); - assert_eq!( - r#"SQL(ParserError("Invalid input syntax for type interval: \"1 month 1 second 1 second\". Repeated type 'second'"))"#, - format!("{err:?}") - ); - } -} diff --git a/datafusion/core/tests/sql/expr.rs b/datafusion/core/tests/sql/expr.rs index 3d5f134a8783..aceb09c299bb 100644 --- a/datafusion/core/tests/sql/expr.rs +++ b/datafusion/core/tests/sql/expr.rs @@ -843,83 +843,83 @@ async fn test_interval_expressions() -> Result<()> { // day nano intervals test_expression!( "interval '1'", - "0 years 0 mons 0 days 0 hours 0 mins 1.000 secs" + "0 years 0 mons 0 days 0 hours 0 mins 1.000000000 secs" ); test_expression!( "interval '1 second'", - "0 years 0 mons 0 days 0 hours 0 mins 1.000 secs" + "0 years 0 mons 0 days 0 hours 0 mins 1.000000000 secs" ); test_expression!( "interval '500 milliseconds'", - "0 years 0 mons 0 days 0 hours 0 mins 0.500 secs" + "0 years 0 mons 0 days 0 hours 0 mins 0.500000000 secs" ); test_expression!( "interval '5 second'", - "0 years 0 mons 0 days 0 hours 0 mins 5.000 secs" + "0 years 0 mons 0 days 0 hours 0 mins 5.000000000 secs" ); test_expression!( "interval '0.5 minute'", - "0 years 0 mons 0 days 0 hours 0 mins 30.000 secs" + "0 years 0 mons 0 days 0 hours 0 mins 30.000000000 secs" ); test_expression!( "interval '.5 minute'", - "0 years 0 mons 0 days 0 hours 0 mins 30.000 secs" + "0 years 0 mons 0 days 0 hours 0 mins 30.000000000 secs" ); test_expression!( "interval '5 minute'", - "0 years 0 mons 0 days 0 hours 5 mins 0.000 secs" + "0 years 0 mons 0 days 0 hours 5 mins 0.000000000 secs" ); test_expression!( "interval '5 minute 1 second'", - "0 years 0 mons 0 days 0 hours 5 mins 1.000 secs" + "0 years 0 mons 0 days 0 hours 5 mins 1.000000000 secs" ); test_expression!( "interval '1 hour'", - "0 years 0 mons 0 days 1 hours 0 mins 0.000 secs" + "0 years 0 mons 0 days 1 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '5 hour'", - "0 years 0 mons 0 days 5 hours 0 mins 0.000 secs" + "0 years 0 mons 0 days 5 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 day'", - "0 years 0 mons 1 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 1 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 week'", - "0 years 0 mons 7 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 7 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '2 weeks'", - "0 years 0 mons 14 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 14 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 day 1'", - "0 years 0 mons 1 days 0 hours 0 mins 1.000 secs" + "0 years 0 mons 1 days 0 hours 0 mins 1.000000000 secs" ); test_expression!( "interval '0.5'", - "0 years 0 mons 0 days 0 hours 0 mins 0.500 secs" + "0 years 0 mons 0 days 0 hours 0 mins 0.500000000 secs" ); test_expression!( "interval '0.5 day 1'", - "0 years 0 mons 0 days 12 hours 0 mins 1.000 secs" + "0 years 0 mons 0 days 12 hours 0 mins 1.000000000 secs" ); test_expression!( "interval '0.49 day'", - "0 years 0 mons 0 days 11 hours 45 mins 36.000 secs" + "0 years 0 mons 0 days 11 hours 45 mins 36.000000000 secs" ); test_expression!( "interval '0.499 day'", - "0 years 0 mons 0 days 11 hours 58 mins 33.600 secs" + "0 years 0 mons 0 days 11 hours 58 mins 33.600000000 secs" ); test_expression!( "interval '0.4999 day'", - "0 years 0 mons 0 days 11 hours 59 mins 51.360 secs" + "0 years 0 mons 0 days 11 hours 59 mins 51.360000000 secs" ); test_expression!( "interval '0.49999 day'", - "0 years 0 mons 0 days 11 hours 59 mins 59.136 secs" + "0 years 0 mons 0 days 11 hours 59 mins 59.136000000 secs" ); test_expression!( "interval '0.49999999999 day'", @@ -927,69 +927,69 @@ async fn test_interval_expressions() -> Result<()> { ); test_expression!( "interval '5 day'", - "0 years 0 mons 5 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 5 days 0 hours 0 mins 0.000000000 secs" ); // Hour is ignored, this matches PostgreSQL test_expression!( "interval '5 day' hour", - "0 years 0 mons 5 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 5 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '5 day 4 hours 3 minutes 2 seconds 100 milliseconds'", - "0 years 0 mons 5 days 4 hours 3 mins 2.100 secs" + "0 years 0 mons 5 days 4 hours 3 mins 2.100000000 secs" ); // month intervals test_expression!( "interval '0.5 month'", - "0 years 0 mons 15 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 15 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '0.5' month", - "0 years 0 mons 15 days 0 hours 0 mins 0.000 secs" + "0 years 0 mons 15 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 month'", - "0 years 1 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 1 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1' MONTH", - "0 years 1 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 1 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '5 month'", - "0 years 5 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 5 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '13 month'", - "1 years 1 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 13 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '0.5 year'", - "0 years 6 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 6 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 year'", - "1 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 12 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 decade'", - "10 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 120 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '2 decades'", - "20 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 240 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '1 century'", - "100 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 1200 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '2 year'", - "2 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 24 mons 0 days 0 hours 0 mins 0.000000000 secs" ); test_expression!( "interval '2' year", - "2 years 0 mons 0 days 0 hours 0 mins 0.00 secs" + "0 years 24 mons 0 days 0 hours 0 mins 0.000000000 secs" ); // complex test_expression!( diff --git a/datafusion/core/tests/sql/mod.rs b/datafusion/core/tests/sql/mod.rs index 00918638e916..e4a9c19578b2 100644 --- a/datafusion/core/tests/sql/mod.rs +++ b/datafusion/core/tests/sql/mod.rs @@ -70,6 +70,7 @@ macro_rules! assert_metrics { macro_rules! test_expression { ($SQL:expr, $EXPECTED:expr) => { + println!("Input:\n {}\nExpected:\n {}\n", $SQL, $EXPECTED); let ctx = SessionContext::new(); let sql = format!("SELECT {}", $SQL); let actual = execute(&ctx, sql.as_str()).await; diff --git a/datafusion/core/tests/sql/timestamp.rs b/datafusion/core/tests/sql/timestamp.rs index e62465203a01..f413aafde809 100644 --- a/datafusion/core/tests/sql/timestamp.rs +++ b/datafusion/core/tests/sql/timestamp.rs @@ -968,52 +968,52 @@ async fn timestamp_array_add_interval() -> Result<()> { let sql = "SELECT ts, ts - INTERVAL '8' MILLISECONDS FROM table_a"; let actual = execute_to_batches(&ctx, sql).await; let expected = vec![ - "+----------------------------+-----------------------------------+", - "| ts | table_a.ts - IntervalDayTime(\"8\") |", - "+----------------------------+-----------------------------------+", - "| 2020-09-08T13:42:29.190855 | 2020-09-08T13:42:29.182855 |", - "| 2020-09-08T12:42:29.190855 | 2020-09-08T12:42:29.182855 |", - "| 2020-09-08T11:42:29.190855 | 2020-09-08T11:42:29.182855 |", - "+----------------------------+-----------------------------------+", + "+----------------------------+----------------------------------------------+", + "| ts | table_a.ts - IntervalMonthDayNano(\"8000000\") |", + "+----------------------------+----------------------------------------------+", + "| 2020-09-08T13:42:29.190855 | 2020-09-08T13:42:29.182855 |", + "| 2020-09-08T12:42:29.190855 | 2020-09-08T12:42:29.182855 |", + "| 2020-09-08T11:42:29.190855 | 2020-09-08T11:42:29.182855 |", + "+----------------------------+----------------------------------------------+", ]; assert_batches_eq!(expected, &actual); let sql = "SELECT ts, ts + INTERVAL '1' SECOND FROM table_b"; let actual = execute_to_batches(&ctx, sql).await; let expected = vec![ - "+----------------------------+--------------------------------------+", - "| ts | table_b.ts + IntervalDayTime(\"1000\") |", - "+----------------------------+--------------------------------------+", - "| 2020-09-08T13:42:29.190855 | 2020-09-08T13:42:30.190855 |", - "| 2020-09-08T12:42:29.190855 | 2020-09-08T12:42:30.190855 |", - "| 2020-09-08T11:42:29.190855 | 2020-09-08T11:42:30.190855 |", - "+----------------------------+--------------------------------------+", + "+----------------------------+-------------------------------------------------+", + "| ts | table_b.ts + IntervalMonthDayNano(\"1000000000\") |", + "+----------------------------+-------------------------------------------------+", + "| 2020-09-08T13:42:29.190855 | 2020-09-08T13:42:30.190855 |", + "| 2020-09-08T12:42:29.190855 | 2020-09-08T12:42:30.190855 |", + "| 2020-09-08T11:42:29.190855 | 2020-09-08T11:42:30.190855 |", + "+----------------------------+-------------------------------------------------+", ]; assert_batches_eq!(expected, &actual); let sql = "SELECT ts, ts + INTERVAL '2' MONTH FROM table_b"; let actual = execute_to_batches(&ctx, sql).await; let expected = vec![ - "+----------------------------+-------------------------------------+", - "| ts | table_b.ts + IntervalYearMonth(\"2\") |", - "+----------------------------+-------------------------------------+", - "| 2020-09-08T13:42:29.190855 | 2020-11-08T13:42:29.190855 |", - "| 2020-09-08T12:42:29.190855 | 2020-11-08T12:42:29.190855 |", - "| 2020-09-08T11:42:29.190855 | 2020-11-08T11:42:29.190855 |", - "+----------------------------+-------------------------------------+", + "+----------------------------+---------------------------------------------------------------------+", + "| ts | table_b.ts + IntervalMonthDayNano(\"158456325028528675187087900672\") |", + "+----------------------------+---------------------------------------------------------------------+", + "| 2020-09-08T13:42:29.190855 | 2020-11-08T13:42:29.190855 |", + "| 2020-09-08T12:42:29.190855 | 2020-11-08T12:42:29.190855 |", + "| 2020-09-08T11:42:29.190855 | 2020-11-08T11:42:29.190855 |", + "+----------------------------+---------------------------------------------------------------------+", ]; assert_batches_eq!(expected, &actual); let sql = "SELECT ts, ts - INTERVAL '16' YEAR FROM table_b"; let actual = execute_to_batches(&ctx, sql).await; let expected = vec![ - "+----------------------------+---------------------------------------+", - "| ts | table_b.ts - IntervalYearMonth(\"192\") |", - "+----------------------------+---------------------------------------+", - "| 2020-09-08T13:42:29.190855 | 2004-09-08T13:42:29.190855 |", - "| 2020-09-08T12:42:29.190855 | 2004-09-08T12:42:29.190855 |", - "| 2020-09-08T11:42:29.190855 | 2004-09-08T11:42:29.190855 |", - "+----------------------------+---------------------------------------+", + "+----------------------------+-----------------------------------------------------------------------+", + "| ts | table_b.ts - IntervalMonthDayNano(\"15211807202738752817960438464512\") |", + "+----------------------------+-----------------------------------------------------------------------+", + "| 2020-09-08T13:42:29.190855 | 2004-09-08T13:42:29.190855 |", + "| 2020-09-08T12:42:29.190855 | 2004-09-08T12:42:29.190855 |", + "| 2020-09-08T11:42:29.190855 | 2004-09-08T11:42:29.190855 |", + "+----------------------------+-----------------------------------------------------------------------+", ]; assert_batches_eq!(expected, &actual); Ok(()) diff --git a/datafusion/core/tests/sqllogictests/test_files/arrow_typeof.slt b/datafusion/core/tests/sqllogictests/test_files/arrow_typeof.slt index 5166d291ac26..37ea6b5287e2 100644 --- a/datafusion/core/tests/sqllogictests/test_files/arrow_typeof.slt +++ b/datafusion/core/tests/sqllogictests/test_files/arrow_typeof.slt @@ -275,9 +275,11 @@ drop table foo ## Intervals: -query error Cannot automatically convert Interval\(DayTime\) to Interval\(MonthDayNano\) +query ? --- select arrow_cast(interval '30 minutes', 'Interval(MonthDayNano)'); +---- +0 years 0 mons 0 days 0 hours 30 mins 0.000000000 secs query ? select arrow_cast('30 minutes', 'Interval(DayTime)'); @@ -297,7 +299,7 @@ select arrow_cast('30 minutes', 'Interval(MonthDayNano)'); ## Duration -query error Cannot automatically convert Interval\(DayTime\) to Duration\(Second\) +query error DataFusion error: Error during planning: Cannot automatically convert Interval\(MonthDayNano\) to Duration\(Second\) --- select arrow_cast(interval '30 minutes', 'Duration(Second)'); diff --git a/datafusion/core/tests/sqllogictests/test_files/interval.slt b/datafusion/core/tests/sqllogictests/test_files/interval.slt index 7142ce6e5020..be5b432e8e09 100644 --- a/datafusion/core/tests/sqllogictests/test_files/interval.slt +++ b/datafusion/core/tests/sqllogictests/test_files/interval.slt @@ -23,7 +23,113 @@ select arrow_typeof(interval '5 months'), arrow_typeof(interval '5 days 3 nanoseconds') ---- -Interval(YearMonth) Interval(MonthDayNano) +Interval(MonthDayNano) Interval(MonthDayNano) + + +## This is incredibly confusing but document it in tests: +# +# years is parsed as a column name +# year is parsed as part of the interval type. +# +# postgres=# select interval '5' year; +# interval +# ---------- +# 5 years +# (1 row) +# +# postgres=# select interval '5' years; +# years +# ---------- +# 00:00:05 +# (1 row) +query ? +select interval '5' years +---- +0 years 0 mons 0 days 0 hours 0 mins 5.000000000 secs + + +# check all different kinds of intervals +query ? +select interval '5' year +---- +0 years 60 mons 0 days 0 hours 0 mins 0.000000000 secs + +query ? +select interval '5' month +---- +0 years 5 mons 0 days 0 hours 0 mins 0.000000000 secs + +query ? +select interval '5' months +---- +0 years 0 mons 0 days 0 hours 0 mins 5.000000000 secs + +query ? +select interval '5' week +---- +0 years 0 mons 35 days 0 hours 0 mins 0.000000000 secs + + +query ? +select interval '5' day +---- +0 years 0 mons 5 days 0 hours 0 mins 0.000000000 secs + +query ? +select interval '5' hour +---- +0 years 0 mons 0 days 5 hours 0 mins 0.000000000 secs + +## This seems wrong (5 mons) +query ? +select interval '5' hours +---- +0 years 0 mons 0 days 0 hours 0 mins 5.000000000 secs + +query ? +select interval '5' minute +---- +0 years 0 mons 0 days 0 hours 5 mins 0.000000000 secs + +query ? +select interval '5' second +---- +0 years 0 mons 0 days 0 hours 0 mins 5.000000000 secs + +query ? +select interval '5' millisecond +---- +0 years 0 mons 0 days 0 hours 0 mins 0.005000000 secs + +query ? +select interval '5' milliseconds +---- +0 years 0 mons 0 days 0 hours 0 mins 0.005000000 secs + +query ? +select interval '5' microsecond +---- +0 years 0 mons 0 days 0 hours 0 mins 0.000005000 secs + +query ? +select interval '5' microseconds +---- +0 years 0 mons 0 days 0 hours 0 mins 0.000005000 secs + +query ? +select interval '5' nanosecond +---- +0 years 0 mons 0 days 0 hours 0 mins 0.000000005 secs + +query ? +select interval '5' nanoseconds +---- +0 years 0 mons 0 days 0 hours 0 mins 0.000000005 secs + + + + + # Use interval SQL type diff --git a/datafusion/optimizer/src/type_coercion.rs b/datafusion/optimizer/src/type_coercion.rs index 0be9c89b6ccb..085259ce9343 100644 --- a/datafusion/optimizer/src/type_coercion.rs +++ b/datafusion/optimizer/src/type_coercion.rs @@ -22,9 +22,7 @@ use std::sync::Arc; use arrow::datatypes::{DataType, IntervalUnit}; use datafusion_common::tree_node::{RewriteRecursion, TreeNodeRewriter}; -use datafusion_common::{ - parse_interval, DFSchema, DFSchemaRef, DataFusionError, Result, ScalarValue, -}; +use datafusion_common::{DFSchema, DFSchemaRef, DataFusionError, Result, ScalarValue}; use datafusion_expr::expr::{self, Between, BinaryExpr, Case, Like, WindowFunction}; use datafusion_expr::logical_plan::Subquery; use datafusion_expr::type_coercion::binary::{ @@ -448,13 +446,7 @@ fn coerce_scalar(target_type: &DataType, value: &ScalarValue) -> Result { - // When `target_type` is `Interval`, we use `parse_interval` since - // `try_from_string` does not support `String` to `Interval` coercions. - if let DataType::Interval(..) = target_type { - parse_interval("millisecond", val) - } else { - ScalarValue::try_from_string(val.clone(), target_type) - } + ScalarValue::try_from_string(val.clone(), target_type) } s => { if s.is_null() { @@ -601,14 +593,7 @@ fn coerce_arguments_for_signature( /// Cast `expr` to the specified type, if possible fn cast_expr(expr: &Expr, to_type: &DataType, schema: &DFSchema) -> Result { - // Special case until Interval coercion is handled in arrow-rs - // https://github.com/apache/arrow-rs/issues/3643 - match (expr, to_type) { - (Expr::Literal(ScalarValue::Utf8(Some(s))), DataType::Interval(_)) => { - parse_interval("millisecond", s.as_str()).map(Expr::Literal) - } - _ => expr.clone().cast_to(to_type, schema), - } + expr.clone().cast_to(to_type, schema) } /// Returns the coerced exprs for each `input_exprs`. diff --git a/datafusion/sql/Cargo.toml b/datafusion/sql/Cargo.toml index 62b706b912eb..3494d89ef60a 100644 --- a/datafusion/sql/Cargo.toml +++ b/datafusion/sql/Cargo.toml @@ -38,6 +38,7 @@ unicode_expressions = [] [dependencies] arrow-schema = { workspace = true } +arrow = { workspace = true } datafusion-common = { path = "../common", version = "21.0.0" } datafusion-expr = { path = "../expr", version = "21.0.0" } log = "^0.4" diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs index 92d52c466277..c936313e208f 100644 --- a/datafusion/sql/src/expr/value.rs +++ b/datafusion/sql/src/expr/value.rs @@ -16,8 +16,9 @@ // under the License. use crate::planner::{ContextProvider, PlannerContext, SqlToRel}; +use arrow::compute::kernels::cast_utils::parse_interval_month_day_nano; use arrow_schema::DataType; -use datafusion_common::{parse_interval, DFSchema, DataFusionError, Result, ScalarValue}; +use datafusion_common::{DFSchema, DataFusionError, Result, ScalarValue}; use datafusion_expr::{lit, Expr}; use log::debug; use sqlparser::ast::{DateTimeField, Expr as SQLExpr, Value}; @@ -202,11 +203,55 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { } }; - let leading_field = leading_field - .as_ref() - .map(|dt| dt.to_string()) - .unwrap_or_else(|| "second".to_string()); + 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 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") + } + } + }; - Ok(lit(parse_interval(&leading_field, &value)?)) + let val = parse_interval_month_day_nano(&value)?; + Ok(lit(ScalarValue::IntervalMonthDayNano(Some(val)))) } } + +// TODO make interval parsing better in arrow-rs / expose `IntervalType` +fn has_units(val: &str) -> bool { + 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") +} diff --git a/datafusion/sql/tests/integration_test.rs b/datafusion/sql/tests/integration_test.rs index ef6142305121..bc65485912d4 100644 --- a/datafusion/sql/tests/integration_test.rs +++ b/datafusion/sql/tests/integration_test.rs @@ -1138,7 +1138,7 @@ fn select_interval_out_of_range() { let sql = "SELECT INTERVAL '100000000000000000 day'"; let err = logical_plan(sql).expect_err("query should have failed"); assert_eq!( - r#"NotImplemented("Interval field value out of range: \"100000000000000000 day\"")"#, + "ArrowError(ParseError(\"Parsed interval field value out of range: 0 months 100000000000000000 days 0 nanos\"))", format!("{err:?}") ); } @@ -2617,7 +2617,8 @@ fn cte_use_same_name_multiple_times() { #[test] fn date_plus_interval_in_projection() { let sql = "select t_date32 + interval '5 days' FROM test"; - let expected = "Projection: test.t_date32 + IntervalDayTime(\"21474836480\")\ + let expected = + "Projection: test.t_date32 + IntervalMonthDayNano(\"92233720368547758080\")\ \n TableScan: test"; quick_test(sql, expected); } @@ -2630,7 +2631,7 @@ fn date_plus_interval_in_filter() { AND cast('1999-12-31' as date) + interval '30 days'"; let expected = "Projection: test.t_date64\ - \n Filter: test.t_date64 BETWEEN CAST(Utf8(\"1999-12-31\") AS Date32) AND CAST(Utf8(\"1999-12-31\") AS Date32) + IntervalDayTime(\"128849018880\")\ + \n Filter: test.t_date64 BETWEEN CAST(Utf8(\"1999-12-31\") AS Date32) AND CAST(Utf8(\"1999-12-31\") AS Date32) + IntervalMonthDayNano(\"553402322211286548480\")\ \n TableScan: test"; quick_test(sql, expected); } @@ -3908,7 +3909,7 @@ fn test_inner_join_with_cast_key() { fn test_multi_grouping_sets() { let sql = "SELECT person.id, person.age FROM person - GROUP BY + GROUP BY person.id, GROUPING SETS ((person.age,person.salary),(person.age))"; From ce89035520f9043b2d49c97aa9bfe87aff9c0ea9 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 31 Mar 2023 14:25:20 -0400 Subject: [PATCH 2/3] cleanup --- datafusion/sql/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datafusion/sql/Cargo.toml b/datafusion/sql/Cargo.toml index 3494d89ef60a..2de3c3017e00 100644 --- a/datafusion/sql/Cargo.toml +++ b/datafusion/sql/Cargo.toml @@ -37,8 +37,8 @@ default = ["unicode_expressions"] unicode_expressions = [] [dependencies] -arrow-schema = { workspace = true } arrow = { workspace = true } +arrow-schema = { workspace = true } datafusion-common = { path = "../common", version = "21.0.0" } datafusion-expr = { path = "../expr", version = "21.0.0" } log = "^0.4" From 4b4ae6b04195a0f12e67e66e1b5fbce4798953cc Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 31 Mar 2023 14:35:58 -0400 Subject: [PATCH 3/3] update datafusion-cli --- datafusion-cli/Cargo.lock | 142 ++++++++++++-------------------------- 1 file changed, 43 insertions(+), 99 deletions(-) diff --git a/datafusion-cli/Cargo.lock b/datafusion-cli/Cargo.lock index c794b4cb2291..0690a13fc71e 100644 --- a/datafusion-cli/Cargo.lock +++ b/datafusion-cli/Cargo.lock @@ -301,7 +301,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -659,7 +659,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -676,7 +676,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -855,6 +855,7 @@ dependencies = [ name = "datafusion-sql" version = "21.0.0" dependencies = [ + "arrow", "arrow-schema", "datafusion-common", "datafusion-expr", @@ -1048,9 +1049,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1063,9 +1064,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1073,15 +1074,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1090,38 +1091,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.12", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1315,9 +1316,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.55" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1525,9 +1526,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "lock_api" @@ -2275,7 +2276,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -2454,9 +2455,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" dependencies = [ "proc-macro2", "quote", @@ -2508,7 +2509,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -2572,7 +2573,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.12", ] [[package]] @@ -2909,11 +2910,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.47.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets 0.47.0", + "windows-targets", ] [[package]] @@ -2922,7 +2923,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -2931,28 +2932,13 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e" -dependencies = [ - "windows_aarch64_gnullvm 0.47.0", - "windows_aarch64_msvc 0.47.0", - "windows_i686_gnu 0.47.0", - "windows_i686_msvc 0.47.0", - "windows_x86_64_gnu 0.47.0", - "windows_x86_64_gnullvm 0.47.0", - "windows_x86_64_msvc 0.47.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2961,84 +2947,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7" - [[package]] name = "winreg" version = "0.10.1"