Skip to content

feat(query): Allow extract parts from interval type #17417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/query/ast/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ pub enum IntervalKind {
Week,
Dow,
Epoch,
MicroSecond,
}

impl Display for IntervalKind {
Expand All @@ -845,6 +846,7 @@ impl Display for IntervalKind {
IntervalKind::Dow => "DOW",
IntervalKind::Week => "WEEK",
IntervalKind::Epoch => "EPOCH",
IntervalKind::MicroSecond => "MICROSECOND",
})
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/query/ast/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,7 @@ pub fn interval_kind(i: Input) -> IResult<IntervalKind> {
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") },
Expand Down Expand Up @@ -1887,6 +1888,10 @@ pub fn interval_kind(i: Input) -> IResult<IntervalKind> {
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
Expand All @@ -1900,6 +1905,7 @@ pub fn interval_kind(i: Input) -> IResult<IntervalKind> {
| #dow
| #week
| #epoch
| #microsecond
),
rule!(
#year_str
Expand All @@ -1913,6 +1919,7 @@ pub fn interval_kind(i: Input) -> IResult<IntervalKind> {
| #dow_str
| #week_str
| #epoch_str
| #microsecond_str
),
))(i)
}
Expand Down
2 changes: 2 additions & 0 deletions src/query/ast/src/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
69 changes: 69 additions & 0 deletions src/query/functions/src/scalars/timestamp/src/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -296,4 +297,72 @@ fn register_number_to_interval(registry: &mut FunctionRegistry) {
output.push(res);
}),
);

registry.register_passthrough_nullable_1_arg::<IntervalType, Int64Type, _, _>(
"to_year",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|val, output, _| {
output.push(val.months() as i64 / 12);
}),
);
registry.register_passthrough_nullable_1_arg::<IntervalType, Int64Type, _, _>(
"to_month",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|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::<IntervalType, Int64Type, _, _>(
"to_day_of_month",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|val, output, _| {
output.push(val.days() as i64);
}),
);
registry.register_passthrough_nullable_1_arg::<IntervalType, Int64Type, _, _>(
"to_hour",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|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::<IntervalType, Int64Type, _, _>(
"to_minute",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|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::<IntervalType, Float64Type, _, _>(
"to_second",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Float64Type>(|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::<IntervalType, Int64Type, _, _>(
"to_microsecond",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Int64Type>(|val, output, _| {
let microseconds = val.microseconds() % 60_000_000;
output.push(microseconds);
}),
);
registry.register_passthrough_nullable_1_arg::<IntervalType, Float64Type, _, _>(
"epoch",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<IntervalType, Float64Type>(|val, output, _| {
let total_seconds = (val.total_micros() as f64) / 1_000_000.0;
output.push(total_seconds.into());
}),
);
}
16 changes: 16 additions & 0 deletions src/query/functions/tests/it/scalars/testdata/function_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/query/sql/src/planner/semantic/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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