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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/HyperGraph.java:
##########
@@ -385,15 +528,46 @@ public boolean addAlias(Alias alias, long subTreeNodes) {
             if (bitmap == 0) {
                 bitmap = subTreeNodes;
                 addToReplaceMap = false;
-                List<NamedExpression> aliasList = 
nodeToLiteralAlias.get(bitmap);
-                if (aliasList == null) {
-                    aliasList = new ArrayList<>(1);
-                    nodeToLiteralAlias.put(bitmap, aliasList);
+                // Constant aliases go into the current Project layer (set up 
by
+                // buildForDPhyper) and will be flushed to 
nodeToProjectedAliases
+                // keyed by the layer's subtree bitmap after the Project is 
processed.
+                if (currentProjectedAliasLayer != null) {
+                    currentProjectedAliasLayer.add(alias);
                 }
-                aliasList.add(alias);
             }
             Preconditions.checkArgument(bitmap > 0, "slot must belong to some 
table");
-            slotToHyperNodeMap.put(aliasSlot, bitmap);
+            boolean mustStayInCurrentAliasLayer = isNullableSide && 
!(alias.child() instanceof Slot);
+            // Map nullable-side alias slots to subTreeNodes instead of the 
minimal
+            // referenced bitmap. Otherwise a later join predicate like s=C.k 
sees s as
+            // {B} (its input slot) and creates a {B}--{C} edge, allowing 
DPHyp to join
+            // B and C before A — but s can only be emitted when {A,B} is 
complete.
+            // Using subTreeNodes (e.g. {A,B}) forces the predicate edge to 
require the
+            // full source subtree, matching the emission key in 
nodeToProjectedAliases.
+            // Note: always use subTreeNodes for nullable-side aliases. A 
Slot-forwarding
+            // alias (e.g. s=A.k) that shares a Project with expression 
aliases cannot
+            // safely use the minimal bitmap — its layer only emits at {A,B}, 
so exposing
+            // it as {A} would let DPHyp form predicate edges before the alias 
exists.
+            slotToHyperNodeMap.put(aliasSlot, mustStayInCurrentAliasLayer ? 
subTreeNodes : bitmap);
+            // Do not add aliases on the nullable side of outer joins to 
aliasReplaceMap.
+            // Aliases on the nullable side (e.g., COALESCE(v, 0) AS dv on the 
right side of
+            // a LEFT JOIN) must execute BEFORE the outer join's 
null-extension.
+            // If added to aliasReplaceMap, they would be unwrapped and 
reconstructed above
+            // the outer join by PlanReceiver.proposeProject(), changing 
execution order
+            // and producing wrong results.
+            // Instead, add them to the current Project layer. buildForDPhyper 
flushes each
+            // layer to nodeToProjectedAliases keyed by subTreeNodes so the 
alias is only
+            // projected when the full original subtree is available, 
preserving the
+            // execution boundary and volatile materialization order.
+            // No replaceNameExpression here: nullable-side aliases are stored 
as
+            // independent layers that reference child-output slots, so 
expansion is
+            // unnecessary and could trigger expression-limit failures for 
large chains
+            // (the same reason PlanUtils.tryMergeProjections keeps layers).
+            if (addToReplaceMap && mustStayInCurrentAliasLayer) {
+                if (currentProjectedAliasLayer != null) {
+                    currentProjectedAliasLayer.add(alias);

Review Comment:
   [P1] Resolve forwarding aliases before storing a later layer
   
   A join-separated chain can reach this branch:
   
   ```text
   X LEFT JOIN
     X
     Project(A.k, x + 1 AS y)
       InnerJoin(A.k = B.k)
         Project(A.k, A.v AS x)
           A
         B
   ```
   
   On the nullable traversal, the lower `x = A.v` has a direct `Slot` child, so 
it is removed into `aliasReplaceMap` and no layer ever emits ExprId `x`. The 
upper `y = x + 1` enters this branch, but the early return stores the original 
expression before `replaceNameExpression` runs. `getAllAliasInputSlotsForNodes` 
therefore asks the leaf to preserve `x`, not `A.v`; no rebuilt child outputs 
`x`, and `PlanReceiver` eventually creates `Project(y=x+1)` over a child that 
lacks that slot, which `CheckAfterRewrite` rejects.
   
   This is distinct from the earlier join-predicate forwarding thread: 
`addJoin` now substitutes forwarding aliases, but preserved Project bodies do 
not. Please apply the safe forwarding map before storing this layer (or retain 
the forwarding producer as a real lower layer), and add this join-separated 
chain as a regression.



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