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>

Reply via email to