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
