diff --git a/src/query/ast/src/ast/expr.rs b/src/query/ast/src/ast/expr.rs index a87e8509d6731..9846400cb4abd 100644 --- a/src/query/ast/src/ast/expr.rs +++ b/src/query/ast/src/ast/expr.rs @@ -829,6 +829,7 @@ pub enum IntervalKind { Week, Dow, Epoch, + MicroSecond, } impl Display for IntervalKind { @@ -845,6 +846,7 @@ impl Display for IntervalKind { IntervalKind::Dow => "DOW", IntervalKind::Week => "WEEK", IntervalKind::Epoch => "EPOCH", + IntervalKind::MicroSecond => "MICROSECOND", }) } } diff --git a/src/query/ast/src/parser/expr.rs b/src/query/ast/src/parser/expr.rs index 50e210332281b..7e251d22c6f7e 100644 --- a/src/query/ast/src/parser/expr.rs +++ b/src/query/ast/src/parser/expr.rs @@ -1843,6 +1843,7 @@ pub fn interval_kind(i: Input) -> IResult { let dow = value(IntervalKind::Dow, rule! { DOW }); let week = value(IntervalKind::Week, rule! { WEEK }); let epoch = value(IntervalKind::Epoch, rule! { EPOCH }); + let microsecond = value(IntervalKind::MicroSecond, rule! { MICROSECOND }); let year_str = value( IntervalKind::Year, rule! { #literal_string_eq_ignore_case("YEAR") }, @@ -1887,6 +1888,10 @@ pub fn interval_kind(i: Input) -> IResult { IntervalKind::Epoch, rule! { #literal_string_eq_ignore_case("EPOCH") }, ); + let microsecond_str = value( + IntervalKind::MicroSecond, + rule! { #literal_string_eq_ignore_case("MICROSECOND") }, + ); alt(( rule!( #year @@ -1900,6 +1905,7 @@ pub fn interval_kind(i: Input) -> IResult { | #dow | #week | #epoch + | #microsecond ), rule!( #year_str @@ -1913,6 +1919,7 @@ pub fn interval_kind(i: Input) -> IResult { | #dow_str | #week_str | #epoch_str + | #microsecond_str ), ))(i) } diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index 3cd2bcfbf195a..e9462cc32a23b 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -583,6 +583,8 @@ pub enum TokenKind { ENGINES, #[token("EPOCH", ignore(ascii_case))] EPOCH, + #[token("MICROSECOND", ignore(ascii_case))] + MICROSECOND, #[token("ERROR_ON_COLUMN_COUNT_MISMATCH", ignore(ascii_case))] ERROR_ON_COLUMN_COUNT_MISMATCH, #[token("ESCAPE", ignore(ascii_case))] diff --git a/src/query/functions/src/scalars/timestamp/src/interval.rs b/src/query/functions/src/scalars/timestamp/src/interval.rs index 4cedd3d1ad058..e9c80e5b4e535 100644 --- a/src/query/functions/src/scalars/timestamp/src/interval.rs +++ b/src/query/functions/src/scalars/timestamp/src/interval.rs @@ -19,6 +19,7 @@ use databend_common_expression::date_helper::EvalMonthsImpl; use databend_common_expression::error_to_null; use databend_common_expression::types::interval::interval_to_string; use databend_common_expression::types::interval::string_to_interval; +use databend_common_expression::types::Float64Type; use databend_common_expression::types::Int64Type; use databend_common_expression::types::IntervalType; use databend_common_expression::types::StringType; @@ -296,4 +297,72 @@ fn register_number_to_interval(registry: &mut FunctionRegistry) { output.push(res); }), ); + + registry.register_passthrough_nullable_1_arg::( + "to_year", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + output.push(val.months() as i64 / 12); + }), + ); + registry.register_passthrough_nullable_1_arg::( + "to_month", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + output.push(val.months() as i64); + }), + ); + // Directly return interval days. Extract need named to_day_of_month + registry.register_passthrough_nullable_1_arg::( + "to_day_of_month", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + output.push(val.days() as i64); + }), + ); + registry.register_passthrough_nullable_1_arg::( + "to_hour", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + let total_seconds = (val.microseconds() as f64) / 1_000_000.0; + let hours = (total_seconds / 3600.0) as i64; + output.push(hours); + }), + ); + registry.register_passthrough_nullable_1_arg::( + "to_minute", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + let total_seconds = (val.microseconds() as f64) / 1_000_000.0; + let minutes = ((total_seconds % 3600.0) / 60.0) as i64; + output.push(minutes); + }), + ); + + registry.register_passthrough_nullable_1_arg::( + "to_second", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + let microseconds = val.microseconds() % 60_000_000; + let seconds = microseconds as f64 / 1_000_000.0; + output.push(seconds.into()); + }), + ); + + registry.register_passthrough_nullable_1_arg::( + "to_microsecond", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + let microseconds = val.microseconds() % 60_000_000; + output.push(microseconds); + }), + ); + registry.register_passthrough_nullable_1_arg::( + "epoch", + |_, _| FunctionDomain::MayThrow, + vectorize_with_builder_1_arg::(|val, output, _| { + let total_seconds = (val.total_micros() as f64) / 1_000_000.0; + output.push(total_seconds.into()); + }), + ); } diff --git a/src/query/functions/tests/it/scalars/testdata/function_list.txt b/src/query/functions/tests/it/scalars/testdata/function_list.txt index 7ee50238a6637..540a16f0faae5 100644 --- a/src/query/functions/tests/it/scalars/testdata/function_list.txt +++ b/src/query/functions/tests/it/scalars/testdata/function_list.txt @@ -1675,6 +1675,8 @@ Functions overloads: 1 epoch(Timestamp NULL) :: Float64 NULL 2 epoch(Int64) :: Interval 3 epoch(Int64 NULL) :: Interval NULL +4 epoch(Interval) :: Float64 +5 epoch(Interval NULL) :: Float64 NULL 0 eq(Variant, Variant) :: Boolean 1 eq(Variant NULL, Variant NULL) :: Boolean NULL 2 eq(String, String) :: Boolean @@ -3558,6 +3560,8 @@ Functions overloads: 1 to_day_of_month(Date NULL) :: UInt8 NULL 2 to_day_of_month(Timestamp) :: UInt8 3 to_day_of_month(Timestamp NULL) :: UInt8 NULL +4 to_day_of_month(Interval) :: Int64 +5 to_day_of_month(Interval NULL) :: Int64 NULL 0 to_day_of_week(Date) :: UInt8 1 to_day_of_week(Date NULL) :: UInt8 NULL 2 to_day_of_week(Timestamp) :: UInt8 @@ -3644,6 +3648,8 @@ Functions overloads: 5 to_hex(Binary NULL) :: String NULL 0 to_hour(Timestamp) :: UInt8 1 to_hour(Timestamp NULL) :: UInt8 NULL +2 to_hour(Interval) :: Int64 +3 to_hour(Interval NULL) :: Int64 NULL 0 to_hours(Int64) :: Interval 1 to_hours(Int64 NULL) :: Interval NULL 0 to_int16(Variant) :: Int16 @@ -3772,6 +3778,8 @@ Functions overloads: 1 to_last_of_year(Date NULL) :: Date NULL 2 to_last_of_year(Timestamp) :: Date 3 to_last_of_year(Timestamp NULL) :: Date NULL +0 to_microsecond(Interval) :: Int64 +1 to_microsecond(Interval NULL) :: Int64 NULL 0 to_microseconds(Int64) :: Interval 1 to_microseconds(Int64 NULL) :: Interval NULL 0 to_millennia(Int64) :: Interval @@ -3780,6 +3788,8 @@ Functions overloads: 1 to_milliseconds(Int64 NULL) :: Interval NULL 0 to_minute(Timestamp) :: UInt8 1 to_minute(Timestamp NULL) :: UInt8 NULL +2 to_minute(Interval) :: Int64 +3 to_minute(Interval NULL) :: Int64 NULL 0 to_minutes(Int64) :: Interval 1 to_minutes(Int64 NULL) :: Interval NULL 0 to_monday(Date) :: Date @@ -3790,6 +3800,8 @@ Functions overloads: 1 to_month(Date NULL) :: UInt8 NULL 2 to_month(Timestamp) :: UInt8 3 to_month(Timestamp NULL) :: UInt8 NULL +4 to_month(Interval) :: Int64 +5 to_month(Interval NULL) :: Int64 NULL 0 to_months(Int64) :: Interval 1 to_months(Int64 NULL) :: Interval NULL 0 to_next_friday(Date) :: Date @@ -3856,6 +3868,8 @@ Functions overloads: 3 to_quarter(Timestamp NULL) :: UInt8 NULL 0 to_second(Timestamp) :: UInt8 1 to_second(Timestamp NULL) :: UInt8 NULL +2 to_second(Interval) :: Float64 +3 to_second(Interval NULL) :: Float64 NULL 0 to_seconds(Int64) :: Interval 1 to_seconds(Int64 NULL) :: Interval NULL 0 to_start_of_day(Timestamp) :: Timestamp @@ -4066,6 +4080,8 @@ Functions overloads: 1 to_year(Date NULL) :: UInt16 NULL 2 to_year(Timestamp) :: UInt16 3 to_year(Timestamp NULL) :: UInt16 NULL +4 to_year(Interval) :: Int64 +5 to_year(Interval NULL) :: Int64 NULL 0 to_years(Int64) :: Interval 1 to_years(Int64 NULL) :: Interval NULL 0 to_yyyymm(Date) :: UInt32 diff --git a/src/query/sql/src/planner/semantic/type_check.rs b/src/query/sql/src/planner/semantic/type_check.rs index 452d0255b0e5b..c6885171185d5 100644 --- a/src/query/sql/src/planner/semantic/type_check.rs +++ b/src/query/sql/src/planner/semantic/type_check.rs @@ -2950,6 +2950,9 @@ impl<'a> TypeChecker<'a> { ASTIntervalKind::Dow => self.resolve_function(span, "to_day_of_week", vec![], &[arg]), ASTIntervalKind::Week => self.resolve_function(span, "to_week_of_year", vec![], &[arg]), ASTIntervalKind::Epoch => self.resolve_function(span, "epoch", vec![], &[arg]), + ASTIntervalKind::MicroSecond => { + self.resolve_function(span, "to_microsecond", vec![], &[arg]) + } } } diff --git a/tests/sqllogictests/suites/base/11_data_type/11_0007_data_type_interval.test b/tests/sqllogictests/suites/base/11_data_type/11_0007_data_type_interval.test index 38c9fef31b8b2..29976b291fa9e 100644 --- a/tests/sqllogictests/suites/base/11_data_type/11_0007_data_type_interval.test +++ b/tests/sqllogictests/suites/base/11_data_type/11_0007_data_type_interval.test @@ -41,3 +41,51 @@ select sum(c) from t where c <= interval '1 day'; statement ok drop table t; + +onlyif http +query T +select EXTRACT(epoch from '1 day 2 hours 3 minutes'::INTERVAL); +---- +93780.0 + +onlyif http +query T +select EXTRACT(minute from '1 day 2 hours 3 minutes'::INTERVAL); +---- +3 + +onlyif http +query T +select EXTRACT(hour from '1 day 48 hours 3600 minutes'::INTERVAL); +---- +108 + +onlyif http +query T +select EXTRACT(day from '1 day 48 hours 3600 minutes'::INTERVAL); +---- +1 + +onlyif http +query T +select EXTRACT(second from '1 day 2 hours 3 minutes 200 seconds 100 microsecond'::INTERVAL); +---- +20.0001 + +onlyif http +query T +select EXTRACT(microsecond from '1 day 2 hours 3 minutes 200 seconds 100 microsecond'::INTERVAL); +---- +20000100 + +onlyif http +query T +select EXTRACT(month from '3 months 60 day 2 hours 3 minutes 200 seconds 100 microsecond'::INTERVAL); +---- +3 + +onlyif http +query T +select EXTRACT(year from '1 years 13 months 60 day 2 hours 3 minutes 200 seconds 100 microsecond'::INTERVAL); +---- +2