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

starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new b9f23d64cb7 [fix](fe) Move SemiJoinCommute to rewrite phase (#66182)
b9f23d64cb7 is described below

commit b9f23d64cb7f419c2bfc24dca841f8a0f452264b
Author: seawinde <[email protected]>
AuthorDate: Thu Aug 6 10:12:31 2026 +0800

    [fix](fe) Move SemiJoinCommute to rewrite phase (#66182)
---
 .../doris/nereids/jobs/executor/Analyzer.java      |  2 -
 .../doris/nereids/jobs/executor/Rewriter.java      |  3 ++
 .../rules/analysis/CollectJoinConstraint.java      | 31 +++++++-----
 .../nereids/rules/rewrite/ReorderJoinTest.java     | 56 +++++++++++++++++++++-
 .../doris/nereids/sqltest/SqlPlanSuiteTest.java    | 10 ++--
 regression-test/data/query_p0/hint/fix_leading.out |  6 +++
 .../suites/query_p0/hint/fix_leading.groovy        | 20 ++++++++
 7 files changed, 109 insertions(+), 19 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Analyzer.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Analyzer.java
index 0400e50c792..eb354d11a47 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Analyzer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Analyzer.java
@@ -52,7 +52,6 @@ import 
org.apache.doris.nereids.rules.analysis.SubqueryToApply;
 import org.apache.doris.nereids.rules.analysis.VariableToLiteral;
 import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
 import org.apache.doris.nereids.rules.rewrite.MergeFilters;
-import org.apache.doris.nereids.rules.rewrite.SemiJoinCommute;
 import org.apache.doris.nereids.rules.rewrite.SimplifyAggGroupBy;
 import org.apache.doris.nereids.trees.plans.logical.LogicalCTEAnchor;
 import org.apache.doris.nereids.trees.plans.logical.LogicalView;
@@ -199,7 +198,6 @@ public class Analyzer extends AbstractBatchJobExecutor {
             topDown(new NormalizeAggregate()),
             topDown(new HavingToFilter()),
             topDown(new QualifyToFilter()),
-            bottomUp(new SemiJoinCommute()),
             bottomUp(
                     new CollectSubQueryAlias(),
                     new CollectJoinConstraint()
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index 549d299692e..e651a9e8fae 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -160,6 +160,7 @@ import 
org.apache.doris.nereids.rules.rewrite.RewriteCteChildren;
 import org.apache.doris.nereids.rules.rewrite.RewriteSearchToSlots;
 import org.apache.doris.nereids.rules.rewrite.RewriteSimpleAggToConstantRule;
 import org.apache.doris.nereids.rules.rewrite.SaltJoin;
+import org.apache.doris.nereids.rules.rewrite.SemiJoinCommute;
 import org.apache.doris.nereids.rules.rewrite.SetPreAggStatus;
 import org.apache.doris.nereids.rules.rewrite.SimplifyEncodeDecode;
 import org.apache.doris.nereids.rules.rewrite.SimplifyWindowExpression;
@@ -335,6 +336,7 @@ public class Rewriter extends AbstractBatchJobExecutor {
                                     ),
                                     // push down SEMI Join
                                     bottomUp(
+                                            new SemiJoinCommute(),
                                             new TransposeSemiJoinLogicalJoin(),
                                             new 
TransposeSemiJoinLogicalJoinProject(),
                                             new TransposeSemiJoinAgg(),
@@ -575,6 +577,7 @@ public class Rewriter extends AbstractBatchJobExecutor {
                                 ),
                                 // push down SEMI Join
                                 bottomUp(
+                                        new SemiJoinCommute(),
                                         new TransposeSemiJoinLogicalJoin(),
                                         new 
TransposeSemiJoinLogicalJoinProject(),
                                         new TransposeSemiJoinAgg(),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CollectJoinConstraint.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CollectJoinConstraint.java
index b8b6d8adfab..eacdd3fdd41 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CollectJoinConstraint.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CollectJoinConstraint.java
@@ -61,6 +61,14 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
                 Long leftHand = 
LongBitmap.computeTableBitmap(join.left().getInputRelations());
                 Long rightHand = 
LongBitmap.computeTableBitmap(join.right().getInputRelations());
                 join.setBitmap(LongBitmap.or(leftHand, rightHand));
+                JoinType joinType = join.getJoinType();
+                if (joinType.isRightJoin()) {
+                    // LEADING constraints model the preserved/output side as 
the left child.
+                    Long originalLeftHand = leftHand;
+                    leftHand = rightHand;
+                    rightHand = originalLeftHand;
+                    joinType = joinType.swap();
+                }
                 List<Expression> expressions = join.getHashJoinConjuncts();
                 Long totalFilterBitMap = 0L;
                 Long nonNullableSlotBitMap = 0L;
@@ -69,11 +77,11 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
                     nonNullableSlotBitMap = 
LongBitmap.or(nonNullableSlotBitMap, nonNullable);
                     Long filterBitMap = calSlotsTableBitMap(leading, 
expression.getInputSlots(), false);
                     totalFilterBitMap = LongBitmap.or(totalFilterBitMap, 
filterBitMap);
-                    if (join.getJoinType().isLeftJoin()) {
+                    if (joinType.isLeftJoin()) {
                         filterBitMap = LongBitmap.or(filterBitMap, rightHand);
                     }
                     leading.getFilters().add(Pair.of(filterBitMap, 
expression));
-                    leading.putConditionJoinType(expression, 
join.getJoinType());
+                    leading.putConditionJoinType(expression, joinType);
                 }
                 expressions = join.getOtherJoinConjuncts();
                 for (Expression expression : expressions) {
@@ -81,13 +89,14 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
                     nonNullableSlotBitMap = 
LongBitmap.or(nonNullableSlotBitMap, nonNullable);
                     Long filterBitMap = calSlotsTableBitMap(leading, 
expression.getInputSlots(), false);
                     totalFilterBitMap = LongBitmap.or(totalFilterBitMap, 
filterBitMap);
-                    if (join.getJoinType().isLeftJoin()) {
+                    if (joinType.isLeftJoin()) {
                         filterBitMap = LongBitmap.or(filterBitMap, rightHand);
                     }
                     leading.getFilters().add(Pair.of(filterBitMap, 
expression));
-                    leading.putConditionJoinType(expression, 
join.getJoinType());
+                    leading.putConditionJoinType(expression, joinType);
                 }
-                collectJoinConstraintList(leading, leftHand, rightHand, join, 
totalFilterBitMap, nonNullableSlotBitMap);
+                collectJoinConstraintList(
+                        leading, leftHand, rightHand, joinType, 
totalFilterBitMap, nonNullableSlotBitMap);
 
                 return ctx.root;
             }).toRule(RuleType.COLLECT_JOIN_CONSTRAINT),
@@ -108,14 +117,14 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
         );
     }
 
-    private void collectJoinConstraintList(LeadingHint leading, Long leftHand, 
Long rightHand, LogicalJoin join,
+    private void collectJoinConstraintList(LeadingHint leading, Long leftHand, 
Long rightHand, JoinType joinType,
                                             Long filterTableBitMap, Long 
nonNullableSlotBitMap) {
         Long totalTables = LongBitmap.or(leftHand, rightHand);
-        if (join.getJoinType().isInnerOrCrossJoin()) {
+        if (joinType.isInnerOrCrossJoin()) {
             
leading.setInnerJoinBitmap(LongBitmap.or(leading.getInnerJoinBitmap(), 
totalTables));
             return;
         }
-        if (join.getJoinType().isFullOuterJoin()) {
+        if (joinType.isFullOuterJoin()) {
             JoinConstraint newJoinConstraint = new JoinConstraint(leftHand, 
rightHand, leftHand, rightHand,
                     JoinType.FULL_OUTER_JOIN, false);
             leading.getJoinConstraintList().add(newJoinConstraint);
@@ -148,7 +157,7 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
 
             if (LongBitmap.isOverlap(leftHand, other.getRightHand())) {
                 if (LongBitmap.isOverlap(filterTableBitMap, 
other.getRightHand())
-                        && (join.getJoinType().isSemiOrAntiJoin()
+                        && (joinType.isSemiOrAntiJoin()
                         || !LongBitmap.isOverlap(nonNullableSlotBitMap, 
other.getMinRightHand()))) {
                     minLeftHand = LongBitmap.or(minLeftHand,
                         other.getLeftHand());
@@ -160,7 +169,7 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
             if (LongBitmap.isOverlap(rightHand, other.getRightHand())) {
                 if (LongBitmap.isOverlap(filterTableBitMap, 
other.getRightHand())
                         || !LongBitmap.isOverlap(filterTableBitMap, 
other.getMinLeftHand())
-                        || join.getJoinType().isSemiOrAntiJoin()
+                        || joinType.isSemiOrAntiJoin()
                         || other.getJoinType().isSemiOrAntiJoin()
                         || !other.isLhsStrict()) {
                     minRightHand = LongBitmap.or(minRightHand, 
other.getLeftHand());
@@ -176,7 +185,7 @@ public class CollectJoinConstraint implements 
RewriteRuleFactory {
         }
 
         JoinConstraint newJoinConstraint = new JoinConstraint(minLeftHand, 
minRightHand, leftHand, rightHand,
-                join.getJoinType(), isStrict);
+                joinType, isStrict);
         leading.getJoinConstraintList().add(newJoinConstraint);
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ReorderJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ReorderJoinTest.java
index c8d8e8af7af..fc9011ff178 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ReorderJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ReorderJoinTest.java
@@ -18,6 +18,8 @@
 package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.jobs.executor.Rewriter;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
@@ -68,6 +70,59 @@ class ReorderJoinTest implements MemoPatternMatchSupported {
         testRightOuterJoinHelper(JoinType.RIGHT_OUTER_JOIN);
     }
 
+    @Test
+    public void testSemiJoinCommuteInRewrite() {
+        for (JoinType joinType : ImmutableList.of(
+                JoinType.RIGHT_OUTER_JOIN, JoinType.RIGHT_SEMI_JOIN, 
JoinType.RIGHT_ANTI_JOIN)) {
+            ConnectContext connectContext = 
MemoTestUtils.createConnectContext();
+            
connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION");
+            PlanChecker checker = PlanChecker.from(connectContext)
+                    .analyze(new LogicalPlanBuilder(scan1)
+                            .join(scan2, joinType, Pair.of(0, 0))
+                            .build())
+                    .matches(logicalJoin().when(join -> join.getJoinType() == 
joinType));
+
+            checker.rewrite()
+                    .matches(logicalJoin().when(join -> join.getJoinType() == 
joinType.swap()));
+        }
+    }
+
+    @Test
+    public void testDisableJoinReorderBeforeRewrite() {
+        for (JoinType joinType : ImmutableList.of(
+                JoinType.RIGHT_OUTER_JOIN, JoinType.RIGHT_SEMI_JOIN, 
JoinType.RIGHT_ANTI_JOIN)) {
+            ConnectContext connectContext = 
MemoTestUtils.createConnectContext();
+            
connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION");
+            PlanChecker checker = PlanChecker.from(connectContext)
+                    .analyze(new LogicalPlanBuilder(scan1)
+                            .join(scan2, joinType, Pair.of(0, 0))
+                            .build());
+
+            connectContext.getSessionVariable().setDisableJoinReorder(true);
+            checker.rewrite()
+                    .matches(logicalJoin().when(join -> join.getJoinType() == 
joinType));
+        }
+    }
+
+    @Test
+    public void testSemiJoinCommuteInMvPreRewrite() {
+        for (JoinType joinType : ImmutableList.of(
+                JoinType.RIGHT_OUTER_JOIN, JoinType.RIGHT_SEMI_JOIN, 
JoinType.RIGHT_ANTI_JOIN)) {
+            ConnectContext connectContext = 
MemoTestUtils.createConnectContext();
+            
connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION");
+            PlanChecker checker = PlanChecker.from(connectContext)
+                    .analyze(new LogicalPlanBuilder(scan1)
+                            .join(scan2, joinType, Pair.of(0, 0))
+                            .build());
+            CascadesContext cascadesContext = checker.getCascadesContext();
+
+            Rewriter.getCteChildrenRewriter(
+                    cascadesContext, 
Rewriter.CTE_CHILDREN_REWRITE_JOBS_MV_REWRITE_USED, false).execute();
+            MemoTestUtils.initMemoAndValidState(cascadesContext);
+            checker.matches(logicalJoin().when(join -> join.getJoinType() == 
joinType.swap()));
+        }
+    }
+
     private void testRightOuterJoinHelper(JoinType joinType) {
         ImmutableList<LogicalPlan> plans = ImmutableList.of(
                 new LogicalPlanBuilder(scan1)
@@ -153,7 +208,6 @@ class ReorderJoinTest implements MemoPatternMatchSupported {
         ConnectContext connectContext = MemoTestUtils.createConnectContext();
         
connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION");
         PlanChecker.from(connectContext, plan2)
-                .applyBottomUp(new SemiJoinCommute())
                 .rewrite()
                 .matchesFromRoot(
                         logicalProject(innerLogicalJoin(
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlPlanSuiteTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlPlanSuiteTest.java
index 879d724c69e..72aa7e3b87c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlPlanSuiteTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlPlanSuiteTest.java
@@ -243,13 +243,13 @@ public class SqlPlanSuiteTest extends SqlTestBase {
                 .printlnTree()
                 .matches(
                     innerLogicalJoin(
-                        logicalFilter().when(
-                                f -> f.getPredicate().toString().equals("(id#2 
>= 4)")),
                         logicalFilter().when(
                                 f -> 
ExpressionUtils.and(f.getConjuncts().stream()
                                         .sorted((a, b) -> 
a.toString().compareTo(b.toString()))
                                         .collect(Collectors.toList()))
-                                        .toString().equals("(id#0 >= 4)"))
+                                        .toString().equals("(id#0 >= 4)")),
+                        logicalFilter().when(
+                                f -> f.getPredicate().toString().equals("(id#2 
>= 4)"))
                     )
 
                 );
@@ -288,8 +288,8 @@ public class SqlPlanSuiteTest extends SqlTestBase {
                 .rewrite()
                 .matches(
                         innerLogicalJoin(
-                                logicalProject(),
-                                logicalProject(leftSemiLogicalJoin())
+                                logicalProject(leftSemiLogicalJoin()),
+                                logicalProject()
                         )
                 );
     }
diff --git a/regression-test/data/query_p0/hint/fix_leading.out 
b/regression-test/data/query_p0/hint/fix_leading.out
index 526c6eac8ea..df4c476d3d5 100644
--- a/regression-test/data/query_p0/hint/fix_leading.out
+++ b/regression-test/data/query_p0/hint/fix_leading.out
@@ -243,6 +243,12 @@ Used: leading(t1 t2 t3 )
 UnUsed:
 SyntaxError:
 
+-- !select4_4 --
+1
+
+-- !select4_6 --
+1
+
 -- !select6_1 --
 PhysicalResultSink
 --hashAgg[GLOBAL]
diff --git a/regression-test/suites/query_p0/hint/fix_leading.groovy 
b/regression-test/suites/query_p0/hint/fix_leading.groovy
index a08a5fa4186..c1709e0d3ea 100644
--- a/regression-test/suites/query_p0/hint/fix_leading.groovy
+++ b/regression-test/suites/query_p0/hint/fix_leading.groovy
@@ -38,6 +38,9 @@ suite("fix_leading") {
     sql """drop table if exists t2;"""
     sql """drop table if exists t3;"""
     sql """drop table if exists t4;"""
+    sql """drop table if exists right_join_a;"""
+    sql """drop table if exists right_join_b;"""
+    sql """drop table if exists right_join_c;"""
 
     sql """create table t1 (c1 int, c11 int) distributed by hash(c1) buckets 3 
properties('replication_num' = '1');"""
     sql """create table t2 (c2 int, c22 int) distributed by hash(c2) buckets 3 
properties('replication_num' = '1');"""
@@ -45,6 +48,13 @@ suite("fix_leading") {
     sql """create table t4 (c4 int, c44 int) distributed by hash(c4) buckets 3 
properties('replication_num' = '1');"""
     sql """create table t5 (c5 int, c55 int) distributed by hash(c5) buckets 3 
properties('replication_num' = '1');"""
     sql """create table t6 (c6 int, c66 int) distributed by hash(c6) buckets 3 
properties('replication_num' = '1');"""
+    sql """create table right_join_a (k int) distributed by hash(k) buckets 1 
properties('replication_num' = '1');"""
+    sql """create table right_join_b (k int) distributed by hash(k) buckets 1 
properties('replication_num' = '1');"""
+    sql """create table right_join_c (k int) distributed by hash(k) buckets 1 
properties('replication_num' = '1');"""
+
+    sql """insert into right_join_a values (1);"""
+    sql """insert into right_join_b values (0), (1);"""
+    sql """insert into right_join_c values (1), (2);"""
 
     streamLoad {
         table "t1"
@@ -196,6 +206,16 @@ suite("fix_leading") {
     qt_select4_2 """select /*+ leading(t1 t2 t3)*/ count(*) from t1 left join 
t2 on c1 > 500 and c2 >500 right join t3 on c3 > 500 and c1 < 200;"""
     qt_select4_3 """explain shape plan select /*+ leading(t1 t2 t3)*/ count(*) 
from t1 left join t2 on c1 > 500 and c2 >500 right join t3 on c3 > 500 and c1 < 
200;"""
 
+    // check right semi join keeps its complete non-output side
+    qt_select4_4 """select /*+ leading(right_join_b right_join_a right_join_c) 
*/ count(*)
+        from right_join_a cross join right_join_c
+        right semi join right_join_b on right_join_a.k = right_join_b.k;"""
+
+    // check right anti join does not push its preserved-side ON predicate 
below the join
+    qt_select4_6 """select /*+ leading(right_join_b right_join_a right_join_c) 
*/ count(*)
+        from right_join_a cross join right_join_c
+        right anti join right_join_b on right_join_a.k = right_join_b.k and 
right_join_b.k > 0;"""
+
     // check whether we have all tables
     explain {
         sql """shape plan select /*+ leading(t1 t2)*/ count(*) from t1 left 
join t2 on c1 > 500 and c2 >500 right join t3 on c3 > 500 and c1 < 200;"""


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to