Repository: calcite
Updated Branches:
  refs/heads/master 0d707e4e4 -> 90f49be63


[CALCITE-2469] RexSimplify should optimize '(NOT x) IS NULL' to 'x IS NULL' 
(pengzhiwei)

Previously it optimized '(NOT x) IS NULL' to 'x IS NOT NULL', which is
wrong.

Generalize the above, to simplify 'f(x) IS NULL' to 'x IS NULL' for any
operator 'f' that is known to be strong. (Julian Hyde)

Close apache/calcite#796


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/90f49be6
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/90f49be6
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/90f49be6

Branch: refs/heads/master
Commit: 90f49be639b9a569e3dc4d8369c08e2db64ea301
Parents: 0d707e4
Author: pengzhiwei <[email protected]>
Authored: Fri Aug 17 14:15:13 2018 +0800
Committer: Julian Hyde <[email protected]>
Committed: Wed Aug 29 16:50:23 2018 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/rex/RexSimplify.java     | 41 ++++++++++++++++++--
 .../java/org/apache/calcite/sql/SqlKind.java    |  5 +++
 .../org/apache/calcite/test/RexProgramTest.java |  8 ++++
 core/src/test/resources/sql/sub-query.iq        |  8 ++--
 4 files changed, 55 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/90f49be6/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java 
b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index f5a99ef..f69ca9a 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -438,16 +438,18 @@ public class RexSimplify {
   }
 
   private RexNode simplifyIs2(SqlKind kind, RexNode a) {
+    final RexNode simplified;
     switch (kind) {
     case IS_NULL:
       // x IS NULL ==> FALSE (if x is not nullable)
-      if (!a.getType().isNullable()) {
-        return rexBuilder.makeLiteral(false);
+      simplified = simplifyIsNull(a);
+      if (simplified != null) {
+        return simplified;
       }
       break;
     case IS_NOT_NULL:
       // x IS NOT NULL ==> TRUE (if x is not nullable)
-      RexNode simplified = simplifyIsNotNull(a);
+      simplified = simplifyIsNotNull(a);
       if (simplified != null) {
         return simplified;
       }
@@ -496,7 +498,11 @@ public class RexSimplify {
       return rexBuilder.makeLiteral(true);
     }
     switch (Strong.policy(a.getKind())) {
+    case NOT_NULL:
+      return rexBuilder.makeLiteral(true);
     case ANY:
+      // "f" is a strong operator, so "f(operand) IS NOT NULL" simplifies to
+      // "operand IS NOT NULL"
       final List<RexNode> operands = new ArrayList<>();
       for (RexNode operand : ((RexCall) a).getOperands()) {
         final RexNode simplified = simplifyIsNotNull(operand);
@@ -524,6 +530,35 @@ public class RexSimplify {
     }
   }
 
+  private RexNode simplifyIsNull(RexNode a) {
+    if (!a.getType().isNullable()) {
+      return rexBuilder.makeLiteral(false);
+    }
+    switch (Strong.policy(a.getKind())) {
+    case NOT_NULL:
+      return rexBuilder.makeLiteral(false);
+    case ANY:
+      // "f" is a strong operator, so "f(operand) IS NULL" simplifies to
+      // "operand IS NULL"
+      final List<RexNode> operands = new ArrayList<>();
+      for (RexNode operand : ((RexCall) a).getOperands()) {
+        final RexNode simplified = simplifyIsNull(operand);
+        if (simplified == null) {
+          operands.add(
+              rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, operand));
+        } else if (simplified.isAlwaysFalse()) {
+          return rexBuilder.makeLiteral(false);
+        } else {
+          operands.add(simplified);
+        }
+      }
+      return RexUtil.composeConjunction(rexBuilder, operands, false);
+    case AS_IS:
+    default:
+      return null;
+    }
+  }
+
   private RexNode simplifyCoalesce(RexCall call) {
     final Set<String> digests = new HashSet<>();
     final List<RexNode> operands = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/calcite/blob/90f49be6/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java 
b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index cbf201f..e77b4be 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -1311,6 +1311,11 @@ public enum SqlKind {
       return IS_NOT_FALSE;
     case IS_NOT_FALSE:
       return IS_NOT_TRUE;
+     // (NOT x) IS NULL => x IS NULL
+     // Similarly (NOT x) IS NOT NULL => x IS NOT NULL
+    case IS_NOT_NULL:
+    case IS_NULL:
+      return this;
     default:
       return this.negate();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/90f49be6/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java 
b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 186594b..92ad323 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -1191,6 +1191,14 @@ public class RexProgramTest extends 
RexProgramBuilderBase {
     checkSimplify(coalesce(iRef, literal1), "COALESCE(?0.i, 1)");
     checkSimplify(coalesce(iRef, plus(iRef, hRef), literal1, hRef),
         "COALESCE(?0.i, +(?0.i, ?0.h), 1)");
+
+    // "(not x) is null" to "x is null"
+    checkSimplify(isNull(not(vBool())), "IS NULL(?0.bool0)");
+    checkSimplify(isNull(not(vBoolNotNull())), "false");
+
+    // "(not x) is not null" to "x is not null"
+    checkSimplify(isNotNull(not(vBool())), "IS NOT NULL(?0.bool0)");
+    checkSimplify(isNotNull(not(vBoolNotNull())), "true");
   }
 
   @Test public void testSimplifyFilter() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/90f49be6/core/src/test/resources/sql/sub-query.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 5048445..6772c21 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -1554,7 +1554,7 @@ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[IS 
NULL($t3)], expr#5=[false], expr
     EnumerableLimit(fetch=[1])
       EnumerableSort(sort0=[$0], dir0=[DESC])
         EnumerableAggregate(group=[{0}], c=[COUNT()])
-          EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[null], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t5)], 
expr#8=[IS NULL($t4)], expr#9=[OR($t6, $t7, $t8)], cs=[$t3], $condition=[$t9])
+          EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[null], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t4)], 
expr#8=[OR($t6, $t7)], cs=[$t3], $condition=[$t8])
             EnumerableTableScan(table=[[scott, DEPT]])
 !plan
 
@@ -1596,7 +1596,7 @@ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[IS 
NULL($t3)], expr#5=[false], expr
     EnumerableLimit(fetch=[1])
       EnumerableSort(sort0=[$0], dir0=[DESC])
         EnumerableAggregate(group=[{0}], c=[COUNT()])
-          EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[10], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t5)], 
expr#8=[OR($t6, $t7)], cs=[$t3], $condition=[$t8])
+          EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[10], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], cs=[$t3], $condition=[$t6])
             EnumerableTableScan(table=[[scott, DEPT]])
 !plan
 
@@ -1850,7 +1850,7 @@ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[false], 
expr#6=[=($t3, $t5)], expr#
   EnumerableJoin(condition=[=($2, $4)], joinType=[left])
     EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5], DEPTNO=[$t7])
       EnumerableTableScan(table=[[scott, EMP]])
-    EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[null], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t5)], 
expr#8=[IS NULL($t4)], expr#9=[OR($t6, $t7, $t8)], cs=[$t3], DEPTNO=[$t0], 
$condition=[$t9])
+    EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[null], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t4)], 
expr#8=[OR($t6, $t7)], cs=[$t3], DEPTNO=[$t0], $condition=[$t8])
       EnumerableTableScan(table=[[scott, DEPT]])
 !plan
 
@@ -1908,7 +1908,7 @@ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[false], 
expr#6=[=($t3, $t5)], expr#
   EnumerableJoin(condition=[=($2, $4)], joinType=[left])
     EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5], DEPTNO=[$t7])
       EnumerableTableScan(table=[[scott, EMP]])
-    EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[10], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], expr#7=[IS NULL($t5)], 
expr#8=[OR($t6, $t7)], cs=[$t3], DEPTNO=[$t0], $condition=[$t8])
+    EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[10], 
expr#5=[CAST($t0):TINYINT], expr#6=[=($t4, $t5)], cs=[$t3], DEPTNO=[$t0], 
$condition=[$t6])
       EnumerableTableScan(table=[[scott, DEPT]])
 !plan
 

Reply via email to