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

jakevin 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 cbdf1af2d5 [feature](Nereids): pushdown Alias through Join. (#17150)
cbdf1af2d5 is described below

commit cbdf1af2d5eb645528aac0e2edb7368454c265b1
Author: jakevin <jakevin...@gmail.com>
AuthorDate: Wed Mar 1 11:33:37 2023 +0800

    [feature](Nereids): pushdown Alias through Join. (#17150)
---
 .../org/apache/doris/nereids/rules/RuleSet.java    |   2 +
 .../org/apache/doris/nereids/rules/RuleType.java   |   1 +
 .../rewrite/logical/PushdownAliasThroughJoin.java  | 126 +++++++++++++++++++++
 .../org/apache/doris/nereids/memo/RankTest.java    |   8 +-
 .../logical/PushdownAliasThroughJoinTest.java      |  99 ++++++++++++++++
 5 files changed, 232 insertions(+), 4 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
index 96d4aa03a3..9b07cd6d6a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
@@ -53,6 +53,7 @@ import 
org.apache.doris.nereids.rules.rewrite.logical.MergeFilters;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeGenerates;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeLimits;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeProjects;
+import org.apache.doris.nereids.rules.rewrite.logical.PushdownAliasThroughJoin;
 import 
org.apache.doris.nereids.rules.rewrite.logical.PushdownExpressionsInHashCondition;
 import 
org.apache.doris.nereids.rules.rewrite.logical.PushdownFilterThroughAggregation;
 import 
org.apache.doris.nereids.rules.rewrite.logical.PushdownFilterThroughJoin;
@@ -104,6 +105,7 @@ public class RuleSet {
             new PushdownFilterThroughRepeat(),
             new PushdownFilterThroughSetOperation(),
             new PushdownProjectThroughLimit(),
+            new PushdownAliasThroughJoin(),
             new EliminateOuterJoin(),
             new MergeProjects(),
             new MergeFilters(),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 99d33a16d5..f6ffda790c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -126,6 +126,7 @@ public enum RuleType {
     PUSHDOWN_FILTER_THROUGH_PROJECT(RuleTypeClass.REWRITE),
     PUSHDOWN_FILTER_THROUGH_PROJECT_UNDER_LIMIT(RuleTypeClass.REWRITE),
     PUSHDOWN_PROJECT_THROUGH_LIMIT(RuleTypeClass.REWRITE),
+    PUSHDOWN_ALIAS_THROUGH_JOIN(RuleTypeClass.REWRITE),
     PUSHDOWN_FILTER_THROUGH_SET_OPERATION(RuleTypeClass.REWRITE),
     // column prune rules,
     COLUMN_PRUNE_AGGREGATION_CHILD(RuleTypeClass.REWRITE),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoin.java
new file mode 100644
index 0000000000..2719fa7bc4
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoin.java
@@ -0,0 +1,126 @@
+// 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.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.UnaryNode;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Pushdown Alias (inside must be Slot) through Join.
+ */
+public class PushdownAliasThroughJoin extends OneRewriteRuleFactory {
+    private boolean isAllSlotOrAliasSlot(LogicalProject<? extends Plan> 
project) {
+        return project.getProjects().stream().allMatch(expr -> {
+            if (expr instanceof Slot) {
+                return true;
+            }
+            if (expr instanceof Alias) {
+                return ((Alias) expr).child() instanceof Slot;
+            }
+            return false;
+        });
+    }
+
+    @Override
+    public Rule build() {
+        return logicalProject(logicalJoin())
+            .when(this::isAllSlotOrAliasSlot)
+            .then(project -> {
+                LogicalJoin<GroupPlan, GroupPlan> join = project.child();
+                // aliasMap { Slot -> Alias<Slot> }
+                Map<Expression, NamedExpression> aliasMap = 
project.getProjects().stream()
+                        .filter(expr -> expr instanceof Alias && ((Alias) 
expr).child() instanceof Slot)
+                        .map(expr -> (Alias) 
expr).collect(Collectors.toMap(UnaryNode::child, expr -> expr));
+                if (aliasMap.isEmpty()) {
+                    return null;
+                }
+                List<NamedExpression> newProjects = 
project.getProjects().stream().map(NamedExpression::toSlot)
+                        .collect(Collectors.toList());
+
+                List<Slot> leftOutput = join.left().getOutput();
+                List<NamedExpression> leftProjects = 
leftOutput.stream().map(slot -> {
+                    NamedExpression alias = aliasMap.get(slot);
+                    if (alias != null) {
+                        return alias;
+                    }
+                    return slot;
+                }).collect(Collectors.toList());
+                List<Slot> rightOutput = join.right().getOutput();
+                List<NamedExpression> rightProjects = 
rightOutput.stream().map(slot -> {
+                    NamedExpression alias = aliasMap.get(slot);
+                    if (alias != null) {
+                        return alias;
+                    }
+                    return slot;
+                }).collect(Collectors.toList());
+
+                Plan left;
+                Plan right;
+                if (leftOutput.equals(leftProjects)) {
+                    left = join.left();
+                } else {
+                    left = new LogicalProject<>(leftProjects, join.left());
+                }
+                if (rightOutput.equals(rightProjects)) {
+                    right = join.right();
+                } else {
+                    right = new LogicalProject<>(rightProjects, join.right());
+                }
+
+                // If condition use alias slot, we should replace condition
+                // project a.id as aid -- join a.id = b.id  =>
+                // join aid = b.id -- project a.id as aid
+                Map<ExprId, Slot> replaceMap = 
aliasMap.entrySet().stream().collect(
+                        Collectors.toMap(entry -> ((Slot) 
entry.getKey()).getExprId(),
+                                entry -> entry.getValue().toSlot()));
+
+                List<Expression> newHash = 
replaceJoinConjuncts(join.getHashJoinConjuncts(), replaceMap);
+                List<Expression> newOther = 
replaceJoinConjuncts(join.getOtherJoinConjuncts(), replaceMap);
+
+                Plan newJoin = join.withConjunctsChildren(newHash, newOther, 
left, right);
+                return new LogicalProject<>(newProjects, newJoin);
+            }).toRule(RuleType.PUSHDOWN_ALIAS_THROUGH_JOIN);
+    }
+
+    private List<Expression> replaceJoinConjuncts(List<Expression> 
joinConjuncts, Map<ExprId, Slot> replaceMaps) {
+        return joinConjuncts.stream().map(expr -> expr.rewriteUp(e -> {
+            if (e instanceof Slot && replaceMaps.containsKey(((Slot) 
e).getExprId())) {
+                return replaceMaps.get(((Slot) e).getExprId());
+            } else {
+                return e;
+            }
+        })).collect(ImmutableList.toImmutableList());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/RankTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/RankTest.java
index 3deb2a9638..f432f4a1cd 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/RankTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/RankTest.java
@@ -32,8 +32,8 @@ public class RankTest extends TPCHTestBase {
     @Test
     void testRank() throws NoSuchFieldException, IllegalAccessException {
         for (int i = 1; i < 22; i++) {
-            Field field = TPCHUtils.class.getField("Q" + String.valueOf(i));
-            System.out.println("Q" + String.valueOf(i));
+            Field field = TPCHUtils.class.getField("Q" + i);
+            System.out.println("Q" + i);
             Memo memo = PlanChecker.from(connectContext)
                     .analyze(field.get(null).toString())
                     .rewrite()
@@ -47,8 +47,8 @@ public class RankTest extends TPCHTestBase {
     @Test
     void testUnrank() throws NoSuchFieldException, IllegalAccessException {
         for (int i = 1; i < 22; i++) {
-            Field field = TPCHUtils.class.getField("Q" + String.valueOf(i));
-            System.out.println("Q" + String.valueOf(i));
+            Field field = TPCHUtils.class.getField("Q" + i);
+            System.out.println("Q" + i);
             Memo memo = PlanChecker.from(connectContext)
                     .analyze(field.get(null).toString())
                     .rewrite()
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoinTest.java
new file mode 100644
index 0000000000..9efa390a16
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownAliasThroughJoinTest.java
@@ -0,0 +1,99 @@
+// 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.rewrite.logical;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.util.LogicalPlanBuilder;
+import org.apache.doris.nereids.util.MemoTestUtils;
+import org.apache.doris.nereids.util.PatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.nereids.util.PlanConstructor;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Test;
+
+class PushdownAliasThroughJoinTest implements PatternMatchSupported {
+
+    @Test
+    void testPushdown() {
+        // condition don't use alias slot
+        LogicalPlan plan = new LogicalPlanBuilder(PlanConstructor.scan1)
+                .join(PlanConstructor.scan2, JoinType.INNER_JOIN, Pair.of(0, 
0))
+                .alias(ImmutableList.of(1, 3), ImmutableList.of("1name", 
"2name"))
+                .build();
+
+        PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+                .applyTopDown(new PushdownAliasThroughJoin())
+                .matches(
+                    logicalProject(
+                        logicalJoin(
+                            logicalProject().when(project -> 
project.getProjects().get(1).toSql().equals("name AS `1name`")),
+                            logicalProject().when(project -> 
project.getProjects().get(1).toSql().equals("name AS `2name`"))
+                        )
+                    ).when(project -> 
project.getProjects().get(0).toSql().equals("1name") && 
project.getProjects().get(1).toSql().equals("2name"))
+                );
+    }
+
+    @Test
+    void testCondition() {
+        // condition use alias slot
+        LogicalPlan plan = new LogicalPlanBuilder(PlanConstructor.scan1)
+                .join(PlanConstructor.scan2, JoinType.INNER_JOIN, Pair.of(0, 
0))
+                .alias(ImmutableList.of(0, 1, 3), ImmutableList.of("1id", 
"1name", "2name"))
+                .build();
+
+        PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+                .applyTopDown(new PushdownAliasThroughJoin())
+                .matches(
+                    logicalProject(
+                        logicalJoin(
+                            logicalProject().when(
+                                    project -> 
project.getProjects().get(0).toSql().equals("id AS `1id`")
+                                            && 
project.getProjects().get(1).toSql().equals("name AS `1name`")),
+                            logicalProject().when(
+                                    project -> 
project.getProjects().get(1).toSql().equals("name AS `2name`"))
+                        ).when(join -> 
join.getHashJoinConjuncts().get(0).toSql().equals("(1id = id)"))
+                    ).when(project -> 
project.getProjects().get(0).toSql().equals("1id")
+                        && project.getProjects().get(1).toSql().equals("1name")
+                        && 
project.getProjects().get(2).toSql().equals("2name"))
+                );
+    }
+
+    @Test
+    void testJustRightSide() {
+        // condition use alias slot
+        LogicalPlan plan = new LogicalPlanBuilder(PlanConstructor.scan1)
+                .join(PlanConstructor.scan2, JoinType.INNER_JOIN, Pair.of(0, 
0))
+                .alias(ImmutableList.of(2, 3), ImmutableList.of("2id", 
"2name"))
+                .build();
+
+        PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+                .applyTopDown(new PushdownAliasThroughJoin())
+                .matches(
+                    logicalProject(
+                        logicalJoin(
+                            logicalOlapScan(),
+                            logicalProject()
+                        ).when(join -> 
join.getHashJoinConjuncts().get(0).toSql().equals("(id = 2id)"))
+                    ).when(project -> 
project.getProjects().get(0).toSql().equals("2id")
+                            && 
project.getProjects().get(1).toSql().equals("2name"))
+                );
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to