diff --git a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs index 033b554402c96..497ad34c99adb 100644 --- a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs +++ b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs @@ -1002,6 +1002,11 @@ impl<'a, S: SimplifyInfo> ExprRewriter for Simplifier<'a, S> { // Expr::Not(inner) => negate_clause(*inner), + // + // Rules for Negative + // + Expr::Negative(inner) => distribute_negation(*inner), + // // Rules for Case // @@ -2122,6 +2127,37 @@ mod tests { assert_eq!(simplify(expr), expected); } + #[test] + fn test_simplify_by_de_morgan_laws() { + // Laws with logical operations + // !(c3 AND c4) --> !c3 OR !c4 + let expr = and(col("c3"), col("c4")).not(); + let expected = or(col("c3").not(), col("c4").not()); + assert_eq!(simplify(expr), expected); + // !(c3 OR c4) --> !c3 AND !c4 + let expr = or(col("c3"), col("c4")).not(); + let expected = and(col("c3").not(), col("c4").not()); + assert_eq!(simplify(expr), expected); + // !(!c3) --> c3 + let expr = col("c3").not().not(); + let expected = col("c3"); + assert_eq!(simplify(expr), expected); + + // Laws with bitwise operations + // !(c3 & c4) --> !c3 | !c4 + let expr = -bitwise_and(col("c3"), col("c4")); + let expected = bitwise_or(-col("c3"), -col("c4")); + assert_eq!(simplify(expr), expected); + // !(c3 | c4) --> !c3 & !c4 + let expr = -bitwise_or(col("c3"), col("c4")); + let expected = bitwise_and(-col("c3"), -col("c4")); + assert_eq!(simplify(expr), expected); + // !(!c3) --> c3 + let expr = -(-col("c3")); + let expected = col("c3"); + assert_eq!(simplify(expr), expected); + } + #[test] fn test_simplify_null_and_false() { let expr = and(lit_bool_null(), lit(false)); @@ -2425,11 +2461,6 @@ mod tests { ) } - #[test] - fn simplify_expr_not_not() { - assert_eq!(simplify(col("c2").not().not().not()), col("c2").not(),); - } - #[test] fn simplify_expr_null_comparison() { // x = null is always null diff --git a/datafusion/optimizer/src/simplify_expressions/utils.rs b/datafusion/optimizer/src/simplify_expressions/utils.rs index 352674c3a68e8..8b3f437dc233e 100644 --- a/datafusion/optimizer/src/simplify_expressions/utils.rs +++ b/datafusion/optimizer/src/simplify_expressions/utils.rs @@ -20,7 +20,7 @@ use datafusion_common::{DataFusionError, Result, ScalarValue}; use datafusion_expr::{ expr::{Between, BinaryExpr}, - expr_fn::{and, concat_ws, or}, + expr_fn::{and, bitwise_and, bitwise_or, concat_ws, or}, lit, BuiltinScalarFunction, Expr, Like, Operator, }; @@ -311,6 +311,45 @@ pub fn negate_clause(expr: Expr) -> Expr { } } +/// bitwise negate a Negative clause +/// input is the clause to be bitwise negated.(args for Negative clause) +/// For BinaryExpr: +/// ~(A & B) ===> ~A | ~B +/// ~(A | B) ===> ~A & ~B +/// For Negative: +/// ~(~A) ===> A +/// For others, use Negative clause +pub fn distribute_negation(expr: Expr) -> Expr { + match expr { + Expr::BinaryExpr(BinaryExpr { left, op, right }) => { + match op { + // ~(A & B) ===> ~A | ~B + Operator::BitwiseAnd => { + let left = distribute_negation(*left); + let right = distribute_negation(*right); + + bitwise_or(left, right) + } + // ~(A | B) ===> ~A & ~B + Operator::BitwiseOr => { + let left = distribute_negation(*left); + let right = distribute_negation(*right); + + bitwise_and(left, right) + } + // use negative clause + _ => Expr::Negative(Box::new(Expr::BinaryExpr(BinaryExpr::new( + left, op, right, + )))), + } + } + // ~(~A) ===> A + Expr::Negative(expr) => *expr, + // use negative clause + _ => Expr::Negative(Box::new(expr)), + } +} + /// Simplify the `concat` function by /// 1. filtering out all `null` literals /// 2. concatenating contiguous literal arguments