diff --git a/datafusion/core/tests/sql/expr.rs b/datafusion/core/tests/sql/expr.rs index e6b819517a75..6046fc6d8b26 100644 --- a/datafusion/core/tests/sql/expr.rs +++ b/datafusion/core/tests/sql/expr.rs @@ -1202,6 +1202,36 @@ async fn nested_subquery() -> Result<()> { Ok(()) } +#[tokio::test] +async fn like_nlike_with_null_lt() { + let ctx = SessionContext::new(); + let sql = "SELECT column1 like NULL as col_null, NULL like column1 as null_col from (values('a'), ('b'), (NULL)) as t"; + let actual = execute_to_batches(&ctx, sql).await; + let expected = vec![ + "+----------+----------+", + "| col_null | null_col |", + "+----------+----------+", + "| | |", + "| | |", + "| | |", + "+----------+----------+", + ]; + assert_batches_eq!(expected, &actual); + + let sql = "SELECT column1 not like NULL as col_null, NULL not like column1 as null_col from (values('a'), ('b'), (NULL)) as t"; + let actual = execute_to_batches(&ctx, sql).await; + let expected = vec![ + "+----------+----------+", + "| col_null | null_col |", + "+----------+----------+", + "| | |", + "| | |", + "| | |", + "+----------+----------+", + ]; + assert_batches_eq!(expected, &actual); +} + #[tokio::test] async fn comparisons_with_null_lt() { let ctx = SessionContext::new(); diff --git a/datafusion/expr/src/binary_rule.rs b/datafusion/expr/src/binary_rule.rs index c9ef1e4963c7..6770fccd7dda 100644 --- a/datafusion/expr/src/binary_rule.rs +++ b/datafusion/expr/src/binary_rule.rs @@ -499,6 +499,7 @@ fn string_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option fn like_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option { string_coercion(lhs_type, rhs_type) .or_else(|| dictionary_coercion(lhs_type, rhs_type)) + .or_else(|| null_coercion(lhs_type, rhs_type)) } /// Coercion rules for Temporal columns: the type that both lhs and rhs can be diff --git a/datafusion/physical-expr/src/expressions/binary.rs b/datafusion/physical-expr/src/expressions/binary.rs index fb6f34c50c41..a97992b4ca02 100644 --- a/datafusion/physical-expr/src/expressions/binary.rs +++ b/datafusion/physical-expr/src/expressions/binary.rs @@ -621,7 +621,7 @@ macro_rules! compute_utf8_op { /// Invoke a compute kernel on a data array and a scalar value macro_rules! compute_utf8_op_scalar { - ($LEFT:expr, $RIGHT:expr, $OP:ident, $DT:ident) => {{ + ($LEFT:expr, $RIGHT:expr, $OP:ident, $DT:ident, $OP_TYPE:expr) => {{ let ll = $LEFT .as_any() .downcast_ref::<$DT>() @@ -631,6 +631,8 @@ macro_rules! compute_utf8_op_scalar { &ll, &string_value, )?)) + } else if $RIGHT.is_null() { + Ok(Arc::new(new_null_array($OP_TYPE, $LEFT.len()))) } else { Err(DataFusionError::Internal(format!( "compute_utf8_op_scalar for '{}' failed to cast literal value {}", @@ -760,9 +762,9 @@ macro_rules! compute_op { } macro_rules! binary_string_array_op_scalar { - ($LEFT:expr, $RIGHT:expr, $OP:ident) => {{ + ($LEFT:expr, $RIGHT:expr, $OP:ident, $OP_TYPE:expr) => {{ let result: Result> = match $LEFT.data_type() { - DataType::Utf8 => compute_utf8_op_scalar!($LEFT, $RIGHT, $OP, StringArray), + DataType::Utf8 => compute_utf8_op_scalar!($LEFT, $RIGHT, $OP, StringArray, $OP_TYPE), other => Err(DataFusionError::Internal(format!( "Data type {:?} not supported for scalar operation '{}' on string array", other, stringify!($OP) @@ -1116,10 +1118,10 @@ impl BinaryExpr { binary_array_op_dyn_scalar!(array, scalar.clone(), neq, bool_type) } Operator::Like => { - binary_string_array_op_scalar!(array, scalar.clone(), like) + binary_string_array_op_scalar!(array, scalar.clone(), like, bool_type) } Operator::NotLike => { - binary_string_array_op_scalar!(array, scalar.clone(), nlike) + binary_string_array_op_scalar!(array, scalar.clone(), nlike, bool_type) } Operator::Plus => { binary_primitive_array_op_scalar!(array, scalar.clone(), add)