Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve case expr constant handling for when <scalar> #14159

Merged
merged 1 commit into from
Jan 20, 2025
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
51 changes: 27 additions & 24 deletions datafusion/physical-expr/src/expressions/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,34 +345,37 @@ impl CaseExpr {
let when_expr = &self.when_then_expr[0].0;
let then_expr = &self.when_then_expr[0].1;

let when_expr_value = when_expr.evaluate(batch)?;
let when_expr_value = match when_expr_value {
match when_expr.evaluate(batch)? {
// WHEN true --> column
ColumnarValue::Scalar(ScalarValue::Boolean(Some(true))) => {
then_expr.evaluate(batch)
}
// WHEN [false | null] --> NULL
ColumnarValue::Scalar(_) => {
ColumnarValue::Array(when_expr_value.into_array(batch.num_rows())?)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has the effect of creating an array out of a scalar -- I remove that and added the special case handling for the values

// return scalar NULL value
ScalarValue::try_from(self.data_type(&batch.schema())?)
.map(ColumnarValue::Scalar)
}
other => other,
};

if let ColumnarValue::Array(bit_mask) = when_expr_value {
let bit_mask = bit_mask
.as_any()
.downcast_ref::<BooleanArray>()
.expect("predicate should evaluate to a boolean array");
// invert the bitmask
let bit_mask = match bit_mask.null_count() {
0 => not(bit_mask)?,
_ => not(&prep_null_mask_filter(bit_mask))?,
};
match then_expr.evaluate(batch)? {
ColumnarValue::Array(array) => {
Ok(ColumnarValue::Array(nullif(&array, &bit_mask)?))
}
ColumnarValue::Scalar(_) => {
internal_err!("expression did not evaluate to an array")
// WHEN column --> column
ColumnarValue::Array(bit_mask) => {
let bit_mask = bit_mask
.as_any()
.downcast_ref::<BooleanArray>()
.expect("predicate should evaluate to a boolean array");
// invert the bitmask
let bit_mask = match bit_mask.null_count() {
0 => not(bit_mask)?,
_ => not(&prep_null_mask_filter(bit_mask))?,
};
match then_expr.evaluate(batch)? {
ColumnarValue::Array(array) => {
Ok(ColumnarValue::Array(nullif(&array, &bit_mask)?))
}
ColumnarValue::Scalar(_) => {
internal_err!("expression did not evaluate to an array")
}
}
}
} else {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually possible as the previous version of the code expands out a constant to an array

                ColumnarValue::Array(when_expr_value.into_array(batch.num_rows())?)

internal_err!("predicate did not evaluate to an array")
}
}

Expand Down
63 changes: 63 additions & 0 deletions datafusion/sqllogictest/test_files/case.slt
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,66 @@ SELECT CASE WHEN a < 5 THEN a + b ELSE b - NVL(a, 0) END FROM foo
NULL
NULL
7

# Reproducer for
# https://github.com/apache/datafusion/issues/14099
query I
SELECT - 79 * + 91 * - COUNT ( * ) * + - 2 * + - NULLIF ( - 49, - COALESCE ( - + 69, - COALESCE ( + COALESCE ( - 20, ( - 18 ) * + COUNT ( * ) + - 93, - CASE 51 WHEN + COUNT ( * ) + 28 THEN 0 ELSE + 29 * + CASE ( 50 ) WHEN - ( - ( CASE WHEN NOT + 37 IS NULL THEN + COUNT ( * ) END ) ) THEN NULL WHEN - 46 + 87 * - 28 THEN 85 WHEN - COUNT ( * ) THEN NULL END END ), COUNT ( * ) - 39 ) * + 22 ) / - COUNT ( * ) )
----
-704522


query B
select case when true then false end from foo;
----
false
false
false
false
false
false

query I
select case when true then a end from foo;
----
1
3
5
NULL
6
NULL

query I
select case when false then a end from foo;
----
NULL
NULL
NULL
NULL
NULL
NULL

query I
select case when null then a end from foo;
----
NULL
NULL
NULL
NULL
NULL
NULL


query B
select case when a=1 then false end from foo;
----
false
false
false
false
false
false


statement ok
drop table foo
Loading