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

huajianlan 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 4f60b37402 [feature](Nereids):refactor and add outer join LAsscom. 
(#11531)
4f60b37402 is described below

commit 4f60b374020291c76e14177be3d03aa7b88197e4
Author: jakevin <[email protected]>
AuthorDate: Mon Aug 8 20:08:12 2022 +0800

    [feature](Nereids):refactor and add outer join LAsscom. (#11531)
    
    refactor and add outer join LAsscom.
    Extract the common function to LAsscomHelper.
---
 .../rules/exploration/join/JoinCommute.java        |   3 +
 .../rules/exploration/join/JoinLAsscom.java        | 123 ++----------
 .../rules/exploration/join/JoinLAsscomHelper.java  | 219 +++++++++++++++++++++
 .../rules/exploration/join/JoinLAsscomProject.java |  59 ++++++
 .../rules/exploration/join/JoinProjectLAsscom.java | 186 -----------------
 .../apache/doris/nereids/trees/plans/JoinType.java |   4 +
 .../org/apache/doris/nereids/util/JoinUtils.java   |  14 +-
 .../java/org/apache/doris/nereids/util/Utils.java  |  16 +-
 ...AsscomTest.java => JoinLAsscomProjectTest.java} |  15 +-
 .../rules/exploration/join/JoinLAsscomTest.java    |  12 +-
 10 files changed, 325 insertions(+), 326 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
index 776d722293..19bf4491b9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
@@ -29,6 +29,9 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalProject;
  */
 @Developing
 public class JoinCommute extends OneExplorationRuleFactory {
+
+    public static final JoinCommute SWAP_OUTER_COMMUTE_BOTTOM_JOIN = new 
JoinCommute(true, SwapType.BOTTOM_JOIN);
+
     private final SwapType swapType;
     private final boolean swapOuter;
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
index 8d9bda83af..5e9d173d68 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
@@ -21,23 +21,11 @@ import org.apache.doris.nereids.annotation.Developing;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
 
 /**
- * Rule for change inner join left associative to right.
+ * Rule for change inner join LAsscom (associative and commutive).
  */
 @Developing
 public class JoinLAsscom extends OneExplorationRuleFactory {
@@ -50,106 +38,19 @@ public class JoinLAsscom extends OneExplorationRuleFactory 
{
      */
     @Override
     public Rule build() {
-        return innerLogicalJoin(innerLogicalJoin(groupPlan(), groupPlan()), 
groupPlan()).then(topJoin -> {
-            if (!check(topJoin)) {
-                return null;
-            }
-
-            LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
-
-            Plan a = bottomJoin.left();
-            Plan b = bottomJoin.right();
-            Plan c = topJoin.right();
-
-            Optional<Expression> optTopJoinOnClause = topJoin.getCondition();
-            // inner join, onClause can't be empty().
-            Preconditions.checkArgument(optTopJoinOnClause.isPresent(),
-                    "bottomJoin in inner join, onClause must be present.");
-            Expression topJoinOnClause = optTopJoinOnClause.get();
-            Optional<Expression> optBottomJoinOnClause = 
bottomJoin.getCondition();
-            Preconditions.checkArgument(optBottomJoinOnClause.isPresent(),
-                    "bottomJoin in inner join, onClause must be present.");
-            Expression bottomJoinOnClause = optBottomJoinOnClause.get();
-
-            List<SlotReference> aOutputSlots = a.getOutput().stream().map(slot 
-> (SlotReference) slot)
-                    .collect(Collectors.toList());
-            List<SlotReference> bOutputSlots = b.getOutput().stream().map(slot 
-> (SlotReference) slot)
-                    .collect(Collectors.toList());
-            List<SlotReference> cOutputSlots = c.getOutput().stream().map(slot 
-> (SlotReference) slot)
-                    .collect(Collectors.toList());
-
-            // Ignore join with some OnClause like:
-            // Join C = B + A for above example.
-            List<Expression> topJoinOnClauseConjuncts = 
ExpressionUtils.extractConjunction(topJoinOnClause);
-            for (Expression topJoinOnClauseConjunct : 
topJoinOnClauseConjuncts) {
-                if 
(ExpressionUtils.isIntersecting(topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
-                        aOutputSlots)
-                        && ExpressionUtils.isIntersecting(
-                        
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
-                        bOutputSlots)
-                        && ExpressionUtils.isIntersecting(
-                        
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
-                        cOutputSlots)
-                ) {
+        return logicalJoin(logicalJoin(), groupPlan())
+            .when(JoinLAsscomHelper::check)
+            .when(join -> join.getJoinType().isInnerJoin() || 
join.getJoinType().isLeftOuterJoin()
+                    && (join.left().getJoinType().isInnerJoin() || 
join.left().getJoinType().isLeftOuterJoin()))
+            .then(topJoin -> {
+
+                LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
+                JoinLAsscomHelper helper = JoinLAsscomHelper.of(topJoin, 
bottomJoin);
+                if (!helper.initJoinOnCondition()) {
                     return null;
                 }
-            }
-            List<Expression> bottomJoinOnClauseConjuncts = 
ExpressionUtils.extractConjunction(bottomJoinOnClause);
-
-            List<Expression> allOnCondition = Lists.newArrayList();
-            allOnCondition.addAll(topJoinOnClauseConjuncts);
-            allOnCondition.addAll(bottomJoinOnClauseConjuncts);
-
-            List<SlotReference> newBottomJoinSlots = Lists.newArrayList();
-            newBottomJoinSlots.addAll(aOutputSlots);
-            newBottomJoinSlots.addAll(cOutputSlots);
-
-            List<Expression> newBottomJoinOnCondition = Lists.newArrayList();
-            List<Expression> newTopJoinOnCondition = Lists.newArrayList();
-            for (Expression onCondition : allOnCondition) {
-                List<SlotReference> slots = 
onCondition.collect(SlotReference.class::isInstance);
-                if (new HashSet<>(newBottomJoinSlots).containsAll(slots)) {
-                    newBottomJoinOnCondition.add(onCondition);
-                } else {
-                    newTopJoinOnCondition.add(onCondition);
-                }
-            }
-
-            // newBottomJoinOnCondition/newTopJoinOnCondition is empty. They 
are cross join.
-            // Example:
-            // A: col1, col2. B: col2, col3. C: col3, col4
-            // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
-            // If (A & B) & C -> (A & C) & B.
-            // (A & C) will be cross join (newBottomJoinOnCondition is empty)
-            if (newBottomJoinOnCondition.isEmpty() || 
newTopJoinOnCondition.isEmpty()) {
-                return null;
-            }
-
-            // new bottom join (a, c)
-            LogicalJoin newBottomJoin = new LogicalJoin(
-                    bottomJoin.getJoinType(),
-                    Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
-                    a, c);
-
-            // TODO: add column map (use project)
-            // SlotReference bind() may have solved this problem.
-            // source: | A       | B | C      |
-            // target: | A       | C      | B |
-
-            // new top join: b
-            LogicalJoin newTopJoin = new LogicalJoin(
-                    topJoin.getJoinType(),
-                    Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
-                    newBottomJoin, b);
-
-            return newTopJoin;
-        }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
-    }
 
-    private boolean check(LogicalJoin topJoin) {
-        if (topJoin.getJoinReorderContext().hasCommute()) {
-            return false;
-        }
-        return true;
+                return helper.newTopJoin();
+            }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
new file mode 100644
index 0000000000..0a67d42bbb
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
@@ -0,0 +1,219 @@
+// 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.doris.nereids.rules.exploration.join;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Common function for JoinLAsscom
+ */
+public class JoinLAsscomHelper {
+    /*
+     *      topJoin                newTopJoin
+     *      /     \                 /     \
+     * bottomJoin  C   -->  newBottomJoin  B
+     *  /    \                  /    \
+     * A      B                A      C
+     */
+    private final LogicalJoin topJoin;
+    private final LogicalJoin<GroupPlan, GroupPlan> bottomJoin;
+    private final Plan a;
+    private final Plan b;
+    private final Plan c;
+
+    private final Expression topJoinOnClause;
+    private final Expression bottomJoinOnClause;
+
+    private final List<SlotReference> aOutputSlots;
+    private final List<SlotReference> bOutputSlots;
+    private final List<SlotReference> cOutputSlots;
+
+    private final List<Expression> newBottomJoinOnCondition = 
Lists.newArrayList();
+    private final List<Expression> newTopJoinOnCondition = 
Lists.newArrayList();
+
+    /**
+     * Init plan and output.
+     */
+    public JoinLAsscomHelper(LogicalJoin<? extends Plan, GroupPlan> topJoin,
+            LogicalJoin<GroupPlan, GroupPlan> bottomJoin) {
+        this.topJoin = topJoin;
+        this.bottomJoin = bottomJoin;
+
+        a = bottomJoin.left();
+        b = bottomJoin.right();
+        c = topJoin.right();
+
+        Preconditions.checkArgument(topJoin.getCondition().isPresent(), 
"topJoin onClause must be present.");
+        topJoinOnClause = topJoin.getCondition().get();
+        Preconditions.checkArgument(bottomJoin.getCondition().isPresent(), 
"bottomJoin onClause must be present.");
+        bottomJoinOnClause = bottomJoin.getCondition().get();
+
+        aOutputSlots = Utils.getOutputSlotReference(a);
+        bOutputSlots = Utils.getOutputSlotReference(b);
+        cOutputSlots = Utils.getOutputSlotReference(c);
+    }
+
+    public static JoinLAsscomHelper of(LogicalJoin<? extends Plan, GroupPlan> 
topJoin,
+            LogicalJoin<GroupPlan, GroupPlan> bottomJoin) {
+        return new JoinLAsscomHelper(topJoin, bottomJoin);
+    }
+
+    /**
+     * Get the onCondition of newTopJoin and newBottomJoin.
+     */
+    public boolean initJoinOnCondition() {
+        List<Expression> topJoinOnClauseConjuncts = 
ExpressionUtils.extractConjunction(topJoinOnClause);
+        for (Expression topJoinOnClauseConjunct : topJoinOnClauseConjuncts) {
+            // Ignore join with some OnClause like:
+            // Join C = B + A for above example.
+            List<SlotReference> topJoinUsedSlot = 
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance);
+            if (ExpressionUtils.isIntersecting(topJoinUsedSlot, aOutputSlots)
+                    && ExpressionUtils.isIntersecting(topJoinUsedSlot, 
bOutputSlots)
+                    && ExpressionUtils.isIntersecting(topJoinUsedSlot, 
cOutputSlots)
+            ) {
+                return false;
+            }
+        }
+
+        List<Expression> allOnCondition = Lists.newArrayList();
+        allOnCondition.addAll(topJoinOnClauseConjuncts);
+        
allOnCondition.addAll(ExpressionUtils.extractConjunction(bottomJoinOnClause));
+
+        HashSet<SlotReference> newBottomJoinSlots = new 
HashSet<>(aOutputSlots);
+        newBottomJoinSlots.addAll(cOutputSlots);
+
+        for (Expression onCondition : allOnCondition) {
+            List<SlotReference> slots = 
onCondition.collect(SlotReference.class::isInstance);
+            if (newBottomJoinSlots.containsAll(slots)) {
+                newBottomJoinOnCondition.add(onCondition);
+            } else {
+                newTopJoinOnCondition.add(onCondition);
+            }
+        }
+
+        // newBottomJoinOnCondition/newTopJoinOnCondition is empty. They are 
cross join.
+        // Example:
+        // A: col1, col2. B: col2, col3. C: col3, col4
+        // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
+        // (A & B) & C -> (A & C) & B.
+        // (A & C) will be cross join (newBottomJoinOnCondition is empty)
+        if (newBottomJoinOnCondition.isEmpty() || 
newTopJoinOnCondition.isEmpty()) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Get projectExpr of left and right.
+     * Just for project-inside.
+     */
+    private Pair<List<NamedExpression>, List<NamedExpression>> 
getProjectExprs() {
+        Preconditions.checkArgument(topJoin.left() instanceof LogicalProject);
+        LogicalProject project = (LogicalProject) topJoin.left();
+
+        List<NamedExpression> projectExprs = project.getProjects();
+        List<NamedExpression> newRightProjectExprs = Lists.newArrayList();
+        List<NamedExpression> newLeftProjectExpr = Lists.newArrayList();
+
+        HashSet<SlotReference> bOutputSlotsSet = new HashSet<>(bOutputSlots);
+        for (NamedExpression projectExpr : projectExprs) {
+            List<SlotReference> usedSlotRefs = 
projectExpr.collect(SlotReference.class::isInstance);
+            if (bOutputSlotsSet.containsAll(usedSlotRefs)) {
+                newRightProjectExprs.add(projectExpr);
+            } else {
+                newLeftProjectExpr.add(projectExpr);
+            }
+        }
+
+        return new Pair<>(newLeftProjectExpr, newRightProjectExprs);
+    }
+
+
+    private LogicalJoin<GroupPlan, GroupPlan> newBottomJoin() {
+        return new LogicalJoin(
+                bottomJoin.getJoinType(),
+                Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
+                a, c);
+    }
+
+    /**
+     * Create topJoin for project-inside.
+     */
+    public LogicalJoin newProjectTopJoin() {
+        Plan left;
+        Plan right;
+
+        List<NamedExpression> newLeftProjectExpr = getProjectExprs().first;
+        List<NamedExpression> newRightProjectExprs = getProjectExprs().second;
+        if (!newLeftProjectExpr.isEmpty()) {
+            left = new LogicalProject<>(newLeftProjectExpr, newBottomJoin());
+        } else {
+            left = newBottomJoin();
+        }
+        if (!newRightProjectExprs.isEmpty()) {
+            right = new LogicalProject<>(newRightProjectExprs, b);
+        } else {
+            right = b;
+        }
+
+        return new LogicalJoin<>(
+                topJoin.getJoinType(),
+                Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
+                left, right);
+    }
+
+    /**
+     * Create topJoin for no-project-inside.
+     */
+    public LogicalJoin newTopJoin() {
+        // TODO: add column map (use project)
+        // SlotReference bind() may have solved this problem.
+        // source: | A       | B | C      |
+        // target: | A       | C      | B |
+
+        return new LogicalJoin(
+                topJoin.getJoinType(),
+                Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
+                newBottomJoin(), b);
+    }
+
+    public static boolean check(LogicalJoin topJoin) {
+        if (topJoin.getJoinReorderContext().hasCommute()) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
new file mode 100644
index 0000000000..e6d99644af
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
@@ -0,0 +1,59 @@
+// 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.doris.nereids.rules.exploration.join;
+
+import org.apache.doris.nereids.annotation.Developing;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+
+/**
+ * Rule for change inner join left associative to right.
+ */
+@Developing
+public class JoinLAsscomProject extends OneExplorationRuleFactory {
+    /*
+     *        topJoin                   newTopJoin
+     *        /     \                   /        \
+     *    project    C          newLeftProject newRightProject
+     *      /            ──►          /            \
+     * bottomJoin                newBottomJoin      B
+     *    /   \                     /   \
+     *   A     B                   A     C
+     */
+    @Override
+    public Rule build() {
+        return logicalJoin(logicalProject(logicalJoin()), groupPlan())
+            .when(JoinLAsscomHelper::check)
+            .when(join -> join.getJoinType().isInnerJoin() || 
join.getJoinType().isLeftOuterJoin()
+                    && (join.left().child().getJoinType().isInnerJoin() || 
join.left().child().getJoinType()
+                    .isLeftOuterJoin()))
+            .then(topJoin -> {
+                LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.left().child();
+
+                JoinLAsscomHelper helper = JoinLAsscomHelper.of(topJoin, 
bottomJoin);
+                if (!helper.initJoinOnCondition()) {
+                    return null;
+                }
+
+                return helper.newProjectTopJoin();
+            }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
deleted file mode 100644
index afe7ac74b8..0000000000
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// 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.doris.nereids.rules.exploration.join;
-
-import org.apache.doris.nereids.annotation.Developing;
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/**
- * Rule for change inner join left associative to right.
- */
-@Developing
-public class JoinProjectLAsscom extends OneExplorationRuleFactory {
-    /*
-     *        topJoin                   newTopJoin
-     *        /     \                   /        \
-     *    project    C          newLeftProject newRightProject
-     *      /            ──►          /            \
-     * bottomJoin                newBottomJoin      B
-     *    /   \                     /   \
-     *   A     B                   A     C
-     */
-    @Override
-    public Rule build() {
-        return innerLogicalJoin(logicalProject(innerLogicalJoin(groupPlan(), 
groupPlan())), groupPlan())
-            .when(this::check)
-            .then(topJoin -> {
-                if (!check(topJoin)) {
-                    return null;
-                }
-
-                LogicalProject<LogicalJoin<GroupPlan, GroupPlan>> project = 
topJoin.left();
-                LogicalJoin<GroupPlan, GroupPlan> bottomJoin = project.child();
-
-                Plan a = bottomJoin.left();
-                Plan b = bottomJoin.right();
-                Plan c = topJoin.right();
-
-                Optional<Expression> optTopJoinOnClause = 
topJoin.getCondition();
-                // inner join, onClause can't be empty().
-                Preconditions.checkArgument(optTopJoinOnClause.isPresent(),
-                        "bottomJoin in inner join, onClause must be present.");
-                Expression topJoinOnClause = optTopJoinOnClause.get();
-                Optional<Expression> optBottomJoinOnClause = 
bottomJoin.getCondition();
-                Preconditions.checkArgument(optBottomJoinOnClause.isPresent(),
-                        "bottomJoin in inner join, onClause must be present.");
-                Expression bottomJoinOnClause = optBottomJoinOnClause.get();
-
-                List<SlotReference> aOutputSlots = 
a.getOutput().stream().map(slot -> (SlotReference) slot)
-                        .collect(Collectors.toList());
-                List<SlotReference> bOutputSlots = 
b.getOutput().stream().map(slot -> (SlotReference) slot)
-                        .collect(Collectors.toList());
-                List<SlotReference> cOutputSlots = 
c.getOutput().stream().map(slot -> (SlotReference) slot)
-                        .collect(Collectors.toList());
-
-                // Ignore join with some OnClause like:
-                // Join C = B + A for above example.
-                List<Expression> topJoinOnClauseConjuncts = 
ExpressionUtils.extractConjunction(topJoinOnClause);
-                for (Expression topJoinOnClauseConjunct : 
topJoinOnClauseConjuncts) {
-                    if (ExpressionUtils.isIntersecting(
-                            
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance), aOutputSlots)
-                            && ExpressionUtils.isIntersecting(
-                            
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
-                            bOutputSlots)
-                            && ExpressionUtils.isIntersecting(
-                            
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
-                            cOutputSlots)
-                    ) {
-                        return null;
-                    }
-                }
-                List<Expression> bottomJoinOnClauseConjuncts = 
ExpressionUtils.extractConjunction(
-                        bottomJoinOnClause);
-
-                List<Expression> allOnCondition = Lists.newArrayList();
-                allOnCondition.addAll(topJoinOnClauseConjuncts);
-                allOnCondition.addAll(bottomJoinOnClauseConjuncts);
-
-                List<SlotReference> newBottomJoinSlots = Lists.newArrayList();
-                newBottomJoinSlots.addAll(aOutputSlots);
-                newBottomJoinSlots.addAll(cOutputSlots);
-
-                List<Expression> newBottomJoinOnCondition = 
Lists.newArrayList();
-                List<Expression> newTopJoinOnCondition = Lists.newArrayList();
-                for (Expression onCondition : allOnCondition) {
-                    List<SlotReference> slots = 
onCondition.collect(SlotReference.class::isInstance);
-                    if (new HashSet<>(newBottomJoinSlots).containsAll(slots)) {
-                        newBottomJoinOnCondition.add(onCondition);
-                    } else {
-                        newTopJoinOnCondition.add(onCondition);
-                    }
-                }
-
-                // newBottomJoinOnCondition/newTopJoinOnCondition is empty. 
They are cross join.
-                // Example:
-                // A: col1, col2. B: col2, col3. C: col3, col4
-                // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
-                // If (A & B) & C -> (A & C) & B.
-                // (A & C) will be cross join (newBottomJoinOnCondition is 
empty)
-                if (newBottomJoinOnCondition.isEmpty() || 
newTopJoinOnCondition.isEmpty()) {
-                    return null;
-                }
-
-                Plan newBottomJoin = new LogicalJoin(
-                        bottomJoin.getJoinType(),
-                        
Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
-                        a, c);
-
-                // Handle project.
-                List<NamedExpression> projectExprs = project.getProjects();
-                List<NamedExpression> newRightProjectExprs = 
Lists.newArrayList();
-                List<NamedExpression> newLeftProjectExpr = 
Lists.newArrayList();
-                for (NamedExpression projectExpr : projectExprs) {
-                    List<SlotReference> usedSlotRefs = 
projectExpr.collect(SlotReference.class::isInstance);
-                    if (new HashSet<>(bOutputSlots).containsAll(usedSlotRefs)) 
{
-                        newRightProjectExprs.add(projectExpr);
-                    } else {
-                        newLeftProjectExpr.add(projectExpr);
-                    }
-                }
-
-                // project include b, add project for right.
-                if (newRightProjectExprs.size() != 0) {
-                    LogicalProject newRightProject = new 
LogicalProject<>(newRightProjectExprs, b);
-
-                    if (newLeftProjectExpr.size() != 0) {
-                        // project include bottom join, add project for left 
bottom join.
-                        LogicalProject newLeftProject = new 
LogicalProject<>(newLeftProjectExpr, newBottomJoin);
-                        return new LogicalJoin(
-                                topJoin.getJoinType(),
-                                
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
-                                newLeftProject, newRightProject);
-                    }
-                    return new LogicalJoin(
-                            topJoin.getJoinType(),
-                            
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
-                            newBottomJoin, newRightProject);
-                }
-
-                return new LogicalJoin(
-                        topJoin.getJoinType(),
-                        
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
-                        newBottomJoin, b);
-            }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
-    }
-
-    private boolean check(LogicalJoin topJoin) {
-        if (topJoin.getJoinReorderContext().hasCommute()) {
-            return false;
-        }
-        return true;
-    }
-
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
index 77b7c447dc..9e7e933d84 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
@@ -100,6 +100,10 @@ public enum JoinType {
         return this == FULL_OUTER_JOIN;
     }
 
+    public final boolean isLeftOuterJoin() {
+        return this == LEFT_OUTER_JOIN;
+    }
+
     public final boolean isSemiOrAntiJoin() {
         return this == LEFT_SEMI_JOIN || this == RIGHT_SEMI_JOIN || this == 
LEFT_ANTI_JOIN || this == RIGHT_ANTI_JOIN;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
index 711e954528..5c66dc85a2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
@@ -29,7 +29,6 @@ import com.google.common.collect.Lists;
 
 import java.util.HashSet;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * Utils for join
@@ -53,10 +52,8 @@ public class JoinUtils {
             return eqConjuncts;
         }
 
-        List<SlotReference> leftSlots = 
join.left().getOutput().stream().map(slot -> (SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> rightSlots = 
join.right().getOutput().stream().map(slot -> (SlotReference) slot)
-                .collect(Collectors.toList());
+        List<SlotReference> leftSlots = 
Utils.getOutputSlotReference(join.left());
+        List<SlotReference> rightSlots = 
Utils.getOutputSlotReference(join.right());
 
         Expression onCondition = join.getCondition().get();
         List<Expression> conjunctList = 
ExpressionUtils.extractConjunction(onCondition);
@@ -93,13 +90,10 @@ public class JoinUtils {
         Pair<List<SlotReference>, List<SlotReference>> childSlots =
                 new Pair<>(Lists.newArrayList(), Lists.newArrayList());
 
-        List<SlotReference> leftSlots = 
join.left().getOutput().stream().map(slot -> (SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> rightSlots = 
join.right().getOutput().stream().map(slot -> (SlotReference) slot)
-                .collect(Collectors.toList());
+        List<SlotReference> leftSlots = 
Utils.getOutputSlotReference(join.left());
+        List<SlotReference> rightSlots = 
Utils.getOutputSlotReference(join.right());
         List<EqualTo> equalToList = getEqualTo(join);
 
-
         for (EqualTo equalTo : equalToList) {
             List<SlotReference> leftOnSlots = 
equalTo.left().collect(SlotReference.class::isInstance);
             List<SlotReference> rightOnSlots = 
equalTo.right().collect(SlotReference.class::isInstance);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
index a5bc161570..cba61cac71 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
@@ -17,11 +17,15 @@
 
 package org.apache.doris.nereids.util;
 
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.Plan;
+
 import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.HashSet;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Utils for Nereids.
@@ -56,11 +60,10 @@ public class Utils {
 
     /**
      * Helper function to eliminate unnecessary checked exception caught 
requirement from the main logic of translator.
-     *
      */
     @SuppressWarnings("unchecked")
     public static <R> R execWithReturnVal(Supplier<R> f) {
-        final Object[] ans = new Object[]{null};
+        final Object[] ans = new Object[] {null};
         try {
             ans[0] = f.get();
         } catch (Exception e) {
@@ -107,4 +110,13 @@ public class Utils {
         }
         return new HashSet<>(one).containsAll(other) && new 
HashSet<>(other).containsAll(one);
     }
+
+    /**
+     * Get SlotReference from output of plam.
+     * Warning, plan must have bound, because exists Slot Cast to 
SlotReference.
+     */
+    public static List<SlotReference> getOutputSlotReference(Plan plan) {
+        return plan.getOutput().stream().map(SlotReference.class::cast)
+                .collect(Collectors.toList());
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
similarity index 90%
rename from 
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
rename to 
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
index 95a106c15d..eda887fe28 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
@@ -31,6 +31,7 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -41,9 +42,8 @@ import org.junit.jupiter.api.Test;
 
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
 
-public class JoinProjectLAsscomTest {
+public class JoinLAsscomProjectTest {
 
     private static final List<LogicalOlapScan> scans = Lists.newArrayList();
     private static final List<List<SlotReference>> outputs = 
Lists.newArrayList();
@@ -58,12 +58,9 @@ public class JoinProjectLAsscomTest {
         scans.add(scan2);
         scans.add(scan3);
 
-        List<SlotReference> t1Output = scan1.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> t2Output = scan2.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> t3Output = scan3.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
+        List<SlotReference> t1Output = Utils.getOutputSlotReference(scan1);
+        List<SlotReference> t2Output = Utils.getOutputSlotReference(scan2);
+        List<SlotReference> t3Output = Utils.getOutputSlotReference(scan3);
 
         outputs.add(t1Output);
         outputs.add(t2Output);
@@ -97,7 +94,7 @@ public class JoinProjectLAsscomTest {
         LogicalJoin<LogicalProject<LogicalJoin<LogicalOlapScan, 
LogicalOlapScan>>, LogicalOlapScan> topJoin
                 = new LogicalJoin<>(JoinType.INNER_JOIN, 
Optional.of(topJoinOnCondition), project, scans.get(2));
 
-        Rule rule = new JoinProjectLAsscom().build();
+        Rule rule = new JoinLAsscomProject().build();
         List<Plan> transform = rule.transform(topJoin, plannerContext);
         Assertions.assertEquals(1, transform.size());
         Assertions.assertTrue(transform.get(0) instanceof LogicalJoin);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
index 11e7bbbbf1..6d7984db5d 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
@@ -28,6 +28,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.collect.Lists;
 import mockit.Mocked;
@@ -37,7 +38,6 @@ import org.junit.jupiter.api.Test;
 
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
 
 public class JoinLAsscomTest {
 
@@ -49,17 +49,13 @@ public class JoinLAsscomTest {
         LogicalOlapScan scan1 = 
PlanConstructor.newLogicalOlapScanWithTable("t1");
         LogicalOlapScan scan2 = 
PlanConstructor.newLogicalOlapScanWithTable("t2");
         LogicalOlapScan scan3 = 
PlanConstructor.newLogicalOlapScanWithTable("t3");
-
         scans.add(scan1);
         scans.add(scan2);
         scans.add(scan3);
 
-        List<SlotReference> t1Output = scan1.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> t2Output = scan2.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
-        List<SlotReference> t3Output = scan3.getOutput().stream().map(slot -> 
(SlotReference) slot)
-                .collect(Collectors.toList());
+        List<SlotReference> t1Output = Utils.getOutputSlotReference(scan1);
+        List<SlotReference> t2Output = Utils.getOutputSlotReference(scan2);
+        List<SlotReference> t3Output = Utils.getOutputSlotReference(scan3);
         outputs.add(t1Output);
         outputs.add(t2Output);
         outputs.add(t3Output);


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

Reply via email to