This is an automated email from the ASF dual-hosted git repository. starocean999 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 4444abc828 avoid contruct groupExpr in graph-simplifier (#16436) 4444abc828 is described below commit 4444abc828d3ab05bd31b6a3720eed2013a7877e Author: 谢健 <jianx...@gmail.com> AuthorDate: Tue Feb 14 17:03:21 2023 +0800 avoid contruct groupExpr in graph-simplifier (#16436) Signed-off-by: xiejiann <jianx...@gmail.com> --- .../java/org/apache/doris/nereids/PlanContext.java | 50 ++++--- .../apache/doris/nereids/cost/CostCalculator.java | 14 +- .../nereids/jobs/joinorder/hypergraph/Edge.java | 8 -- .../jobs/joinorder/hypergraph/GraphSimplifier.java | 149 +++++++++++---------- .../nereids/properties/RequestPropertyDeriver.java | 4 +- .../doris/nereids/stats/StatsCalculator.java | 1 - .../doris/nereids/trees/plans/GroupPlan.java | 6 + 7 files changed, 118 insertions(+), 114 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java index 6ad6327291..1d2a1dd343 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java @@ -17,15 +17,12 @@ package org.apache.doris.nereids; -import org.apache.doris.common.Id; import org.apache.doris.nereids.memo.GroupExpression; -import org.apache.doris.nereids.properties.LogicalProperties; -import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.statistics.StatsDeriveResult; -import com.google.common.base.Preconditions; - +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -35,44 +32,43 @@ import java.util.List; * Inspired by GPORCA-CExpressionHandle. */ public class PlanContext { - // attached group expression - private final GroupExpression groupExpression; + private List<StatsDeriveResult> childrenStats = new ArrayList<>(); + private StatsDeriveResult planStats = new StatsDeriveResult(0); + private int arity = 0; /** * Constructor for PlanContext. */ public PlanContext(GroupExpression groupExpression) { - this.groupExpression = groupExpression; - } - - public GroupExpression getGroupExpression() { - return groupExpression; + this.arity = groupExpression.arity(); + if (groupExpression.getOwnerGroup() == null) { + return; + } + planStats = groupExpression.getOwnerGroup().getStatistics(); + childrenStats = new ArrayList<>(); + for (int i = 0; i < groupExpression.arity(); i++) { + childrenStats.add(groupExpression.childStatistics(i)); + } } - public StatsDeriveResult getStatisticsWithCheck() { - StatsDeriveResult statistics = groupExpression.getOwnerGroup().getStatistics(); - Preconditions.checkNotNull(statistics); - return statistics; + public PlanContext(StatsDeriveResult planStats, StatsDeriveResult... childrenStats) { + this.planStats = planStats; + this.childrenStats = Arrays.asList(childrenStats); + this.arity = this.childrenStats.size(); } - public LogicalProperties childLogicalPropertyAt(int index) { - return groupExpression.child(index).getLogicalProperties(); + public int arity() { + return arity; } - public List<Slot> getChildOutputSlots(int index) { - return childLogicalPropertyAt(index).getOutput(); - } - - public List<Id> getChildOutputIds(int index) { - return childLogicalPropertyAt(index).getOutputExprIds(); + public StatsDeriveResult getStatisticsWithCheck() { + return planStats; } /** * Get child statistics. */ public StatsDeriveResult getChildStatistics(int index) { - StatsDeriveResult statistics = groupExpression.child(index).getStatistics(); - Preconditions.checkNotNull(statistics); - return statistics; + return childrenStats.get(index); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java index 51fb7076f4..994566362b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java @@ -90,6 +90,14 @@ public class CostCalculator { return costWeight.calculate(costEstimate); } + public static double calculateCost(Plan plan, PlanContext planContext) { + CostEstimator costCalculator = new CostEstimator(); + CostEstimate costEstimate = plan.accept(costCalculator, planContext); + CostWeight costWeight = new CostWeight(CPU_WEIGHT, MEMORY_WEIGHT, NETWORK_WEIGHT, + ConnectContext.get().getSessionVariable().getNereidsCboPenaltyFactor()); + return costWeight.calculate(costEstimate); + } + private static class CostEstimator extends PlanVisitor<CostEstimate, PlanContext> { @Override public CostEstimate visit(Plan plan, PlanContext context) { @@ -226,8 +234,8 @@ public class CostCalculator { @Override public CostEstimate visitPhysicalHashJoin( PhysicalHashJoin<? extends Plan, ? extends Plan> physicalHashJoin, PlanContext context) { - Preconditions.checkState(context.getGroupExpression().arity() == 2); - StatsDeriveResult outputStats = physicalHashJoin.getGroupExpression().get().getOwnerGroup().getStatistics(); + Preconditions.checkState(context.arity() == 2); + StatsDeriveResult outputStats = context.getStatisticsWithCheck(); double outputRowCount = outputStats.getRowCount(); StatsDeriveResult probeStats = context.getChildStatistics(0); @@ -268,7 +276,7 @@ public class CostCalculator { PhysicalNestedLoopJoin<? extends Plan, ? extends Plan> nestedLoopJoin, PlanContext context) { // TODO: copy from physicalHashJoin, should update according to physical nested loop join properties. - Preconditions.checkState(context.getGroupExpression().arity() == 2); + Preconditions.checkState(context.arity() == 2); StatsDeriveResult leftStatistics = context.getChildStatistics(0); StatsDeriveResult rightStatistics = context.getChildStatistics(1); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Edge.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Edge.java index 87a8876a80..e6c247fdee 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Edge.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/Edge.java @@ -19,7 +19,6 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph; import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.plans.GroupPlan; 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; @@ -126,13 +125,6 @@ public class Edge { return join.getExpressions().get(0); } - private double getRowCount(Plan plan) { - if (plan instanceof GroupPlan) { - return ((GroupPlan) plan).getGroup().getStatistics().getRowCount(); - } - return plan.getGroupExpression().get().getOwnerGroup().getStatistics().getRowCount(); - } - @Override public String toString() { return String.format("<%s - %s>", LongBitmap.toString(left), LongBitmap.toString(right)); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/GraphSimplifier.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/GraphSimplifier.java index 9f71e217cd..a0817daf9d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/GraphSimplifier.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/GraphSimplifier.java @@ -17,15 +17,17 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph; +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.PlanContext; +import org.apache.doris.nereids.cost.CostCalculator; import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap; import org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver.Counter; -import org.apache.doris.nereids.memo.Group; -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.plans.GroupPlan; -import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.stats.JoinEstimation; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; +import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin; +import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin; +import org.apache.doris.nereids.util.JoinUtils; +import org.apache.doris.statistics.StatsDeriveResult; import com.google.common.base.Preconditions; @@ -41,8 +43,8 @@ import javax.annotation.Nullable; * GraphSimplifier is used to simplify HyperGraph {@link HyperGraph}. * <p> * Related paper: - * - [Neu09] Neumann: “Query Simplification: Graceful Degradation for Join-Order Optimization”. - * - [Rad19] Radke and Neumann: “LinDP++: Generalizing Linearized DP to Crossproducts and Non-Inner Joins”. + * - [Neu09] Neumann: “Query Simplification: Graceful Degradation for Join-Order Optimization”. + * - [Rad19] Radke and Neumann: “LinDP++: Generalizing Linearized DP to Crossproducts and Non-Inner Joins”. */ public class GraphSimplifier { // Note that each index in the graph simplifier is the half of the actual index @@ -56,10 +58,11 @@ public class GraphSimplifier { private final PriorityQueue<BestSimplification> priorityQueue = new PriorityQueue<>(); // The graph we are simplifying private final HyperGraph graph; - // It cached the plan in simplification. we don't store it in hyper graph, + // It cached the plan stats in simplification. we don't store it in hyper graph, // because it's just used for simulating join. In fact, the graph simplifier // just generate the partial order of join operator. - private final HashMap<Long, Plan> cachePlan = new HashMap<>(); + private final HashMap<Long, StatsDeriveResult> cacheStats = new HashMap<>(); + private final HashMap<Long, Double> cacheCost = new HashMap<>(); private final Stack<SimplificationStep> appliedSteps = new Stack<>(); private final Stack<SimplificationStep> unAppliedSteps = new Stack<>(); @@ -77,7 +80,8 @@ public class GraphSimplifier { simplifications.add(bestSimplification); } for (Node node : graph.getNodes()) { - cachePlan.put(node.getNodeMap(), node.getPlan()); + cacheStats.put(node.getNodeMap(), node.getGroup().getStatistics()); + cacheCost.put(node.getNodeMap(), node.getGroup().getStatistics().getRowCount()); } circleDetector = new CircleDetector(edgeSize); @@ -179,7 +183,7 @@ public class GraphSimplifier { } appliedSteps.push(bestStep); Preconditions.checkArgument( - cachePlan.containsKey(bestStep.newLeft) && cachePlan.containsKey(bestStep.newRight), + cacheStats.containsKey(bestStep.newLeft) && cacheStats.containsKey(bestStep.newRight), String.format("%s - %s", bestStep.newLeft, bestStep.newRight)); graph.modifyEdge(bestStep.afterIndex, bestStep.newLeft, bestStep.newRight); if (needProcessNeighbor) { @@ -306,8 +310,8 @@ public class GraphSimplifier { long right1 = edge1.getRight(); long left2 = edge2.getLeft(); long right2 = edge2.getRight(); - Edge edge1Before2; - Edge edge2Before1; + Pair<StatsDeriveResult, Edge> edge1Before2; + Pair<StatsDeriveResult, Edge> edge2Before1; List<Long> superBitset = new ArrayList<>(); if (tryGetSuperset(left1, left2, superBitset)) { // (common Join1 right1) Join2 right2 @@ -337,53 +341,53 @@ public class GraphSimplifier { return Optional.of(simplificationStep); } - Edge threeLeftJoin(long bitmap1, Edge edge1, long bitmap2, Edge edge2, long bitmap3) { + Pair<StatsDeriveResult, Edge> threeLeftJoin(long bitmap1, Edge edge1, long bitmap2, Edge edge2, long bitmap3) { // (plan1 edge1 plan2) edge2 plan3 // The join may have redundant table, e.g., t1,t2 join t3 join t2,t4 // Therefore, the cost is not accurate Preconditions.checkArgument( - cachePlan.containsKey(bitmap1) && cachePlan.containsKey(bitmap2) && cachePlan.containsKey(bitmap3)); - LogicalJoin leftPlan = simulateJoin(cachePlan.get(bitmap1), edge1.getJoin(), cachePlan.get(bitmap2)); - LogicalJoin join = simulateJoin(leftPlan, edge2.getJoin(), cachePlan.get(bitmap3)); - Edge edge = new Edge(join, -1); + cacheStats.containsKey(bitmap1) && cacheStats.containsKey(bitmap2) && cacheStats.containsKey(bitmap3)); + StatsDeriveResult leftStats = JoinEstimation.estimate(cacheStats.get(bitmap1), cacheStats.get(bitmap2), + edge1.getJoin()); + StatsDeriveResult joinStats = JoinEstimation.estimate(leftStats, cacheStats.get(bitmap3), edge2.getJoin()); + Edge edge = new Edge(edge2.getJoin(), -1); long newLeft = LongBitmap.newBitmapUnion(bitmap1, bitmap2); // To avoid overlapping the left and the right, the newLeft is calculated, Note the // newLeft is not totally include the bitset1 and bitset2, we use circle detector to trace the dependency newLeft = LongBitmap.andNot(newLeft, bitmap3); edge.addLeftNodes(newLeft); edge.addRightNode(edge2.getRight()); - cachePlan.put(newLeft, leftPlan); - return edge; + cacheStats.put(newLeft, leftStats); + cacheCost.put(newLeft, calCost(edge2, leftStats, cacheStats.get(bitmap1), cacheStats.get(bitmap2))); + return Pair.of(joinStats, edge); } - Edge threeRightJoin(long bitmap1, Edge edge1, long bitmap2, Edge edge2, long bitmap3) { + Pair<StatsDeriveResult, Edge> threeRightJoin(long bitmap1, Edge edge1, long bitmap2, Edge edge2, long bitmap3) { Preconditions.checkArgument( - cachePlan.containsKey(bitmap1) && cachePlan.containsKey(bitmap2) && cachePlan.containsKey(bitmap3)); + cacheStats.containsKey(bitmap1) && cacheStats.containsKey(bitmap2) && cacheStats.containsKey(bitmap3)); // plan1 edge1 (plan2 edge2 plan3) - LogicalJoin rightPlan = simulateJoin(cachePlan.get(bitmap2), edge2.getJoin(), cachePlan.get(bitmap3)); - LogicalJoin join = simulateJoin(cachePlan.get(bitmap1), edge1.getJoin(), rightPlan); - Edge edge = new Edge(join, -1); + StatsDeriveResult rightStats = JoinEstimation.estimate(cacheStats.get(bitmap2), cacheStats.get(bitmap3), + edge2.getJoin()); + StatsDeriveResult joinStats = JoinEstimation.estimate(cacheStats.get(bitmap1), rightStats, edge1.getJoin()); + Edge edge = new Edge(edge1.getJoin(), -1); long newRight = LongBitmap.newBitmapUnion(bitmap2, bitmap3); newRight = LongBitmap.andNot(newRight, bitmap1); edge.addLeftNode(edge1.getLeft()); edge.addRightNode(newRight); - cachePlan.put(newRight, rightPlan); - return edge; + cacheStats.put(newRight, rightStats); + cacheCost.put(newRight, calCost(edge2, rightStats, cacheStats.get(bitmap2), cacheStats.get(bitmap3))); + return Pair.of(joinStats, edge); } - private Group getGroup(Plan plan) { - if (plan instanceof GroupPlan) { - return ((GroupPlan) plan).getGroup(); - } - Preconditions.checkArgument(plan.getGroupExpression().isPresent(), - "All plan in GraphSimplifier must have a group"); - return plan.getGroupExpression().get().getOwnerGroup(); - } - - private SimplificationStep orderJoin(Edge edge1Before2, Edge edge2Before1, int edgeIndex1, int edgeIndex2) { - double cost1Before2 = getSimpleCost(edge1Before2.getJoin()); - double cost2Before1 = getSimpleCost(edge2Before1.getJoin()); + private SimplificationStep orderJoin(Pair<StatsDeriveResult, Edge> edge1Before2, + Pair<StatsDeriveResult, Edge> edge2Before1, int edgeIndex1, int edgeIndex2) { + double cost1Before2 = calCost(edge1Before2.second, edge1Before2.first, + cacheStats.get(edge1Before2.second.getLeft()), + cacheStats.get(edge1Before2.second.getRight())); + double cost2Before1 = calCost(edge2Before1.second, edge1Before2.first, + cacheStats.get(edge1Before2.second.getLeft()), + cacheStats.get(edge1Before2.second.getRight())); double benefit = Double.MAX_VALUE; SimplificationStep step; // Choose the plan with smaller cost and make the simplification step to replace the old edge by it. @@ -392,15 +396,17 @@ public class GraphSimplifier { benefit = cost2Before1 / cost1Before2; } // choose edge1Before2 - step = new SimplificationStep(benefit, edgeIndex1, edgeIndex2, edge1Before2.getLeft(), - edge1Before2.getRight(), graph.getEdge(edgeIndex2).getLeft(), graph.getEdge(edgeIndex2).getRight()); + step = new SimplificationStep(benefit, edgeIndex1, edgeIndex2, edge1Before2.second.getLeft(), + edge1Before2.second.getRight(), graph.getEdge(edgeIndex2).getLeft(), + graph.getEdge(edgeIndex2).getRight()); } else { if (cost2Before1 != 0) { benefit = cost1Before2 / cost2Before1; } // choose edge2Before1 - step = new SimplificationStep(benefit, edgeIndex2, edgeIndex1, edge2Before1.getLeft(), - edge2Before1.getRight(), graph.getEdge(edgeIndex1).getLeft(), graph.getEdge(edgeIndex1).getRight()); + step = new SimplificationStep(benefit, edgeIndex2, edgeIndex1, edge2Before1.second.getLeft(), + edge2Before1.second.getRight(), graph.getEdge(edgeIndex1).getLeft(), + graph.getEdge(edgeIndex1).getRight()); } return step; } @@ -416,35 +422,32 @@ public class GraphSimplifier { return false; } - private double getSimpleCost(Plan plan) { - if (!(plan instanceof LogicalJoin)) { - return plan.getGroupExpression().get().getOwnerGroup().getStatistics().getRowCount(); - } - return plan.getGroupExpression().get().getCostByProperties(PhysicalProperties.ANY); - } - - private LogicalJoin simulateJoin(Plan plan1, LogicalJoin join, Plan plan2) { - // In Graph Simplifier, we use the simple cost model, that is - // Plan.cost = Plan.rowCount + Plan.children1.cost + Plan.children2.cost - // The reason is that this cost model has ASI (adjacent sequence interchange) property. - // TODO: consider network, data distribution cost - LogicalJoin newJoin = new LogicalJoin<>(join.getJoinType(), plan1, plan2); - List<Group> children = new ArrayList<>(); - children.add(getGroup(plan1)); - children.add(getGroup(plan2)); - double cost = getSimpleCost(plan1) + getSimpleCost(plan2); - GroupExpression groupExpression = new GroupExpression(newJoin, children); - // FIXME: use wrong constructor - Group group = new Group(null, groupExpression, null); - StatsCalculator.estimate(groupExpression); - cost += group.getStatistics().getRowCount(); - - List<PhysicalProperties> inputs = new ArrayList<>(); - inputs.add(PhysicalProperties.ANY); - inputs.add(PhysicalProperties.ANY); - groupExpression.updateLowestCostTable(PhysicalProperties.ANY, inputs, cost); - - return newJoin.withGroupExpression(Optional.of(groupExpression)); + private double calCost(Edge edge, StatsDeriveResult stats, + StatsDeriveResult leftStats, StatsDeriveResult rightStats) { + LogicalJoin join = edge.getJoin(); + PlanContext planContext = new PlanContext(stats, leftStats, rightStats); + double cost = 0; + if (JoinUtils.shouldNestedLoopJoin(join)) { + PhysicalNestedLoopJoin nestedLoopJoin = new PhysicalNestedLoopJoin<>( + join.getJoinType(), + join.getHashJoinConjuncts(), + join.getOtherJoinConjuncts(), + join.getLogicalProperties(), + join.left(), + join.right()); + cost = CostCalculator.calculateCost(nestedLoopJoin, planContext); + } else { + PhysicalHashJoin hashJoin = new PhysicalHashJoin<>( + join.getJoinType(), + join.getHashJoinConjuncts(), + join.getOtherJoinConjuncts(), + join.getHint(), + join.getLogicalProperties(), + join.left(), + join.right()); + cost = CostCalculator.calculateCost(hashJoin, planContext); + } + return cost + cacheCost.get(edge.getLeft()) + cacheCost.get(edge.getRight()); } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java index 08f0fe1b8d..d2161cfa9a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java @@ -86,8 +86,8 @@ public class RequestPropertyDeriver extends PlanVisitor<Void, PlanContext> { } List<PhysicalProperties> requiredPropertyList = - Lists.newArrayListWithCapacity(context.getGroupExpression().arity()); - for (int i = context.getGroupExpression().arity(); i > 0; --i) { + Lists.newArrayListWithCapacity(context.arity()); + for (int i = context.arity(); i > 0; --i) { requiredPropertyList.add(PhysicalProperties.ANY); } addRequestPropertyToChildren(requiredPropertyList); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java index a53f1ef82f..c29793238d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java @@ -101,7 +101,6 @@ import java.util.stream.Collectors; * Used to calculate the stats for each plan */ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void> { - private final GroupExpression groupExpression; private StatsCalculator(GroupExpression groupExpression) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java index 8967891521..a47968e1bd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java @@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.statistics.StatsDeriveResult; import com.google.common.collect.ImmutableList; @@ -58,6 +59,11 @@ public class GroupPlan extends LogicalLeaf { return ImmutableList.of(); } + @Override + public StatsDeriveResult getStats() { + throw new IllegalStateException("GroupPlan can not invoke getStats()"); + } + @Override public GroupPlan withOutput(List<Slot> output) { throw new IllegalStateException("GroupPlan can not invoke withOutput()"); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org