This is an automated email from the ASF dual-hosted git repository. 924060929 pushed a commit to branch fe_local_shuffle_rebase_wip in repository https://gitbox.apache.org/repos/asf/doris.git
commit 0d7aa6dbf53cdba04c2bac5fbabc5a1a1acd6c8b Author: 924060929 <[email protected]> AuthorDate: Wed May 27 10:02:48 2026 +0800 [test](local shuffle) cover RepeatNode noRequire + return-child-distribution behaviors Add a PlanShapeDsl repeat() factory and two LocalExchangePlannerTest cases pinning the two halves of the RepeatNode local-exchange fix: - testRepeatNoRequireKeepsHashLocalExchangeAboveRepeat: with a pooling scan upstream, the hash LE is placed ABOVE the Repeat (Agg <- LE(LOCAL_HASH) <- LE(PT) <- Repeat <- scan). repeat() pins the Repeat position so the buggy below-repeat placement would fail the assertion. - testRepeatReturnsChildDistributionSkipsRedundantHash: with a non-pooling colocate scan, the child BUCKET_HASH distribution propagates through the Repeat and satisfies the agg requirement, so no redundant LOCAL_HASH is inserted (returning NOOP would force one). --- .../org/apache/doris/planner/PlanShapeDsl.java | 4 +++ .../apache/doris/qe/LocalExchangePlannerTest.java | 38 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/fe/fe-core/src/test/java/org/apache/doris/planner/PlanShapeDsl.java b/fe/fe-core/src/test/java/org/apache/doris/planner/PlanShapeDsl.java index bc96ca91353..4e5ce68073f 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/planner/PlanShapeDsl.java +++ b/fe/fe-core/src/test/java/org/apache/doris/planner/PlanShapeDsl.java @@ -123,6 +123,10 @@ public interface PlanShapeDsl { return PlanShape.partitionSort(children); } + default PlanShape<RepeatNode> repeat(PlanShape<?>... children) { + return PlanShape.node(RepeatNode.class, children); + } + // ---- assertion entry points ---- default void assertMatches(PlanNode root, PlanShape<?> shape) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/LocalExchangePlannerTest.java b/fe/fe-core/src/test/java/org/apache/doris/qe/LocalExchangePlannerTest.java index 55b3c62b87c..afcd8f04c6e 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/qe/LocalExchangePlannerTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/qe/LocalExchangePlannerTest.java @@ -542,6 +542,44 @@ public class LocalExchangePlannerTest extends TestWithFeService implements PlanS LocalExchangeType.LOCAL_EXECUTION_HASH_SHUFFLE); } + @Test + public void testRepeatNoRequireKeepsHashLocalExchangeAboveRepeat() throws Exception { + // Behavior 1 of the RepeatNode fix — noRequire (tpcds q67, +73%). + // RepeatNode recurses with noRequire() instead of forwarding the streaming + // agg's HASH require to its child. So when the pooling scan upstream does NOT + // already provide the distribution, the parent inserts the LE(LOCAL_HASH) + // ABOVE the Repeat, never below it: + // Agg <- LE(LOCAL_HASH) <- LE(PASSTHROUGH) <- Repeat <- scan + // Pinning Repeat with repeat() (not anyTree) distinguishes the fixed plan from + // the buggy one (buggy forwarded the require, so the LE landed below the + // Repeat, hashing the pre-repeat rows by the child's single upstream key and + // collapsing them onto one instance). + setupLocalShuffleSession(null); + assertPlanShape( + "select k1, k2, count(*) from test.t1 group by grouping sets((k1), (k1, k2))", + anyTree( + agg( + localExchange(LOCAL_HASH, + localExchange(PT, + repeat(anyTree(olapScan("t1")))))))); + } + + @Test + public void testRepeatReturnsChildDistributionSkipsRedundantHash() throws Exception { + // Behavior 2 of the RepeatNode fix — return enforceResult.second (tpcds q70). + // RepeatNode reports its child's real output distribution to the parent (not + // NOOP). With a non-pooling colocate scan, the child's BUCKET_HASH + // distribution propagates through the Repeat and already satisfies the agg's + // hash requirement, so the parent's satisfy-check SKIPS inserting any LE — no + // LOCAL_HASH appears. Had RepeatNode returned NOOP (the discarded v1), the + // satisfy-check would fail and force a redundant LE(LOCAL_HASH) that + // re-shuffles the post-repeat rows into skew. + setupLocalShuffleSession(sv -> sv.setIgnoreStorageDataDistribution(false)); + assertNoLocalExchangeOfType( + "select k1, k2, count(*) from test.t1 group by grouping sets((k1), (k1, k2))", + LocalExchangeType.LOCAL_EXECUTION_HASH_SHUFFLE); + } + @Test public void testLocalAndGlobalExecutionHashShufflePreferType() { PlanTranslatorContext translatorContext = new PlanTranslatorContext(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
