[CALCITE-1668] Simplify "1 = 1" to true, "1 > 2" to false (Kevin Risden)

Close apache/calcite#387


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

Branch: refs/heads/master
Commit: 255cd96a79eb68fcf852b8e7af6299d7dfb356c7
Parents: 8df27ce
Author: Kevin Risden <[email protected]>
Authored: Sun Feb 19 15:56:35 2017 -0600
Committer: Julian Hyde <[email protected]>
Committed: Fri Mar 3 12:00:05 2017 -0800

----------------------------------------------------------------------
 .../java/org/apache/calcite/rex/RexUtil.java    | 81 +++++++++++++++-----
 .../org/apache/calcite/test/RelBuilderTest.java | 14 ++++
 .../org/apache/calcite/test/RexProgramTest.java | 65 ++++++++++++++++
 3 files changed, 140 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/255cd96a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java 
b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index a6b9e4e..fd60098 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1641,33 +1641,74 @@ public class RexUtil {
     case LESS_THAN:
     case LESS_THAN_OR_EQUAL:
     case NOT_EQUALS:
-      final List<RexNode> operands = ((RexCall) e).getOperands();
-      if (RexUtil.eq(operands.get(0), operands.get(1))
-          && (unknownAsFalse
-          || (!operands.get(0).getType().isNullable()
-              && !operands.get(1).getType().isNullable()))) {
-        switch (e.getKind()) {
-        case EQUALS:
-        case GREATER_THAN_OR_EQUAL:
-        case LESS_THAN_OR_EQUAL:
-          // "x = x" simplifies to "x is not null" (similarly <= and >=)
-          return simplify(rexBuilder,
-              rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL,
-                  operands.get(0)));
-        default:
-          // "x != x" simplifies to "false" (similarly < and >)
-          return rexBuilder.makeLiteral(false);
-        }
-      }
-      return simplifyCall(rexBuilder, (RexCall) e);
+      return simplifyComparison(rexBuilder, (RexCall) e, unknownAsFalse);
     default:
       return e;
     }
   }
 
-  private static RexNode simplifyCall(RexBuilder rexBuilder, RexCall e) {
+  // e must be a comparison (=, >, >=, <, <=, !=)
+  private static RexNode simplifyComparison(RexBuilder rexBuilder, RexCall e,
+      boolean unknownAsFalse) {
     final List<RexNode> operands = new ArrayList<>(e.operands);
     simplifyList(rexBuilder, operands);
+
+    // Simplify "x <op> x"
+    final RexNode o0 = operands.get(0);
+    final RexNode o1 = operands.get(1);
+    if (RexUtil.eq(o0, o1)
+        && (unknownAsFalse
+            || (!o0.getType().isNullable()
+                && !o1.getType().isNullable()))) {
+      switch (e.getKind()) {
+      case EQUALS:
+      case GREATER_THAN_OR_EQUAL:
+      case LESS_THAN_OR_EQUAL:
+        // "x = x" simplifies to "x is not null" (similarly <= and >=)
+        return simplify(rexBuilder,
+            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, o0));
+      default:
+        // "x != x" simplifies to "false" (similarly < and >)
+        return rexBuilder.makeLiteral(false);
+      }
+    }
+
+    // Simplify "<literal1> <op> <literal2>"
+    // For example, "1 = 2" becomes FALSE;
+    // "1 != 1" becomes FALSE;
+    // "1 != NULL" becomes UNKNOWN (or FALSE if unknownAsFalse);
+    // "1 != '1'" is unchanged because the types are not the same.
+    if (o0.isA(SqlKind.LITERAL)
+        && o1.isA(SqlKind.LITERAL)
+        && o0.getType().equals(o1.getType())) {
+      final Comparable v0 = ((RexLiteral) o0).getValue();
+      final Comparable v1 = ((RexLiteral) o1).getValue();
+      if (v0 == null || v1 == null) {
+        return unknownAsFalse
+            ? rexBuilder.makeLiteral(false)
+            : rexBuilder.makeNullLiteral(e.getType());
+      }
+      @SuppressWarnings("unchecked")
+      final int comparisonResult = v0.compareTo(v1);
+      switch (e.getKind()) {
+      case EQUALS:
+        return rexBuilder.makeLiteral(comparisonResult == 0);
+      case GREATER_THAN:
+        return rexBuilder.makeLiteral(comparisonResult > 0);
+      case GREATER_THAN_OR_EQUAL:
+        return rexBuilder.makeLiteral(comparisonResult >= 0);
+      case LESS_THAN:
+        return rexBuilder.makeLiteral(comparisonResult < 0);
+      case LESS_THAN_OR_EQUAL:
+        return rexBuilder.makeLiteral(comparisonResult <= 0);
+      case NOT_EQUALS:
+        return rexBuilder.makeLiteral(comparisonResult != 0);
+      default:
+        throw new AssertionError();
+      }
+    }
+
+    // If none of the arguments were simplified, return the call unchanged.
     if (operands.equals(e.operands)) {
       return e;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/255cd96a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 842caf1..4eafeae 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -208,6 +208,20 @@ public class RelBuilderTest {
         is("LogicalTableScan(table=[[scott, EMP]])\n"));
   }
 
+  @Test public void testScanFilterTriviallyFalse() {
+    // Equivalent SQL:
+    //   SELECT *
+    //   FROM emp
+    //   WHERE 1 = 2
+    final RelBuilder builder = RelBuilder.create(config().build());
+    RelNode root =
+        builder.scan("EMP")
+            .filter(builder.equals(builder.literal(1), builder.literal(2)))
+            .build();
+    assertThat(str(root),
+        is("LogicalValues(tuples=[[]])\n"));
+  }
+
   @Test public void testScanFilterEquals() {
     // Equivalent SQL:
     //   SELECT *

http://git-wip-us.apache.org/repos/asf/calcite/blob/255cd96a/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 9ce8017..c1f567c 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -1308,6 +1308,71 @@ public class RexProgramTest {
         "1970-01-01 00:00:00"); // different from Hive
   }
 
+  @Test public void testSimplifyLiterals() {
+    final RexLiteral literalAbc = rexBuilder.makeLiteral("abc");
+    final RexLiteral literalDef = rexBuilder.makeLiteral("def");
+
+    final RexLiteral literalZero = 
rexBuilder.makeExactLiteral(BigDecimal.ZERO);
+    final RexLiteral literalOne = rexBuilder.makeExactLiteral(BigDecimal.ONE);
+    final RexLiteral literalOneDotZero = rexBuilder.makeExactLiteral(new 
BigDecimal(1.0));
+
+    // Check string comparison
+    checkSimplify(eq(literalAbc, literalAbc), "true");
+    checkSimplify(eq(literalAbc, literalDef), "false");
+    checkSimplify(ne(literalAbc, literalAbc), "false");
+    checkSimplify(ne(literalAbc, literalDef), "true");
+    checkSimplify(gt(literalAbc, literalDef), "false");
+    checkSimplify(gt(literalDef, literalAbc), "true");
+    checkSimplify(gt(literalDef, literalDef), "false");
+    checkSimplify(ge(literalAbc, literalDef), "false");
+    checkSimplify(ge(literalDef, literalAbc), "true");
+    checkSimplify(ge(literalDef, literalDef), "true");
+    checkSimplify(lt(literalAbc, literalDef), "true");
+    checkSimplify(lt(literalAbc, literalDef), "true");
+    checkSimplify(lt(literalDef, literalDef), "false");
+    checkSimplify(le(literalAbc, literalDef), "true");
+    checkSimplify(le(literalDef, literalAbc), "false");
+    checkSimplify(le(literalDef, literalDef), "true");
+
+    // Check whole number comparison
+    checkSimplify(eq(literalZero, literalOne), "false");
+    checkSimplify(eq(literalOne, literalZero), "false");
+    checkSimplify(ne(literalZero, literalOne), "true");
+    checkSimplify(ne(literalOne, literalZero), "true");
+    checkSimplify(gt(literalZero, literalOne), "false");
+    checkSimplify(gt(literalOne, literalZero), "true");
+    checkSimplify(gt(literalOne, literalOne), "false");
+    checkSimplify(ge(literalZero, literalOne), "false");
+    checkSimplify(ge(literalOne, literalZero), "true");
+    checkSimplify(ge(literalOne, literalOne), "true");
+    checkSimplify(lt(literalZero, literalOne), "true");
+    checkSimplify(lt(literalOne, literalZero), "false");
+    checkSimplify(lt(literalOne, literalOne), "false");
+    checkSimplify(le(literalZero, literalOne), "true");
+    checkSimplify(le(literalOne, literalZero), "false");
+    checkSimplify(le(literalOne, literalOne), "true");
+
+    // Check decimal equality comparison
+    checkSimplify(eq(literalOne, literalOneDotZero), "true");
+    checkSimplify(eq(literalOneDotZero, literalOne), "true");
+    checkSimplify(ne(literalOne, literalOneDotZero), "false");
+    checkSimplify(ne(literalOneDotZero, literalOne), "false");
+
+    // Check different types shouldn't change simplification
+    checkSimplifyUnchanged(eq(literalZero, literalAbc));
+    checkSimplifyUnchanged(eq(literalAbc, literalZero));
+    checkSimplifyUnchanged(ne(literalZero, literalAbc));
+    checkSimplifyUnchanged(ne(literalAbc, literalZero));
+    checkSimplifyUnchanged(gt(literalZero, literalAbc));
+    checkSimplifyUnchanged(gt(literalAbc, literalZero));
+    checkSimplifyUnchanged(ge(literalZero, literalAbc));
+    checkSimplifyUnchanged(ge(literalAbc, literalZero));
+    checkSimplifyUnchanged(lt(literalZero, literalAbc));
+    checkSimplifyUnchanged(lt(literalAbc, literalZero));
+    checkSimplifyUnchanged(le(literalZero, literalAbc));
+    checkSimplifyUnchanged(le(literalAbc, literalZero));
+  }
+
   private Calendar cal(int y, int m, int d, int h, int mm, int s) {
     final Calendar c = Util.calendar();
     c.set(Calendar.YEAR, y);

Reply via email to