Skip to content

Commit fbe6c7c

Browse files
committed
update interval test and coercion rule
1 parent 269790a commit fbe6c7c

File tree

4 files changed

+70
-16
lines changed

4 files changed

+70
-16
lines changed

datafusion/core/tests/sqllogictests/test_files/timestamps.slt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,24 @@ SELECT '2000-01-01T00:00:00'::timestamp + INTERVAL '8' YEAR;
211211
query P
212212
SELECT '2000-01-01T00:00:00'::timestamp + INTERVAL '8' MONTH;
213213
----
214+
2000-09-01T00:00:00
215+
216+
query P
217+
SELECT INTERVAL '8' DAY + timestamp '2013-07-01 12:00:00';
218+
----
219+
2013-07-09T12:00:00
220+
221+
query P
222+
SELECT INTERVAL '8' DAY + '2000-01-01T00:00:00'::timestamp;
223+
----
224+
2000-01-09T00:00:00
225+
226+
query P
227+
SELECT INTERVAL '8' YEAR + '2000-01-01T00:00:00'::timestamp;
228+
----
229+
2008-01-01T00:00:00
230+
231+
query P
232+
SELECT INTERVAL '8' MONTH + '2000-01-01T00:00:00'::timestamp;
233+
----
214234
2000-09-01T00:00:00

datafusion/expr/src/type_coercion.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ pub fn is_timestamp(dt: &DataType) -> bool {
6767
matches!(dt, DataType::Timestamp(_, _))
6868
}
6969

70+
/// Determine whether the given data type 'dt' is a `Interval`.
71+
pub fn is_interval(dt: &DataType) -> bool {
72+
matches!(dt, DataType::Interval(_))
73+
}
74+
7075
/// Determine whether the given data type `dt` is a `Date`.
7176
pub fn is_date(dt: &DataType) -> bool {
7277
matches!(dt, DataType::Date32 | DataType::Date64)

datafusion/expr/src/type_coercion/binary.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
//! Coercion rules for matching argument types for binary operators
1919
20-
use crate::type_coercion::{is_date, is_numeric, is_timestamp};
20+
use crate::type_coercion::{is_date, is_interval, is_numeric, is_timestamp};
2121
use crate::Operator;
2222
use arrow::compute::can_cast_types;
2323
use arrow::datatypes::{
@@ -114,22 +114,12 @@ pub fn coerce_types(
114114
| Operator::GtEq
115115
| Operator::LtEq => comparison_coercion(lhs_type, rhs_type),
116116
Operator::Plus | Operator::Minus
117-
if is_date(lhs_type) || is_timestamp(lhs_type) =>
117+
if is_date(lhs_type)
118+
|| is_date(rhs_type)
119+
|| is_timestamp(lhs_type)
120+
|| is_timestamp(rhs_type) =>
118121
{
119-
match rhs_type {
120-
// timestamp/date +/- interval returns timestamp/date
121-
DataType::Interval(_) => Some(lhs_type.clone()),
122-
// providing more helpful error message
123-
DataType::Date32 | DataType::Date64 | DataType::Timestamp(_, _) => {
124-
return Err(DataFusionError::Plan(
125-
format!(
126-
"'{lhs_type:?} {op} {rhs_type:?}' is an unsupported operation. \
127-
addition/subtraction on dates/timestamps only supported with interval types"
128-
),
129-
));
130-
}
131-
_ => None,
132-
}
122+
temporal_add_sub_coercion(lhs_type, rhs_type, op)?
133123
}
134124
// for math expressions, the final value of the coercion is also the return type
135125
// because coercion favours higher information types
@@ -199,6 +189,35 @@ pub fn comparison_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<D
199189
.or_else(|| string_numeric_coercion(lhs_type, rhs_type))
200190
}
201191

192+
/// Return the output type from performing addition or subtraction operations on temporal data types
193+
pub fn temporal_add_sub_coercion(
194+
lhs_type: &DataType,
195+
rhs_type: &DataType,
196+
op: &Operator,
197+
) -> Result<Option<DataType>> {
198+
// interval + date or timestamp
199+
if is_interval(lhs_type) && (is_date(rhs_type) || is_timestamp(rhs_type)) {
200+
return Ok(Some(rhs_type.clone()));
201+
}
202+
203+
// date or timestamp + interval
204+
if is_interval(rhs_type) && (is_date(lhs_type) || is_timestamp(lhs_type)) {
205+
return Ok(Some(lhs_type.clone()));
206+
}
207+
208+
// date or timestamp + date or timestamp
209+
if (is_date(lhs_type) || is_timestamp(lhs_type))
210+
&& (is_date(rhs_type) || is_timestamp(rhs_type))
211+
{
212+
return Err(DataFusionError::Plan(
213+
format!(
214+
"'{lhs_type:?} {op} {rhs_type:?}' is an unsupported operation. \
215+
addition/subtraction on dates/timestamps only supported with interval types"
216+
),));
217+
}
218+
Ok(None)
219+
}
220+
202221
/// Returns the output type of applying numeric operations such as `=`
203222
/// to arguments `lhs_type` and `rhs_type` if one is numeric and one
204223
/// is `Utf8`/`LargeUtf8`.

datafusion/physical-expr/src/planner.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ pub fn create_physical_expr(
197197
rhs,
198198
input_schema,
199199
)?)),
200+
(
201+
DataType::Interval(_),
202+
Operator::Plus | Operator::Minus,
203+
DataType::Date32 | DataType::Date64 | DataType::Timestamp(_, _),
204+
) => Ok(Arc::new(DateTimeIntervalExpr::try_new(
205+
rhs,
206+
*op,
207+
lhs,
208+
input_schema,
209+
)?)),
200210
_ => {
201211
// Note that the logical planner is responsible
202212
// for type coercion on the arguments (e.g. if one

0 commit comments

Comments
 (0)