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>