Skip to content

Commit f7c7d92

Browse files
authored
Fix: build_predicate_expression method doesn't process false expr correctly (#15995)
* Fix: build_predicate_expression method doesn't process false correctly * fix test
1 parent 808e673 commit f7c7d92

File tree

1 file changed

+60
-4
lines changed

1 file changed

+60
-4
lines changed

datafusion/physical-optimizer/src/pruning.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,13 @@ fn is_always_true(expr: &Arc<dyn PhysicalExpr>) -> bool {
751751
.unwrap_or_default()
752752
}
753753

754+
fn is_always_false(expr: &Arc<dyn PhysicalExpr>) -> bool {
755+
expr.as_any()
756+
.downcast_ref::<phys_expr::Literal>()
757+
.map(|l| matches!(l.value(), ScalarValue::Boolean(Some(false))))
758+
.unwrap_or_default()
759+
}
760+
754761
/// Describes which columns statistics are necessary to evaluate a
755762
/// [`PruningPredicate`].
756763
///
@@ -1439,6 +1446,11 @@ fn build_predicate_expression(
14391446
required_columns: &mut RequiredColumns,
14401447
unhandled_hook: &Arc<dyn UnhandledPredicateHook>,
14411448
) -> Arc<dyn PhysicalExpr> {
1449+
if is_always_false(expr) {
1450+
// Shouldn't return `unhandled_hook.handle(expr)`
1451+
// Because it will transfer false to true.
1452+
return Arc::clone(expr);
1453+
}
14421454
// predicate expression can only be a binary expression
14431455
let expr_any = expr.as_any();
14441456
if let Some(is_null) = expr_any.downcast_ref::<phys_expr::IsNullExpr>() {
@@ -1538,13 +1550,21 @@ fn build_predicate_expression(
15381550
build_predicate_expression(&right, schema, required_columns, unhandled_hook);
15391551
// simplify boolean expression if applicable
15401552
let expr = match (&left_expr, op, &right_expr) {
1553+
(left, Operator::And, right)
1554+
if is_always_false(left) || is_always_false(right) =>
1555+
{
1556+
Arc::new(phys_expr::Literal::new(ScalarValue::Boolean(Some(false))))
1557+
}
15411558
(left, Operator::And, _) if is_always_true(left) => right_expr,
15421559
(_, Operator::And, right) if is_always_true(right) => left_expr,
15431560
(left, Operator::Or, right)
15441561
if is_always_true(left) || is_always_true(right) =>
15451562
{
15461563
Arc::new(phys_expr::Literal::new(ScalarValue::Boolean(Some(true))))
15471564
}
1565+
(left, Operator::Or, _) if is_always_false(left) => right_expr,
1566+
(_, Operator::Or, right) if is_always_false(right) => left_expr,
1567+
15481568
_ => Arc::new(phys_expr::BinaryExpr::new(left_expr, op, right_expr)),
15491569
};
15501570
return expr;
@@ -1904,7 +1924,7 @@ mod tests {
19041924

19051925
use super::*;
19061926
use datafusion_common::test_util::batches_to_string;
1907-
use datafusion_expr::{col, lit};
1927+
use datafusion_expr::{and, col, lit, or};
19081928
use insta::assert_snapshot;
19091929

19101930
use arrow::array::Decimal128Array;
@@ -3585,12 +3605,10 @@ mod tests {
35853605

35863606
prune_with_expr(
35873607
// false
3588-
// constant literals that do NOT refer to any columns are currently not evaluated at all, hence the result is
3589-
// "all true"
35903608
lit(false),
35913609
&schema,
35923610
&statistics,
3593-
&[true, true, true, true, true],
3611+
&[false, false, false, false, false],
35943612
);
35953613
}
35963614

@@ -5171,4 +5189,42 @@ mod tests {
51715189
let unhandled_hook = Arc::new(ConstantUnhandledPredicateHook::default()) as _;
51725190
build_predicate_expression(&expr, schema, required_columns, &unhandled_hook)
51735191
}
5192+
5193+
#[test]
5194+
fn test_build_predicate_expression_with_false() {
5195+
let expr = lit(ScalarValue::Boolean(Some(false)));
5196+
let schema = Schema::empty();
5197+
let res =
5198+
test_build_predicate_expression(&expr, &schema, &mut RequiredColumns::new());
5199+
let expected = logical2physical(&expr, &schema);
5200+
assert_eq!(&res, &expected);
5201+
}
5202+
5203+
#[test]
5204+
fn test_build_predicate_expression_with_and_false() {
5205+
let schema = Schema::new(vec![Field::new("c1", DataType::Utf8View, false)]);
5206+
let expr = and(
5207+
col("c1").eq(lit("a")),
5208+
lit(ScalarValue::Boolean(Some(false))),
5209+
);
5210+
let res =
5211+
test_build_predicate_expression(&expr, &schema, &mut RequiredColumns::new());
5212+
let expected = logical2physical(&lit(ScalarValue::Boolean(Some(false))), &schema);
5213+
assert_eq!(&res, &expected);
5214+
}
5215+
5216+
#[test]
5217+
fn test_build_predicate_expression_with_or_false() {
5218+
let schema = Schema::new(vec![Field::new("c1", DataType::Utf8View, false)]);
5219+
let left_expr = col("c1").eq(lit("a"));
5220+
let right_expr = lit(ScalarValue::Boolean(Some(false)));
5221+
let res = test_build_predicate_expression(
5222+
&or(left_expr.clone(), right_expr.clone()),
5223+
&schema,
5224+
&mut RequiredColumns::new(),
5225+
);
5226+
let expected =
5227+
"c1_null_count@2 != row_count@3 AND c1_min@0 <= a AND a <= c1_max@1";
5228+
assert_eq!(res.to_string(), expected);
5229+
}
51745230
}

0 commit comments

Comments
 (0)