This is an automated email from the ASF dual-hosted git repository. rubenql pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push: new d667123585 [CALCITE-5952] SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates into its inputs d667123585 is described below commit d667123585bf518edd6a9bf93e23c1785fe03376 Author: Leonid Chistov <leonid.chis...@neuroblade.com> AuthorDate: Mon Sep 4 18:33:56 2023 +0300 [CALCITE-5952] SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates into its inputs --- .../rel/rules/SemiJoinJoinTransposeRule.java | 12 +++ .../calcite/runtime/RelOptRulesRuntimeTest.java | 97 ++++++++++++++++++++++ .../org/apache/calcite/test/RelOptRulesTest.java | 92 ++++++++++++++++++++ .../org/apache/calcite/test/RelOptRulesTest.xml | 62 ++++++++++++++ 4 files changed, 263 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java index d314b2819a..f523209893 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java @@ -114,6 +114,18 @@ public class SemiJoinJoinTransposeRule return; } + // join type needs to allow pushing predicate (represented as semi-join in our case) + // to the corresponding input + if (nKeysFromX > 0) { + if (!join.getJoinType().canPushLeftFromAbove()) { + return; + } + } else { + if (!join.getJoinType().canPushRightFromAbove()) { + return; + } + } + // need to convert the semi-join condition and possibly the keys final RexNode newSemiJoinFilter; int[] adjustments = new int[nTotalFields]; diff --git a/core/src/test/java/org/apache/calcite/runtime/RelOptRulesRuntimeTest.java b/core/src/test/java/org/apache/calcite/runtime/RelOptRulesRuntimeTest.java new file mode 100644 index 0000000000..7faa2385eb --- /dev/null +++ b/core/src/test/java/org/apache/calcite/runtime/RelOptRulesRuntimeTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.runtime; + +import org.apache.calcite.adapter.java.ReflectiveSchema; +import org.apache.calcite.config.CalciteConnectionProperty; +import org.apache.calcite.config.Lex; +import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.rules.CoreRules; +import org.apache.calcite.test.CalciteAssert; +import org.apache.calcite.test.schemata.hr.HrSchema; + +import org.junit.jupiter.api.Test; + +import java.util.function.Consumer; + +/** + * Unit tests for rules in {@code org.apache.calcite.rel} and subpackages. + */ +public class RelOptRulesRuntimeTest { + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void semiJoinLeftJoinTransposeTest() { + tester(true, new HrSchema()) + .withRel( + builder -> builder.scan("s", "depts") + .scan("s", "emps") + .join(JoinRelType.LEFT, + builder.equals( + builder.field(2, 0, "deptno"), + builder.field(2, 1, "deptno")) + ) + .scan("s", "dependents") + .semiJoin( + builder.equals( + builder.field(2, 0, "empid"), + builder.field(2, 1, "empid"))) + .build() + ) + .withHook(Hook.PLANNER, (Consumer<RelOptPlanner>) planner -> + planner.addRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE) + ) + .returnsUnordered(); + } + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void semiJoinRightJoinTransposeTest() { + tester(true, new HrSchema()) + .withRel( + builder -> builder.scan("s", "emps") + .scan("s", "depts") + .join(JoinRelType.RIGHT, + builder.equals( + builder.field(2, 0, "deptno"), + builder.field(2, 1, "deptno")) + ) + .scan("s", "dependents") + .semiJoin( + builder.equals( + builder.field(2, 0, "empid"), + builder.field(2, 1, "empid"))) + .build() + ) + .withHook(Hook.PLANNER, (Consumer<RelOptPlanner>) planner -> + planner.addRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE) + ) + .returnsUnordered(); + } + + private CalciteAssert.AssertThat tester(boolean forceDecorrelate, Object schema) { + return CalciteAssert.that() + .with(CalciteConnectionProperty.LEX, Lex.JAVA) + .with(CalciteConnectionProperty.FORCE_DECORRELATE, forceDecorrelate) + .withSchema("s", new ReflectiveSchema(schema)); + } +} 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 a7b4088219..bc129f765a 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -2767,6 +2767,98 @@ class RelOptRulesTest extends RelOptTestBase { .check(); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void testPushSemiJoinToLeftJoinLeftInput() { + // tests the case that semijoin can be pushed to the left input of the left join + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("DEPT") + .scan("EMP") + .join(JoinRelType.LEFT, + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")) + ) + .scan("BONUS") + .semiJoin( + b.equals( + b.field(2, 0, "DNAME"), + b.field(2, 1, "JOB"))) + .build(); + relFn(relFn).withRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE).check(); + } + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void testPushSemiJoinToRightJoinRightInput() { + // tests the case that semijoin can be pushed to the right input of the right join + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") + .scan("DEPT") + .join(JoinRelType.RIGHT, + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")) + ) + .scan("BONUS") + .semiJoin( + b.equals( + b.field(2, 0, "DNAME"), + b.field(2, 1, "JOB"))) + .build(); + relFn(relFn).withRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE).check(); + } + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void testCanNotPushSemiJoinToLeftJoinRightInput() { + // tests the case that semijoin cannot be pushed to the right input of the left join + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("EMP") + .scan("DEPT") + .join(JoinRelType.LEFT, + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")) + ) + .scan("BONUS") + .semiJoin( + b.equals( + b.field(2, 0, "DNAME"), + b.field(2, 1, "JOB"))) + .build(); + relFn(relFn).withRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE).checkUnchanged(); + } + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5952">[CALCITE-5952] + * SemiJoinJoinTransposeRule should check if JoinType supports pushing predicates + * into its inputs</a>. */ + @Test void testCanNotPushSemiJoinToRightJoinLeftInput() { + // tests the case that semijoin cannot be pushed to the left input of the right join + final Function<RelBuilder, RelNode> relFn = b -> b + .scan("DEPT") + .scan("EMP") + .join(JoinRelType.RIGHT, + b.equals( + b.field(2, 0, "DEPTNO"), + b.field(2, 1, "DEPTNO")) + ) + .scan("BONUS") + .semiJoin( + b.equals( + b.field(2, 0, "DNAME"), + b.field(2, 1, "JOB"))) + .build(); + relFn(relFn).withRule(CoreRules.SEMI_JOIN_JOIN_TRANSPOSE).checkUnchanged(); + } + @Test void testPushSemiJoinPastFilter() { final String sql = "select e.ename from emp e, dept d\n" + "where e.deptno = d.deptno and e.ename = 'foo'"; 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 8b965e3c6a..ae52af1a55 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -1370,6 +1370,28 @@ LogicalProject(EMPNO=[$0]) LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($7, $8), =($9, 'ddd'))], joinType=[anti]) LogicalTableScan(table=[[scott, EMP]]) LogicalTableScan(table=[[scott, DEPT]]) +]]> + </Resource> + </TestCase> + <TestCase name="testCanNotPushSemiJoinToLeftJoinRightInput"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($9, $12)], joinType=[semi]) + LogicalJoin(condition=[=($7, $8)], joinType=[left]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> + </TestCase> + <TestCase name="testCanNotPushSemiJoinToRightJoinLeftInput"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($1, $12)], joinType=[semi]) + LogicalJoin(condition=[=($0, $10)], joinType=[right]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, BONUS]]) ]]> </Resource> </TestCase> @@ -10712,6 +10734,46 @@ LogicalProject(ENAME=[$0], EXPR$1=[$1], EXPR$2=[$2], DEPTNO=[$3]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) +]]> + </Resource> + </TestCase> + <TestCase name="testPushSemiJoinToLeftJoinLeftInput"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($1, $12)], joinType=[semi]) + LogicalJoin(condition=[=($0, $10)], joinType=[left]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +LogicalJoin(condition=[=($0, $10)], joinType=[left]) + LogicalJoin(condition=[=($1, $4)], joinType=[semi]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) + LogicalTableScan(table=[[scott, EMP]]) +]]> + </Resource> + </TestCase> + <TestCase name="testPushSemiJoinToRightJoinRightInput"> + <Resource name="planBefore"> + <![CDATA[ +LogicalJoin(condition=[=($9, $12)], joinType=[semi]) + LogicalJoin(condition=[=($7, $8)], joinType=[right]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +LogicalJoin(condition=[=($7, $8)], joinType=[right]) + LogicalTableScan(table=[[scott, EMP]]) + LogicalJoin(condition=[=($1, $4)], joinType=[semi]) + LogicalTableScan(table=[[scott, DEPT]]) + LogicalTableScan(table=[[scott, BONUS]]) ]]> </Resource> </TestCase>