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

englefly 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 5dc46efcdf [feature](nereids) eliminate cascading outer join (#23754)
5dc46efcdf is described below

commit 5dc46efcdfc8e38408d9d20ad3a7fc79c0427895
Author: minghong <[email protected]>
AuthorDate: Mon Sep 4 16:00:18 2023 +0800

    [feature](nereids) eliminate cascading outer join (#23754)
    
    after eliminate outer join, create is-not-null predicate, and then this 
is-not-null predicate can be used to eliminate descendant outer join. the newly 
created is-not-null predicate will be eliminated in EliminateNotNull rule.
---
 .../nereids/rules/rewrite/EliminateOuterJoin.java  | 29 +++++++++++++++++++-
 .../org/apache/doris/nereids/util/JoinUtils.java   | 32 ++++++++++++++++------
 2 files changed, 52 insertions(+), 9 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoin.java
index 83cc37ed0b..66d536e863 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoin.java
@@ -19,11 +19,13 @@ package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.util.JoinUtils;
 import org.apache.doris.nereids.util.TypeUtils;
 import org.apache.doris.nereids.util.Utils;
 
@@ -63,7 +65,32 @@ public class EliminateOuterJoin extends 
OneRewriteRuleFactory {
             }
 
             JoinType newJoinType = tryEliminateOuterJoin(join.getJoinType(), 
canFilterLeftNull, canFilterRightNull);
-            return filter.withChildren(join.withJoinType(newJoinType));
+            Set<Expression> conjuncts = new HashSet<>();
+            join.getHashJoinConjuncts().forEach(expression -> {
+                EqualTo equalTo = (EqualTo) expression;
+                if (canFilterLeftNull) {
+                    
JoinUtils.addIsNotNullIfNullableToCollection(equalTo.left(), conjuncts);
+                }
+                if (canFilterRightNull) {
+                    
JoinUtils.addIsNotNullIfNullableToCollection(equalTo.right(), conjuncts);
+                }
+            });
+            JoinUtils.JoinSlotCoverageChecker checker = new 
JoinUtils.JoinSlotCoverageChecker(
+                    join.left().getOutput(),
+                    join.right().getOutput());
+            
join.getOtherJoinConjuncts().stream().filter(EqualTo.class::isInstance).forEach(expr
 -> {
+                EqualTo equalTo = (EqualTo) expr;
+                if (checker.isHashJoinCondition(equalTo)) {
+                    if (canFilterLeftNull) {
+                        
JoinUtils.addIsNotNullIfNullableToCollection(equalTo.left(), conjuncts);
+                    }
+                    if (canFilterRightNull) {
+                        
JoinUtils.addIsNotNullIfNullableToCollection(equalTo.right(), conjuncts);
+                    }
+                }
+            });
+            conjuncts.addAll(filter.getConjuncts());
+            return 
filter.withConjuncts(conjuncts).withChildren(join.withJoinType(newJoinType));
         }).toRule(RuleType.ELIMINATE_OUTER_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 a13a22ccb6..6c6a4fa280 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
@@ -27,6 +27,7 @@ import 
org.apache.doris.nereids.properties.DistributionSpecReplicated;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.IsNull;
 import org.apache.doris.nereids.trees.expressions.Not;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.BitmapContains;
@@ -41,6 +42,7 @@ import org.apache.doris.qe.ConnectContext;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -60,20 +62,34 @@ public class JoinUtils {
         return !(join.getJoinType().isRightJoin() || 
join.getJoinType().isFullOuterJoin());
     }
 
-    private static final class JoinSlotCoverageChecker {
+    /**
+     * for a given expr, if expr is nullable, add 'expr is not null' in to 
container.
+     * this is used to eliminate outer join.
+     * for example: (A left join B on A.a=B.b) join C on B.x=C.x
+     * inner join condition B.x=C.x implies that 'B.x is not null' can be used 
to filter B,
+     * with 'B.x is not null' predicate, we could eliminate outer join, and 
the join transformed to
+     * (A join B on A.a=B.b) join C on B.x=C.x
+     */
+    public static void addIsNotNullIfNullableToCollection(Expression expr, 
Collection<Expression> container) {
+        if (expr.nullable()) {
+            Not not = new Not(new IsNull(expr));
+            not.isGeneratedIsNotNull = true;
+            container.add(not);
+        }
+    }
+
+    /**
+     * util class
+     */
+    public static final class JoinSlotCoverageChecker {
         Set<ExprId> leftExprIds;
         Set<ExprId> rightExprIds;
 
-        JoinSlotCoverageChecker(List<Slot> left, List<Slot> right) {
+        public JoinSlotCoverageChecker(List<Slot> left, List<Slot> right) {
             leftExprIds = 
left.stream().map(Slot::getExprId).collect(Collectors.toSet());
             rightExprIds = 
right.stream().map(Slot::getExprId).collect(Collectors.toSet());
         }
 
-        JoinSlotCoverageChecker(Set<ExprId> left, Set<ExprId> right) {
-            leftExprIds = left;
-            rightExprIds = right;
-        }
-
         /**
          * PushDownExpressionInHashConjuncts ensure the "slots" is only one 
slot.
          */
@@ -96,7 +112,7 @@ public class JoinUtils {
          * @param equalTo a conjunct in on clause condition
          * @return true if the equal can be used as hash join condition
          */
-        boolean isHashJoinCondition(EqualTo equalTo) {
+        public boolean isHashJoinCondition(EqualTo equalTo) {
             Set<Slot> equalLeft = 
equalTo.left().collect(Slot.class::isInstance);
             if (equalLeft.isEmpty()) {
                 return false;


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

Reply via email to