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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/HyperGraph.java:
##########
@@ -385,17 +406,33 @@ public boolean addAlias(Alias alias, long subTreeNodes) {
             if (bitmap == 0) {
                 bitmap = subTreeNodes;
                 addToReplaceMap = false;
-                List<NamedExpression> aliasList = 
nodeToLiteralAlias.get(bitmap);
+                List<NamedExpression> aliasList = 
nodeToProjectedAliases.get(bitmap);
                 if (aliasList == null) {
                     aliasList = new ArrayList<>(1);
-                    nodeToLiteralAlias.put(bitmap, aliasList);
+                    nodeToProjectedAliases.put(bitmap, aliasList);
                 }
                 aliasList.add(alias);
             }
             Preconditions.checkArgument(bitmap > 0, "slot must belong to some 
table");
             slotToHyperNodeMap.put(aliasSlot, 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 nodeToProjectedAliases so they get 
projected at the leaf
+            // level, preserving the original execution boundary below the 
outer join.
+            if (addToReplaceMap && isNullableSide) {

Review Comment:
   [P1] Preserve the original Project boundary
   
   This map is keyed by the alias's referenced bitmap, so a Project above a 
nested join can be rebuilt below it. For example:
   
   ```text
   X LEFT JOIN
     X
     Project(A.k, coalesce(B.v,0) AS dv)
       A LEFT JOIN B
   ```
   
   The ancestor marks the right subtree nullable, but `addAlias` records `dv` 
under `{B}`. `addGroup({B})` therefore evaluates it on B; when B is empty, the 
inner LEFT JOIN null-extends `dv` to NULL. In the original tree the inner join 
first supplies `B.v=NULL`, then the Project returns 0, so a matching X/A row 
changes from 0 to NULL. The same placement also moves 
`NoneMovableFunction`/volatile aliases across inner-join filtering or row 
multiplication. Please retain the source Project's subtree/stage rather than 
emitting at the smallest referenced bitmap, and cover nested outer and 
non-movable cases.
   



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/HyperGraph.java:
##########
@@ -385,17 +406,33 @@ public boolean addAlias(Alias alias, long subTreeNodes) {
             if (bitmap == 0) {
                 bitmap = subTreeNodes;
                 addToReplaceMap = false;
-                List<NamedExpression> aliasList = 
nodeToLiteralAlias.get(bitmap);
+                List<NamedExpression> aliasList = 
nodeToProjectedAliases.get(bitmap);
                 if (aliasList == null) {
                     aliasList = new ArrayList<>(1);
-                    nodeToLiteralAlias.put(bitmap, aliasList);
+                    nodeToProjectedAliases.put(bitmap, aliasList);
                 }
                 aliasList.add(alias);
             }
             Preconditions.checkArgument(bitmap > 0, "slot must belong to some 
table");
             slotToHyperNodeMap.put(aliasSlot, 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 nodeToProjectedAliases so they get 
projected at the leaf
+            // level, preserving the original execution boundary below the 
outer join.
+            if (addToReplaceMap && isNullableSide) {
+                List<NamedExpression> aliasList = 
nodeToProjectedAliases.get(bitmap);
+                if (aliasList == null) {
+                    aliasList = new ArrayList<>(1);
+                    nodeToProjectedAliases.put(bitmap, aliasList);
+                }
+                aliasList.add(alias);

Review Comment:
   [P1] Keep dependent aliases in separate projection layers
   
   `aliasList.add(alias)` happens before replacement, and aliases on this path 
never enter `aliasReplaceMap`. A reachable nullable subtree can contain 
`Project(y=x+1)` above a join whose B child defines `Project(coalesce(B.v,0) AS 
x)`. Both aliases map to `{B}`, so `addGroup({B})` builds one 
`LogicalProject([x=..., y=x+1], B)`. Project expressions read their child, not 
sibling outputs; B does not output ExprId x, and `CheckAfterRewrite` rejects y. 
Preserve ordered Project layers or safely expand dependencies before coalescing 
same-bitmap aliases; add a chained-alias regression.
   



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/receiver/PlanReceiver.java:
##########
@@ -299,8 +299,8 @@ private LogicalPlan proposeProject(LogicalPlan join, 
List<Edge> edges, long left
                 allProjects.add(slot);
             }
         }
-        if (hyperGraph.hasLiteralAlias()) {
-            allProjects.addAll(hyperGraph.getLiteralAlias(left, right));
+        if (hyperGraph.hasProjectedAliases()) {
+            allProjects.addAll(hyperGraph.getProjectedAliases(left, right));

Review Comment:
   [P1] Retain pending alias inputs until their bitmap is built
   
   For a nullable child `Project(A.v+B.v AS s) over (A JOIN B on keys)`, this 
entry is withheld until `{A,B}`. `calculateRequiredSlots` sees final slot s and 
unused edge keys, but not A.v/B.v, so leaf projects prune those values. When 
line 303 finally appends s, the rebuilt join no longer outputs either input, 
and `CheckAfterRewrite` fails. The legacy receiver explicitly adds input slots 
of pending complex projects. Expose pending aliases to required-slot 
calculation (including transitive inputs) until emission, and add a multi-node 
alias 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