From e729824c0f8e6fbcc43882103a0cc437141aabec Mon Sep 17 00:00:00 2001 From: Jefffrey <22608443+Jefffrey@users.noreply.github.com> Date: Mon, 14 Nov 2022 17:46:28 +1100 Subject: [PATCH] Add additional expr boolean simplification rules --- .../simplify_expressions/expr_simplifier.rs | 56 +++++++++++++++++++ .../src/simplify_expressions/utils.rs | 5 ++ 2 files changed, 61 insertions(+) diff --git a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs index 4fe284bee9523..40285eedc4b60 100644 --- a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs +++ b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs @@ -470,6 +470,22 @@ impl<'a, S: SimplifyInfo> ExprRewriter for Simplifier<'a, S> { op: Or, right, }) if is_false(&right) => *left, + // A OR !A ---> true (if A not nullable) + Expr::BinaryExpr(BinaryExpr { + left, + op: Or, + right, + }) if is_not_of(&right, &left) && !info.nullable(&left)? => { + Expr::Literal(ScalarValue::Boolean(Some(true))) + } + // !A OR A ---> true (if A not nullable) + Expr::BinaryExpr(BinaryExpr { + left, + op: Or, + right, + }) if is_not_of(&left, &right) && !info.nullable(&right)? => { + Expr::Literal(ScalarValue::Boolean(Some(true))) + } // (..A..) OR A --> (..A..) Expr::BinaryExpr(BinaryExpr { left, @@ -523,6 +539,22 @@ impl<'a, S: SimplifyInfo> ExprRewriter for Simplifier<'a, S> { op: And, right, }) if is_false(&right) => *right, + // A AND !A ---> false (if A not nullable) + Expr::BinaryExpr(BinaryExpr { + left, + op: And, + right, + }) if is_not_of(&right, &left) && !info.nullable(&left)? => { + Expr::Literal(ScalarValue::Boolean(Some(false))) + } + // !A AND A ---> false (if A not nullable) + Expr::BinaryExpr(BinaryExpr { + left, + op: And, + right, + }) if is_not_of(&left, &right) && !info.nullable(&right)? => { + Expr::Literal(ScalarValue::Boolean(Some(false))) + } // (..A..) AND A --> (..A..) Expr::BinaryExpr(BinaryExpr { left, @@ -1077,6 +1109,18 @@ mod tests { assert_eq!(simplify(expr), expected); } + #[test] + fn test_simplify_or_not_self() { + // A OR !A if A is not nullable --> true + // !A OR A if A is not nullable --> true + let expr_a = col("c2_non_null").or(col("c2_non_null").not()); + let expr_b = col("c2_non_null").not().or(col("c2_non_null")); + let expected = lit(true); + + assert_eq!(simplify(expr_a), expected); + assert_eq!(simplify(expr_b), expected); + } + #[test] fn test_simplify_and_false() { let expr_a = lit(false).and(col("c2")); @@ -1105,6 +1149,18 @@ mod tests { assert_eq!(simplify(expr_b), expected); } + #[test] + fn test_simplify_and_not_self() { + // A AND !A if A is not nullable --> false + // !A AND A if A is not nullable --> false + let expr_a = col("c2_non_null").and(col("c2_non_null").not()); + let expr_b = col("c2_non_null").not().and(col("c2_non_null")); + let expected = lit(false); + + assert_eq!(simplify(expr_a), expected); + assert_eq!(simplify(expr_b), expected); + } + #[test] fn test_simplify_multiply_by_one() { let expr_a = binary_expr(col("c2"), Operator::Multiply, lit(1)); diff --git a/datafusion/optimizer/src/simplify_expressions/utils.rs b/datafusion/optimizer/src/simplify_expressions/utils.rs index d9314e329458a..4df3f507d68a1 100644 --- a/datafusion/optimizer/src/simplify_expressions/utils.rs +++ b/datafusion/optimizer/src/simplify_expressions/utils.rs @@ -152,6 +152,11 @@ pub fn is_op_with(target_op: Operator, haystack: &Expr, needle: &Expr) -> bool { matches!(haystack, Expr::BinaryExpr(BinaryExpr { left, op, right }) if op == &target_op && (needle == left.as_ref() || needle == right.as_ref())) } +/// returns true if `not_expr` is !`expr` +pub fn is_not_of(not_expr: &Expr, expr: &Expr) -> bool { + matches!(not_expr, Expr::Not(inner) if expr == inner.as_ref()) +} + /// returns the contained boolean value in `expr` as /// `Expr::Literal(ScalarValue::Boolean(v))`. pub fn as_bool_lit(expr: Expr) -> Result> {