github-actions[bot] commented on code in PR #68188:
URL: https://github.com/apache/doris/pull/68188#discussion_r4045840657


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -2628,6 +2628,35 @@ public PlanFragment visitPhysicalSetOperation(
             setOperationNode.setColocate(true);
         }
 
+        // Storage-bucketed children only appear when the FE local shuffle 
planner is active:
+        // ChildrenPropertiesRegulator and RequestPropertyDeriver both gate 
the bucket-shuffle
+        // alternative on enableLocalShufflePlanner. Gate the marker on the 
same flag so the
+        // dependency is explicit and a future planner change that produced a 
STORAGE_BUCKETED
+        // distribution outside the local-shuffle planner cannot silently mark 
BUCKET_SHUFFLE here.
+        //
+        // Within that gate a storage-bucketed child means the regulator chose 
the bucket shuffle
+        // alternative (it enforces the other children onto the basic child's 
buckets), so the marker
+        // simply follows that decision. It must not re-check the table id 
independently: the basic
+        // child selection in the regulator is the single place that vets the 
layout, and re-checking
+        // here could suppress a bucket shuffle the property model already 
committed to and desync a
+        // parent that aligned to the set operation output.
+        //
+        // Unlike hash join, BUCKET_SHUFFLE is not exclusive with isColocate 
above: for a set
+        // operation isColocate describes the bucket-aligned scheduling of the 
fragment (the
+        // basic child scans buckets directly), while BUCKET_SHUFFLE describes 
how the other
+        // children arrive (bucket-shuffle exchanges). Both routes converge to 
the same
+        // bucket-hash local exchange requirement in 
SetOperationNode.enforceAndDeriveLocalExchange.
+        if (context.getSessionVariable() != null
+                && context.getSessionVariable().isEnableLocalShufflePlanner()) 
{

Review Comment:
   `setOperationBucketShuffleAllowed()` permits bucket-shuffled set operations 
when `enable_local_shuffle=false` even if `enable_local_shuffle_planner=false`, 
but this guard leaves the translated node unmarked. With a bucket-pruned UNION 
(or right/later-basic EXCEPT), 
`UnassignedScanBucketOlapTableJob.shouldFillUpInstances()` then skips 
missing-bucket receivers because it recognizes the set operation only through 
`isBucketShuffle()`. The exchange still targets those bucket indexes, whose 
dummy destinations discard rows. Please derive the marker from the actual 
storage-bucketed plan/the same eligibility predicate and add a 
`false/false/true` result case.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriver.java:
##########
@@ -453,6 +455,80 @@ public PhysicalProperties 
visitPhysicalSetOperation(PhysicalSetOperation setOper
         if 
(childrenDistribution.stream().allMatch(DistributionSpecGather.class::isInstance))
 {
             return PhysicalProperties.GATHER;
         }
+
+        // After ChildrenPropertiesRegulator the children distributions are 
already legal, so the
+        // output is derived by describing what the children provide:
+        // 1. one or more NATURAL children: output the first NATURAL child's 
distribution
+        //    (several NATURAL children behave like colocate);
+        // 2. no NATURAL but some STORAGE_BUCKETED child: output its 
STORAGE_BUCKETED distribution;
+        // 3. all EXECUTION_BUCKETED children: output the execution hash (the 
generic loop below);
+        // 4. anything else (e.g. random): output a non-specific property 
(also below).
+        // When the basic child does not directly output its shuffle columns, 
or the children do
+        // not agree, this falls through to the generic loop below, which is 
equivalence-set aware
+        // and checks that every child maps its shuffle columns to the same 
set-operation output
+        // positions before it claims a bucketed output. The basic child is 
recomputed from the
+        // children distributions instead of being carried as mutable planner 
state, because mutable
+        // state does not survive the with-copies in chooseBestPlan() and the
+        // RecomputePhysicalPropertiesPostProcessor re-derivation, while this 
recomputation is
+        // deterministic on any copy of the plan.
+        int distributeToChildIndex = -1;
+        int firstStorageBucketedIndex = -1;
+        for (int i = 0; i < childrenDistribution.size(); i++) {
+            if (childrenDistribution.get(i) instanceof DistributionSpecHash) {
+                ShuffleType childShuffleType
+                        = ((DistributionSpecHash) 
childrenDistribution.get(i)).getShuffleType();
+                if (childShuffleType == ShuffleType.NATURAL) {
+                    distributeToChildIndex = i;
+                    break;
+                } else if (childShuffleType == ShuffleType.STORAGE_BUCKETED
+                        && firstStorageBucketedIndex < 0) {
+                    firstStorageBucketedIndex = i;
+                }
+            }
+        }
+        if (distributeToChildIndex < 0) {
+            distributeToChildIndex = firstStorageBucketedIndex;
+        }
+        if (distributeToChildIndex >= 0) {
+            DistributionSpecHash childDistribution
+                    = (DistributionSpecHash) 
childrenDistribution.get(distributeToChildIndex);
+            List<SlotReference> childToIndex = 
setOperation.getRegularChildrenOutputs().get(distributeToChildIndex);
+            Map<ExprId, Integer> idToOutputIndex = new LinkedHashMap<>();
+            for (int j = 0; j < childToIndex.size(); j++) {
+                idToOutputIndex.put(childToIndex.get(j).getExprId(), j);
+            }
+
+            List<ExprId> orderedShuffledColumns = 
childDistribution.getOrderedShuffledColumns();
+            List<ExprId> setOperationDistributeColumnIds = new ArrayList<>();
+            for (ExprId tableDistributeColumnId : orderedShuffledColumns) {
+                Integer index = idToOutputIndex.get(tableDistributeColumnId);
+                if (index == null) {
+                    break;
+                }
+                
setOperationDistributeColumnIds.add(setOperation.getOutput().get(index).getExprId());
+            }
+            // check whether the set operation output all distribution columns 
of the child
+            if (setOperationDistributeColumnIds.size() == 
orderedShuffledColumns.size()) {
+                // Keep the basic child's specific storage layout as the set 
operation output. When

Review Comment:
   When the basic child's bucket key is directly exposed at output 0 but 
equivalent to output 1, a parent request on output 1 makes the regulator align 
the other set-operation arms on output 1. This fast path nevertheless records a 
specific `NATURAL(output[0])` property. `calculateEnforce()` caches that exact 
property with the output-1 child requirements before adding the immediate 
corrective exchange; if another join-order alternative later requires output 0, 
the equal-cost correct entry does not replace the stale child list because 
`updateLowestCostTable()` only replaces on lower cost. A parent can then select 
exact `NATURAL(output[0])`, and `chooseBestPlan()` reconstructs children still 
hashed on output 1, which is wrong for UNION arms where the two values differ. 
Please derive or validate the same ordered output-position mapping across every 
child instead of returning from the basic child's direct ExprIds alone, and add 
a memo-reuse result case with unequal sibling columns.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to