|
17 | 17 |
|
18 | 18 | //! Coercion rules for matching argument types for binary operators |
19 | 19 |
|
20 | | -use crate::type_coercion::is_numeric; |
| 20 | +use crate::type_coercion::{is_date, is_numeric, is_timestamp}; |
21 | 21 | use crate::Operator; |
22 | 22 | use arrow::compute::can_cast_types; |
23 | 23 | use arrow::datatypes::{ |
@@ -121,14 +121,22 @@ pub fn coerce_types( |
121 | 121 | Operator::Like | Operator::NotLike | Operator::ILike | Operator::NotILike => { |
122 | 122 | like_coercion(lhs_type, rhs_type) |
123 | 123 | } |
124 | | - // date +/- interval returns date |
125 | 124 | Operator::Plus | Operator::Minus |
126 | | - if (*lhs_type == DataType::Date32 |
127 | | - || *lhs_type == DataType::Date64 |
128 | | - || matches!(lhs_type, DataType::Timestamp(_, _))) => |
| 125 | + if is_date(lhs_type) || is_timestamp(lhs_type) => |
129 | 126 | { |
130 | 127 | match rhs_type { |
| 128 | + // timestamp/date +/- interval returns timestamp/date |
131 | 129 | DataType::Interval(_) => Some(lhs_type.clone()), |
| 130 | + // providing more helpful error message |
| 131 | + DataType::Date32 | DataType::Date64 | DataType::Timestamp(_, _) => { |
| 132 | + return Err(DataFusionError::Plan( |
| 133 | + format!( |
| 134 | + "'{:?} {} {:?}' is an unsupported operation. \ |
| 135 | + addition/subtraction on dates/timestamps only supported with interval types", |
| 136 | + lhs_type, op, rhs_type |
| 137 | + ), |
| 138 | + )); |
| 139 | + } |
132 | 140 | _ => None, |
133 | 141 | } |
134 | 142 | } |
@@ -672,6 +680,7 @@ mod tests { |
672 | 680 | use super::*; |
673 | 681 | use crate::Operator; |
674 | 682 | use arrow::datatypes::DataType; |
| 683 | + use datafusion_common::assert_contains; |
675 | 684 | use datafusion_common::DataFusionError; |
676 | 685 | use datafusion_common::Result; |
677 | 686 |
|
@@ -848,6 +857,25 @@ mod tests { |
848 | 857 | }}; |
849 | 858 | } |
850 | 859 |
|
| 860 | + #[test] |
| 861 | + fn test_date_timestamp_arithmetic_error() -> Result<()> { |
| 862 | + let err = coerce_types( |
| 863 | + &DataType::Timestamp(TimeUnit::Nanosecond, None), |
| 864 | + &Operator::Minus, |
| 865 | + &DataType::Timestamp(TimeUnit::Nanosecond, None), |
| 866 | + ) |
| 867 | + .unwrap_err() |
| 868 | + .to_string(); |
| 869 | + assert_contains!(&err, "'Timestamp(Nanosecond, None) - Timestamp(Nanosecond, None)' is an unsupported operation. addition/subtraction on dates/timestamps only supported with interval types"); |
| 870 | + |
| 871 | + let err = coerce_types(&DataType::Date32, &Operator::Plus, &DataType::Date64) |
| 872 | + .unwrap_err() |
| 873 | + .to_string(); |
| 874 | + assert_contains!(&err, "'Date32 + Date64' is an unsupported operation. addition/subtraction on dates/timestamps only supported with interval types"); |
| 875 | + |
| 876 | + Ok(()) |
| 877 | + } |
| 878 | + |
851 | 879 | #[test] |
852 | 880 | fn test_type_coercion() -> Result<()> { |
853 | 881 | test_coercion_binary_rule!( |
|
0 commit comments