Skip to content

Commit 1e1b598

Browse files
committed
feat(cube): Coerce numeric types to Boolean in comparisons
1 parent a6c2008 commit 1e1b598

File tree

2 files changed

+23
-1
lines changed

2 files changed

+23
-1
lines changed

datafusion/expr-common/src/type_coercion/binary.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ pub fn comparison_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<D
496496
return Some(lhs_type.clone());
497497
}
498498
binary_numeric_coercion(lhs_type, rhs_type)
499+
.or_else(|| number_boolean_coercion(lhs_type, rhs_type))
499500
.or_else(|| dictionary_comparison_coercion(lhs_type, rhs_type, true))
500501
.or_else(|| temporal_coercion_nonstrict_timezone(lhs_type, rhs_type))
501502
.or_else(|| string_coercion(lhs_type, rhs_type))
@@ -524,6 +525,16 @@ pub fn values_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataT
524525
.or_else(|| binary_coercion(lhs_type, rhs_type))
525526
}
526527

528+
/// Coerce `lhs_type` and `rhs_type` to Boolean when doing a number-vs.-bool comparison.
529+
fn number_boolean_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
530+
use arrow::datatypes::DataType::*;
531+
match (lhs_type, rhs_type) {
532+
(Boolean, _) if rhs_type.is_numeric() => Some(Boolean),
533+
(_, Boolean) if lhs_type.is_numeric() => Some(Boolean),
534+
_ => None,
535+
}
536+
}
537+
527538
/// Coerce `lhs_type` and `rhs_type` to a common type for the purposes of a comparison operation
528539
/// where one is numeric and one is `Utf8`/`LargeUtf8`.
529540
fn string_numeric_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {

datafusion/optimizer/src/analyzer/type_coercion.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1473,10 +1473,21 @@ mod test {
14731473
assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, expected)?;
14741474

14751475
let empty = empty_with_type(DataType::Int64);
1476+
let plan =
1477+
LogicalPlan::Projection(Projection::try_new(vec![expr.clone()], empty)?);
1478+
// Cube: We support type conversion for boolean-int comparisons.
1479+
let expected = "Projection: CAST(a AS Boolean) IS TRUE\n EmptyRelation";
1480+
assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, expected)?;
1481+
// let ret = assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, "");
1482+
// let err = ret.unwrap_err().to_string();
1483+
// assert!(err.contains("Cannot infer common argument type for comparison operation Int64 IS DISTINCT FROM Boolean"), "{err}");
1484+
1485+
// Cube: Add original test case for non-coercion but with a non-numeric type.
1486+
let empty = empty_with_type(DataType::Binary);
14761487
let plan = LogicalPlan::Projection(Projection::try_new(vec![expr], empty)?);
14771488
let ret = assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, "");
14781489
let err = ret.unwrap_err().to_string();
1479-
assert!(err.contains("Cannot infer common argument type for comparison operation Int64 IS DISTINCT FROM Boolean"), "{err}");
1490+
assert!(err.contains("Cannot infer common argument type for comparison operation Binary IS DISTINCT FROM Boolean"), "{err}");
14801491

14811492
// is not true
14821493
let expr = col("a").is_not_true();

0 commit comments

Comments
 (0)