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

zhenchen 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 0febc55f17 [CALCITE-7089] Implement a rule for converting a RIGHT JOIN 
to a LEFT JOIN
0febc55f17 is described below

commit 0febc55f1773dd4221f501e97caa7ef8a6211045
Author: liuyuhanalex <[email protected]>
AuthorDate: Tue Aug 5 11:25:31 2025 +0800

    [CALCITE-7089] Implement a rule for converting a RIGHT JOIN to a LEFT JOIN
---
 .../org/apache/calcite/rel/rules/CoreRules.java    |  5 +++++
 .../apache/calcite/rel/rules/JoinCommuteRule.java  | 15 ++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.java   | 12 +++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 23 ++++++++++++++++++++++
 4 files changed, 55 insertions(+)

diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java 
b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
index ac8c8729b4..1e222b639e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
@@ -620,6 +620,11 @@ private CoreRules() {}
   public static final JoinCommuteRule JOIN_COMMUTE_OUTER =
       JoinCommuteRule.Config.SWAP_OUTER.toRule();
 
+  /** As {@link #JOIN_COMMUTE} but only swaps RIGHT joins to LEFT joins. */
+  @RuleConfig(value = "RIGHT_TO_LEFT_ONLY")
+  public static final JoinCommuteRule JOIN_COMMUTE_RIGHT_TO_LEFT =
+      JoinCommuteRule.Config.RIGHT_TO_LEFT_ONLY.toRule();
+
   /** Rule to convert an
    * {@link LogicalJoin inner join} to a
    * {@link LogicalFilter filter} on top of a
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
index c2c06f0675..012a00eb83 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
@@ -150,6 +150,11 @@ public static RexNode swapJoinCond(RexNode cond, Join 
join, RexBuilder rexBuilde
       return false;
     }
 
+    // If rightToLeftOnly is enabled, only allow RIGHT joins to be swapped
+    if (config.isRightToLeftOnly() && join.getJoinType() != JoinRelType.RIGHT) 
{
+      return false;
+    }
+
     // Suppress join with "true" condition (that is, cartesian joins).
     return config.isAllowAlwaysTrueCondition()
         || !join.getCondition().isAlwaysTrue();
@@ -241,6 +246,8 @@ public interface Config extends RelRule.Config {
 
     Config SWAP_OUTER = DEFAULT.withSwapOuter(true);
 
+    Config RIGHT_TO_LEFT_ONLY = 
DEFAULT.withRightToLeftOnly(true).withSwapOuter(true);
+
     @Override default JoinCommuteRule toRule() {
       return new JoinCommuteRule(this);
     }
@@ -273,5 +280,13 @@ default Config withOperandFor(Class<? extends Join> 
joinClass) {
 
     /** Sets {@link #isAllowAlwaysTrueCondition()}. */
     Config withAllowAlwaysTrueCondition(boolean allowAlwaysTrueCondition);
+
+    /** If true, only RIGHT JOIN will be swapped; default false. */
+    @Value.Default default boolean isRightToLeftOnly() {
+      return false;
+    }
+
+    /** Sets {@link #isRightToLeftOnly()}. */
+    Config withRightToLeftOnly(boolean isRightToLeftOnly);
   }
 }
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 a48bf6907a..df8789414d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -7914,6 +7914,18 @@ private HepProgram getTransitiveProgram() {
     sql(sql).withProgram(program).check();
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7089";>[CALCITE-7089]
+   * Implement a rule for converting a RIGHT JOIN to a LEFT JOIN</a>. */
+  @Test void testSwapRightToLeftOnly() {
+    final HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(CoreRules.JOIN_COMMUTE_RIGHT_TO_LEFT)
+        .build();
+    final String sql = "select 1 from sales.dept d right outer join sales.emp 
e\n"
+        + " on d.deptno = e.deptno";
+    sql(sql).withProgram(program).check();
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4042";>[CALCITE-4042]
    * JoinCommuteRule must not match SEMI / ANTI join</a>. */
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 03278394ab..0e1f49959e 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -20515,6 +20515,29 @@ LogicalAggregate(group=[{0, 1}])
     <Resource name="planAfter">
       <![CDATA[
 LogicalValues(tuples=[[{ 1, 'a' }, { 2, 'b' }, { 1, 'b' }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testSwapRightToLeftOnly">
+    <Resource name="sql">
+      <![CDATA[select 1 from sales.dept d right outer join sales.emp e
+ on d.deptno = e.deptno]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EXPR$0=[1])
+  LogicalJoin(condition=[=($0, $9)], joinType=[right])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalProject(EXPR$0=[1])
+  LogicalProject(DEPTNO=[$9], NAME=[$10], EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO0=[$7], SLACKER=[$8])
+    LogicalJoin(condition=[=($9, $7)], joinType=[left])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to