Skip to content
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
56 changes: 56 additions & 0 deletions datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

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

Nice! It's quit complicated deal with null.
in datafsuion:

❯ select true or NULL ;
Plan("'Boolean OR Null' can't be evaluated because there isn't a common type to coerce the types to")
❯ select true or FALSE ;
+---------------------------------+
| Boolean(true) OR Boolean(false) |
+---------------------------------+
| true                            |
+---------------------------------+
1 row in set. Query took 0.003 seconds.
❯

RUN in spark-sql

spark-sql> select true or false;
true
Time taken: 3.139 seconds, Fetched 1 row(s)
spark-sql> select true or NULL;
true
Time taken: 0.076 seconds, Fetched 1 row(s)
spark-sql> select FALSE or NULL;
NULL
Time taken: 0.06 seconds, Fetched 1 row(s)
spark-sql>

I think its ok get error when run NULL or xxx

Copy link
Contributor

Choose a reason for hiding this comment

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

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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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"));
Copy link
Member

Choose a reason for hiding this comment

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

tests win! 👍

Copy link
Member

Choose a reason for hiding this comment

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

Without this change, these test fail.

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));
Expand Down
5 changes: 5 additions & 0 deletions datafusion/optimizer/src/simplify_expressions/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<bool>> {
Expand Down