924060929 commented on code in PR #66477:
URL: https://github.com/apache/doris/pull/66477#discussion_r4346392534


##########
fe/fe-core/src/main/java/org/apache/doris/planner/LocalExchangeNode.java:
##########
@@ -56,6 +60,12 @@ public LocalExchangeNode(PlanNodeId id, PlanNode inputNode, 
LocalExchangeType ex
         this.children.add(inputNode);
         this.exchangeType = exchangeType;
         this.fragment = inputNode.getFragment();
+        // Preserve the effective storage layout through passthrough/unary 
nodes as well as direct
+        // ExchangeNode and OlapScanNode children.
+        HashDistributionInfo.HashType childHashType = 
inputNode.getStorageDistributionHashType();
+        if (childHashType != null) {
+            this.distributionHashType = childHashType;
+        }

Review Comment:
   Could we preserve an explicit layout state here instead of turning a 
missing/ambiguous layout into CRC32?
   
   This PR adds both `PlanNode.getStorageDistributionHashType()` and 
`collectStorageHashTypes()`. The former returns `null` for both "no known 
bucket layout" and "conflicting child layouts"; the latter traverses the tree 
again to distinguish them. Both traversals must independently implement the 
same Exchange boundary and broadcast/NLJ probe-side rules. Here, however, a 
null child result leaves the new field at its default CRC32, and 
`getStorageDistributionHashType()` then advertises a definite layout.
   
   A reduced, manually constructed tree demonstrates the contract inconsistency:
   
   ```text
   Union(IDENTITY scan, CRC32 scan)
     -> null; PlanFragment.toThrift() detects the conflict
   
   PASSTHROUGH
     Union(IDENTITY scan, CRC32 scan)
     -> this wrapper reports CRC32; the fragment skips that conflict check
   ```
   
   A passthrough wrapper should not erase a layout conflict. This is a 
source-derived internal-contract example, **not a demonstrated production 
bug**: I have not established a normal SQL path producing this tree. The 
current UNION ANY path inserts execution-exchange boundaries, and 
storage-bucketed set operations align their inputs. This is also distinct from 
the earlier cross-fragment/broadcast collection comment, whose boundary fixes 
are present at this head.
   
   A minimal change within this PR would be:
   
   1. Use one shared layout result that distinguishes `NONE`, 
`KNOWN(hashType)`, and `CONFLICT` (and `UNKNOWN` separately if needed), rather 
than an enum/null result plus a second collection traversal. Retain the 
existing receiver/probe boundary rules in that one derivation.
   2. Make PASSTHROUGH preserve the child's state. A bucket local exchange must 
receive an explicit target storage layout; an execution shuffle must not claim 
the source storage algorithm merely through inheritance. Keep legacy CRC32 
defaults at the compatibility boundary, rather than using them to resolve 
current-plan ambiguity.
   3. Resolve/validate this result when forming the translated fragment/local 
exchange; let serialization encode it and assert the contract. The existing 
fragment-level tag for BE-native exchanges can remain for this change—no 
pipeline redesign is required.
   4. Add focused tests for bare versus passthrough-wrapped conflicts, 
layout-less inputs, and the existing Exchange/broadcast/NLJ boundaries.
   
   This would give the new algorithm propagation a single, explicit contract 
without requiring all layout information to come from `DistributionSpecHash`, 
which is insufficient for some STORAGE_ANY/multi-partition paths.



-- 
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