This is an automated email from the ASF dual-hosted git repository. rubenql pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push: new d46137a RelOptRulesTest improvements: - Provide relFn pattern mechanism to test a RelNode function instead of a sql string - Refactor several tests to use relFn - Refactor similar tests by using common auxiliary methods - Correct auxiliary methods names (use "check" prefix instead of "test" prefix) d46137a is described below commit d46137a197a2840ea5ff9f3b38bb86d423c9af11 Author: rubenada <rube...@gmail.com> AuthorDate: Wed Jun 16 15:05:51 2021 +0100 RelOptRulesTest improvements: - Provide relFn pattern mechanism to test a RelNode function instead of a sql string - Refactor several tests to use relFn - Refactor similar tests by using common auxiliary methods - Correct auxiliary methods names (use "check" prefix instead of "test" prefix) --- .../org/apache/calcite/test/RelOptRulesTest.java | 1266 ++++++-------------- .../org/apache/calcite/test/RelOptTestBase.java | 58 +- .../org/apache/calcite/test/TopDownOptTest.java | 2 +- .../org/apache/calcite/test/RelOptRulesTest.xml | 177 ++- 4 files changed, 573 insertions(+), 930 deletions(-) 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 f76b62e..d2b53c1 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -134,6 +134,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Locale; +import java.util.function.Function; import java.util.function.Predicate; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -391,105 +392,60 @@ class RelOptRulesTest extends RelOptTestBase { * <a href="https://issues.apache.org/jira/browse/CALCITE-3170">[CALCITE-3170] * ANTI join on conditions push down generates wrong plan</a>. */ @Test void testCanNotPushAntiJoinConditionsToLeft() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select * from emp // where emp.deptno // not in (select dept.deptno from dept where emp.deptno > 20) - RelNode left = relBuilder.scan("EMP").build(); - RelNode right = relBuilder.scan("DEPT").build(); - RelNode relNode = relBuilder.push(left) - .push(right) - .antiJoin( - relBuilder.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO")), - relBuilder.call(SqlStdOperatorTable.GREATER_THAN, - RexInputRef.of(0, left.getRowType()), - relBuilder.literal(20))) - .project(relBuilder.field(0)) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_CONDITION_PUSH) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkCanNotPushSemiOrAntiJoinConditionsToLeft(JoinRelType.ANTI); } @Test void testCanNotPushAntiJoinConditionsToRight() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select * from emp // where emp.deptno // not in (select dept.deptno from dept where dept.dname = 'ddd') - RelNode relNode = relBuilder.scan("EMP") + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") .scan("DEPT") .antiJoin( - relBuilder.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO")), - relBuilder.equals(relBuilder.field(2, 1, "DNAME"), - relBuilder.literal("ddd"))) - .project(relBuilder.field(0)) + b.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")), + b.equals(b.field(2, 1, "DNAME"), + b.literal("ddd"))) + .project(b.field(0)) .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_CONDITION_PUSH) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn).withRule(CoreRules.JOIN_CONDITION_PUSH).checkUnchanged(); } /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-3171">[CALCITE-3171] * SemiJoin on conditions push down throws IndexOutOfBoundsException</a>. */ @Test void testPushSemiJoinConditionsToLeft() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select * from emp // where emp.deptno // in (select dept.deptno from dept where emp.empno > 20) - RelNode left = relBuilder.scan("EMP").build(); - RelNode right = relBuilder.scan("DEPT").build(); - RelNode relNode = relBuilder.push(left) - .push(right) - .semiJoin( - relBuilder.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO")), - relBuilder.call(SqlStdOperatorTable.GREATER_THAN, - RexInputRef.of(0, left.getRowType()), - relBuilder.literal(20))) - .project(relBuilder.field(0)) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_PUSH_EXPRESSIONS) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkCanNotPushSemiOrAntiJoinConditionsToLeft(JoinRelType.SEMI); + } + + private void checkCanNotPushSemiOrAntiJoinConditionsToLeft(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("EMP").build(); + RelNode right = b.scan("DEPT").build(); + return b.push(left) + .push(right) + .join(type, + b.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")), + b.call(SqlStdOperatorTable.GREATER_THAN, + RexInputRef.of(0, left.getRowType()), + b.literal(20))) + .project(b.field(0)) + .build(); + }; + relFn(relFn).withRule(CoreRules.JOIN_PUSH_EXPRESSIONS).checkUnchanged(); } /** Test case for @@ -515,61 +471,54 @@ class RelOptRulesTest extends RelOptTestBase { * <a href="https://issues.apache.org/jira/browse/CALCITE-3887">[CALCITE-3887] * Filter and Join conditions may not need to retain nullability during simplifications</a>. */ @Test void testPushSemiJoinConditions() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - RelNode left = relBuilder.scan("EMP") - .project( - relBuilder.field("DEPTNO"), - relBuilder.field("ENAME")) - .build(); - RelNode right = relBuilder.scan("DEPT") - .project( - relBuilder.field("DEPTNO"), - relBuilder.field("DNAME")) - .build(); - - relBuilder.push(left).push(right); - - RexInputRef ref1 = relBuilder.field(2, 0, "DEPTNO"); - RexInputRef ref2 = relBuilder.field(2, 1, "DEPTNO"); - RexInputRef ref3 = relBuilder.field(2, 0, "ENAME"); - RexInputRef ref4 = relBuilder.field(2, 1, "DNAME"); - - // ref1 IS NOT DISTINCT FROM ref2 - RexCall cond1 = (RexCall) relBuilder.call( - SqlStdOperatorTable.OR, - relBuilder.call(SqlStdOperatorTable.EQUALS, ref1, ref2), - relBuilder.call(SqlStdOperatorTable.AND, - relBuilder.call(SqlStdOperatorTable.IS_NULL, ref1), - relBuilder.call(SqlStdOperatorTable.IS_NULL, ref2))); - - // ref3 IS NOT DISTINCT FROM ref4 - RexCall cond2 = (RexCall) relBuilder.call( - SqlStdOperatorTable.OR, - relBuilder.call(SqlStdOperatorTable.EQUALS, ref3, ref4), - relBuilder.call(SqlStdOperatorTable.AND, - relBuilder.call(SqlStdOperatorTable.IS_NULL, ref3), - relBuilder.call(SqlStdOperatorTable.IS_NULL, ref4))); - - RexNode cond = relBuilder.call(SqlStdOperatorTable.AND, cond1, cond2); - RelNode relNode = relBuilder.semiJoin(cond) - .project(relBuilder.field(0)) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_PUSH_EXPRESSIONS) - .addRuleInstance(CoreRules.SEMI_JOIN_PROJECT_TRANSPOSE) - .addRuleInstance(CoreRules.JOIN_REDUCE_EXPRESSIONS) - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("EMP") + .project( + b.field("DEPTNO"), + b.field("ENAME")) + .build(); + RelNode right = b.scan("DEPT") + .project( + b.field("DEPTNO"), + b.field("DNAME")) + .build(); + + b.push(left).push(right); + + RexInputRef ref1 = b.field(2, 0, "DEPTNO"); + RexInputRef ref2 = b.field(2, 1, "DEPTNO"); + RexInputRef ref3 = b.field(2, 0, "ENAME"); + RexInputRef ref4 = b.field(2, 1, "DNAME"); + + // ref1 IS NOT DISTINCT FROM ref2 + RexCall cond1 = (RexCall) b.call( + SqlStdOperatorTable.OR, + b.call(SqlStdOperatorTable.EQUALS, ref1, ref2), + b.call(SqlStdOperatorTable.AND, + b.call(SqlStdOperatorTable.IS_NULL, ref1), + b.call(SqlStdOperatorTable.IS_NULL, ref2))); + + // ref3 IS NOT DISTINCT FROM ref4 + RexCall cond2 = (RexCall) b.call( + SqlStdOperatorTable.OR, + b.call(SqlStdOperatorTable.EQUALS, ref3, ref4), + b.call(SqlStdOperatorTable.AND, + b.call(SqlStdOperatorTable.IS_NULL, ref3), + b.call(SqlStdOperatorTable.IS_NULL, ref4))); + + RexNode cond = b.call(SqlStdOperatorTable.AND, cond1, cond2); + return b.semiJoin(cond) + .project(b.field(0)) + .build(); + }; - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn) + .withRule( + CoreRules.JOIN_PUSH_EXPRESSIONS, + CoreRules.SEMI_JOIN_PROJECT_TRANSPOSE, + CoreRules.JOIN_REDUCE_EXPRESSIONS, + CoreRules.FILTER_REDUCE_EXPRESSIONS) + .check(); } @Test void testFullOuterJoinSimplificationToLeftOuter() { @@ -619,78 +568,43 @@ class RelOptRulesTest extends RelOptTestBase { * <a href="https://issues.apache.org/jira/browse/CALCITE-3225">[CALCITE-3225] * JoinToMultiJoinRule should not match SEMI/ANTI LogicalJoin</a>. */ @Test void testJoinToMultiJoinDoesNotMatchSemiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select * from // (select * from emp join dept ON emp.deptno = emp.deptno) t // where emp.job in (select job from bonus) - RelNode left = relBuilder.scan("EMP").build(); - RelNode right = relBuilder.scan("DEPT").build(); - RelNode semiRight = relBuilder.scan("BONUS").build(); - RelNode relNode = relBuilder.push(left) - .push(right) - .join(JoinRelType.INNER, - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .push(semiRight) - .semiJoin( - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "JOB"), - relBuilder.field(2, 1, "JOB"))) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_TO_MULTI_JOIN) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkJoinToMultiJoinDoesNotMatchSemiOrAntiJoin(JoinRelType.SEMI); } /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-3225">[CALCITE-3225] * JoinToMultiJoinRule should not match SEMI/ANTI LogicalJoin</a>. */ @Test void testJoinToMultiJoinDoesNotMatchAntiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select * from // (select * from emp join dept ON emp.deptno = emp.deptno) t // where not exists (select job from bonus where emp.job = bonus.job) - RelNode left = relBuilder.scan("EMP").build(); - RelNode right = relBuilder.scan("DEPT").build(); - RelNode antiRight = relBuilder.scan("BONUS").build(); - RelNode relNode = relBuilder.push(left) - .push(right) - .join(JoinRelType.INNER, - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .push(antiRight) - .antiJoin( - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "JOB"), - relBuilder.field(2, 1, "JOB"))) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_TO_MULTI_JOIN) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkJoinToMultiJoinDoesNotMatchSemiOrAntiJoin(JoinRelType.ANTI); + } + + private void checkJoinToMultiJoinDoesNotMatchSemiOrAntiJoin(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("EMP").build(); + RelNode right = b.scan("DEPT").build(); + RelNode semiRight = b.scan("BONUS").build(); + return b.push(left) + .push(right) + .join(JoinRelType.INNER, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .push(semiRight) + .join(type, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "JOB"), + b.field(2, 1, "JOB"))) + .build(); + }; + relFn(relFn).withRule(CoreRules.JOIN_TO_MULTI_JOIN).check(); } @Test void testPushFilterPastAgg() { @@ -802,116 +716,60 @@ class RelOptRulesTest extends RelOptTestBase { .withDescription("FilterJoinRule:no-filter") .as(FilterJoinRule.JoinConditionPushRule.Config.class) .toRule(); - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - - RelNode left = relBuilder.scan("DEPT").build(); - RelNode right = relBuilder.scan("EMP").build(); - RelNode plan = relBuilder.push(left) - .push(right) - .semiJoin( - relBuilder.and( - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, 0), - relBuilder.field(2, 1, 7)), - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 1, 5), - relBuilder.literal(100)))) - .project(relBuilder.field(1)) - .build(); - - final String planBefore = NL + RelOptUtil.toString(plan); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(join) - .build(); - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(plan); - RelNode output = hepPlanner.findBestExp(); + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("DEPT").build(); + RelNode right = b.scan("EMP").build(); + return b.push(left) + .push(right) + .semiJoin( + b.and( + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, 0), + b.field(2, 1, 7)), + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 1, 5), + b.literal(100)))) + .project(b.field(1)) + .build(); + }; - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn).withRule(join).check(); } @Test void testSemiJoinProjectTranspose() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select a.name from dept a // where a.deptno in (select b.deptno * 2 from dept); - - RelNode left = relBuilder.scan("DEPT").build(); - RelNode right = relBuilder.scan("DEPT") - .project( - relBuilder.call( - SqlStdOperatorTable.MULTIPLY, relBuilder.literal(2), relBuilder.field(0))) - .aggregate(relBuilder.groupKey(ImmutableBitSet.of(0))).build(); - - RelNode plan = relBuilder.push(left) - .push(right) - .semiJoin( - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, 0), - relBuilder.field(2, 1, 0))) - .project(relBuilder.field(1)) - .build(); - - final String planBefore = NL + RelOptUtil.toString(plan); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.PROJECT_JOIN_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(plan); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkSemiOrAntiJoinProjectTranspose(JoinRelType.SEMI); } @Test void testAntiJoinProjectTranspose() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select a.name from dept a // where a.deptno not in (select b.deptno * 2 from dept); - - RelNode left = relBuilder.scan("DEPT").build(); - RelNode right = relBuilder.scan("DEPT") - .project( - relBuilder.call( - SqlStdOperatorTable.MULTIPLY, relBuilder.literal(2), relBuilder.field(0))) - .aggregate(relBuilder.groupKey(ImmutableBitSet.of(0))).build(); - - RelNode plan = relBuilder.push(left) - .push(right) - .antiJoin( - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, 0), - relBuilder.field(2, 1, 0))) - .project(relBuilder.field(1)) - .build(); - - final String planBefore = NL + RelOptUtil.toString(plan); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.PROJECT_JOIN_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(plan); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkSemiOrAntiJoinProjectTranspose(JoinRelType.ANTI); + } + + private void checkSemiOrAntiJoinProjectTranspose(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("DEPT").build(); + RelNode right = b.scan("DEPT") + .project( + b.call( + SqlStdOperatorTable.MULTIPLY, b.literal(2), b.field(0))) + .aggregate(b.groupKey(ImmutableBitSet.of(0))).build(); + + return b.push(left) + .push(right) + .join(type, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, 0), + b.field(2, 1, 0))) + .project(b.field(1)) + .build(); + }; + relFn(relFn).withRule(CoreRules.PROJECT_JOIN_TRANSPOSE).check(); } @Test void testJoinProjectTranspose1() { @@ -1712,79 +1570,38 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testProjectCorrelateTransposeRuleSemiCorrelate() { - RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - RelNode left = relBuilder - .values(new String[]{"f", "f2"}, "1", "2").build(); - - CorrelationId correlationId = new CorrelationId(0); - RexNode rexCorrel = - relBuilder.getRexBuilder().makeCorrel( - left.getRowType(), - correlationId); - - RelNode right = relBuilder - .values(new String[]{"f3", "f4"}, "1", "2") - .project(relBuilder.field(0), - relBuilder.getRexBuilder() - .makeFieldAccess(rexCorrel, 0)) - .build(); - LogicalCorrelate correlate = new LogicalCorrelate(left.getCluster(), - left.getTraitSet(), left, right, correlationId, - ImmutableBitSet.of(0), JoinRelType.SEMI); - - relBuilder.push(correlate); - RelNode relNode = relBuilder.project(relBuilder.field(0)) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.PROJECT_CORRELATE_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkProjectCorrelateTransposeRuleSemiOrAntiCorrelate(JoinRelType.SEMI); } @Test void testProjectCorrelateTransposeRuleAntiCorrelate() { - RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - RelNode left = relBuilder - .values(new String[]{"f", "f2"}, "1", "2").build(); - - CorrelationId correlationId = new CorrelationId(0); - RexNode rexCorrel = - relBuilder.getRexBuilder().makeCorrel( - left.getRowType(), - correlationId); - - RelNode right = relBuilder - .values(new String[]{"f3", "f4"}, "1", "2") - .project(relBuilder.field(0), - relBuilder.getRexBuilder().makeFieldAccess(rexCorrel, 0)).build(); - LogicalCorrelate correlate = new LogicalCorrelate(left.getCluster(), - left.getTraitSet(), left, right, correlationId, - ImmutableBitSet.of(0), JoinRelType.ANTI); - - relBuilder.push(correlate); - RelNode relNode = relBuilder.project(relBuilder.field(0)) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.PROJECT_CORRELATE_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); + checkProjectCorrelateTransposeRuleSemiOrAntiCorrelate(JoinRelType.ANTI); + } + + private void checkProjectCorrelateTransposeRuleSemiOrAntiCorrelate(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b + .values(new String[]{"f", "f2"}, "1", "2").build(); + + CorrelationId correlationId = new CorrelationId(0); + RexNode rexCorrel = + b.getRexBuilder().makeCorrel( + left.getRowType(), + correlationId); + + RelNode right = b + .values(new String[]{"f3", "f4"}, "1", "2") + .project(b.field(0), + b.getRexBuilder().makeFieldAccess(rexCorrel, 0)).build(); + LogicalCorrelate correlate = new LogicalCorrelate(left.getCluster(), + left.getTraitSet(), left, right, correlationId, + ImmutableBitSet.of(0), type); + + b.push(correlate); + return b.project(b.field(0)) + .build(); + }; - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn).withRule(CoreRules.PROJECT_CORRELATE_TRANSPOSE).check(); } @Test void testProjectCorrelateTransposeWithExprCond() { @@ -2030,97 +1847,52 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testPushJoinThroughUnionOnRightDoesNotMatchSemiJoin() { - final RelBuilder builder = RelBuilder.create(RelBuilderTest.config().build()); - // build a rel equivalent to sql: // select r1.sal from // emp r1 where r1.deptno in - // (select deptno from dept d1 where deptno > 100 + // (select deptno from dept d1 where deptno < 10 // union all // select deptno from dept d2 where deptno > 20) - RelNode left = builder.scan("EMP").build(); - RelNode right = builder - .scan("DEPT") - .filter( - builder.call(SqlStdOperatorTable.GREATER_THAN, - builder.field("DEPTNO"), - builder.literal(100))) - .project(builder.field("DEPTNO")) - .scan("DEPT") - .filter( - builder.call(SqlStdOperatorTable.GREATER_THAN, - builder.field("DEPTNO"), - builder.literal(20))) - .project(builder.field("DEPTNO")) - .union(true) - .build(); - RelNode relNode = builder.push(left).push(right) - .semiJoin( - builder.call(SqlStdOperatorTable.EQUALS, - builder.field(2, 0, "DEPTNO"), - builder.field(2, 1, "DEPTNO"))) - .project(builder.field("SAL")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_RIGHT_UNION_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelType.SEMI); } @Test void testPushJoinThroughUnionOnRightDoesNotMatchAntiJoin() { - final RelBuilder builder = RelBuilder.create(RelBuilderTest.config().build()); - // build a rel equivalent to sql: // select r1.sal from // emp r1 where r1.deptno not in // (select deptno from dept d1 where deptno < 10 // union all // select deptno from dept d2 where deptno > 20) - RelNode left = builder.scan("EMP").build(); - RelNode right = builder - .scan("DEPT") - .filter( - builder.call(SqlStdOperatorTable.LESS_THAN, - builder.field("DEPTNO"), - builder.literal(10))) - .project(builder.field("DEPTNO")) - .scan("DEPT") - .filter( - builder.call(SqlStdOperatorTable.GREATER_THAN, - builder.field("DEPTNO"), - builder.literal(20))) - .project(builder.field("DEPTNO")) - .union(true) - .build(); - RelNode relNode = builder.push(left).push(right) - .antiJoin( - builder.call(SqlStdOperatorTable.EQUALS, - builder.field(2, 0, "DEPTNO"), - builder.field(2, 1, "DEPTNO"))) - .project(builder.field("SAL")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.JOIN_RIGHT_UNION_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelType.ANTI); + } + + private void checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode left = b.scan("EMP").build(); + RelNode right = b + .scan("DEPT") + .filter( + b.call(SqlStdOperatorTable.LESS_THAN, + b.field("DEPTNO"), + b.literal(10))) + .project(b.field("DEPTNO")) + .scan("DEPT") + .filter( + b.call(SqlStdOperatorTable.GREATER_THAN, + b.field("DEPTNO"), + b.literal(20))) + .project(b.field("DEPTNO")) + .union(true) + .build(); + return b.push(left).push(right) + .join(type, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .project(b.field("SAL")) + .build(); + }; + relFn(relFn).withRule(CoreRules.JOIN_RIGHT_UNION_TRANSPOSE).checkUnchanged(); } @Test void testMergeFilterWithJoinCondition() { @@ -3008,8 +2780,6 @@ class RelOptRulesTest extends RelOptTestBase { * Constant reducer must not duplicate calls to non-deterministic * functions</a>. */ @Test void testReduceConstantsNonDeterministicFunction() { - final DiffRepository diffRepos = getDiffRepos(); - final SqlOperator nonDeterministicOp = new SqlSpecialOperator("NDC", SqlKind.OTHER_FUNCTION, 0, false, ReturnTypes.INTEGER, null, null) { @@ -3022,34 +2792,18 @@ class RelOptRulesTest extends RelOptTestBase { // SELECT sal, n // FROM (SELECT sal, NDC() AS n FROM emp) // WHERE n > 10 - final RelBuilder builder = - RelBuilder.create(RelBuilderTest.config().build()); - final RelNode root = - builder.scan("EMP") - .project(builder.field("SAL"), - builder.alias(builder.call(nonDeterministicOp), "N")) + final Function<RelBuilder, RelNode> relFn = b -> + b.scan("EMP") + .project(b.field("SAL"), + b.alias(b.call(nonDeterministicOp), "N")) .filter( - builder.call(SqlStdOperatorTable.GREATER_THAN, - builder.field("N"), builder.literal(10))) + b.call(SqlStdOperatorTable.GREATER_THAN, + b.field("N"), b.literal(10))) .build(); - HepProgram preProgram = new HepProgramBuilder().build(); - HepPlanner prePlanner = new HepPlanner(preProgram); - prePlanner.setRoot(root); - final RelNode relBefore = prePlanner.findBestExp(); - final String planBefore = NL + RelOptUtil.toString(relBefore); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - - final HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(CoreRules.PROJECT_REDUCE_EXPRESSIONS) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(root); - final RelNode relAfter = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(relAfter); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + relFn(relFn) + .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, CoreRules.PROJECT_REDUCE_EXPRESSIONS) + .checkUnchanged(); } /** Checks that constant reducer duplicates calls to dynamic functions, if @@ -3206,12 +2960,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from (\n" + "select * from emp where false) as e\n" + "join dept as d on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testLeftEmptyLeftJoin() { @@ -3219,12 +2968,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from (\n" + " select * from emp where false) e\n" + "left join dept d on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testLeftEmptyRightJoin() { @@ -3233,12 +2977,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from (\n" + " select * from emp where false) e\n" + "right join dept d on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testLeftEmptyFullJoin() { @@ -3247,74 +2986,28 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from (\n" + " select * from emp where false) e\n" + "full join dept d on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testLeftEmptySemiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder - .scan("EMP").empty() - .scan("DEPT") - .semiJoin(relBuilder - .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - // Plan should be empty - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + checkLeftEmptySemiOrAntiJoin(JoinRelType.SEMI); } @Test void testLeftEmptyAntiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder + checkLeftEmptySemiOrAntiJoin(JoinRelType.ANTI); + } + + private void checkLeftEmptySemiOrAntiJoin(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> b .scan("EMP").empty() .scan("DEPT") - .antiJoin(relBuilder + .join(type, b .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE) + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .project(b.field("EMPNO")) .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - // Plan should be empty - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + checkEmptyJoin(relFn(relFn)); } @Test void testRightEmptyInnerJoin() { @@ -3322,12 +3015,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from emp e\n" + "join (select * from dept where false) as d\n" + "on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testRightEmptyLeftJoin() { @@ -3336,12 +3024,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from emp e\n" + "left join (select * from dept where false) as d\n" + "on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testRightEmptyRightJoin() { @@ -3349,12 +3032,7 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from emp e\n" + "right join (select * from dept where false) as d\n" + "on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testRightEmptyFullJoin() { @@ -3363,109 +3041,53 @@ class RelOptRulesTest extends RelOptTestBase { final String sql = "select * from emp e\n" + "full join (select * from dept where false) as d\n" + "on e.deptno = d.deptno"; - sql(sql) - .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS, - PruneEmptyRules.PROJECT_INSTANCE, - PruneEmptyRules.JOIN_LEFT_INSTANCE, - PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .check(); + checkEmptyJoin(sql(sql)); } @Test void testRightEmptySemiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder - .scan("EMP") - .scan("DEPT").empty() - .semiJoin(relBuilder - .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - // Plan should be empty - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + checkRightEmptyAntiJoin(JoinRelType.SEMI); } @Test void testRightEmptyAntiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder + checkRightEmptyAntiJoin(JoinRelType.ANTI); + } + + private void checkRightEmptyAntiJoin(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> b .scan("EMP") .scan("DEPT").empty() - .antiJoin(relBuilder + .join(type, b .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .project(b.field("EMPNO")) .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE) - .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - // Plan should be scan("EMP") (i.e. join's left child) - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + checkEmptyJoin(relFn(relFn)); } @Test void testRightEmptyAntiJoinNonEqui() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder + final Function<RelBuilder, RelNode> relFn = b -> b .scan("EMP") .scan("DEPT").empty() - .antiJoin(relBuilder + .antiJoin(b .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO")), - relBuilder + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")), + b .equals( - relBuilder.field(2, 0, "SAL"), - relBuilder.literal(2000))) - .project(relBuilder.field("EMPNO")) - .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_REDUCE_EXPRESSIONS) - .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE) - .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE) + b.field(2, 0, "SAL"), + b.literal(2000))) + .project(b.field("EMPNO")) .build(); + checkEmptyJoin(relFn(relFn)); + } - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - // Plan should be scan("EMP") (i.e. join's left child) - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + private void checkEmptyJoin(RelOptTestBase.Sql sql) { + sql.withRule( + CoreRules.FILTER_REDUCE_EXPRESSIONS, + PruneEmptyRules.PROJECT_INSTANCE, + PruneEmptyRules.JOIN_LEFT_INSTANCE, + PruneEmptyRules.JOIN_RIGHT_INSTANCE).check(); } @Test void testEmptySort() { @@ -3478,27 +3100,13 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testEmptySort2() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode relNode = relBuilder + final Function<RelBuilder, RelNode> relFn = b -> b .scan("DEPT").empty() .sort( - relBuilder.field("DNAME"), - relBuilder.field("DEPTNO")) - .build(); - - final HepProgram program = new HepProgramBuilder() - .addRuleInstance(PruneEmptyRules.SORT_INSTANCE) + b.field("DNAME"), + b.field("DEPTNO")) .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = NL + RelOptUtil.toString(relNode); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + relFn(relFn).withRule(PruneEmptyRules.SORT_INSTANCE).check(); } @Test void testEmptySortLimitZero() { @@ -3603,48 +3211,44 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testReduceCaseWhenWithCast() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RexBuilder rexBuilder = relBuilder.getRexBuilder(); - final RelDataType type = rexBuilder.getTypeFactory().createSqlType(SqlTypeName.BIGINT); - - RelNode left = relBuilder - .values(new String[]{"x", "y"}, 1, 2).build(); - RexNode ref = rexBuilder.makeInputRef(left, 0); - RexLiteral literal1 = rexBuilder.makeLiteral(1, type); - RexLiteral literal2 = rexBuilder.makeLiteral(2, type); - RexLiteral literal3 = rexBuilder.makeLiteral(3, type); - - // CASE WHEN x % 2 = 1 THEN x < 2 - // WHEN x % 3 = 2 THEN x < 1 - // ELSE x < 3 - final RexNode caseRexNode = rexBuilder.makeCall(SqlStdOperatorTable.CASE, - rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, - rexBuilder.makeCall(SqlStdOperatorTable.MOD, ref, literal2), literal1), - rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal2), - rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, - rexBuilder.makeCall(SqlStdOperatorTable.MOD, ref, literal3), literal2), - rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal1), - rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal3)); - - final RexNode castNode = rexBuilder.makeCast(rexBuilder.getTypeFactory(). - createTypeWithNullability(caseRexNode.getType(), true), caseRexNode); - final RelNode root = relBuilder - .push(left) - .project(castNode) - .build(); + final Function<RelBuilder, RelNode> relFn = b -> { + final RexBuilder rexBuilder = b.getRexBuilder(); + final RelDataType type = rexBuilder.getTypeFactory().createSqlType(SqlTypeName.BIGINT); + + RelNode left = b + .values(new String[]{"x", "y"}, 1, 2).build(); + RexNode ref = rexBuilder.makeInputRef(left, 0); + RexLiteral literal1 = rexBuilder.makeLiteral(1, type); + RexLiteral literal2 = rexBuilder.makeLiteral(2, type); + RexLiteral literal3 = rexBuilder.makeLiteral(3, type); + + // CASE WHEN x % 2 = 1 THEN x < 2 + // WHEN x % 3 = 2 THEN x < 1 + // ELSE x < 3 + final RexNode caseRexNode = rexBuilder.makeCall(SqlStdOperatorTable.CASE, + rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, + rexBuilder.makeCall(SqlStdOperatorTable.MOD, ref, literal2), literal1), + rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal2), + rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, + rexBuilder.makeCall(SqlStdOperatorTable.MOD, ref, literal3), literal2), + rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal1), + rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, ref, literal3)); + + final RexNode castNode = rexBuilder.makeCast(rexBuilder.getTypeFactory(). + createTypeWithNullability(caseRexNode.getType(), true), caseRexNode); + return b + .push(left) + .project(castNode) + .build(); + }; HepProgramBuilder builder = new HepProgramBuilder(); builder.addRuleClass(ReduceExpressionsRule.class); HepPlanner hepPlanner = new HepPlanner(builder.build()); hepPlanner.addRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS); - hepPlanner.setRoot(root); - RelNode output = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn).with(hepPlanner).checkUnchanged(); } private void basePushAggThroughUnion() { @@ -5514,88 +5118,56 @@ class RelOptRulesTest extends RelOptTestBase { * <a href="https://issues.apache.org/jira/browse/CALCITE-4042">[CALCITE-4042] * JoinCommuteRule must not match SEMI / ANTI join</a>. */ @Test void testSwapSemiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode input = relBuilder - .scan("EMP") - .scan("DEPT") - .semiJoin(relBuilder - .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) - .build(); - testSwapJoinShouldNotMatch(input); + checkSwapJoinShouldNotMatch(JoinRelType.SEMI); } /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-4042">[CALCITE-4042] * JoinCommuteRule must not match SEMI / ANTI join</a>. */ @Test void testSwapAntiJoin() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode input = relBuilder - .scan("EMP") - .scan("DEPT") - .antiJoin(relBuilder - .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("EMPNO")) - .build(); - testSwapJoinShouldNotMatch(input); + checkSwapJoinShouldNotMatch(JoinRelType.ANTI); } - private void testSwapJoinShouldNotMatch(RelNode input) { - final HepProgram program = new HepProgramBuilder() - .addMatchLimit(1) - .addRuleInstance(CoreRules.JOIN_COMMUTE_OUTER) + private void checkSwapJoinShouldNotMatch(JoinRelType type) { + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") + .scan("DEPT") + .join(type, + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .project(b.field("EMPNO")) .build(); - - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(input); - final RelNode output = hepPlanner.findBestExp(); - - final String planBefore = RelOptUtil.toString(input); - final String planAfter = RelOptUtil.toString(output); - assertEquals(planBefore, planAfter); + relFn(relFn).withRule(CoreRules.JOIN_COMMUTE_OUTER).checkUnchanged(); } /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-4621">[CALCITE-4621] * SemiJoinRule throws AssertionError on ANTI join</a>. */ @Test void testJoinToSemiJoinRuleOnAntiJoin() { - testSemiJoinRuleOnAntiJoin(CoreRules.JOIN_TO_SEMI_JOIN); + checkSemiJoinRuleOnAntiJoin(CoreRules.JOIN_TO_SEMI_JOIN); } /** Test case for * <a href="https://issues.apache.org/jira/browse/CALCITE-4621">[CALCITE-4621] * SemiJoinRule throws AssertionError on ANTI join</a>. */ @Test void testProjectToSemiJoinRuleOnAntiJoin() { - testSemiJoinRuleOnAntiJoin(CoreRules.PROJECT_TO_SEMI_JOIN); + checkSemiJoinRuleOnAntiJoin(CoreRules.PROJECT_TO_SEMI_JOIN); } - private void testSemiJoinRuleOnAntiJoin(RelOptRule rule) { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - final RelNode input = relBuilder + private void checkSemiJoinRuleOnAntiJoin(RelOptRule rule) { + final Function<RelBuilder, RelNode> relFn = b -> b .scan("DEPT") .scan("EMP") - .project(relBuilder.field("DEPTNO")) + .project(b.field("DEPTNO")) .distinct() - .antiJoin(relBuilder - .equals( - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .project(relBuilder.field("DNAME")) - .build(); - - final HepProgram program = new HepProgramBuilder() - .addRuleInstance(rule) + .antiJoin( + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .project(b.field("DNAME")) .build(); - final HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(input); - final RelNode output = hepPlanner.findBestExp(); - final String planBefore = RelOptUtil.toString(input); - final String planAfter = RelOptUtil.toString(output); - assertEquals(planBefore, planAfter); + relFn(relFn).withRule(rule).checkUnchanged(); } @Test void testPushJoinCondDownToProject() { @@ -6069,30 +5641,18 @@ class RelOptRulesTest extends RelOptTestBase { * <a href="https://issues.apache.org/jira/browse/CALCITE-3188">[CALCITE-3188] * IndexOutOfBoundsException in ProjectFilterTransposeRule when executing SELECT COUNT(*)</a>. */ @Test void testProjectFilterTransposeRuleOnEmptyRowType() { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); // build a rel equivalent to sql: // select `empty` from emp // where emp.deptno = 20 - RelNode relNode = relBuilder.scan("EMP") - .filter(relBuilder + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") + .filter(b .equals( - relBuilder.field(1, 0, "DEPTNO"), - relBuilder.literal(20))) + b.field(1, 0, "DEPTNO"), + b.literal(20))) .project(ImmutableList.of()) .build(); - - HepProgram program = new HepProgramBuilder() - .addRuleInstance(CoreRules.PROJECT_FILTER_TRANSPOSE) - .build(); - - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + relFn(relFn).withRule(CoreRules.PROJECT_FILTER_TRANSPOSE).check(); } @Test void testFlattenUncorrelatedCallBelowEquals() { @@ -6370,31 +5930,13 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testFilterRemoveIsNotDistinctFromRule() { - final DiffRepository diffRepos = getDiffRepos(); - final RelBuilder builder = RelBuilder.create(RelBuilderTest.config().build()); - RelNode root = builder + final Function<RelBuilder, RelNode> relFn = b -> b .scan("EMP") .filter( - builder.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, - builder.field("DEPTNO"), builder.literal(20))) - .build(); - - HepProgram preProgram = new HepProgramBuilder().build(); - HepPlanner prePlanner = new HepPlanner(preProgram); - prePlanner.setRoot(root); - final RelNode relBefore = prePlanner.findBestExp(); - final String planBefore = NL + RelOptUtil.toString(relBefore); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - - HepProgram hepProgram = new HepProgramBuilder() - .addRuleInstance(CoreRules.FILTER_EXPAND_IS_NOT_DISTINCT_FROM) + b.call(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, + b.field("DEPTNO"), b.literal(20))) .build(); - - HepPlanner hepPlanner = new HepPlanner(hepProgram); - hepPlanner.setRoot(root); - final RelNode relAfter = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(relAfter); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + relFn(relFn).withRule(CoreRules.FILTER_EXPAND_IS_NOT_DISTINCT_FROM).check(); } /** Creates an environment for testing spatial queries. */ @@ -6499,36 +6041,27 @@ class RelOptRulesTest extends RelOptTestBase { } @Test void testExchangeRemoveConstantKeysRule() { - final DiffRepository diffRepos = getDiffRepos(); - final RelBuilder builder = RelBuilder.create(RelBuilderTest.config().build()); - RelNode root = builder + final Function<RelBuilder, RelNode> relFn = b -> b .scan("EMP") .filter( - builder.call(SqlStdOperatorTable.EQUALS, - builder.field("EMPNO"), builder.literal(10))) + b.call( + SqlStdOperatorTable.EQUALS, + b.field("EMPNO"), + b.literal(10))) .exchange(RelDistributions.hash(ImmutableList.of(0))) - .project(builder.field(0), builder.field(1)) - .sortExchange(RelDistributions.hash(ImmutableList.of(0, 1)), - RelCollations.of(new RelFieldCollation(0), new RelFieldCollation(1))) - .build(); - - HepProgram preProgram = new HepProgramBuilder().build(); - HepPlanner prePlanner = new HepPlanner(preProgram); - prePlanner.setRoot(root); - final RelNode relBefore = prePlanner.findBestExp(); - final String planBefore = NL + RelOptUtil.toString(relBefore); - diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - - HepProgram hepProgram = new HepProgramBuilder() - .addRuleInstance(CoreRules.EXCHANGE_REMOVE_CONSTANT_KEYS) - .addRuleInstance(CoreRules.SORT_EXCHANGE_REMOVE_CONSTANT_KEYS) + .project( + b.field(0), + b.field(1)) + .sortExchange( + RelDistributions.hash(ImmutableList.of(0, 1)), + RelCollations.of(new RelFieldCollation(0), new RelFieldCollation(1))) .build(); - HepPlanner hepPlanner = new HepPlanner(hepProgram); - hepPlanner.setRoot(root); - final RelNode relAfter = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(relAfter); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); + relFn(relFn) + .withRule( + CoreRules.EXCHANGE_REMOVE_CONSTANT_KEYS, + CoreRules.SORT_EXCHANGE_REMOVE_CONSTANT_KEYS) + .check(); } @Test void testReduceAverageWithNoReduceSum() { @@ -6755,7 +6288,7 @@ class RelOptRulesTest extends RelOptTestBase { Tester tester = createTester().withDecorrelation(true) .withClusterFactory(cluster -> RelOptCluster.create(planner, cluster.getRexBuilder())); return new Sql(tester, sql, null, planner, - ImmutableMap.of(), ImmutableList.of()); + ImmutableMap.of(), ImmutableList.of(), null); } /** @@ -7119,15 +6652,11 @@ class RelOptRulesTest extends RelOptTestBase { } private void checkJoinCommuteRuleWithAlwaysTrueConditionDisallowed(boolean allowAlwaysTrue) { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - - RelNode left = relBuilder.scan("EMP").build(); - RelNode right = relBuilder.scan("DEPT").build(); - - RelNode relNode = relBuilder.push(left) - .push(right) + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") + .scan("DEPT") .join(JoinRelType.INNER, - relBuilder.literal(true)) + b.literal(true)) .build(); JoinCommuteRule.Config ruleConfig = JoinCommuteRule.Config.DEFAULT; @@ -7139,15 +6668,13 @@ class RelOptRulesTest extends RelOptTestBase { .addMatchLimit(1) .addRuleInstance(ruleConfig.toRule()) .build(); - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + if (allowAlwaysTrue) { + relFn(relFn).with(hepPlanner).check(); + } else { + relFn(relFn).with(hepPlanner).checkUnchanged(); + } } @Test void testJoinAssociateRuleWithBottomAlwaysTrueConditionAllowed() { @@ -7159,24 +6686,24 @@ class RelOptRulesTest extends RelOptTestBase { } private void checkJoinAssociateRuleWithBottomAlwaysTrueCondition(boolean allowAlwaysTrue) { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - - RelNode bottomLeft = relBuilder.scan("EMP").build(); - RelNode bottomRight = relBuilder.scan("DEPT").build(); - RelNode top = relBuilder.scan("BONUS").build(); - - RelNode relNode = relBuilder.push(bottomLeft) - .push(bottomRight) - .join(JoinRelType.INNER, - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .push(top) - .join(JoinRelType.INNER, - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "JOB"), - relBuilder.field(2, 1, "JOB"))) - .build(); + final Function<RelBuilder, RelNode> relFn = b -> { + RelNode bottomLeft = b.scan("EMP").build(); + RelNode bottomRight = b.scan("DEPT").build(); + RelNode top = b.scan("BONUS").build(); + + return b.push(bottomLeft) + .push(bottomRight) + .join(JoinRelType.INNER, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .push(top) + .join(JoinRelType.INNER, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "JOB"), + b.field(2, 1, "JOB"))) + .build(); + }; JoinAssociateRule.Config ruleConfig = JoinAssociateRule.Config.DEFAULT; if (!allowAlwaysTrue) { @@ -7188,15 +6715,13 @@ class RelOptRulesTest extends RelOptTestBase { .addMatchOrder(HepMatchOrder.TOP_DOWN) .addRuleInstance(ruleConfig.toRule()) .build(); - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + if (allowAlwaysTrue) { + relFn(relFn).with(hepPlanner).check(); + } else { + relFn(relFn).with(hepPlanner).checkUnchanged(); + } } @Test void testJoinAssociateRuleWithTopAlwaysTrueConditionAllowed() { @@ -7208,22 +6733,23 @@ class RelOptRulesTest extends RelOptTestBase { } private void checkJoinAssociateRuleWithTopAlwaysTrueCondition(boolean allowAlwaysTrue) { - final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); - - RelNode bottomLeft = relBuilder.scan("EMP").build(); - RelNode bottomRight = relBuilder.scan("BONUS").build(); - RelNode top = relBuilder.scan("DEPT").build(); - - RelNode relNode = relBuilder.push(bottomLeft) - .push(bottomRight) - .join(JoinRelType.INNER, - relBuilder.literal(true)) - .push(top) - .join(JoinRelType.INNER, - relBuilder.call(SqlStdOperatorTable.EQUALS, - relBuilder.field(2, 0, "DEPTNO"), - relBuilder.field(2, 1, "DEPTNO"))) - .build(); + final Function<RelBuilder, RelNode> relFn = b -> { + + RelNode bottomLeft = b.scan("EMP").build(); + RelNode bottomRight = b.scan("BONUS").build(); + RelNode top = b.scan("DEPT").build(); + + return b.push(bottomLeft) + .push(bottomRight) + .join(JoinRelType.INNER, + b.literal(true)) + .push(top) + .join(JoinRelType.INNER, + b.call(SqlStdOperatorTable.EQUALS, + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO"))) + .build(); + }; JoinAssociateRule.Config ruleConfig = JoinAssociateRule.Config.DEFAULT; if (!allowAlwaysTrue) { @@ -7235,14 +6761,12 @@ class RelOptRulesTest extends RelOptTestBase { .addMatchOrder(HepMatchOrder.TOP_DOWN) .addRuleInstance(ruleConfig.toRule()) .build(); - HepPlanner hepPlanner = new HepPlanner(program); - hepPlanner.setRoot(relNode); - RelNode output = hepPlanner.findBestExp(); - final String planAfter = NL + RelOptUtil.toString(output); - final DiffRepository diffRepos = getDiffRepos(); - diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); - SqlToRelTestBase.assertValid(output); + if (allowAlwaysTrue) { + relFn(relFn).with(hepPlanner).check(); + } else { + relFn(relFn).with(hepPlanner).checkUnchanged(); + } } } diff --git a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java index 52bbfaa..5d04585 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java @@ -44,6 +44,8 @@ import org.apache.calcite.util.Closer; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -73,6 +75,22 @@ abstract class RelOptTestBase extends SqlToRelTestBase { } /** + * Checks the plan for a given {@link RelNode} supplier before/after executing a given rule, + * with a pre-program to prepare the tree. + * + * @param tester Tester + * @param preProgram Program to execute before comparing before state + * @param planner Planner + * @param relFn {@link RelNode} supplier + * @param unchanged Whether the rule is to have no effect + */ + private void checkPlanning(Tester tester, HepProgram preProgram, + RelOptPlanner planner, Function<RelBuilder, RelNode> relFn, boolean unchanged) { + RelNode relInitial = relFn.apply(RelBuilder.create(RelBuilderTest.config().build())); + checkPlanning(tester, preProgram, planner, relInitial, unchanged); + } + + /** * Checks the plan for a SQL statement before/after executing a given rule, * with a pre-program to prepare the tree. * @@ -88,9 +106,13 @@ abstract class RelOptTestBase extends SqlToRelTestBase { String sql2 = diffRepos.expand("sql", sql); final RelRoot root = tester.convertSqlToRel(sql2); final RelNode relInitial = root.rel; + checkPlanning(tester, preProgram, planner, relInitial, unchanged); + } + private void checkPlanning(Tester tester, HepProgram preProgram, + RelOptPlanner planner, RelNode relInitial, boolean unchanged) { assertNotNull(relInitial); - + final DiffRepository diffRepos = getDiffRepos(); List<RelMetadataProvider> list = new ArrayList<>(); list.add(DefaultRelMetadataProvider.INSTANCE); planner.registerMetadataProviders(list); @@ -144,10 +166,15 @@ abstract class RelOptTestBase extends SqlToRelTestBase { /** Sets the SQL statement for a test. */ Sql sql(String sql) { final Sql s = - new Sql(tester, sql, null, null, ImmutableMap.of(), ImmutableList.of()); + new Sql(tester, sql, null, null, ImmutableMap.of(), ImmutableList.of(), null); return s.withRelBuilderConfig(b -> b.withPruneInputOfAggregate(false)); } + /** Initiates a test case with a given {@link RelNode} supplier. */ + Sql relFn(Function<RelBuilder, RelNode> relFn) { + return sql("?").relFn(relFn); + } + /** Allows fluent testing. */ class Sql { private final Tester tester; @@ -156,10 +183,12 @@ abstract class RelOptTestBase extends SqlToRelTestBase { private final RelOptPlanner planner; private final ImmutableMap<Hook, Consumer> hooks; private ImmutableList<Function<Tester, Tester>> transforms; + private final @Nullable Function<RelBuilder, RelNode> relFn; Sql(Tester tester, String sql, HepProgram preProgram, RelOptPlanner planner, ImmutableMap<Hook, Consumer> hooks, - ImmutableList<Function<Tester, Tester>> transforms) { + ImmutableList<Function<Tester, Tester>> transforms, + @Nullable Function<RelBuilder, RelNode> relFn) { this.tester = Objects.requireNonNull(tester, "tester"); this.sql = Objects.requireNonNull(sql, "sql"); if (sql.contains(" \n")) { @@ -169,15 +198,20 @@ abstract class RelOptTestBase extends SqlToRelTestBase { this.planner = planner; this.hooks = Objects.requireNonNull(hooks, "hooks"); this.transforms = Objects.requireNonNull(transforms, "transforms"); + this.relFn = relFn; + } + + Sql relFn(Function<RelBuilder, RelNode> relFn) { + return new Sql(tester, sql, null, null, ImmutableMap.of(), ImmutableList.of(), relFn); } public Sql withTester(UnaryOperator<Tester> transform) { final Tester tester2 = transform.apply(tester); - return new Sql(tester2, sql, preProgram, planner, hooks, transforms); + return new Sql(tester2, sql, preProgram, planner, hooks, transforms, relFn); } public Sql withPre(HepProgram preProgram) { - return new Sql(tester, sql, preProgram, planner, hooks, transforms); + return new Sql(tester, sql, preProgram, planner, hooks, transforms, relFn); } public Sql withPreRule(RelOptRule... rules) { @@ -189,12 +223,12 @@ abstract class RelOptTestBase extends SqlToRelTestBase { } public Sql with(HepPlanner hepPlanner) { - return new Sql(tester, sql, preProgram, hepPlanner, hooks, transforms); + return new Sql(tester, sql, preProgram, hepPlanner, hooks, transforms, relFn); } public Sql with(HepProgram program) { final HepPlanner hepPlanner = new HepPlanner(program); - return new Sql(tester, sql, preProgram, hepPlanner, hooks, transforms); + return new Sql(tester, sql, preProgram, hepPlanner, hooks, transforms, relFn); } public Sql withRule(RelOptRule... rules) { @@ -210,7 +244,7 @@ abstract class RelOptTestBase extends SqlToRelTestBase { private Sql withTransform(Function<Tester, Tester> transform) { final ImmutableList<Function<Tester, Tester>> transforms = FlatLists.append(this.transforms, transform); - return new Sql(tester, sql, preProgram, planner, hooks, transforms); + return new Sql(tester, sql, preProgram, planner, hooks, transforms, relFn); } /** Adds a hook and a handler for that hook. Calcite will create a thread @@ -219,7 +253,7 @@ abstract class RelOptTestBase extends SqlToRelTestBase { public <T> Sql withHook(Hook hook, Consumer<T> handler) { final ImmutableMap<Hook, Consumer> hooks = FlatLists.append(this.hooks, hook, handler); - return new Sql(tester, sql, preProgram, planner, hooks, transforms); + return new Sql(tester, sql, preProgram, planner, hooks, transforms, relFn); } // CHECKSTYLE: IGNORE 1 @@ -300,7 +334,11 @@ abstract class RelOptTestBase extends SqlToRelTestBase { for (Function<Tester, Tester> transform : transforms) { t = transform.apply(t); } - checkPlanning(t, preProgram, planner, sql, unchanged); + if (relFn != null) { + checkPlanning(t, preProgram, planner, relFn, unchanged); + } else { + checkPlanning(t, preProgram, planner, sql, unchanged); + } } } } diff --git a/core/src/test/java/org/apache/calcite/test/TopDownOptTest.java b/core/src/test/java/org/apache/calcite/test/TopDownOptTest.java index c88ac59..c1e006b 100644 --- a/core/src/test/java/org/apache/calcite/test/TopDownOptTest.java +++ b/core/src/test/java/org/apache/calcite/test/TopDownOptTest.java @@ -820,7 +820,7 @@ class Query extends RelOptTestBase { final Sql sql = new Sql(tester, this.sql, null, planner, ImmutableMap.of(), - ImmutableList.of()); + ImmutableList.of(), null); sql.check(); } } 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 1a131c1..c60297b 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -1265,7 +1265,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$ where emp.deptno not in (select dept.deptno from dept where emp.deptno > 20)]]> </Resource> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalProject(EMPNO=[$0]) LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($7, $8), >($0, 20))], joinType=[anti]) @@ -1280,7 +1280,7 @@ LogicalProject(EMPNO=[$0]) where emp.deptno not in (select dept.deptno from dept where dept.dname = 'ddd')]]> </Resource> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalProject(EMPNO=[$0]) LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($7, $8), =($9, 'ddd'))], joinType=[anti]) @@ -4078,6 +4078,15 @@ LogicalProject(EMPNO=[$0], DEPTNO=[$1], W_COUNT=[$2]) </Resource> </TestCase> <TestCase name="testJoinAssociateRuleWithBottomAlwaysTrueConditionAllowed"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($2, $12)], joinType=[inner]) + LogicalJoin(condition=[=($7, $8)], joinType=[inner]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalJoin(condition=[AND(=($2, $12), =($7, $8))], joinType=[inner]) @@ -4089,7 +4098,7 @@ LogicalJoin(condition=[AND(=($2, $12), =($7, $8))], joinType=[inner]) </Resource> </TestCase> <TestCase name="testJoinAssociateRuleWithBottomAlwaysTrueConditionDisallowed"> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalJoin(condition=[=($2, $12)], joinType=[inner]) LogicalJoin(condition=[=($7, $8)], joinType=[inner]) @@ -4100,6 +4109,15 @@ LogicalJoin(condition=[=($2, $12)], joinType=[inner]) </Resource> </TestCase> <TestCase name="testJoinAssociateRuleWithTopAlwaysTrueConditionAllowed"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($7, $12)], joinType=[inner]) + LogicalJoin(condition=[true], joinType=[inner]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, BONUS]]) + LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalJoin(condition=[=($7, $12)], joinType=[inner]) @@ -4111,7 +4129,7 @@ LogicalJoin(condition=[=($7, $12)], joinType=[inner]) </Resource> </TestCase> <TestCase name="testJoinAssociateRuleWithTopAlwaysTrueConditionDisallowed"> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalJoin(condition=[=($7, $12)], joinType=[inner]) LogicalJoin(condition=[true], joinType=[inner]) @@ -4122,6 +4140,13 @@ LogicalJoin(condition=[=($7, $12)], joinType=[inner]) </Resource> </TestCase> <TestCase name="testJoinCommuteRuleWithAlwaysTrueConditionAllowed"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[true], joinType=[inner]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalProject(EMPNO=[$3], ENAME=[$4], JOB=[$5], MGR=[$6], HIREDATE=[$7], SAL=[$8], COMM=[$9], DEPTNO=[$10], DEPTNO0=[$0], DNAME=[$1], LOC=[$2]) @@ -4132,7 +4157,7 @@ LogicalProject(EMPNO=[$3], ENAME=[$4], JOB=[$5], MGR=[$6], HIREDATE=[$7], SAL=[$ </Resource> </TestCase> <TestCase name="testJoinCommuteRuleWithAlwaysTrueConditionDisallowed"> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalJoin(condition=[true], joinType=[inner]) LogicalTableScan(table=[[scott, EMP]]) @@ -4468,6 +4493,15 @@ LogicalProject(SAL=[$5]) (select * from emp join dept ON emp.deptno = emp.deptno) t where not exists (select job from bonus where emp.job = bonus.job)]]> </Resource> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($2, $12)], joinType=[anti]) + LogicalJoin(condition=[=($7, $8)], joinType=[inner]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalJoin(condition=[=($2, $12)], joinType=[anti]) @@ -4484,6 +4518,15 @@ LogicalJoin(condition=[=($2, $12)], joinType=[anti]) (select * from emp join dept ON emp.deptno = emp.deptno) t where emp.job in (select job from bonus))]]> </Resource> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($2, $12)], joinType=[semi]) + LogicalJoin(condition=[=($7, $8)], joinType=[inner]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalJoin(condition=[=($2, $12)], joinType=[semi]) @@ -4494,6 +4537,18 @@ LogicalJoin(condition=[=($2, $12)], joinType=[semi]) ]]> </Resource> </TestCase> + <TestCase name="testJoinToSemiJoinRuleOnAntiJoin"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(DNAME=[$1]) + LogicalJoin(condition=[=($0, $3)], joinType=[anti]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalAggregate(group=[{0}]) + LogicalProject(DEPTNO=[$7]) + LogicalTableScan(table=[[scott, EMP]]) +]]> + </Resource> + </TestCase> <TestCase name="testLeftEmptyAntiJoin"> <Resource name="planBefore"> <![CDATA[ @@ -5583,6 +5638,15 @@ LogicalProject(C_NATIONKEY=[$1], FAKE_COL2=[$2]) </Resource> </TestCase> <TestCase name="testProjectCorrelateTransposeRuleAntiCorrelate"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(f=[$0]) + LogicalCorrelate(correlation=[$cor0], joinType=[anti], requiredColumns=[{0}]) + LogicalValues(tuples=[[{ '1', '2' }]]) + LogicalProject(f3=[$0], $f1=[$cor0.f]) + LogicalValues(tuples=[[{ '1', '2' }]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalCorrelate(correlation=[$cor0], joinType=[anti], requiredColumns=[{0}]) @@ -5629,6 +5693,15 @@ FROM emp e1 where exists (select empno, deptno from dept d2 where e1.deptno = d2 </Resource> </TestCase> <TestCase name="testProjectCorrelateTransposeRuleSemiCorrelate"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(f=[$0]) + LogicalCorrelate(correlation=[$cor0], joinType=[semi], requiredColumns=[{0}]) + LogicalValues(tuples=[[{ '1', '2' }]]) + LogicalProject(f3=[$0], $f1=[$cor0.f]) + LogicalValues(tuples=[[{ '1', '2' }]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalCorrelate(correlation=[$cor0], joinType=[semi], requiredColumns=[{0}]) @@ -5700,6 +5773,13 @@ LogicalProject(EXPR$0=[SUM($2) OVER (PARTITION BY $0)], EXPR$1=[COUNT($3) OVER ( </Resource> </TestCase> <TestCase name="testProjectFilterTransposeRuleOnEmptyRowType"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject + LogicalFilter(condition=[=($7, 20)]) + LogicalTableScan(table=[[scott, EMP]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalProject @@ -6053,6 +6133,18 @@ LogicalProject(JOB=[$0], EXPR$1=[SUM($2) OVER (PARTITION BY $1)]) ]]> </Resource> </TestCase> + <TestCase name="testProjectToSemiJoinRuleOnAntiJoin"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(DNAME=[$1]) + LogicalJoin(condition=[=($0, $3)], joinType=[anti]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalAggregate(group=[{0}]) + LogicalProject(DEPTNO=[$7]) + LogicalTableScan(table=[[scott, EMP]]) +]]> + </Resource> + </TestCase> <TestCase name="testProjectToWindowRuleForMultipleWindows"> <Resource name="sql"> <![CDATA[select @@ -8339,20 +8431,6 @@ LogicalProject(SAL=[$5]) LogicalTableScan(table=[[scott, DEPT]]) ]]> </Resource> - <Resource name="planAfter"> - <![CDATA[ -LogicalProject(SAL=[$5]) - LogicalJoin(condition=[=($7, $8)], joinType=[anti]) - LogicalTableScan(table=[[scott, EMP]]) - LogicalUnion(all=[true]) - LogicalProject(DEPTNO=[$0]) - LogicalFilter(condition=[<($0, 10)]) - LogicalTableScan(table=[[scott, DEPT]]) - LogicalProject(DEPTNO=[$0]) - LogicalFilter(condition=[>($0, 20)]) - LogicalTableScan(table=[[scott, DEPT]]) -]]> - </Resource> </TestCase> <TestCase name="testPushJoinThroughUnionOnRightDoesNotMatchSemiJoin"> <Resource name="sql"> @@ -8365,21 +8443,7 @@ LogicalProject(SAL=[$5]) LogicalTableScan(table=[[scott, EMP]]) LogicalUnion(all=[true]) LogicalProject(DEPTNO=[$0]) - LogicalFilter(condition=[>($0, 100)]) - LogicalTableScan(table=[[scott, DEPT]]) - LogicalProject(DEPTNO=[$0]) - LogicalFilter(condition=[>($0, 20)]) - LogicalTableScan(table=[[scott, DEPT]]) -]]> - </Resource> - <Resource name="planAfter"> - <![CDATA[ -LogicalProject(SAL=[$5]) - LogicalJoin(condition=[=($7, $8)], joinType=[semi]) - LogicalTableScan(table=[[scott, EMP]]) - LogicalUnion(all=[true]) - LogicalProject(DEPTNO=[$0]) - LogicalFilter(condition=[>($0, 100)]) + LogicalFilter(condition=[<($0, 10)]) LogicalTableScan(table=[[scott, DEPT]]) LogicalProject(DEPTNO=[$0]) LogicalFilter(condition=[>($0, 20)]) @@ -9134,6 +9198,16 @@ LogicalProject(EXPR$0=[+($1, $5)], EXPR$1=[SUM($6) OVER (PARTITION BY $4)]) </Resource> </TestCase> <TestCase name="testPushSemiJoinConditions"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(DEPTNO=[$0]) + LogicalJoin(condition=[AND(OR(=($0, $2), AND(IS NULL($0), IS NULL($2))), OR(=($1, $3), AND(IS NULL($1), IS NULL($3))))], joinType=[semi]) + LogicalProject(DEPTNO=[$7], ENAME=[$1]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalProject(DEPTNO=[$0], DNAME=[$1]) + LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> <Resource name="planAfter"> <![CDATA[ LogicalProject(DEPTNO=[$0]) @@ -9151,7 +9225,7 @@ LogicalProject(DEPTNO=[$0]) where emp.deptno in (select dept.deptno from dept where emp.empno > 20)]]> </Resource> - <Resource name="planAfter"> + <Resource name="planBefore"> <![CDATA[ LogicalProject(EMPNO=[$0]) LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($7, $8), >($0, 20))], joinType=[semi]) @@ -9710,12 +9784,6 @@ LogicalProject($f0=[CAST(CASE(=(MOD($0, 2:BIGINT), 1), <($0, 2:BIGINT), =(MOD($0 LogicalValues(tuples=[[{ 1, 2 }]]) ]]> </Resource> - <Resource name="planAfter"> - <![CDATA[ -LogicalProject($f0=[CAST(CASE(=(MOD($0, 2:BIGINT), 1), <($0, 2:BIGINT), =(MOD($0, 3:BIGINT), 2), <($0, 1:BIGINT), <($0, 3:BIGINT))):BOOLEAN]) - LogicalValues(tuples=[[{ 1, 2 }]]) -]]> - </Resource> </TestCase> <TestCase name="testReduceCastAndConsts"> <Resource name="sql"> @@ -10291,13 +10359,6 @@ LogicalFilter(condition=[>($1, 10)]) LogicalTableScan(table=[[scott, EMP]]) ]]> </Resource> - <Resource name="planAfter"> - <![CDATA[ -LogicalFilter(condition=[>($1, 10)]) - LogicalProject(SAL=[$5], N=[NDC()]) - LogicalTableScan(table=[[scott, EMP]]) -]]> - </Resource> </TestCase> <TestCase name="testReduceConstantsNull"> <Resource name="sql"> @@ -12211,6 +12272,16 @@ EnumerableProject(N_REGIONKEY=[ITEM($0, 'N_REGIONKEY')]) ]]> </Resource> </TestCase> + <TestCase name="testSwapAntiJoin"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(EMPNO=[$0]) + LogicalJoin(condition=[=($7, $8)], joinType=[anti]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> + </TestCase> <TestCase name="testSwapOuterJoin"> <Resource name="sql"> <![CDATA[select 1 from sales.dept d left outer join sales.emp e @@ -12257,6 +12328,16 @@ LogicalProject(NAME=[$10], ENAME=[$1]) ]]> </Resource> </TestCase> + <TestCase name="testSwapSemiJoin"> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(EMPNO=[$0]) + LogicalJoin(condition=[=($7, $8)], joinType=[semi]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> + </TestCase> <TestCase name="testTransitiveInferenceAggregate"> <Resource name="sql"> <![CDATA[select 1 from (select deptno, count(*) from sales.emp where deptno > 7