Skip to content
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
90 changes: 90 additions & 0 deletions src/query/functions/src/scalars/timestamp/src/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ use databend_common_exception::Result;
use databend_common_expression::date_helper::calc_date_to_timestamp;
use databend_common_expression::date_helper::today_date;
use databend_common_expression::date_helper::DateConverter;
use databend_common_expression::date_helper::EvalDaysImpl;
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::timestamp_tz::TimestampTzType;
use databend_common_expression::types::DateType;
use databend_common_expression::types::Float64Type;
use databend_common_expression::types::Int64Type;
use databend_common_expression::types::IntervalType;
Expand Down Expand Up @@ -113,6 +115,26 @@ fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) {
),
);

registry.register_passthrough_nullable_2_arg::<DateType, IntervalType, DateType, _, _>(
"plus",
|_, _, _| FunctionDomain::MayThrow,
vectorize_with_builder_2_arg::<DateType, IntervalType, DateType>(
|date, interval, output, ctx| {
eval_date_plus(date, interval, output, ctx);
},
),
);

registry.register_passthrough_nullable_2_arg::<IntervalType, DateType, DateType, _, _>(
"plus",
|_, _, _| FunctionDomain::MayThrow,
vectorize_with_builder_2_arg::<IntervalType, DateType, DateType>(
|interval, date, output, ctx| {
eval_date_plus(date, interval, output, ctx);
},
),
);

registry
.register_passthrough_nullable_2_arg::<TimestampType, IntervalType, TimestampType, _, _>(
"plus",
Expand Down Expand Up @@ -206,6 +228,16 @@ fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) {
),
);

registry.register_passthrough_nullable_2_arg::<DateType, IntervalType, DateType, _, _>(
"minus",
|_, _, _| FunctionDomain::MayThrow,
vectorize_with_builder_2_arg::<DateType, IntervalType, DateType>(
|date, interval, output, ctx| {
eval_date_minus(date, interval, output, ctx);
},
),
);

registry
.register_passthrough_nullable_2_arg::<TimestampType, IntervalType, TimestampType, _, _>(
"minus",
Expand Down Expand Up @@ -436,6 +468,64 @@ fn eval_timestamp_minus<F1, F2, T>(
}
}

fn eval_date_plus(
date: i32,
interval: months_days_micros,
output: &mut Vec<i32>,
ctx: &mut EvalContext,
) {
match apply_interval_to_date(date, interval, &ctx.func_ctx.tz, true) {
Ok(result) => output.push(result),
Err(err) => {
ctx.set_error(output.len(), err);
output.push(0);
}
}
}

fn eval_date_minus(
date: i32,
interval: months_days_micros,
output: &mut Vec<i32>,
ctx: &mut EvalContext,
) {
match apply_interval_to_date(date, interval, &ctx.func_ctx.tz, false) {
Ok(result) => output.push(result),
Err(err) => {
ctx.set_error(output.len(), err);
output.push(0);
}
}
}

fn apply_interval_to_date(
mut date: i32,
interval: months_days_micros,
tz: &TimeZone,
is_addition: bool,
) -> std::result::Result<i32, String> {
if interval.microseconds() != 0 {
return Err(
"DATE +/- INTERVAL with time parts should be evaluated as TIMESTAMP".to_string(),
);
}

let (days, months) = if is_addition {
(interval.days(), interval.months())
} else {
(-interval.days(), -interval.months())
};

if days != 0 {
date = EvalDaysImpl::eval_date(date, days);
}
if months != 0 {
date = EvalMonthsImpl::eval_date(date, tz, months, false)?;
}

Ok(date)
}

fn register_number_to_interval(registry: &mut FunctionRegistry) {
registry.register_passthrough_nullable_1_arg::<Int64Type, IntervalType, _, _>(
"to_centuries",
Expand Down
30 changes: 18 additions & 12 deletions src/query/functions/tests/it/scalars/testdata/function_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2918,10 +2918,12 @@ Functions overloads:
233 minus(Timestamp NULL, Int64 NULL) :: Timestamp NULL
234 minus(Interval, Interval) :: Interval
235 minus(Interval NULL, Interval NULL) :: Interval NULL
236 minus(Timestamp, Interval) :: Timestamp
237 minus(Timestamp NULL, Interval NULL) :: Timestamp NULL
238 minus(TimestampTz, Interval) :: TimestampTz
239 minus(TimestampTz NULL, Interval NULL) :: TimestampTz NULL
236 minus(Date, Interval) :: Date
237 minus(Date NULL, Interval NULL) :: Date NULL
238 minus(Timestamp, Interval) :: Timestamp
239 minus(Timestamp NULL, Interval NULL) :: Timestamp NULL
240 minus(TimestampTz, Interval) :: TimestampTz
241 minus(TimestampTz NULL, Interval NULL) :: TimestampTz NULL
0 modulo(UInt8, UInt8) :: UInt8
1 modulo(UInt8 NULL, UInt8 NULL) :: UInt8 NULL
2 modulo(UInt8, UInt16) :: UInt16
Expand Down Expand Up @@ -3641,14 +3643,18 @@ Functions overloads:
204 plus(Timestamp NULL, Int64 NULL) :: Timestamp NULL
205 plus(Interval, Interval) :: Interval
206 plus(Interval NULL, Interval NULL) :: Interval NULL
207 plus(Timestamp, Interval) :: Timestamp
208 plus(Timestamp NULL, Interval NULL) :: Timestamp NULL
209 plus(TimestampTz, Interval) :: TimestampTz
210 plus(TimestampTz NULL, Interval NULL) :: TimestampTz NULL
211 plus(Interval, Timestamp) :: Timestamp
212 plus(Interval NULL, Timestamp NULL) :: Timestamp NULL
213 plus(Interval, TimestampTz) :: TimestampTz
214 plus(Interval NULL, TimestampTz NULL) :: TimestampTz NULL
207 plus(Date, Interval) :: Date
208 plus(Date NULL, Interval NULL) :: Date NULL
209 plus(Interval, Date) :: Date
210 plus(Interval NULL, Date NULL) :: Date NULL
211 plus(Timestamp, Interval) :: Timestamp
212 plus(Timestamp NULL, Interval NULL) :: Timestamp NULL
213 plus(TimestampTz, Interval) :: TimestampTz
214 plus(TimestampTz NULL, Interval NULL) :: TimestampTz NULL
215 plus(Interval, Timestamp) :: Timestamp
216 plus(Interval NULL, Timestamp NULL) :: Timestamp NULL
217 plus(Interval, TimestampTz) :: TimestampTz
218 plus(Interval NULL, TimestampTz NULL) :: TimestampTz NULL
0 point_in_ellipses FACTORY
0 point_in_polygon FACTORY
1 point_in_polygon FACTORY
Expand Down
107 changes: 107 additions & 0 deletions src/query/sql/src/planner/semantic/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3130,6 +3130,8 @@ impl<'a> TypeChecker<'a> {
Self::rewrite_substring(&mut args);
}

self.adjust_date_interval_function_args(func_name, &mut args)?;

// Type check
let mut arguments = args.iter().map(|v| v.as_raw_expr()).collect::<Vec<_>>();
// inject the params
Expand Down Expand Up @@ -3459,13 +3461,118 @@ impl<'a> TypeChecker<'a> {
}
Ok(Box::new((res, ty)))
}
BinaryOperator::Plus | BinaryOperator::Minus => {
let name = op.to_func_name();
let (mut left_expr, left_type) = *self.resolve(left)?;
let (mut right_expr, right_type) = *self.resolve(right)?;
self.adjust_date_interval_operands(
op,
&mut left_expr,
&left_type,
&mut right_expr,
&right_type,
)?;
self.resolve_scalar_function_call(span, name.as_str(), vec![], vec![
left_expr, right_expr,
])
}
other => {
let name = other.to_func_name();
self.resolve_function(span, name.as_str(), vec![], &[left, right])
}
}
}

fn adjust_date_interval_operands(
&self,
op: &BinaryOperator,
left_expr: &mut ScalarExpr,
left_type: &DataType,
right_expr: &mut ScalarExpr,
right_type: &DataType,
) -> Result<()> {
match op {
BinaryOperator::Plus => {
self.adjust_single_date_interval_operand(
left_expr, left_type, right_expr, right_type,
)?;
self.adjust_single_date_interval_operand(
right_expr, right_type, left_expr, left_type,
)?;
}
BinaryOperator::Minus => {
self.adjust_single_date_interval_operand(
left_expr, left_type, right_expr, right_type,
)?;
}
_ => {}
}
Ok(())
}

fn adjust_date_interval_function_args(
&self,
func_name: &str,
args: &mut [ScalarExpr],
) -> Result<()> {
if args.len() != 2 {
return Ok(());
}
let op = if func_name.eq_ignore_ascii_case("plus") {
BinaryOperator::Plus
} else if func_name.eq_ignore_ascii_case("minus") {
BinaryOperator::Minus
} else {
return Ok(());
};
let (left_slice, right_slice) = args.split_at_mut(1);
let left_expr = &mut left_slice[0];
let right_expr = &mut right_slice[0];
let left_type = left_expr.data_type()?;
let right_type = right_expr.data_type()?;
self.adjust_date_interval_operands(&op, left_expr, &left_type, right_expr, &right_type)
}

fn adjust_single_date_interval_operand(
&self,
date_expr: &mut ScalarExpr,
date_type: &DataType,
interval_expr: &ScalarExpr,
interval_type: &DataType,
) -> Result<()> {
if date_type.remove_nullable() != DataType::Date
|| interval_type.remove_nullable() != DataType::Interval
{
return Ok(());
}

if self.interval_contains_only_date_parts(interval_expr)? {
return Ok(());
}

// Preserve nullability when casting DATE to TIMESTAMP
let target_type = if date_type.is_nullable_or_null() {
DataType::Timestamp.wrap_nullable()
} else {
DataType::Timestamp
};
*date_expr = wrap_cast(date_expr, &target_type);
Ok(())
}

fn interval_contains_only_date_parts(&self, interval_expr: &ScalarExpr) -> Result<bool> {
let expr = interval_expr.as_expr()?;
let (folded, _) = ConstantFolder::fold(&expr, &self.func_ctx, &BUILTIN_FUNCTIONS);
if let EExpr::Constant(Constant {
scalar: Scalar::Interval(value),
..
}) = folded
{
return Ok(value.microseconds() == 0);
}
Ok(false)
}

/// Resolve unary expressions.
pub fn resolve_unary_op(
&mut self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ select to_interval('120000000000 months');
----
00:00:00

skipif mysql
query T
select plus('2022-02-02'::date, interval '1 day 1 second');
----
2022-02-03 00:00:01.000000

skipif mysql
query T
select plus('2022-02-02'::date, interval '1 day');
----
2022-02-03

skipif mysql
query T
select '2022-01-01'::timestamp - '2021-01-01'::timestamp
Expand Down
Loading
Loading