Skip to content

Commit

Permalink
[CALCITE-4189] Simplify 'p OR (p IS NOT TRUE)' to 'TRUE'
Browse files Browse the repository at this point in the history
Simplify 'p OR NOT p' to 'TRUE' (if p is not nullable), or to
'null AND p IS NOT NULL' (if p is nullable).
GeodeFilter support converting 'p IS NOT NULL' to 'p <> null'.
  • Loading branch information
macroguo-ghy authored and kgyrtkirk committed Oct 3, 2023
1 parent 87cdfd9 commit ab3f5b0
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 5 deletions.
20 changes: 20 additions & 0 deletions core/src/main/java/org/apache/calcite/rex/RexSimplify.java
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,26 @@ private RexNode simplifyOrs(List<RexNode> terms, RexUnknownAs unknownAs) {
}
}
break;
case IS_NOT_TRUE:
RexNode arg = ((RexCall) term).getOperands().get(0);
if (isSafeExpression(arg) && terms.contains(arg)) {
return trueLiteral;
}
break;
case NOT:
RexNode x = ((RexCall) term).getOperands().get(0);
if (isSafeExpression(x) && terms.contains(x)) {
if (!x.getType().isNullable()) {
return trueLiteral;
}

final RexNode isNotNull =
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, x);
terms.set(terms.indexOf(x), simplifyIs((RexCall) isNotNull, unknownAs));
terms.set(i, rexBuilder.makeNullLiteral(x.getType()));
i--;
}
break;
default:
break;
}
Expand Down
44 changes: 43 additions & 1 deletion core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2318,7 +2318,7 @@ trueLiteral, literal(2),
isTrue(vBool()), literal(1),
isNotTrue(vBool()), literal(1),
literal(2)),
"CASE(OR(?0.bool0, IS NOT TRUE(?0.bool0)), 1, 2)");
"1");
}

@Test void testSimplifyCaseBranchesCollapse2() {
Expand Down Expand Up @@ -3304,6 +3304,48 @@ private static String getString(Map<RexNode, RexNode> map) {
"IS NULL(?0.int1)");
}

/** Unit test for
* <a href="https://issues.apache.org/jira/browse/CALCITE-4189">[CALCITE-4189]
* Simplify 'p OR (p IS NOT TRUE)' to 'TRUE'</a>. */
@Test public void testSimplifyOrNot2() {
final SqlOperator func =
SqlBasicFunction.create("func", ReturnTypes.BOOLEAN_NULLABLE, OperandTypes.VARIADIC);
final RexNode unsafeRel = rexBuilder.makeCall(func, div(vInt(0), literal(2)));
// x OR x IS NOT TRUE ==> "true"
checkSimplify(or(vBool(), isNotTrue(vBool())), "true");
checkSimplify(or(vBoolNotNull(), isNotTrue(vBoolNotNull())), "true");
// outside unsafe expression will not prevent simplification
checkSimplify(or(unsafeRel, vBool(), isNotTrue(vBool())), "true");

// x OR NOT x ==> "true" (if x is not nullable)
checkSimplify(or(vBoolNotNull(), not(vBoolNotNull())), "true");
checkSimplify(or(unsafeRel, vBoolNotNull(), not(vBoolNotNull())), "true");

// x OR NOT x ==> x IS NOT NULL OR NULL (if x is nullable)
checkSimplify3(or(vBool(), not(vBool())),
"OR(IS NOT NULL(?0.bool0), null)",
"IS NOT NULL(?0.bool0)",
"true");
checkSimplify3(or(unsafeRel, vBool(), not(vBool())),
"OR(func(/(?0.int0, 2)), IS NOT NULL(?0.bool0), null)",
"OR(func(/(?0.int0, 2)), IS NOT NULL(?0.bool0))",
"true");
// remove all redundant NULL
checkSimplify3(or(vBool(0), not(vBool(0)), vBool(1), not(vBool(1))),
"OR(IS NOT NULL(?0.bool0), null, IS NOT NULL(?0.bool1))",
"OR(IS NOT NULL(?0.bool0), IS NOT NULL(?0.bool1))",
"true");

// unsafe expression can not be simplified
checkSimplify3(or(unsafeRel, isNotTrue(unsafeRel)),
"OR(func(/(?0.int0, 2)), IS NOT TRUE(func(/(?0.int0, 2))))",
"OR(func(/(?0.int0, 2)), IS NOT TRUE(func(/(?0.int0, 2))))",
"OR(func(/(?0.int0, 2)), NOT(func(/(?0.int0, 2))))");
checkSimplifyUnchanged(or(unsafeRel, not(unsafeRel)));

checkSimplify(or(unsafeRel, trueLiteral), "true");
}

private void checkSarg(String message, Sarg sarg,
Matcher<Integer> complexityMatcher, Matcher<String> stringMatcher) {
assertThat(message, sarg.complexity(), complexityMatcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ private String translateMatch2(RexNode node) {
return translateBinary(">=", "<=", (RexCall) node);
case INPUT_REF:
return translateBinary2("=", node, rexBuilder.makeLiteral(true));
case IS_NOT_NULL:
child = ((RexCall) node).getOperands().get(0);
return translateBinary2("<>", child, rexBuilder.makeNullLiteral(node.getType()));
case NOT:
child = ((RexCall) node).getOperands().get(0);
if (child.getKind() == SqlKind.CAST) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.apache.calcite.sql.validate.SqlValidatorUtil;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

import org.immutables.value.Value;

Expand Down Expand Up @@ -314,10 +315,7 @@ private static boolean isEqualityOnKey(RexNode node, List<String> fieldNames) {

private static boolean isBooleanColumnReference(RexNode node, List<String> fieldNames) {
// FIXME Ignore casts for rel and assume they aren't really necessary
if (node.isA(SqlKind.CAST)) {
node = ((RexCall) node).getOperands().get(0);
}
if (node.isA(SqlKind.NOT)) {
while (node.isA(ImmutableList.of(SqlKind.NOT, SqlKind.CAST, SqlKind.IS_NOT_NULL))) {
node = ((RexCall) node).getOperands().get(0);
}
if (node.isA(SqlKind.INPUT_REF)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ private CalciteAssert.AssertThat calciteAssert() {
+ "WHERE booleanValue = true"));
}

@Test void testSqlBooleanColumnIsNotNullFilter() {
calciteAssert()
.query("SELECT booleanValue as booleanValue "
+ "FROM geode.allDataTypesRegion WHERE booleanValue is not null")
.returnsCount(3)
.queryContains(
GeodeAssertions.query("SELECT booleanValue AS booleanValue FROM /allDataTypesRegion "
+ "WHERE booleanValue <> null"));
}

@Test void testSqlBooleanColumnFilter() {
calciteAssert()
.query("SELECT booleanValue as booleanValue "
Expand Down

0 comments on commit ab3f5b0

Please sign in to comment.