This is an automated email from the ASF dual-hosted git repository.

zzwqqq 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 b577854562 [CALCITE-7833] MULTI_JOIN_OPTIMIZE_BUSHY may turn an outer 
join into an inner join
b577854562 is described below

commit b5778545628b7e0809a67b1d82a3458a1e447d96
Author: Dongsheng He <[email protected]>
AuthorDate: Wed Sep 30 21:10:57 2026 +0800

    [CALCITE-7833] MULTI_JOIN_OPTIMIZE_BUSHY may turn an outer join into an 
inner join
---
 .../rel/rules/MultiJoinOptimizeBushyRule.java      | 15 +++++--------
 .../org/apache/calcite/test/RelOptRulesTest.java   | 18 +++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 26 ++++++++++++++++++++++
 3 files changed, 49 insertions(+), 10 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
index 6336b997e7..ca89260cbe 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
@@ -104,16 +104,11 @@ public 
MultiJoinOptimizeBushyRule(RelFactories.JoinFactory joinFactory,
     final RelMetadataQuery mq = call.getMetadataQuery();
 
     final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
-    for (int i = 0; i < multiJoin.getNumJoinFactors(); i++) {
-      ImmutableBitSet outerJoinFactors = multiJoin.getOuterJoinFactors(i);
-      if (outerJoinFactors == null) {
-        continue;
-      }
-      if (!outerJoinFactors.isEmpty()) {
-        // Refuse to apply this rule to a multijoin with outer joins,
-        // since this rule cannot handle outer joins.
-        return;
-      }
+
+    if (multiJoinRel.isFullOuterJoin() || multiJoinRel.containsOuter()) {
+      // Refuse to apply this rule to a multijoin with outer joins,
+      // since this rule cannot handle outer joins.
+      return;
     }
 
     final List<Vertex> vertexes = new ArrayList<>();
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 a7945606bc..ef9d2ffbb3 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -4656,6 +4656,24 @@ private void 
checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelT
     sql(sql).withPre(preProgram).withProgram(program).check();
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7833";>[CALCITE-7833]
+   * MULTI_JOIN_OPTIMIZE_BUSHY may turn an outer join into an inner join</a>. 
*/
+  @Test void testBushyJoinRuleSkipsFullOuterJoin() {
+    sql("select * from emp full join dept on emp.deptno = dept.deptno")
+        .withPreRule(CoreRules.JOIN_TO_MULTI_JOIN)
+        .withRule(CoreRules.MULTI_JOIN_OPTIMIZE_BUSHY)
+        .checkUnchanged();
+  }
+
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7833";>[CALCITE-7833]
+   * MULTI_JOIN_OPTIMIZE_BUSHY may turn an outer join into an inner join</a>. 
*/
+  @Test void testBushyJoinRuleSkipsLeftJoinOnTrue() {
+    sql("select * from emp left join dept on true")
+        .withPreRule(CoreRules.JOIN_TO_MULTI_JOIN)
+        .withRule(CoreRules.MULTI_JOIN_OPTIMIZE_BUSHY)
+        .checkUnchanged();
+  }
+
   @Test void testConvertMultiJoinRule() {
     final String sql = "select e1.ename from emp e1, dept d, emp e2\n"
         + "where e1.deptno = d.deptno and d.deptno = e2.deptno";
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 97e54f5c7c..e626fe7018 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -1894,6 +1894,32 @@ LogicalProject(ENAME=[$1])
   MultiJoin(joinFilter=[true], isFullOuterJoin=[false], joinTypes=[[INNER, 
LEFT]], outerJoinConditions=[[NULL, =($1, $10)]], projFields=[[ALL, ALL]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testBushyJoinRuleSkipsFullOuterJoin">
+    <Resource name="sql">
+      <![CDATA[select * from emp full join dept on emp.deptno = dept.deptno]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  MultiJoin(joinFilter=[=($7, $9)], isFullOuterJoin=[true], joinTypes=[[INNER, 
INNER]], outerJoinConditions=[[NULL, NULL]], projFields=[[ALL, ALL]])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testBushyJoinRuleSkipsLeftJoinOnTrue">
+    <Resource name="sql">
+      <![CDATA[select * from emp left join dept on true]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+  MultiJoin(joinFilter=[true], isFullOuterJoin=[false], joinTypes=[[INNER, 
LEFT]], outerJoinConditions=[[NULL, true]], projFields=[[ALL, ALL]])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to