diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java index b28db7c0f8f670..500f0360a5e7f1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java @@ -808,9 +808,13 @@ public PlanFragment visitPhysicalTopN(PhysicalTopN topN, PlanTra PlanFragment inputFragment = topN.child(0).accept(this, context); PlanFragment currentFragment = inputFragment; - //1. generate new fragment for sort when the child is exchangeNode - if (inputFragment.getPlanRoot() instanceof ExchangeNode) { - Preconditions.checkArgument(!topN.getSortPhase().isLocal()); + //1. Generate new fragment for sort when the child is exchangeNode, If the child is + // mergingExchange, it means we have always generated a new fragment when processing mergeSort + if (inputFragment.getPlanRoot() instanceof ExchangeNode + && !((ExchangeNode) inputFragment.getPlanRoot()).isMergingExchange()) { + // Except LocalTopN->MergeTopN, we don't allow localTopN's child is Exchange Node + Preconditions.checkArgument(!topN.getSortPhase().isLocal(), + "local sort requires any property but child is" + inputFragment.getPlanRoot()); DataPartition outputPartition = DataPartition.UNPARTITIONED; ExchangeNode exchangeNode = (ExchangeNode) inputFragment.getPlanRoot(); inputFragment.setOutputPartition(outputPartition); @@ -822,20 +826,28 @@ public PlanFragment visitPhysicalTopN(PhysicalTopN topN, PlanTra // 2. According to the type of sort, generate physical plan if (!topN.getSortPhase().isMerge()) { - // For localSort or Gather->Sort, we just need to add sortNode + // For localSort or Gather->Sort, we just need to add TopNNode SortNode sortNode = translateSortNode(topN, inputFragment.getPlanRoot(), context); + sortNode.setOffset(topN.getOffset()); + sortNode.setLimit(topN.getLimit()); currentFragment.addPlanRoot(sortNode); } else { // For mergeSort, we need to push sortInfo to exchangeNode if (!(currentFragment.getPlanRoot() instanceof ExchangeNode)) { // if there is no exchange node for mergeSort - // e.g., localSort -> mergeSort + // e.g., mergeTopN -> localTopN // It means the local has satisfied the Gather property. We can just ignore mergeSort + currentFragment.getPlanRoot().setOffset(topN.getOffset()); + currentFragment.getPlanRoot().setLimit(topN.getLimit()); return currentFragment; } - Preconditions.checkArgument(inputFragment.getPlanRoot() instanceof SortNode); + Preconditions.checkArgument(inputFragment.getPlanRoot() instanceof SortNode, + "mergeSort' child must be sortNode"); SortNode sortNode = (SortNode) inputFragment.getPlanRoot(); - ((ExchangeNode) currentFragment.getPlanRoot()).setMergeInfo(sortNode.getSortInfo()); + ExchangeNode exchangeNode = (ExchangeNode) currentFragment.getPlanRoot(); + exchangeNode.setMergeInfo(sortNode.getSortInfo()); + exchangeNode.setLimit(topN.getLimit()); + exchangeNode.setOffset(topN.getOffset()); } return currentFragment; } @@ -1388,38 +1400,12 @@ public PlanFragment visitPhysicalLimit(PhysicalLimit physicalLim if (inputFragment == null) { return inputFragment; } - + // For case globalLimit(l, o) -> LocalLimit(l+o, 0), that is the LocalLimit has already gathered + // The globalLimit can overwrite the limit and offset, so it's still correct PlanNode child = inputFragment.getPlanRoot(); - - // physical plan: limit --> sort - // after translate, it could be: - // 1. limit->sort => set (limit and offset) on sort - // 2. limit->exchange->sort => set (limit and offset) on exchange, set sort.limit = limit+offset - if (child instanceof SortNode) { - SortNode sort = (SortNode) child; - sort.setLimit(physicalLimit.getLimit()); - sort.setOffset(physicalLimit.getOffset()); - return inputFragment; - } - if (child instanceof ExchangeNode) { - ExchangeNode exchangeNode = (ExchangeNode) child; - exchangeNode.setLimit(physicalLimit.getLimit()); - // we do not check if this is a merging exchange here, - // since this guaranteed by translating logic plan to physical plan - exchangeNode.setOffset(physicalLimit.getOffset()); - if (exchangeNode.getChild(0) instanceof SortNode) { - SortNode sort = (SortNode) exchangeNode.getChild(0); - sort.setLimit(physicalLimit.getLimit() + physicalLimit.getOffset()); - sort.setOffset(0); - } - return inputFragment; - } - // for other PlanNode, just set limit as limit+offset - child.setLimit(physicalLimit.getLimit() + physicalLimit.getOffset()); - PlanFragment planFragment = exchangeToMergeFragment(inputFragment, context); - planFragment.getPlanRoot().setLimit(physicalLimit.getLimit()); - planFragment.getPlanRoot().setOffSetDirectly(physicalLimit.getOffset()); - return planFragment; + child.setLimit(physicalLimit.getLimit()); + child.setOffset(physicalLimit.getOffset()); + return inputFragment; } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/NereidsRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/NereidsRewriter.java index cfe29964f16f6d..1d51420a7edbcf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/NereidsRewriter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/NereidsRewriter.java @@ -61,6 +61,7 @@ import org.apache.doris.nereids.rules.rewrite.logical.PushFilterInsideJoin; import org.apache.doris.nereids.rules.rewrite.logical.PushdownLimit; import org.apache.doris.nereids.rules.rewrite.logical.ReorderJoin; +import org.apache.doris.nereids.rules.rewrite.logical.SplitLimit; import java.util.List; @@ -192,6 +193,7 @@ public class NereidsRewriter extends BatchRewriteJob { new EliminateAggregate(), new MergeSetOperations(), new PushdownLimit(), + new SplitLimit(), new BuildAggForUnion() )), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 185e74368b3fe0..3947e8139b3cc6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -194,6 +194,7 @@ import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; import org.apache.doris.nereids.trees.plans.JoinHint; import org.apache.doris.nereids.trees.plans.JoinType; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.algebra.Aggregate; import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier; @@ -1346,7 +1347,7 @@ private LogicalPlan withLimit(LogicalPlan input, Optional li if (offsetToken != null) { offset = Long.parseLong(offsetToken.getText()); } - return new LogicalLimit<>(limit, offset, input); + return new LogicalLimit<>(limit, offset, LimitPhase.ORIGIN, input); }); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriver.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriver.java index 4de8c769fee735..4623ba311ee970 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriver.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriver.java @@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort; import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows; import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute; import org.apache.doris.nereids.trees.plans.physical.PhysicalEsScan; @@ -39,10 +40,8 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin; import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan; import org.apache.doris.nereids.trees.plans.physical.PhysicalProject; -import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort; import org.apache.doris.nereids.trees.plans.physical.PhysicalStorageLayerAggregate; import org.apache.doris.nereids.trees.plans.physical.PhysicalTVFRelation; -import org.apache.doris.nereids.trees.plans.physical.PhysicalTopN; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.nereids.util.JoinUtils; @@ -105,13 +104,8 @@ public PhysicalProperties visitPhysicalHashAggregate( } @Override - public PhysicalProperties visitPhysicalTopN(PhysicalTopN topN, PlanContext context) { - Preconditions.checkState(childrenOutputProperties.size() == 1); - return new PhysicalProperties(DistributionSpecGather.INSTANCE, new OrderSpec(topN.getOrderKeys())); - } - - @Override - public PhysicalProperties visitPhysicalQuickSort(PhysicalQuickSort sort, PlanContext context) { + public PhysicalProperties visitAbstractPhysicalSort(AbstractPhysicalSort sort, + PlanContext context) { Preconditions.checkState(childrenOutputProperties.size() == 1); if (sort.getSortPhase().isLocal()) { return new PhysicalProperties( 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 77d17f27013660..2ca4fdc169306a 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 @@ -29,11 +29,12 @@ import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction; import org.apache.doris.nereids.trees.plans.JoinHint; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort; import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows; import org.apache.doris.nereids.trees.plans.physical.PhysicalGenerate; import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin; +import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit; import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin; -import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.nereids.util.ExpressionUtils; import org.apache.doris.nereids.util.JoinUtils; @@ -94,7 +95,7 @@ public Void visit(Plan plan, PlanContext context) { } @Override - public Void visitPhysicalQuickSort(PhysicalQuickSort sort, PlanContext context) { + public Void visitAbstractPhysicalSort(AbstractPhysicalSort sort, PlanContext context) { if (!sort.getSortPhase().isLocal()) { addRequestPropertyToChildren(PhysicalProperties.GATHER); } else { @@ -103,6 +104,16 @@ public Void visitPhysicalQuickSort(PhysicalQuickSort sort, PlanC return null; } + @Override + public Void visitPhysicalLimit(PhysicalLimit limit, PlanContext context) { + if (limit.isGlobal()) { + addRequestPropertyToChildren(PhysicalProperties.GATHER); + } else { + addRequestPropertyToChildren(PhysicalProperties.ANY); + } + return null; + } + @Override public Void visitPhysicalHashJoin(PhysicalHashJoin hashJoin, PlanContext context) { JoinHint hint = hashJoin.getHint(); 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 cf55dedddd5da3..13c47df6a4815b 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 @@ -188,13 +188,15 @@ public enum RuleType { INNER_TO_CROSS_JOIN(RuleTypeClass.REWRITE), REWRITE_SENTINEL(RuleTypeClass.REWRITE), + // split limit + SPLIT_LIMIT(RuleTypeClass.REWRITE), // limit push down PUSH_LIMIT_THROUGH_JOIN(RuleTypeClass.REWRITE), PUSH_LIMIT_THROUGH_PROJECT_JOIN(RuleTypeClass.REWRITE), PUSH_LIMIT_THROUGH_UNION(RuleTypeClass.REWRITE), PUSH_LIMIT_THROUGH_ONE_ROW_RELATION(RuleTypeClass.REWRITE), PUSH_LIMIT_THROUGH_EMPTY_RELATION(RuleTypeClass.REWRITE), - + PUSH_LIMIT_INTO_SORT(RuleTypeClass.REWRITE), // adjust nullable ADJUST_NULLABLE_ON_AGGREGATE(RuleTypeClass.REWRITE), ADJUST_NULLABLE_ON_ASSERT_NUM_ROWS(RuleTypeClass.REWRITE), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalLimitToPhysicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalLimitToPhysicalLimit.java index 836d0b03445f4c..5742cee12ea0e1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalLimitToPhysicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalLimitToPhysicalLimit.java @@ -30,6 +30,7 @@ public Rule build() { return logicalLimit().then(limit -> new PhysicalLimit<>( limit.getLimit(), limit.getOffset(), + limit.getPhase(), limit.getLogicalProperties(), limit.child()) ).toRule(RuleType.LOGICAL_LIMIT_TO_PHYSICAL_LIMIT_RULE); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalTopNToPhysicalTopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalTopNToPhysicalTopN.java index ac90ff8acc1631..bf675fe26495ba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalTopNToPhysicalTopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalTopNToPhysicalTopN.java @@ -38,10 +38,16 @@ public Rule build() { .toRule(RuleType.LOGICAL_TOP_N_TO_PHYSICAL_TOP_N_RULE); } + /** + * before: logicalTopN(off, limit) + * after: + * gatherTopN(limit, off, require gather) + * mergeTopN(limit, off, require gather) -> localTopN(off+limit, 0, require any) + */ private List> twoPhaseSort(LogicalTopN logicalTopN) { - PhysicalTopN localSort = new PhysicalTopN(logicalTopN.getOrderKeys(), logicalTopN.getLimit(), - logicalTopN.getOffset(), SortPhase.LOCAL_SORT, logicalTopN.getLogicalProperties(), logicalTopN.child(0) - ); + PhysicalTopN localSort = new PhysicalTopN(logicalTopN.getOrderKeys(), + logicalTopN.getLimit() + logicalTopN.getOffset(), 0, SortPhase.LOCAL_SORT, + logicalTopN.getLogicalProperties(), logicalTopN.child(0)); PhysicalTopN twoPhaseSort = new PhysicalTopN<>(logicalTopN.getOrderKeys(), logicalTopN.getLimit(), logicalTopN.getOffset(), SortPhase.MERGE_SORT, logicalTopN.getLogicalProperties(), localSort); PhysicalTopN onePhaseSort = new PhysicalTopN<>(logicalTopN.getOrderKeys(), logicalTopN.getLimit(), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java index ca776b83b7899b..a7447f1b357fa7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java @@ -28,6 +28,7 @@ import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.JoinHint; import org.apache.doris.nereids.trees.plans.JoinType; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalApply; @@ -117,7 +118,7 @@ private Plan unCorrelatedToJoin(LogicalApply unapply) { } private Plan unCorrelatedNotExist(LogicalApply unapply) { - LogicalLimit newLimit = new LogicalLimit<>(1, 0, (LogicalPlan) unapply.right()); + LogicalLimit newLimit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, (LogicalPlan) unapply.right()); Alias alias = new Alias(new Count(), "count(*)"); LogicalAggregate newAgg = new LogicalAggregate<>(new ArrayList<>(), ImmutableList.of(alias), newLimit); @@ -128,7 +129,7 @@ private Plan unCorrelatedNotExist(LogicalApply unapply) { } private Plan unCorrelatedExist(LogicalApply unapply) { - LogicalLimit newLimit = new LogicalLimit<>(1, 0, (LogicalPlan) unapply.right()); + LogicalLimit newLimit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, (LogicalPlan) unapply.right()); return new LogicalJoin<>(JoinType.CROSS_JOIN, (LogicalPlan) unapply.left(), newLimit); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimits.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimits.java index 4b3ec22fb0810e..1d6b0ab48f5a91 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimits.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimits.java @@ -42,13 +42,16 @@ public class MergeLimits extends OneRewriteRuleFactory { @Override public Rule build() { - return logicalLimit(logicalLimit()).then(upperLimit -> { - LogicalLimit bottomLimit = upperLimit.child(); - return new LogicalLimit<>( - Math.min(upperLimit.getLimit(), Math.max(bottomLimit.getLimit() - upperLimit.getOffset(), 0)), - bottomLimit.getOffset() + upperLimit.getOffset(), - bottomLimit.child() - ); - }).toRule(RuleType.MERGE_LIMITS); + return logicalLimit(logicalLimit()) + .when(upperLimit -> upperLimit.getPhase().equals(upperLimit.child().getPhase())) + .then(upperLimit -> { + LogicalLimit bottomLimit = upperLimit.child(); + return new LogicalLimit<>( + Math.min(upperLimit.getLimit(), + Math.max(bottomLimit.getLimit() - upperLimit.getOffset(), 0)), + bottomLimit.getOffset() + upperLimit.getOffset(), + bottomLimit.getPhase(), bottomLimit.child() + ); + }).toRule(RuleType.MERGE_LIMITS); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimit.java index 1b2b1a442630be..b5b4614410427e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimit.java @@ -28,6 +28,8 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; +import org.apache.doris.nereids.trees.plans.logical.LogicalSort; +import org.apache.doris.nereids.trees.plans.logical.LogicalTopN; import org.apache.doris.nereids.trees.plans.logical.LogicalUnion; import com.google.common.collect.ImmutableList; @@ -82,6 +84,16 @@ public List buildRules() { return limit.withChildren(union.withChildren(newUnionChildren)); }) .toRule(RuleType.PUSH_LIMIT_THROUGH_UNION), + // limit -> sort ==> topN + logicalLimit(logicalSort()) + .then(limit -> { + LogicalSort sort = limit.child(); + LogicalTopN topN = new LogicalTopN(sort.getOrderKeys(), + limit.getLimit(), + limit.getOffset(), + sort.child(0)); + return topN; + }).toRule(RuleType.PUSH_LIMIT_INTO_SORT), logicalLimit(logicalOneRowRelation()) .then(limit -> limit.getLimit() > 0 ? limit.child() : new LogicalEmptyRelation(limit.child().getOutput())) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownProjectThroughLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownProjectThroughLimit.java index b9f0f70d2a1161..c1705250a54a30 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownProjectThroughLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownProjectThroughLimit.java @@ -51,8 +51,8 @@ public Rule build() { return logicalProject(logicalLimit(any())).thenApply(ctx -> { LogicalProject> logicalProject = ctx.root; LogicalLimit logicalLimit = logicalProject.child(); - return new LogicalLimit<>(logicalLimit.getLimit(), - logicalLimit.getOffset(), new LogicalProject<>(logicalProject.getProjects(), + return new LogicalLimit<>(logicalLimit.getLimit(), logicalLimit.getOffset(), + logicalLimit.getPhase(), new LogicalProject<>(logicalProject.getProjects(), logicalLimit.child())); }).toRule(RuleType.PUSHDOWN_PROJECT_THROUGH_LIMIT); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimit.java new file mode 100644 index 00000000000000..abd7b0a49c486d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimit.java @@ -0,0 +1,48 @@ +// 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.plans.LimitPhase; +import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; + +/** + * Split limit into two phase + * before: + * Limit(origin) limit, offset + * after: + * Limit(global) limit, offset + * | + * Limit(local) limit + offset, 0 + */ +public class SplitLimit extends OneRewriteRuleFactory { + @Override + public Rule build() { + return logicalLimit().when(limit -> !limit.isSplit()) + .then(limit -> { + long l = limit.getLimit(); + long o = limit.getOffset(); + return new LogicalLimit<>(l, o, + LimitPhase.GLOBAL, new LogicalLimit<>(l + o, 0, LimitPhase.LOCAL, limit.child()) + ); + }).toRule(RuleType.SPLIT_LIMIT); + } +} + diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LimitPhase.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LimitPhase.java new file mode 100644 index 00000000000000..705c712ef4a1ea --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LimitPhase.java @@ -0,0 +1,38 @@ +// 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.trees.plans; + +/** + * Limit phase for logical and physical limit, like + * LocalLimit -> Gather -> GlobalLimit + * Origin is used to mark the limit operator that has not been split into 2-phase + */ +public enum LimitPhase { + LOCAL("LOCAL"), + GLOBAL("GLOBAL"), + ORIGIN("ORIGIN"); + private final String name; + + LimitPhase(String name) { + this.name = name; + } + + public boolean isLocal() { + return this == LOCAL; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/TopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/TopN.java index d79fe003ed1e25..c214dffbbf0a8f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/TopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/TopN.java @@ -22,7 +22,7 @@ */ public interface TopN extends Sort { - int getOffset(); + long getOffset(); - int getLimit(); + long getLimit(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java index 75ff5f1ea994c3..d632b959e15b93 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.algebra.Limit; @@ -44,19 +45,28 @@ * offset 100 */ public class LogicalLimit extends LogicalUnary implements Limit { - + private final LimitPhase phase; private final long limit; private final long offset; - public LogicalLimit(long limit, long offset, CHILD_TYPE child) { - this(limit, offset, Optional.empty(), Optional.empty(), child); + public LogicalLimit(long limit, long offset, LimitPhase phase, CHILD_TYPE child) { + this(limit, offset, phase, Optional.empty(), Optional.empty(), child); } - public LogicalLimit(long limit, long offset, Optional groupExpression, + public LogicalLimit(long limit, long offset, LimitPhase phase, Optional groupExpression, Optional logicalProperties, CHILD_TYPE child) { super(PlanType.LOGICAL_LIMIT, groupExpression, logicalProperties, child); this.limit = limit; this.offset = offset; + this.phase = phase; + } + + public LimitPhase getPhase() { + return phase; + } + + public boolean isSplit() { + return phase != LimitPhase.ORIGIN; } public long getLimit() { @@ -94,7 +104,7 @@ public boolean equals(Object o) { return false; } LogicalLimit that = (LogicalLimit) o; - return limit == that.limit && offset == that.offset; + return limit == that.limit && offset == that.offset && phase == that.phase; } @Override @@ -108,17 +118,17 @@ public List getExpressions() { @Override public Plan withGroupExpression(Optional groupExpression) { - return new LogicalLimit<>(limit, offset, groupExpression, Optional.of(getLogicalProperties()), child()); + return new LogicalLimit<>(limit, offset, phase, groupExpression, Optional.of(getLogicalProperties()), child()); } @Override public Plan withLogicalProperties(Optional logicalProperties) { - return new LogicalLimit<>(limit, offset, Optional.empty(), logicalProperties, child()); + return new LogicalLimit<>(limit, offset, phase, Optional.empty(), logicalProperties, child()); } @Override public LogicalLimit withChildren(List children) { Preconditions.checkArgument(children.size() == 1); - return new LogicalLimit<>(limit, offset, children.get(0)); + return new LogicalLimit<>(limit, offset, phase, children.get(0)); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java index da78e27cef3b9a..cb07601ffa18cb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java @@ -41,17 +41,17 @@ public class LogicalTopN extends LogicalUnary implements TopN { private final List orderKeys; - private final int limit; - private final int offset; + private final long limit; + private final long offset; - public LogicalTopN(List orderKeys, int limit, int offset, CHILD_TYPE child) { + public LogicalTopN(List orderKeys, long limit, long offset, CHILD_TYPE child) { this(orderKeys, limit, offset, Optional.empty(), Optional.empty(), child); } /** * Constructor for LogicalSort. */ - public LogicalTopN(List orderKeys, int limit, int offset, Optional groupExpression, + public LogicalTopN(List orderKeys, long limit, long offset, Optional groupExpression, Optional logicalProperties, CHILD_TYPE child) { super(PlanType.LOGICAL_TOP_N, groupExpression, logicalProperties, child); this.orderKeys = ImmutableList.copyOf(Objects.requireNonNull(orderKeys, "orderKeys can not be null")); @@ -68,11 +68,11 @@ public List getOrderKeys() { return orderKeys; } - public int getOffset() { + public long getOffset() { return offset; } - public int getLimit() { + public long getLimit() { return limit; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java index dfab80443ca387..8d803d0f884da5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.properties.PhysicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.algebra.Limit; @@ -39,14 +40,14 @@ * Physical limit plan */ public class PhysicalLimit extends PhysicalUnary implements Limit { - + private final LimitPhase phase; private final long limit; private final long offset; public PhysicalLimit(long limit, long offset, - LogicalProperties logicalProperties, + LimitPhase phase, LogicalProperties logicalProperties, CHILD_TYPE child) { - this(limit, offset, Optional.empty(), logicalProperties, child); + this(limit, offset, phase, Optional.empty(), logicalProperties, child); } /** @@ -57,11 +58,12 @@ public PhysicalLimit(long limit, long offset, * @param offset the number of tuples skipped. */ public PhysicalLimit(long limit, long offset, - Optional groupExpression, LogicalProperties logicalProperties, + LimitPhase phase, Optional groupExpression, LogicalProperties logicalProperties, CHILD_TYPE child) { super(PlanType.PHYSICAL_LIMIT, groupExpression, logicalProperties, child); this.limit = limit; this.offset = offset; + this.phase = phase; } /** @@ -70,14 +72,16 @@ public PhysicalLimit(long limit, long offset, * * @param limit the number of tuples retrieved. * @param offset the number of tuples skipped. + * @param phase the phase of 2-phase limit. */ - public PhysicalLimit(long limit, long offset, Optional groupExpression, + public PhysicalLimit(long limit, long offset, LimitPhase phase, Optional groupExpression, LogicalProperties logicalProperties, PhysicalProperties physicalProperties, StatsDeriveResult statsDeriveResult, CHILD_TYPE child) { super(PlanType.PHYSICAL_LIMIT, groupExpression, logicalProperties, physicalProperties, statsDeriveResult, child); this.limit = limit; this.offset = offset; + this.phase = phase; } public long getLimit() { @@ -88,10 +92,18 @@ public long getOffset() { return offset; } + public LimitPhase getPhase() { + return phase; + } + + public boolean isGlobal() { + return phase == LimitPhase.GLOBAL; + } + @Override public Plan withChildren(List children) { Preconditions.checkArgument(children.size() == 1); - return new PhysicalLimit<>(limit, offset, getLogicalProperties(), children.get(0)); + return new PhysicalLimit<>(limit, offset, phase, getLogicalProperties(), children.get(0)); } @Override @@ -101,18 +113,18 @@ public List getExpressions() { @Override public PhysicalLimit withGroupExpression(Optional groupExpression) { - return new PhysicalLimit<>(limit, offset, groupExpression, getLogicalProperties(), child()); + return new PhysicalLimit<>(limit, offset, phase, groupExpression, getLogicalProperties(), child()); } @Override public PhysicalLimit withLogicalProperties(Optional logicalProperties) { - return new PhysicalLimit<>(limit, offset, logicalProperties.get(), child()); + return new PhysicalLimit<>(limit, offset, phase, logicalProperties.get(), child()); } @Override public PhysicalLimit withPhysicalPropertiesAndStats(PhysicalProperties physicalProperties, StatsDeriveResult statsDeriveResult) { - return new PhysicalLimit<>(limit, offset, groupExpression, getLogicalProperties(), physicalProperties, + return new PhysicalLimit<>(limit, offset, phase, groupExpression, getLogicalProperties(), physicalProperties, statsDeriveResult, child()); } @@ -125,7 +137,7 @@ public boolean equals(Object o) { return false; } PhysicalLimit that = (PhysicalLimit) o; - return offset == that.offset && limit == that.limit; + return offset == that.offset && limit == that.limit && phase == that.phase; } @Override @@ -143,6 +155,7 @@ public String toString() { return Utils.toSqlString("PhysicalLimit", "limit", limit, "offset", offset, + "phase", phase, "stats", statsDeriveResult ); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalTopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalTopN.java index 13f52eb10380ec..dc01b0332ae8d3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalTopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalTopN.java @@ -40,10 +40,10 @@ */ public class PhysicalTopN extends AbstractPhysicalSort implements TopN { - private final int limit; - private final int offset; + private final long limit; + private final long offset; - public PhysicalTopN(List orderKeys, int limit, int offset, + public PhysicalTopN(List orderKeys, long limit, long offset, SortPhase phase, LogicalProperties logicalProperties, CHILD_TYPE child) { this(orderKeys, limit, offset, phase, Optional.empty(), logicalProperties, child); } @@ -51,7 +51,7 @@ public PhysicalTopN(List orderKeys, int limit, int offset, /** * Constructor of PhysicalHashJoinNode. */ - public PhysicalTopN(List orderKeys, int limit, int offset, + public PhysicalTopN(List orderKeys, long limit, long offset, SortPhase phase, Optional groupExpression, LogicalProperties logicalProperties, CHILD_TYPE child) { super(PlanType.PHYSICAL_TOP_N, orderKeys, phase, groupExpression, logicalProperties, child); @@ -63,7 +63,7 @@ public PhysicalTopN(List orderKeys, int limit, int offset, /** * Constructor of PhysicalHashJoinNode. */ - public PhysicalTopN(List orderKeys, int limit, int offset, + public PhysicalTopN(List orderKeys, long limit, long offset, SortPhase phase, Optional groupExpression, LogicalProperties logicalProperties, PhysicalProperties physicalProperties, StatsDeriveResult statsDeriveResult, CHILD_TYPE child) { super(PlanType.PHYSICAL_TOP_N, orderKeys, phase, groupExpression, logicalProperties, physicalProperties, @@ -73,11 +73,11 @@ public PhysicalTopN(List orderKeys, int limit, int offset, this.offset = offset; } - public int getLimit() { + public long getLimit() { return limit; } - public int getOffset() { + public long getOffset() { return offset; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java index de6c98f2f79a89..3cda38d6fb6bde 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java @@ -317,7 +317,7 @@ public R visitPhysicalWindow(PhysicalWindow window, C context) { } public R visitPhysicalTopN(PhysicalTopN topN, C context) { - return visit(topN, context); + return visitAbstractPhysicalSort(topN, context); } public R visitPhysicalLimit(PhysicalLimit limit, C context) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java index e268f5a091b73f..40d9d6289ac22c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java @@ -31,6 +31,7 @@ import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.LeafPlan; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; @@ -361,7 +362,7 @@ public void a2newA() { @Test public void a2bc() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit = new LogicalLimit<>(1, 0, student); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student"))) .applyBottomUp( @@ -396,7 +397,7 @@ public void a2ba() { // invalid case Assertions.assertThrows(IllegalStateException.class, () -> { UnboundRelation student = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit = new LogicalLimit<>(1, 0, student); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, student) .applyBottomUp( @@ -414,7 +415,7 @@ public void a2ba() { UnboundRelation a = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); UnboundRelation a2 = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit = new LogicalLimit<>(1, 0, a2); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, a2); PlanChecker.from(connectContext, a) .setMaxInvokeTimesPerRule(1000) .applyBottomUp( @@ -479,8 +480,8 @@ public void a2ab() { @Test public void a2bcd() { LogicalOlapScan scan = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, scan); - LogicalLimit> limit10 = new LogicalLimit<>(10, 0, limit5); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, scan); + LogicalLimit> limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit5); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -507,7 +508,7 @@ public void a2bcd() { @Test public void ab2a() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -531,7 +532,7 @@ public void ab2a() { @Test public void ab2NewA() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -555,7 +556,7 @@ public void ab2NewA() { @Test public void ab2GroupB() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -577,7 +578,7 @@ public void ab2GroupB() { @Test public void ab2PlanB() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -599,7 +600,7 @@ public void ab2PlanB() { @Test public void ab2c() { UnboundRelation relation = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, relation); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, relation); LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); PlanChecker.from(connectContext, limit10) @@ -622,10 +623,10 @@ public void ab2c() { @Test public void ab2cd() { UnboundRelation relation = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, relation); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, relation); LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, student); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -650,8 +651,8 @@ public void ab2cd() { @Test public void ab2cb() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -681,7 +682,7 @@ public void ab2NewANewB() { Assertions.assertThrowsExactly(IllegalStateException.class, () -> { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit10) .setMaxInvokeTimesPerRule(1000) @@ -707,8 +708,8 @@ public void ab2ba() { Assertions.assertThrowsExactly(IllegalStateException.class, () -> { UnboundRelation student = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, student); - LogicalLimit> limit10 = new LogicalLimit<>(10, 0, limit5); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, student); + LogicalLimit> limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit5); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -733,11 +734,11 @@ public void ab2ba() { @Test public void ab2cde() { UnboundRelation student = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit3 = new LogicalLimit<>(3, 0, student); + LogicalLimit limit3 = new LogicalLimit<>(3, 0, LimitPhase.ORIGIN, student); LogicalOlapScan scan = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, scan); - LogicalLimit> limit10 = new LogicalLimit<>(10, 0, limit5); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, scan); + LogicalLimit> limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit5); PlanChecker.from(connectContext, limit3) .applyBottomUp( @@ -766,8 +767,8 @@ public void ab2cde() { public void abc2bac() { UnboundRelation student = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, student); - LogicalLimit> limit10 = new LogicalLimit<>(10, 0, limit5); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, student); + LogicalLimit> limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit5); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -805,8 +806,8 @@ public void abc2bac() { public void abc2bc() { UnboundRelation student = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("student")); - LogicalLimit limit5 = new LogicalLimit<>(5, 0, student); - LogicalLimit> limit10 = new LogicalLimit<>(10, 0, limit5); + LogicalLimit limit5 = new LogicalLimit<>(5, 0, LimitPhase.ORIGIN, student); + LogicalLimit> limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit5); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -829,7 +830,7 @@ public void abc2bc() { @Test public void testRewriteBottomPlanToOnePlan() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit = new LogicalLimit<>(1, 0, student); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, student); LogicalOlapScan score = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); @@ -848,10 +849,10 @@ public void testRewriteBottomPlanToOnePlan() { @Test public void testRewriteBottomPlanToMultiPlan() { LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); LogicalOlapScan score = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); - LogicalLimit limit1 = new LogicalLimit<>(1, 0, score); + LogicalLimit limit1 = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, score); PlanChecker.from(connectContext, limit10) .applyBottomUp( @@ -892,7 +893,7 @@ public void testRewriteUnboundPlanToBound() { @Test public void testRecomputeLogicalProperties() { UnboundRelation unboundTable = new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("score")); - LogicalLimit unboundLimit = new LogicalLimit<>(1, 0, unboundTable); + LogicalLimit unboundLimit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, unboundTable); LogicalOlapScan boundTable = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); LogicalLimit boundLimit = unboundLimit.withChildren(ImmutableList.of(boundTable)); @@ -924,7 +925,7 @@ public void testRecomputeLogicalProperties() { @Test public void testEliminateRootWithChildGroupInTwoLevels() { LogicalOlapScan scan = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); - LogicalLimit limit = new LogicalLimit<>(1, 0, scan); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, scan); PlanChecker.from(connectContext, limit) .applyBottomUp(logicalLimit().then(LogicalLimit::child)) @@ -936,7 +937,7 @@ public void testEliminateRootWithChildGroupInTwoLevels() { @Test public void testEliminateRootWithChildPlanInTwoLevels() { LogicalOlapScan scan = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); - LogicalLimit limit = new LogicalLimit<>(1, 0, scan); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, scan); PlanChecker.from(connectContext, limit) .applyBottomUp(logicalLimit(any()).then(LogicalLimit::child)) @@ -948,7 +949,7 @@ public void testEliminateRootWithChildPlanInTwoLevels() { @Test public void testEliminateTwoLevelsToOnePlan() { LogicalOlapScan score = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); - LogicalLimit limit = new LogicalLimit<>(1, 0, score); + LogicalLimit limit = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, score); LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); @@ -968,10 +969,10 @@ public void testEliminateTwoLevelsToOnePlan() { @Test public void testEliminateTwoLevelsToTwoPlans() { LogicalOlapScan score = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score); - LogicalLimit limit1 = new LogicalLimit<>(1, 0, score); + LogicalLimit limit1 = new LogicalLimit<>(1, 0, LimitPhase.ORIGIN, score); LogicalOlapScan student = new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student); - LogicalLimit limit10 = new LogicalLimit<>(10, 0, student); + LogicalLimit limit10 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, student); PlanChecker.from(connectContext, limit1) .applyBottomUp(logicalLimit(any()).when(limit1::equals).then(l -> limit10)) @@ -998,7 +999,7 @@ public void testEliminateTwoLevelsToTwoPlans() { public void test() { PlanChecker.from(MemoTestUtils.createConnectContext()) .analyze(new LogicalLimit<>(10, 0, - new LogicalJoin<>(JoinType.LEFT_OUTER_JOIN, + LimitPhase.ORIGIN, new LogicalJoin<>(JoinType.LEFT_OUTER_JOIN, ImmutableList.of(new EqualTo(new UnboundSlot("sid"), new UnboundSlot("id"))), new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.score), new LogicalOlapScan(RelationUtil.newRelationId(), PlanConstructor.student) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java index 9cd2888b385349..6d7b2413ffae8b 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java @@ -33,6 +33,7 @@ import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinHint; import org.apache.doris.nereids.trees.plans.JoinType; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.SortPhase; import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalJoin; @@ -385,6 +386,7 @@ public void testQuickSort() { public void testTopN() { SlotReference key = new SlotReference("col1", IntegerType.INSTANCE); List orderKeys = Lists.newArrayList(new OrderKey(key, true, true)); + // localSort require any PhysicalTopN sort = new PhysicalTopN<>(orderKeys, 10, 10, SortPhase.LOCAL_SORT, logicalProperties, groupPlan); GroupExpression groupExpression = new GroupExpression(sort); PhysicalProperties child = new PhysicalProperties(DistributionSpecReplicated.INSTANCE, @@ -394,6 +396,17 @@ public void testTopN() { ChildOutputPropertyDeriver deriver = new ChildOutputPropertyDeriver(Lists.newArrayList(child)); PhysicalProperties result = deriver.getOutputProperties(groupExpression); Assertions.assertEquals(orderKeys, result.getOrderSpec().getOrderKeys()); + Assertions.assertEquals(DistributionSpecReplicated.INSTANCE, result.getDistributionSpec()); + // merge/gather sort requires gather + sort = new PhysicalTopN<>(orderKeys, 10, 10, SortPhase.MERGE_SORT, logicalProperties, groupPlan); + groupExpression = new GroupExpression(sort); + child = new PhysicalProperties(DistributionSpecReplicated.INSTANCE, + new OrderSpec(Lists.newArrayList( + new OrderKey(new SlotReference("ignored", IntegerType.INSTANCE), true, true)))); + + deriver = new ChildOutputPropertyDeriver(Lists.newArrayList(child)); + result = deriver.getOutputProperties(groupExpression); + Assertions.assertEquals(orderKeys, result.getOrderSpec().getOrderKeys()); Assertions.assertEquals(DistributionSpecGather.INSTANCE, result.getDistributionSpec()); } @@ -401,7 +414,7 @@ public void testTopN() { public void testLimit() { SlotReference key = new SlotReference("col1", IntegerType.INSTANCE); List orderKeys = Lists.newArrayList(new OrderKey(key, true, true)); - PhysicalLimit limit = new PhysicalLimit<>(10, 10, logicalProperties, groupPlan); + PhysicalLimit limit = new PhysicalLimit<>(10, 10, LimitPhase.ORIGIN, logicalProperties, groupPlan); GroupExpression groupExpression = new GroupExpression(limit); PhysicalProperties child = new PhysicalProperties(DistributionSpecReplicated.INSTANCE, new OrderSpec(orderKeys)); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/ImplementationTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/ImplementationTest.java index 1e7f72dd2eb8ff..10dfea7032f7fc 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/ImplementationTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/ImplementationTest.java @@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.plans.GroupPlan; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; @@ -108,7 +109,7 @@ public void toPhysicalTopNTest() { public void toPhysicalLimitTest() { int limit = 10; int offset = 100; - LogicalLimit logicalLimit = new LogicalLimit<>(limit, offset, groupPlan); + LogicalLimit logicalLimit = new LogicalLimit<>(limit, offset, LimitPhase.LOCAL, groupPlan); PhysicalPlan physicalPlan = executeImplementationRule(logicalLimit); Assertions.assertEquals(PlanType.PHYSICAL_LIMIT, physicalPlan.getType()); PhysicalLimit physicalLimit = (PhysicalLimit) physicalPlan; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateLimitTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateLimitTest.java index 77cb6df00ff167..44a88ac0c8f51f 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateLimitTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateLimitTest.java @@ -18,12 +18,17 @@ package org.apache.doris.nereids.rules.rewrite.logical; import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.properties.OrderKey; import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation; import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; +import org.apache.doris.nereids.trees.plans.logical.LogicalSort; +import org.apache.doris.nereids.trees.plans.logical.LogicalTopN; 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; @@ -31,6 +36,7 @@ import org.junit.jupiter.api.Test; import java.util.List; +import java.util.stream.Collectors; /** * MergeConsecutiveFilter ut @@ -39,7 +45,7 @@ public class EliminateLimitTest { @Test public void testEliminateLimit() { LogicalOlapScan scan = PlanConstructor.newLogicalOlapScan(0, "t1", 0); - LogicalLimit limit = new LogicalLimit<>(0, 0, scan); + LogicalLimit limit = new LogicalLimit<>(0, 0, LimitPhase.ORIGIN, scan); CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(limit); List rules = Lists.newArrayList(new EliminateLimit().build()); @@ -48,4 +54,17 @@ public void testEliminateLimit() { Plan actual = cascadesContext.getMemo().copyOut(); Assertions.assertTrue(actual instanceof LogicalEmptyRelation); } + + @Test + public void testLimitSort() { + LogicalOlapScan scan = PlanConstructor.newLogicalOlapScan(0, "t1", 0); + LogicalLimit limit = new LogicalLimit<>(1, 1, LimitPhase.ORIGIN, + new LogicalSort<>(scan.getOutput().stream().map(c -> new OrderKey(c, true, true)).collect(Collectors.toList()), + scan)); + + Plan actual = PlanChecker.from(MemoTestUtils.createConnectContext(), limit) + .rewrite() + .getPlan(); + Assertions.assertTrue(actual instanceof LogicalTopN); + } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimitsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimitsTest.java index fa7270def952b5..869dec982f1a37 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimitsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeLimitsTest.java @@ -20,6 +20,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; import org.apache.doris.nereids.trees.plans.logical.RelationUtil; import org.apache.doris.nereids.util.MemoTestUtils; @@ -33,10 +34,10 @@ public class MergeLimitsTest { @Test public void testMergeConsecutiveLimits() { - LogicalLimit limit3 = new LogicalLimit<>(3, 5, new UnboundRelation( + LogicalLimit limit3 = new LogicalLimit<>(3, 5, LimitPhase.ORIGIN, new UnboundRelation( RelationUtil.newRelationId(), Lists.newArrayList("db", "t"))); - LogicalLimit limit2 = new LogicalLimit<>(2, 0, limit3); - LogicalLimit limit1 = new LogicalLimit<>(10, 0, limit2); + LogicalLimit limit2 = new LogicalLimit<>(2, 0, LimitPhase.ORIGIN, limit3); + LogicalLimit limit1 = new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, limit2); CascadesContext context = MemoTestUtils.createCascadesContext(limit1); List rules = Lists.newArrayList(new MergeLimits().build()); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimitTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimitTest.java index ea1fc5894297df..57e38b32d1fd2e 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimitTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownLimitTest.java @@ -22,6 +22,7 @@ import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.plans.JoinType; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.RelationId; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; @@ -204,7 +205,6 @@ public void testTranslate() { // plan among fragments has duplicate elements. (s1, s2) -> s1) ); - // limit is push down to left scan of `t1`. Assertions.assertEquals(2, nameToScan.size()); Assertions.assertEquals(5, nameToScan.get("t1").getLimit()); @@ -212,6 +212,14 @@ public void testTranslate() { ); } + @Test + public void testLimitPushSort() { + PlanChecker.from(connectContext) + .analyze("select k1 from t1 order by k1 limit 1") + .rewrite() + .matches(logicalTopN()); + } + @Test public void testLimitPushUnion() { PlanChecker.from(connectContext) @@ -229,8 +237,10 @@ public void testLimitPushUnion() { logicalOlapScan().when(scan -> "t2".equals(scan.getTable().getName())) ), logicalLimit( - logicalProject( - logicalOlapScan().when(scan -> "t3".equals(scan.getTable().getName())) + logicalLimit( + logicalProject( + logicalOlapScan().when(scan -> "t3".equals(scan.getTable().getName())) + ) ) ) ) @@ -261,12 +271,12 @@ private Plan generatePlan(JoinType joinType, boolean hasProject) { if (hasProject) { // return limit -> project -> join - return new LogicalLimit<>(10, 0, new LogicalProject<>( + return new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, new LogicalProject<>( ImmutableList.of(new UnboundSlot("sid"), new UnboundSlot("id")), join)); } else { // return limit -> join - return new LogicalLimit<>(10, 0, join); + return new LogicalLimit<>(10, 0, LimitPhase.ORIGIN, join); } } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimitTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimitTest.java new file mode 100644 index 00000000000000..174f5a90b4353a --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/SplitLimitTest.java @@ -0,0 +1,41 @@ +// 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.trees.plans.LimitPhase; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; +import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; +import org.apache.doris.nereids.util.MemoTestUtils; +import org.apache.doris.nereids.util.PlanChecker; +import org.apache.doris.nereids.util.PlanConstructor; + +import org.junit.jupiter.api.Test; + +public class SplitLimitTest { + private final LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0); + + @Test + void testSplitLimit() { + Plan plan = new LogicalLimit<>(0, 0, LimitPhase.ORIGIN, scan1); + plan = PlanChecker.from(MemoTestUtils.createConnectContext(), plan) + .rewrite() + .getPlan(); + plan.anyMatch(x -> x instanceof LogicalLimit && ((LogicalLimit) x).isSplit()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java index e931957be2ec4c..94f6a07d333380 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java @@ -28,6 +28,8 @@ import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.FakePlan; import org.apache.doris.nereids.trees.plans.GroupPlan; +import org.apache.doris.nereids.trees.plans.LimitPhase; +import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; @@ -279,7 +281,9 @@ public void testLimit() { GroupPlan groupPlan = new GroupPlan(childGroup); childGroup.setStatistics(childStats); - LogicalLimit logicalLimit = new LogicalLimit<>(1, 2, groupPlan); + LogicalLimit logicalLimit = new LogicalLimit<>(1, 2, + LimitPhase.GLOBAL, new LogicalLimit<>(1, 2, LimitPhase.LOCAL, groupPlan) + ); GroupExpression groupExpression = new GroupExpression(logicalLimit, ImmutableList.of(childGroup)); Group ownerGroup = newGroup(); ownerGroup.addGroupExpression(groupExpression); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java index aed7bbbbd549bd..be62fc278bed24 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java @@ -46,7 +46,7 @@ public class PlanToStringTest { @Test public void testLogicalLimit(@Mocked Plan child) { - LogicalLimit plan = new LogicalLimit<>(0, 0, child); + LogicalLimit plan = new LogicalLimit<>(0, 0, LimitPhase.ORIGIN, child); Assertions.assertEquals("LogicalLimit ( limit=0, offset=0 )", plan.toString()); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java index c719302ac5e078..2bca62bab13791 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java @@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.plans.JoinHint; import org.apache.doris.nereids.trees.plans.JoinType; +import org.apache.doris.nereids.trees.plans.LimitPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; @@ -122,7 +123,7 @@ public LogicalPlanBuilder joinEmptyOn(LogicalPlan right, JoinType joinType) { } public LogicalPlanBuilder limit(long limit, long offset) { - LogicalLimit limitPlan = new LogicalLimit<>(limit, offset, this.plan); + LogicalLimit limitPlan = new LogicalLimit<>(limit, offset, LimitPhase.ORIGIN, this.plan); return from(limitPlan); }