You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2017/09/05 14:36:45 UTC

[02/16] calcite git commit: [CALCITE-1953] Rewrite "NOT (x IS FALSE)" to "x IS NOT FALSE"; "x IS TRUE" would be wrong

[CALCITE-1953] Rewrite "NOT (x IS FALSE)" to "x IS NOT FALSE"; "x IS TRUE" would be wrong

SqlKind.negate() != SqlKind.negateNullSafe()

Close apache/calcite#521


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

Branch: refs/heads/branch-1.14
Commit: 2156d8265178f92c52f94ef47588a89ab1fdb7de
Parents: d3a7c0d
Author: MinJi Kim <mi...@apache.org>
Authored: Sat Oct 8 10:30:58 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Aug 29 10:12:28 2017 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/rex/RexSimplify.java     |  2 +-
 .../java/org/apache/calcite/sql/SqlKind.java    | 43 +++++++++++++++++---
 .../apache/calcite/test/RelOptRulesTest.java    | 15 +++++++
 .../org/apache/calcite/test/RexProgramTest.java | 27 ++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 25 ++++++++++++
 .../calcite/test/SqlToRelConverterTest.xml      |  2 +-
 6 files changed, 106 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/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 34f01a0..67ac1b8 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -325,7 +325,7 @@ public class RexSimplify {
       // Note that
       //   (NOT x) IS TRUE !=> x IS FALSE
       // because of null values.
-      final SqlOperator notKind = RexUtil.op(kind.negate());
+      final SqlOperator notKind = RexUtil.op(kind.negateNullSafe());
       final RexNode arg = ((RexCall) a).operands.get(0);
       return simplify(rexBuilder.makeCall(notKind, arg));
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/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 62dedb6..ad7c4e2 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -1170,19 +1170,31 @@ public enum SqlKind {
 
   /** Returns the kind that you get if you apply NOT to this kind.
    *
-   * <p>For example, {@code IS_NOT_NULL.negate()} returns {@link #IS_NULL}. */
+   * <p>For example, {@code IS_NOT_NULL.negate()} returns {@link #IS_NULL}.
+   *
+   * <p>For {@link #IS_TRUE}, {@link #IS_FALSE}, {@link #IS_NOT_TRUE},
+   * {@link #IS_NOT_FALSE}, nullable inputs need to be treated carefully.
+   *
+   * <p>{@code NOT(IS_TRUE(null))} = {@code NOT(false)} = {@code true},
+   * while {@code IS_FALSE(null)} = {@code false},
+   * so {@code NOT(IS_TRUE(X))} should be {@code IS_NOT_TRUE(X)}.
+   * On the other hand,
+   * {@code IS_TRUE(NOT(null))} = {@code IS_TRUE(null)} = {@code false}.
+   *
+   * <p>This is why negate() != negateNullSafe() for these operators.
+   */
   public SqlKind negate() {
     switch (this) {
     case IS_TRUE:
-      return IS_FALSE;
+      return IS_NOT_TRUE;
     case IS_FALSE:
-      return IS_TRUE;
+      return IS_NOT_FALSE;
     case IS_NULL:
       return IS_NOT_NULL;
     case IS_NOT_TRUE:
-      return IS_NOT_FALSE;
+      return IS_TRUE;
     case IS_NOT_FALSE:
-      return IS_NOT_TRUE;
+      return IS_FALSE;
     case IS_NOT_NULL:
       return IS_NULL;
     case IS_DISTINCT_FROM:
@@ -1195,7 +1207,18 @@ public enum SqlKind {
   }
 
   /** Returns the kind that you get if you negate this kind.
-   * To conform to null semantics, null value should not be compared. */
+   * To conform to null semantics, null value should not be compared.
+   *
+   * <p>For {@link #IS_TRUE}, {@link #IS_FALSE}, {@link #IS_NOT_TRUE} and
+   * {@link #IS_NOT_FALSE}, nullable inputs need to be treated carefully:
+   *
+   * <ul>
+   * <li>NOT(IS_TRUE(null)) = NOT(false) = true
+   * <li>IS_TRUE(NOT(null)) = IS_TRUE(null) = false
+   * <li>IS_FALSE(null) = false
+   * <li>IS_NOT_TRUE(null) = true
+   * </ul>
+   */
   public SqlKind negateNullSafe() {
     switch (this) {
     case EQUALS:
@@ -1210,6 +1233,14 @@ public enum SqlKind {
       return GREATER_THAN;
     case GREATER_THAN_OR_EQUAL:
       return LESS_THAN;
+    case IS_TRUE:
+      return IS_FALSE;
+    case IS_FALSE:
+      return IS_TRUE;
+    case IS_NOT_TRUE:
+      return IS_NOT_FALSE;
+    case IS_NOT_FALSE:
+      return IS_NOT_TRUE;
     default:
       return this.negate();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index f43f624..d9aea0d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -161,6 +161,21 @@ public class RelOptRulesTest extends RelOptTestBase {
     return DiffRepository.lookup(RelOptRulesTest.class);
   }
 
+  @Test public void testReduceNot() {
+    HepProgram preProgram = new HepProgramBuilder()
+        .build();
+
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(ReduceExpressionsRule.class);
+    HepPlanner hepPlanner = new HepPlanner(builder.build());
+    hepPlanner.addRule(ReduceExpressionsRule.FILTER_INSTANCE);
+
+    final String sql = "select *\n"
+        + "from (select (case when sal > 1000 then null else false end) as caseCol from emp)\n"
+        + "where NOT(caseCol)";
+    checkPlanning(tester, preProgram, hepPlanner, sql, true);
+  }
+
   @Test public void testReduceNestedCaseWhen() {
     HepProgram preProgram = new HepProgramBuilder()
         .build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/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 ca9e04f..7cbcfa6 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -1640,6 +1640,33 @@ public class RexProgramTest {
     }
     return map2.toString();
   }
+
+  @Test public void testSimplifyNot() {
+    final RelDataType booleanNullableType =
+        typeFactory.createTypeWithNullability(
+            typeFactory.createSqlType(SqlTypeName.BOOLEAN), true);
+    final RexNode booleanInput = rexBuilder.makeInputRef(booleanNullableType, 0);
+    final RexNode isFalse = rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, booleanInput);
+    final RexCall result = (RexCall) simplify(isFalse);
+    assertThat(result.getType().isNullable(), is(false));
+    assertThat(result.getOperator(), is((SqlOperator) SqlStdOperatorTable.IS_FALSE));
+    assertThat(result.getOperands().size(), is(1));
+    assertThat(result.getOperands().get(0), is(booleanInput));
+
+    // Make sure that IS_FALSE(IS_FALSE(nullable boolean)) != IS_TRUE(nullable boolean)
+    // IS_FALSE(IS_FALSE(null)) = IS_FALSE(false) = true
+    // IS_TRUE(null) = false
+    final RexNode isFalseIsFalse = rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, isFalse);
+    final RexCall result2 = (RexCall) simplify(isFalseIsFalse);
+    assertThat(result2.getType().isNullable(), is(false));
+    assertThat(result2.getOperator(), is((SqlOperator) SqlStdOperatorTable.IS_NOT_FALSE));
+    assertThat(result2.getOperands().size(), is(1));
+    assertThat(result2.getOperands().get(0), is(booleanInput));
+  }
+
+  private RexNode simplify(RexNode e) {
+    return new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR).simplify(e);
+  }
 }
 
 // End RexProgramTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 28c3b9d..2da30be 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -16,6 +16,31 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 <Root>
+    <TestCase name="testReduceNot">
+        <Resource name="sql">
+            <![CDATA[select sal
+from emp
+where case when (sal = 1000) then
+(case when sal = 1000 then null else 1 end is null) else
+(case when sal = 2000 then null else 1 end is null) end is true]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(CASECOL=[$0])
+  LogicalFilter(condition=[NOT($0)])
+    LogicalProject(CASECOL=[CASE(>($5, 1000), null, false)])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(CASECOL=[$0])
+  LogicalFilter(condition=[NOT($0)])
+    LogicalProject(CASECOL=[CASE(>($5, 1000), null, false)])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testReduceNestedCaseWhen">
         <Resource name="sql">
             <![CDATA[select sal

http://git-wip-us.apache.org/repos/asf/calcite/blob/2156d826/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index f96583d..2e8f904 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2001,7 +2001,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EMPNO=[$0], EXPR$1=[IS FALSE($11)])
+LogicalProject(EMPNO=[$0], EXPR$1=[IS NOT TRUE($11)])
   LogicalJoin(condition=[=($9, $10)], joinType=[left])
     LogicalProject($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7], $f8=[$8], $f9=[$7])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])