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]

Reply via email to