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 <[email protected]>
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>