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 6ddbd204e7 [fix](Nereids): Update plan when prune column in DPHyp 
(#14880)
6ddbd204e7 is described below

commit 6ddbd204e7416ae2a86acfc8f11396da21e77243
Author: 谢健 <[email protected]>
AuthorDate: Thu Dec 15 21:59:55 2022 +0800

    [fix](Nereids): Update plan when prune column in DPHyp (#14880)
---
 .../org/apache/doris/nereids/NereidsPlanner.java   | 21 +++++
 .../doris/nereids/jobs/joinorder/JoinOrderJob.java | 64 +++++++++++++--
 .../jobs/joinorder/hypergraph/HyperGraph.java      | 88 ++++++++++++++++-----
 .../nereids/jobs/joinorder/hypergraph/Node.java    |  4 +
 .../joinorder/hypergraph/SubgraphEnumerator.java   | 22 +++---
 .../hypergraph/receiver/AbstractReceiver.java      | 13 ++--
 .../joinorder/hypergraph/receiver/Counter.java     |  8 +-
 .../hypergraph/receiver/PlanReceiver.java          | 49 +++++++++---
 .../java/org/apache/doris/nereids/memo/Group.java  |  5 ++
 .../java/org/apache/doris/nereids/memo/Memo.java   | 20 +++--
 .../nereids/trees/expressions/Expression.java      |  4 +
 .../nereids/jobs/joinorder/JoinOrderJobTest.java   | 58 --------------
 .../doris/nereids/jobs/joinorder/TPCHTest.java}    | 38 +++++----
 .../doris/nereids/sqltest/JoinOrderJobTest.java    | 91 ++++++++++++++++++++++
 .../doris/nereids/util/HyperGraphBuilder.java      | 10 +--
 .../org/apache/doris/nereids/util/PlanChecker.java | 25 +++++-
 16 files changed, 375 insertions(+), 145 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
index ee75a01ed9..433f96f183 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
@@ -29,6 +29,7 @@ import 
org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
 import org.apache.doris.nereids.jobs.batch.NereidsRewriteJobExecutor;
 import org.apache.doris.nereids.jobs.batch.OptimizeRulesJob;
 import org.apache.doris.nereids.jobs.cascades.DeriveStatsJob;
+import org.apache.doris.nereids.jobs.joinorder.JoinOrderJob;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.metrics.event.CounterEvent;
@@ -36,9 +37,11 @@ import 
org.apache.doris.nereids.processor.post.PlanPostProcessors;
 import org.apache.doris.nereids.processor.pre.PlanPreprocessors;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import 
org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.planner.PlanFragment;
 import org.apache.doris.planner.Planner;
@@ -209,6 +212,24 @@ public class NereidsPlanner extends Planner {
     }
 
     private void joinReorder() {
+        Group root = getRoot();
+        boolean changeRoot = false;
+        if (root.isJoinGroup()) {
+            // If the root group is join group, DPHyp can change the root 
group.
+            // To keep the root group is not changed, we add a project 
operator above join
+            List<Slot> outputs = 
root.getLogicalExpression().getPlan().getOutput();
+            GroupExpression newExpr = new GroupExpression(
+                    new LogicalProject(outputs, 
root.getLogicalExpression().getPlan()),
+                    Lists.newArrayList(root));
+            root = new Group();
+            root.addGroupExpression(newExpr);
+            changeRoot = true;
+        }
+        cascadesContext.pushJob(new JoinOrderJob(root, 
cascadesContext.getCurrentJobContext()));
+        cascadesContext.getJobScheduler().executeJobPool(cascadesContext);
+        if (changeRoot) {
+            
cascadesContext.getMemo().setRoot(root.getLogicalExpression().child(0));
+        }
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJob.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJob.java
index 1ef38df1d4..bdfaaa95f2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJob.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJob.java
@@ -17,24 +17,36 @@
 
 package org.apache.doris.nereids.jobs.joinorder;
 
+import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.jobs.Job;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.jobs.JobType;
+import org.apache.doris.nereids.jobs.cascades.DeriveStatsJob;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.GraphSimplifier;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.HyperGraph;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.SubgraphEnumerator;
 import 
org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver.PlanReceiver;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.rules.rewrite.logical.ColumnPruning;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
-import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Join Order job with DPHyp
  */
 public class JoinOrderJob extends Job {
     private final Group group;
+    private final Set<NamedExpression> otherProject = new HashSet<>();
 
     public JoinOrderJob(Group group, JobContext context) {
         super(JobType.JOIN_ORDER, context);
@@ -43,12 +55,16 @@ public class JoinOrderJob extends Job {
 
     @Override
     public void execute() throws AnalysisException {
-        Preconditions.checkArgument(!group.isJoinGroup());
         GroupExpression rootExpr = group.getLogicalExpression();
         int arity = rootExpr.arity();
         for (int i = 0; i < arity; i++) {
             rootExpr.setChild(i, optimizePlan(rootExpr.child(i)));
         }
+        CascadesContext cascadesContext = context.getCascadesContext();
+        cascadesContext.topDownRewrite(new ColumnPruning());
+        cascadesContext.pushJob(
+                new DeriveStatsJob(group.getLogicalExpression(), 
cascadesContext.getCurrentJobContext()));
+        cascadesContext.getJobScheduler().executeJobPool(cascadesContext);
     }
 
     private Group optimizePlan(Group group) {
@@ -66,7 +82,7 @@ public class JoinOrderJob extends Job {
     private Group optimizeJoin(Group group) {
         HyperGraph hyperGraph = new HyperGraph();
         buildGraph(group, hyperGraph);
-        // Right now, we just hardcode the limit with 10000, maybe we need a 
better way to set it
+        // TODO: Right now, we just hardcode the limit with 10000, maybe we 
need a better way to set it
         int limit = 10000;
         PlanReceiver planReceiver = new PlanReceiver(limit);
         SubgraphEnumerator subgraphEnumerator = new 
SubgraphEnumerator(planReceiver, hyperGraph);
@@ -77,13 +93,22 @@ public class JoinOrderJob extends Job {
                 throw new RuntimeException("DPHyp can not enumerate all sub 
graphs with limit=" + limit);
             }
         }
-
         Group optimized = planReceiver.getBestPlan(hyperGraph.getNodesMap());
-        return copyToMemo(optimized);
+        Group memoRoot = copyToMemo(optimized);
+
+        // For other projects, such as project constant or project nullable, 
we construct a new project above root
+        if (otherProject.size() != 0) {
+            
otherProject.addAll(memoRoot.getLogicalExpression().getPlan().getOutput());
+            LogicalProject logicalProject = new LogicalProject(new 
ArrayList<>(otherProject),
+                    memoRoot.getLogicalExpression().getPlan());
+            GroupExpression groupExpression = new 
GroupExpression(logicalProject, Lists.newArrayList(group));
+            memoRoot = 
context.getCascadesContext().getMemo().copyInGroupExpression(groupExpression);
+        }
+        return memoRoot;
     }
 
     private Group copyToMemo(Group root) {
-        if (!root.isJoinGroup()) {
+        if (root.getGroupId() != null) {
             return root;
         }
         GroupExpression groupExpression = root.getLogicalExpression();
@@ -105,6 +130,11 @@ public class JoinOrderJob extends Job {
      * @param hyperGraph build hyperGraph
      */
     public void buildGraph(Group group, HyperGraph hyperGraph) {
+        if (group.isProjectGroup()) {
+            buildGraph(group.getLogicalExpression().child(0), hyperGraph);
+            processProjectPlan(hyperGraph, group);
+            return;
+        }
         if (!group.isJoinGroup()) {
             hyperGraph.addNode(optimizePlan(group));
             return;
@@ -113,4 +143,26 @@ public class JoinOrderJob extends Job {
         buildGraph(group.getLogicalExpression().child(1), hyperGraph);
         hyperGraph.addEdge(group);
     }
+
+    /**
+     * Process project expression in HyperGraph
+     * 1. If it's a simple expression for column pruning, we just ignore it
+     * 2. If it's an alias that may be used in the join operator, we need to 
add it to graph
+     * 3. If it's other expression, we can ignore them and add it after 
optimizing
+     * 4. If it's a project only associate with one table, it's seen as an 
endNode just like a table
+     */
+    private void processProjectPlan(HyperGraph hyperGraph, Group group) {
+        LogicalProject<? extends Plan> logicalProject = (LogicalProject<? 
extends Plan>) group.getLogicalExpression()
+                .getPlan();
+
+        for (NamedExpression expr : logicalProject.getProjects()) {
+            if (expr.isAlias()) {
+                if (!hyperGraph.addAlias((Alias) expr, group)) {
+                    break;
+                }
+            } else if (!expr.isSlot()) {
+                otherProject.add(expr);
+            }
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java
index 7e3f96af02..365a12a727 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java
@@ -19,7 +19,9 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
 
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap;
 import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.trees.expressions.Alias;
 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.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -29,6 +31,8 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -37,8 +41,11 @@ import java.util.Set;
  * It's used for join ordering
  */
 public class HyperGraph {
-    private List<Edge> edges = new ArrayList<>();
-    private List<Node> nodes = new ArrayList<>();
+    private final List<Edge> edges = new ArrayList<>();
+    private final List<Node> nodes = new ArrayList<>();
+    private final HashSet<Group> nodeSet = new HashSet<>();
+    private final HashMap<Slot, Long> slotToNodeMap = new HashMap<>();
+    private final HashMap<Long, NamedExpression> complexProject = new 
HashMap<>();
 
     public List<Edge> getEdges() {
         return edges;
@@ -60,12 +67,59 @@ public class HyperGraph {
         return nodes.get(index);
     }
 
+    /**
+     * Store the relation between Alias Slot and Original Slot and its 
expression
+     * e.g. a = b
+     * project((c + d) as b)
+     * Note if the alias if the alias only associated with one endNode,
+     * e.g. a = b
+     * project((c + 1) as b)
+     * we need to replace the group of that node with this project group.
+     *
+     * @param alias The alias Expression in project Operator
+     */
+    public boolean addAlias(Alias alias, Group group) {
+        long bitmap = LongBitmap.newBitmap();
+        for (Slot slot : alias.getInputSlots()) {
+            bitmap = LongBitmap.or(bitmap, slotToNodeMap.get(slot));
+        }
+        Slot aliasSlot = alias.toSlot();
+        Preconditions.checkArgument(!slotToNodeMap.containsKey(aliasSlot));
+        slotToNodeMap.put(aliasSlot, bitmap);
+        if (LongBitmap.getCardinality(bitmap) == 1) {
+            int index = LongBitmap.lowestOneIndex(bitmap);
+            nodeSet.remove(nodes.get(index).getGroup());
+            nodeSet.add(group);
+            nodes.get(index).replaceGroupWith(group);
+            return false;
+        }
+        complexProject.put(bitmap, alias);
+        return true;
+    }
+
+    /**
+     * add end node to HyperGraph
+     *
+     * @param group The group that is the end node in graph
+     */
     public void addNode(Group group) {
         Preconditions.checkArgument(!group.isJoinGroup());
-        // TODO: replace plan with group expression or others
+        for (Slot slot : group.getLogicalExpression().getPlan().getOutput()) {
+            Preconditions.checkArgument(!slotToNodeMap.containsKey(slot));
+            slotToNodeMap.put(slot, LongBitmap.newBitmap(nodes.size()));
+        }
+        nodeSet.add(group);
         nodes.add(new Node(nodes.size(), group));
     }
 
+    public boolean isNodeGroup(Group group) {
+        return nodeSet.contains(group);
+    }
+
+    public HashMap<Long, NamedExpression> getComplexProject() {
+        return complexProject;
+    }
+
     /**
      * try to add edge for join group
      *
@@ -78,15 +132,12 @@ public class HyperGraph {
             LogicalJoin singleJoin = new LogicalJoin(join.getJoinType(), 
ImmutableList.of(expression), join.left(),
                     join.right());
             Edge edge = new Edge(singleJoin, edges.size());
-            long bitmap = findNodes(expression.getInputSlots());
-            Preconditions.checkArgument(LongBitmap.getCardinality(bitmap) == 2,
-                    String.format("HyperGraph has not supported polynomial %s 
yet", expression));
-            int leftIndex = LongBitmap.nextSetBit(bitmap, 0);
-            long left = LongBitmap.newBitmap(leftIndex);
-            edge.addLeftNode(left);
-            int rightIndex = LongBitmap.nextSetBit(bitmap, leftIndex + 1);
-            long right = LongBitmap.newBitmap(rightIndex);
-            edge.addRightNode(right);
+            Preconditions.checkArgument(expression.children().size() == 2);
+
+            long left = calNodeMap(expression.child(0).getInputSlots());
+            edge.setLeft(left);
+            long right = calNodeMap(expression.child(1).getInputSlots());
+            edge.setRight(right);
             for (int nodeIndex : 
LongBitmap.getIterator(edge.getReferenceNodes())) {
                 nodes.get(nodeIndex).attachEdge(edge);
             }
@@ -96,15 +147,12 @@ public class HyperGraph {
         // We don't implement this trick now.
     }
 
-    private long findNodes(Set<Slot> slots) {
+    private long calNodeMap(Set<Slot> slots) {
+        Preconditions.checkArgument(slots.size() != 0);
         long bitmap = LongBitmap.newBitmap();
-        for (Node node : nodes) {
-            for (Slot slot : node.getPlan().getOutput()) {
-                if (slots.contains(slot)) {
-                    bitmap = LongBitmap.set(bitmap, node.getIndex());
-                    break;
-                }
-            }
+        for (Slot slot : slots) {
+            Preconditions.checkArgument(slotToNodeMap.containsKey(slot));
+            bitmap = LongBitmap.or(bitmap, slotToNodeMap.get(slot));
         }
         return bitmap;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Node.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Node.java
index e25be46ad2..ee26e31629 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Node.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Node.java
@@ -37,6 +37,10 @@ public class Node {
         this.index = index;
     }
 
+    public void replaceGroupWith(Group group) {
+        this.group = group;
+    }
+
     public int getIndex() {
         return index;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/SubgraphEnumerator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/SubgraphEnumerator.java
index 4cf59f37b5..c88bf23b6c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/SubgraphEnumerator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/SubgraphEnumerator.java
@@ -120,7 +120,7 @@ public class SubgraphEnumerator {
                 if (edges.isEmpty()) {
                     continue;
                 }
-                if (!receiver.emitCsgCmp(csg, newCmp, edges)) {
+                if (!receiver.emitCsgCmp(csg, newCmp, edges, 
hyperGraph.getComplexProject())) {
                     return false;
                 }
             }
@@ -144,16 +144,15 @@ public class SubgraphEnumerator {
         forbiddenNodes = LongBitmap.or(forbiddenNodes, csg);
         long neighborhoods = neighborhoodCalculator.calcNeighborhood(csg, 
LongBitmap.clone(forbiddenNodes),
                 edgeCalculator);
-
         for (int nodeIndex : LongBitmap.getReverseIterator(neighborhoods)) {
             long cmp = LongBitmap.newBitmap(nodeIndex);
             // whether there is an edge between csg and cmp
             List<Edge> edges = edgeCalculator.connectCsgCmp(csg, cmp);
-            if (edges.isEmpty()) {
-                continue;
-            }
-            if (!receiver.emitCsgCmp(csg, cmp, edges)) {
-                return false;
+
+            if (!edges.isEmpty()) {
+                if (!receiver.emitCsgCmp(csg, cmp, edges, 
hyperGraph.getComplexProject())) {
+                    return false;
+                }
             }
 
             // In order to avoid enumerate repeated cmp, e.g.,
@@ -191,7 +190,6 @@ public class SubgraphEnumerator {
             forbiddenNodes = LongBitmap.or(forbiddenNodes, subgraph);
             neighborhoods = LongBitmap.andNot(neighborhoods, forbiddenNodes);
             forbiddenNodes = LongBitmap.or(forbiddenNodes, neighborhoods);
-
             for (Edge edge : 
edgeCalculator.foundComplexEdgesContain(subgraph)) {
                 long left = edge.getLeft();
                 long right = edge.getRight();
@@ -268,11 +266,11 @@ public class SubgraphEnumerator {
             simpleContains.or(containSimpleEdges.get(bitmap1));
             simpleContains.or(containSimpleEdges.get(bitmap2));
             BitSet complexContains = new BitSet();
-            simpleContains.or(containComplexEdges.get(bitmap1));
-            simpleContains.or(containComplexEdges.get(bitmap2));
+            complexContains.or(containComplexEdges.get(bitmap1));
+            complexContains.or(containComplexEdges.get(bitmap2));
             BitSet overlaps = new BitSet();
-            simpleContains.or(overlapEdges.get(bitmap1));
-            simpleContains.or(overlapEdges.get(bitmap2));
+            overlaps.or(overlapEdges.get(bitmap1));
+            overlaps.or(overlapEdges.get(bitmap2));
             for (int index : overlaps.stream().toArray()) {
                 Edge edge = edges.get(index);
                 if (isContainEdge(subgraph, edge)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
index 975c351248..d72da70a7c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
@@ -19,20 +19,23 @@ package 
org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver;
 
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.Edge;
 import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 
+import java.util.HashMap;
 import java.util.List;
 
 /**
  * A interface of receiver
  */
 public interface AbstractReceiver {
-    public boolean emitCsgCmp(long csg, long cmp, List<Edge> edges);
+    boolean emitCsgCmp(long csg, long cmp, List<Edge> edges,
+            HashMap<Long, NamedExpression> projectExpression);
 
-    public void addGroup(long bitSet, Group group);
+    void addGroup(long bitSet, Group group);
 
-    public boolean contain(long bitSet);
+    boolean contain(long bitSet);
 
-    public void reset();
+    void reset();
 
-    public Group getBestPlan(long bitSet);
+    Group getBestPlan(long bitSet);
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/Counter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/Counter.java
index 7326bee3f5..9dcaf266e2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/Counter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/Counter.java
@@ -20,6 +20,7 @@ package 
org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.Edge;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap;
 import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 
 import com.google.common.base.Preconditions;
 
@@ -31,9 +32,9 @@ import java.util.List;
  */
 public class Counter implements AbstractReceiver {
     // limit define the max number of csg-cmp pair in this Receiver
-    private int limit;
+    private final int limit;
     private int emitCount = 0;
-    private HashMap<Long, Integer> counter = new HashMap<>();
+    private final HashMap<Long, Integer> counter = new HashMap<>();
 
     public Counter() {
         this.limit = Integer.MAX_VALUE;
@@ -51,7 +52,8 @@ public class Counter implements AbstractReceiver {
      * @param edges the join operator
      * @return the left and the right can be connected by the edge
      */
-    public boolean emitCsgCmp(long left, long right, List<Edge> edges) {
+    public boolean emitCsgCmp(long left, long right, List<Edge> edges,
+            HashMap<Long, NamedExpression> projectExpression) {
         Preconditions.checkArgument(counter.containsKey(left));
         Preconditions.checkArgument(counter.containsKey(right));
         emitCount += 1;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/PlanReceiver.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/PlanReceiver.java
index 93a2f0ce47..26f6c846ca 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/PlanReceiver.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/PlanReceiver.java
@@ -24,8 +24,9 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.stats.StatsCalculator;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -60,7 +61,8 @@ public class PlanReceiver implements AbstractReceiver {
      * @return the left and the right can be connected by the edge
      */
     @Override
-    public boolean emitCsgCmp(long left, long right, List<Edge> edges) {
+    public boolean emitCsgCmp(long left, long right, List<Edge> edges,
+            HashMap<Long, NamedExpression> projectExpression) {
         Preconditions.checkArgument(planTable.containsKey(left));
         Preconditions.checkArgument(planTable.containsKey(right));
         emitCount += 1;
@@ -81,9 +83,12 @@ public class PlanReceiver implements AbstractReceiver {
         if (!planTable.containsKey(fullKey)
                 || 
planTable.get(fullKey).getLogicalExpression().getCostByProperties(PhysicalProperties.ANY)
                 > 
winnerGroup.getLogicalExpression().getCostByProperties(PhysicalProperties.ANY)) 
{
+            // When we decide to store the new Plan, we need to add the 
complex project to it.
+            winnerGroup = tryAddProject(winnerGroup, projectExpression, 
fullKey);
             planTable.put(fullKey, winnerGroup);
         }
         return true;
+
     }
 
     @Override
@@ -108,11 +113,39 @@ public class PlanReceiver implements AbstractReceiver {
         return planTable.get(bitmap);
     }
 
-    private double getSimpleCost(Plan plan) {
-        if (!(plan instanceof LogicalJoin)) {
-            return 
plan.getGroupExpression().get().getOwnerGroup().getStatistics().getRowCount();
+    private double getSimpleCost(Group group) {
+        if (!group.isJoinGroup()) {
+            return group.getStatistics().getRowCount();
+        }
+        return 
group.getLogicalExpression().getCostByProperties(PhysicalProperties.ANY);
+    }
+
+    private Group tryAddProject(Group group, HashMap<Long, NamedExpression> 
projectExpression, long fullKey) {
+        List<NamedExpression> projects = new ArrayList<>();
+        List<Long> removedKey = new ArrayList<>();
+        for (Long bitmap : projectExpression.keySet()) {
+            if (LongBitmap.isSubset(bitmap, fullKey)) {
+                NamedExpression namedExpression = 
projectExpression.get(bitmap);
+                projects.add(namedExpression);
+                removedKey.add(bitmap);
+            }
         }
-        return 
plan.getGroupExpression().get().getCostByProperties(PhysicalProperties.ANY);
+        for (Long bitmap : removedKey) {
+            projectExpression.remove(bitmap);
+        }
+        if (projects.size() != 0) {
+            LogicalProject logicalProject = new LogicalProject<>(projects,
+                    group.getLogicalExpression().getPlan());
+            GroupExpression groupExpression = new 
GroupExpression(logicalProject, Lists.newArrayList(group));
+            groupExpression.updateLowestCostTable(PhysicalProperties.ANY,
+                    Lists.newArrayList(PhysicalProperties.ANY, 
PhysicalProperties.ANY),
+                    
group.getLogicalExpression().getCostByProperties(PhysicalProperties.ANY));
+            Group projectGroup = new Group();
+            projectGroup.addGroupExpression(groupExpression);
+            StatsCalculator.estimate(groupExpression);
+            return projectGroup;
+        }
+        return group;
     }
 
     private Group constructGroup(long left, long right, List<Edge> edges) {
@@ -120,10 +153,8 @@ public class PlanReceiver implements AbstractReceiver {
         Preconditions.checkArgument(planTable.containsKey(right));
         Group leftGroup = planTable.get(left);
         Group rightGroup = planTable.get(right);
-        Plan leftPlan = leftGroup.getLogicalExpression().getPlan();
-        Plan rightPlan = rightGroup.getLogicalExpression().getPlan();
 
-        double cost = getSimpleCost(leftPlan) + getSimpleCost(rightPlan);
+        double cost = getSimpleCost(leftGroup) + getSimpleCost(rightGroup);
         List<Expression> conditions = new ArrayList<>();
         for (Edge edge : edges) {
             conditions.addAll(edge.getJoin().getExpressions());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
index cfce2e6d28..fb2adb9ac6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.properties.PhysicalProperties;
 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.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.util.TreeStringUtils;
 import org.apache.doris.statistics.StatsDeriveResult;
 
@@ -322,6 +323,10 @@ public class Group {
         return getLogicalExpression().getPlan() instanceof LogicalJoin;
     }
 
+    public boolean isProjectGroup() {
+        return getLogicalExpression().getPlan() instanceof LogicalProject;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index 1c24ed1772..ba6a1d73fb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -60,7 +60,7 @@ public class Memo {
     private final Map<GroupId, Group> groups = Maps.newLinkedHashMap();
     // we could not use Set, because Set does not have get method.
     private final Map<GroupExpression, GroupExpression> groupExpressions = 
Maps.newHashMap();
-    private final Group root;
+    private Group root;
 
     // FOR TEST ONLY
     public Memo() {
@@ -71,10 +71,24 @@ public class Memo {
         root = init(plan);
     }
 
+    public static long getStateId() {
+        return stateId;
+    }
+
     public Group getRoot() {
         return root;
     }
 
+    /**
+     * This function used to update the root group when DPHyp change the root 
Group
+     * Note it only used in DPHyp
+     *
+     * @param root The new root Group
+     */
+    public void setRoot(Group root) {
+        this.root = root;
+    }
+
     public List<Group> getGroups() {
         return ImmutableList.copyOf(groups.values());
     }
@@ -83,10 +97,6 @@ public class Memo {
         return groupExpressions;
     }
 
-    public static long getStateId() {
-        return stateId;
-    }
-
     /**
      * Add plan to Memo.
      *
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
index 9ffa29b45e..bdf3d37763 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
@@ -143,6 +143,10 @@ public abstract class Expression extends 
AbstractTreeNode<Expression> implements
         return this instanceof Slot;
     }
 
+    public boolean isAlias() {
+        return this instanceof Alias;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJobTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJobTest.java
deleted file mode 100644
index 93edf18663..0000000000
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/JoinOrderJobTest.java
+++ /dev/null
@@ -1,58 +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.jobs.joinorder;
-
-import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.trees.plans.JoinType;
-import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
-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.PlanChecker;
-import org.apache.doris.nereids.util.PlanConstructor;
-
-import com.google.common.collect.Lists;
-import org.junit.jupiter.api.Test;
-
-public class JoinOrderJobTest {
-    private final LogicalOlapScan scan1 = 
PlanConstructor.newLogicalOlapScan(1, "t1", 0);
-    private final LogicalOlapScan scan2 = 
PlanConstructor.newLogicalOlapScan(2, "t2", 0);
-    private final LogicalOlapScan scan3 = 
PlanConstructor.newLogicalOlapScan(3, "t3", 0);
-    private final LogicalOlapScan scan4 = 
PlanConstructor.newLogicalOlapScan(4, "t4", 0);
-    private final LogicalOlapScan scan5 = 
PlanConstructor.newLogicalOlapScan(5, "t5", 0);
-
-    @Test
-    void testJoinOrderJob() {
-        LogicalPlan plan = new LogicalPlanBuilder(scan1)
-                .hashJoinUsing(
-                        new LogicalPlanBuilder(scan2)
-                                .hashJoinUsing(scan3, JoinType.INNER_JOIN, 
Pair.of(0, 1))
-                                .hashJoinUsing(scan4, JoinType.INNER_JOIN, 
Pair.of(0, 1))
-                                .hashJoinUsing(scan5, JoinType.INNER_JOIN, 
Pair.of(0, 1))
-                                .build(),
-                        JoinType.INNER_JOIN, Pair.of(0, 1)
-                )
-                .project(Lists.newArrayList(1))
-                .build();
-        System.out.println(plan.treeString());
-        PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
-                .deriveStats()
-                .orderJoin()
-                .printlnTree();
-    }
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/TPCHTest.java
similarity index 60%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
copy to 
fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/TPCHTest.java
index 975c351248..7eaf021459 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/receiver/AbstractReceiver.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/TPCHTest.java
@@ -15,24 +15,22 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver;
-
-import org.apache.doris.nereids.jobs.joinorder.hypergraph.Edge;
-import org.apache.doris.nereids.memo.Group;
-
-import java.util.List;
-
-/**
- * A interface of receiver
- */
-public interface AbstractReceiver {
-    public boolean emitCsgCmp(long csg, long cmp, List<Edge> edges);
-
-    public void addGroup(long bitSet, Group group);
-
-    public boolean contain(long bitSet);
-
-    public void reset();
-
-    public Group getBestPlan(long bitSet);
+package org.apache.doris.nereids.jobs.joinorder;
+
+import org.apache.doris.nereids.datasets.tpch.TPCHTestBase;
+import org.apache.doris.nereids.datasets.tpch.TPCHUtils;
+import org.apache.doris.nereids.util.PlanChecker;
+
+import org.junit.jupiter.api.Test;
+
+public class TPCHTest extends TPCHTestBase {
+    @Test
+    void testQ5() {
+        PlanChecker.from(connectContext)
+                .analyze(TPCHUtils.Q5)
+                .rewrite()
+                .deriveStats()
+                .orderJoin()
+                .optimize();
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/JoinOrderJobTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/JoinOrderJobTest.java
new file mode 100644
index 0000000000..ac11c5ebbb
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/JoinOrderJobTest.java
@@ -0,0 +1,91 @@
+// 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.sqltest;
+
+import org.apache.doris.nereids.util.PlanChecker;
+
+import org.junit.jupiter.api.Test;
+
+public class JoinOrderJobTest extends SqlTestBase {
+    @Test
+    protected void testSimpleSQL() {
+        String sql = "select * from T1, T2, T3, T4 "
+                + "where "
+                + "T1.id = T2.id and "
+                + "T2.score = T3.score and "
+                + "T3.id = T4.id";
+        PlanChecker.from(connectContext)
+                .analyze(sql)
+                .rewrite()
+                .deriveStats()
+                .orderJoin()
+                .printlnTree();
+    }
+
+    @Test
+    protected void testSimpleSQLWithProject() {
+        String sql = "select T1.id from T1, T2, T3, T4 "
+                + "where "
+                + "T1.id = T2.id and "
+                + "T2.score = T3.score and "
+                + "T3.id = T4.id";
+        PlanChecker.from(connectContext)
+                .analyze(sql)
+                .rewrite()
+                .deriveStats()
+                .orderJoin()
+                .printlnTree();
+    }
+
+    @Test
+    protected void testComplexProject() {
+        String sql = "select count(*) \n"
+                + "from \n"
+                + "T1, \n"
+                + "(\n"
+                + "select (T2.score + T3.score) as score from T2 join T3 on 
T2.id = T3.id"
+                + ") subTable, \n"
+                + "( \n"
+                + "select (T4.id*2) as id from T4"
+                + ") doubleT4 \n"
+                + "where \n"
+                + "T1.id = doubleT4.id and \n"
+                + "T1.score = subTable.score;\n";
+        PlanChecker.from(connectContext)
+                .analyze(sql)
+                .rewrite()
+                .deriveStats()
+                .orderJoin()
+                .printlnTree();
+    }
+
+    @Test
+    protected void test() {
+        String sql = "select count(*) \n"
+                + "from \n"
+                + "T1 \n"
+                + " join (\n"
+                + "select (1) from T2"
+                + ") subTable; \n";
+        PlanChecker.from(connectContext)
+                .analyze(sql)
+                .rewrite()
+                .deriveStats()
+                .printlnTree();
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/HyperGraphBuilder.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/HyperGraphBuilder.java
index 7a107880b9..49e80acccf 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/HyperGraphBuilder.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/HyperGraphBuilder.java
@@ -46,9 +46,9 @@ import java.util.Optional;
 import java.util.Set;
 
 public class HyperGraphBuilder {
-    private List<Integer> rowCounts = new ArrayList<>();
-    private HashMap<BitSet, LogicalPlan> plans = new HashMap<>();
-    private HashMap<BitSet, List<Integer>> schemas = new HashMap<>();
+    private final List<Integer> rowCounts = new ArrayList<>();
+    private final HashMap<BitSet, LogicalPlan> plans = new HashMap<>();
+    private final HashMap<BitSet, List<Integer>> schemas = new HashMap<>();
 
     public HyperGraph build() {
         assert plans.size() == 1 : "there are cross join";
@@ -215,9 +215,9 @@ public class HyperGraphBuilder {
         List<Expression> conditions = new ArrayList<>(join.getExpressions());
         Set<Slot> inputs = condition.getInputSlots();
         if (leftSlots.containsAll(inputs)) {
-            left = (LogicalJoin) attachCondition(condition, (LogicalJoin) 
left);
+            left = attachCondition(condition, (LogicalJoin) left);
         } else if (rightSlots.containsAll(inputs)) {
-            right = (LogicalJoin) attachCondition(condition, (LogicalJoin) 
right);
+            right = attachCondition(condition, (LogicalJoin) right);
         } else {
             conditions.add(condition);
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
index a86ce9ed6c..fa7376885e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.StatementContext;
 import org.apache.doris.nereids.glue.LogicalPlanAdapter;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.jobs.batch.NereidsRewriteJobExecutor;
+import org.apache.doris.nereids.jobs.batch.OptimizeRulesJob;
 import org.apache.doris.nereids.jobs.cascades.DeriveStatsJob;
 import org.apache.doris.nereids.jobs.joinorder.JoinOrderJob;
 import org.apache.doris.nereids.memo.Group;
@@ -38,8 +39,10 @@ import org.apache.doris.nereids.rules.RuleFactory;
 import org.apache.doris.nereids.rules.RuleSet;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.OriginStatement;
@@ -165,6 +168,11 @@ public class PlanChecker {
         return this;
     }
 
+    public PlanChecker optimize() {
+        new OptimizeRulesJob(cascadesContext).execute();
+        return this;
+    }
+
     public PlanChecker implement() {
         Plan plan = 
transformToPhysicalPlan(cascadesContext.getMemo().getRoot());
         Assertions.assertTrue(plan instanceof PhysicalPlan);
@@ -269,9 +277,22 @@ public class PlanChecker {
     }
 
     public PlanChecker orderJoin() {
-        cascadesContext.pushJob(
-                new JoinOrderJob(cascadesContext.getMemo().getRoot(), 
cascadesContext.getCurrentJobContext()));
+        Group root = cascadesContext.getMemo().getRoot();
+        boolean changeRoot = false;
+        if (root.isJoinGroup()) {
+            List<Slot> outputs = 
root.getLogicalExpression().getPlan().getOutput();
+            GroupExpression newExpr = new GroupExpression(
+                    new LogicalProject(outputs, 
root.getLogicalExpression().getPlan()),
+                    Lists.newArrayList(root));
+            root = new Group();
+            root.addGroupExpression(newExpr);
+            changeRoot = true;
+        }
+        cascadesContext.pushJob(new JoinOrderJob(root, 
cascadesContext.getCurrentJobContext()));
         cascadesContext.getJobScheduler().executeJobPool(cascadesContext);
+        if (changeRoot) {
+            
cascadesContext.getMemo().setRoot(root.getLogicalExpression().child(0));
+        }
         return this;
     }
 


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


Reply via email to