[CALCITE-1638] Simplify "$x = $x" to "$x is not null"
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/34997ad1 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/34997ad1 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/34997ad1 Branch: refs/heads/master Commit: 34997ad1bb0b4ff0d4ed738859127872029ebf0e Parents: 24c25fc Author: Julian Hyde <[email protected]> Authored: Wed Feb 22 21:26:26 2017 -0800 Committer: Julian Hyde <[email protected]> Committed: Wed Feb 22 22:17:28 2017 -0800 ---------------------------------------------------------------------- .../java/org/apache/calcite/rex/RexCall.java | 9 ++-- .../java/org/apache/calcite/rex/RexUtil.java | 18 ++++++++ .../org/apache/calcite/test/RexProgramTest.java | 45 ++++++++++++++++++++ .../org/apache/calcite/test/RelOptRulesTest.xml | 12 +++--- core/src/test/resources/sql/misc.iq | 14 ++++++ core/src/test/resources/sql/sub-query.iq | 6 +-- 6 files changed, 89 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/core/src/main/java/org/apache/calcite/rex/RexCall.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java index 6ae235b..5a1f112 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCall.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java @@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlSyntax; import org.apache.calcite.util.Litmus; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.util.List; @@ -55,14 +56,10 @@ public class RexCall extends RexNode { RelDataType type, SqlOperator op, List<? extends RexNode> operands) { - assert type != null : "precondition: type != null"; - assert op != null : "precondition: op != null"; - assert operands != null : "precondition: operands != null"; - this.type = type; - this.op = op; + this.type = Preconditions.checkNotNull(type); + this.op = Preconditions.checkNotNull(op); this.operands = ImmutableList.copyOf(operands); assert op.getKind() != null : op; - assert op.validRexOperands(operands.size(), Litmus.THROW) : this; } http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/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 4f7492d..fa19883 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java @@ -1615,6 +1615,24 @@ 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); default: return e; http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/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 7efa890..d021a5b 100644 --- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java @@ -195,6 +195,10 @@ public class RexProgramTest { return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, n1, n2); } + private RexNode ne(RexNode n1, RexNode n2) { + return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, n1, n2); + } + private RexNode le(RexNode n1, RexNode n2) { return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, n1, n2); } @@ -850,6 +854,8 @@ public class RexProgramTest { final RelDataType booleanType = typeFactory.createSqlType(SqlTypeName.BOOLEAN); final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER); + final RelDataType intNullableType = + typeFactory.createTypeWithNullability(intType, true); final RelDataType rowType = typeFactory.builder() .add("a", booleanType) .add("b", booleanType) @@ -859,6 +865,7 @@ public class RexProgramTest { .add("f", booleanType) .add("g", booleanType) .add("h", intType) + .add("i", intNullableType) .build(); final RexDynamicParam range = rexBuilder.makeDynamicParam(rowType, 0); @@ -867,6 +874,8 @@ public class RexProgramTest { final RexNode cRef = rexBuilder.makeFieldAccess(range, 2); final RexNode dRef = rexBuilder.makeFieldAccess(range, 3); final RexNode eRef = rexBuilder.makeFieldAccess(range, 4); + final RexNode hRef = rexBuilder.makeFieldAccess(range, 7); + final RexNode iRef = rexBuilder.makeFieldAccess(range, 8); final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE); // and: remove duplicates @@ -985,6 +994,42 @@ public class RexProgramTest { or(lt(aRef, literal1), and(trueLiteral, or(falseLiteral, falseLiteral))), "<(?0.a, 1)"); + + // "x = x" simplifies to "x is not null" + checkSimplify(eq(literal1, literal1), "true"); + checkSimplify(eq(hRef, hRef), "true"); + checkSimplify2(eq(iRef, iRef), "=(?0.i, ?0.i)", "IS NOT NULL(?0.i)"); + checkSimplify(eq(iRef, hRef), "=(?0.i, ?0.h)"); + + // "x <= x" simplifies to "x is not null" + checkSimplify(le(literal1, literal1), "true"); + checkSimplify(le(hRef, hRef), "true"); + checkSimplify2(le(iRef, iRef), "<=(?0.i, ?0.i)", "IS NOT NULL(?0.i)"); + checkSimplify(le(iRef, hRef), "<=(?0.i, ?0.h)"); + + // "x >= x" simplifies to "x is not null" + checkSimplify(ge(literal1, literal1), "true"); + checkSimplify(ge(hRef, hRef), "true"); + checkSimplify2(ge(iRef, iRef), ">=(?0.i, ?0.i)", "IS NOT NULL(?0.i)"); + checkSimplify(ge(iRef, hRef), ">=(?0.i, ?0.h)"); + + // "x != x" simplifies to "false" + checkSimplify(ne(literal1, literal1), "false"); + checkSimplify(ne(hRef, hRef), "false"); + checkSimplify2(ne(iRef, iRef), "<>(?0.i, ?0.i)", "false"); + checkSimplify(ne(iRef, hRef), "<>(?0.i, ?0.h)"); + + // "x < x" simplifies to "false" + checkSimplify(lt(literal1, literal1), "false"); + checkSimplify(lt(hRef, hRef), "false"); + checkSimplify2(lt(iRef, iRef), "<(?0.i, ?0.i)", "false"); + checkSimplify(lt(iRef, hRef), "<(?0.i, ?0.h)"); + + // "x > x" simplifies to "false" + checkSimplify(gt(literal1, literal1), "false"); + checkSimplify(gt(hRef, hRef), "false"); + checkSimplify2(gt(iRef, iRef), ">(?0.i, ?0.i)", "false"); + checkSimplify(gt(iRef, hRef), ">(?0.i, ?0.h)"); } @Test public void testSimplifyFilter() { http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/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 713a92c..6c03478 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -3913,8 +3913,7 @@ ProjectRel(EXPR$0=[1]) <![CDATA[ LogicalProject(EXPR$0=[1]) LogicalJoin(condition=[=($7, $16)], joinType=[inner]) - LogicalFilter(condition=[=(1, 1)]) - LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> </Resource> @@ -3922,8 +3921,7 @@ LogicalProject(EXPR$0=[1]) <![CDATA[ LogicalProject(EXPR$0=[1]) LogicalJoin(condition=[=($7, $16)], joinType=[inner]) - LogicalFilter(condition=[=(1, 1)]) - LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> </Resource> @@ -4187,7 +4185,7 @@ LogicalProject(DEPTNO=[$0]) <TestCase name="testCastInAggregateReduceFunctions"> <Resource name="sql"> <![CDATA[select name, stddev_pop(deptno), avg(deptno), stddev_samp(deptno),var_pop(deptno), var_samp(deptno) - from sales.dept group by name]]> +from sales.dept group by name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6693,7 +6691,9 @@ LogicalAggregate(group=[{0}], EXPR$1=[SUM($2)], EXPR$2=[AVG($1)]) <TestCase name="testCastInAggregateExpandDistinctAggregatesRule"> <Resource name="sql"> <![CDATA[select name, sum(distinct cn), sum(distinct sm) -from (select name, count(dept.deptno) as cn,sum(dept.deptno) as sm from sales.dept group by name) +from ( + select name, count(dept.deptno) as cn,sum(dept.deptno) as sm + from sales.dept group by name) group by name]]> </Resource> <Resource name="planBefore"> http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/core/src/test/resources/sql/misc.iq ---------------------------------------------------------------------- diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq index 39a32d5..cbeb56a 100644 --- a/core/src/test/resources/sql/misc.iq +++ b/core/src/test/resources/sql/misc.iq @@ -1826,4 +1826,18 @@ select count(*) as c from "scott".emp where not (ename in ('Fred')); !ok +# [CALCITE-1638] Simplify "$x = $x" to "$x is not null" +select count(*) as c from "scott".emp where deptno > deptno; ++---+ +| C | ++---+ +| 0 | ++---+ +(1 row) + +!ok +EnumerableAggregate(group=[{}], C=[COUNT()]) + EnumerableValues(tuples=[[]]) +!plan + # End misc.iq http://git-wip-us.apache.org/repos/asf/calcite/blob/34997ad1/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 26329ec..ff1b824 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -40,20 +40,20 @@ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], expr#6=[=($t1, $t5)], expr#7=[f EnumerableValues(tuples=[[{ 0 }]]) EnumerableCalc(expr#0=[{inputs}], expr#1=[2], EXPR$0=[$t1]) EnumerableValues(tuples=[[{ 0 }]]) - EnumerableCalc(expr#0=[{inputs}], expr#1=[1], expr#2=[=($t1, $t1)], expr#3=[null], expr#4=[3], expr#5=[CASE($t2, $t3, $t4)], EXPR$0=[$t5]) + EnumerableCalc(expr#0=[{inputs}], expr#1=[true], expr#2=[null], expr#3=[3], expr#4=[CASE($t1, $t2, $t3)], EXPR$0=[$t4]) EnumerableValues(tuples=[[{ 0 }]]) EnumerableAggregate(group=[{}], c=[COUNT()], ck=[COUNT($0)]) EnumerableUnion(all=[true]) EnumerableCalc(expr#0=[{inputs}], expr#1=[1], EXPR$0=[$t1]) EnumerableValues(tuples=[[{ 0 }]]) - EnumerableCalc(expr#0=[{inputs}], expr#1=[1], expr#2=[=($t1, $t1)], expr#3=[null], expr#4=[3], expr#5=[CASE($t2, $t3, $t4)], EXPR$0=[$t5]) + EnumerableCalc(expr#0=[{inputs}], expr#1=[true], expr#2=[null], expr#3=[3], expr#4=[CASE($t1, $t2, $t3)], EXPR$0=[$t4]) EnumerableValues(tuples=[[{ 0 }]]) EnumerableAggregate(group=[{0, 1}]) EnumerableCalc(expr#0=[{inputs}], expr#1=[true], proj#0..1=[{exprs}]) EnumerableUnion(all=[true]) EnumerableCalc(expr#0=[{inputs}], expr#1=[1], EXPR$0=[$t1]) EnumerableValues(tuples=[[{ 0 }]]) - EnumerableCalc(expr#0=[{inputs}], expr#1=[1], expr#2=[=($t1, $t1)], expr#3=[null], expr#4=[3], expr#5=[CASE($t2, $t3, $t4)], EXPR$0=[$t5]) + EnumerableCalc(expr#0=[{inputs}], expr#1=[true], expr#2=[null], expr#3=[3], expr#4=[CASE($t1, $t2, $t3)], EXPR$0=[$t4]) EnumerableValues(tuples=[[{ 0 }]]) !plan
