morrySnow commented on code in PR #65682:
URL: https://github.com/apache/doris/pull/65682#discussion_r3671912125


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/HyperGraph.java:
##########
@@ -330,23 +404,75 @@ public void updateNode(int idx, Group group) {
          *      latest join edges index
          */
         private Pair<BitSet, Long> buildForDPhyper(GroupExpression 
groupExpression) {
+            return buildForDPhyper(groupExpression, false);
+        }
+
+        private Pair<BitSet, Long> buildForDPhyper(GroupExpression 
groupExpression, boolean isNullableSide) {
             // process Project
             if (isValidProject(groupExpression.getPlan())) {
                 LogicalProject<?> project = (LogicalProject<?>) 
groupExpression.getPlan();
-                Pair<BitSet, Long> res = 
buildForDPhyper(groupExpression.child(0).getLogicalExpressions().get(0));
+                Pair<BitSet, Long> res = buildForDPhyper(
+                        
groupExpression.child(0).getLogicalExpressions().get(0), isNullableSide);
+                // Start a new layer for this Project. Each source Project 
becomes one
+                // LogicalProject layer, preserving materialization boundaries 
for
+                // volatile expressions (e.g., uuid()) that 
PlanUtils.canMergeWithProjections
+                // would otherwise reject.
+                List<NamedExpression> savedLayer = 
this.currentProjectedAliasLayer;
+                this.currentProjectedAliasLayer = new ArrayList<>();
                 for (NamedExpression expr : project.getProjects()) {
                     if (expr instanceof Alias) {
-                        this.addAlias((Alias) expr, res.second);
+                        this.addAlias((Alias) expr, res.second, 
isNullableSide);
                     }
                 }
+                // Flush the layer if non-empty. If aliases for this key 
already
+                // exist, resolve cross-layer references (e.g., z = x + 1 where
+                // x was defined by an earlier Project on the same subtree) and
+                // merge into the existing single layer. Cross-layer resolution
+                // is safe because volatile/non-movable expressions are already
+                // excluded by isValidProject.
+                if (!this.currentProjectedAliasLayer.isEmpty()) {
+                    long key = res.second;
+                    List<NamedExpression> existing = 
nodeToProjectedAliases.get(key);
+                    if (existing != null) {
+                        Map<Slot, Expression> replaceMap = new 
LinkedHashMap<>();
+                        for (NamedExpression a : existing) {
+                            if (a instanceof Alias) {
+                                replaceMap.put(a.toSlot(), ((Alias) 
a).child());
+                            }
+                        }
+                        for (NamedExpression expr : 
currentProjectedAliasLayer) {
+                            existing.add((NamedExpression) 
ExpressionUtils.replace(expr, replaceMap));

Review Comment:
   [P1] Preserve the expression-limit fallback
   
   This unconditional replacement flattens every same-bitmap Project chain, but 
normal Project merging deliberately uses `PlanUtils.tryMergeProjections` and 
keeps layers separate when expansion hits `EXPRESSION_EXCEEDS_LIMIT`. In a 
nullable-side chain `x1=B.v+B.v`, `x2=x1+x1`, ... the original/rewrite plan 
remains valid once the configured width/depth limit prevents further merging; 
DPHyp graph construction expands the chain again here and `Expression` 
construction throws, so enabling DPHyp turns a valid query into an analysis 
failure. Preserve ordered layers, or catch the limit and retain the 
materialization boundary, and add a limit-path regression.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraphv2/receiver/PlanReceiver.java:
##########
@@ -291,29 +300,61 @@ public Group getBestPlan(long bitmap) {
 
     private LogicalPlan proposeProject(LogicalPlan join, List<Edge> edges, 
long left, long right) {
         Set<Slot> outputSet = join.getOutputSet();
-        // calculate required columns by all parents
-        Set<Slot> requireSlots = calculateRequiredSlots(left, right, edges);
+        // calculate required columns by all parents (final outputs + unused 
edges)
+        Set<Slot> parentRequireSlots = calculateRequiredSlots(left, right, 
edges);
+        // Pending projected aliases may reference input slots (e.g., A.v, B.v 
for
+        // s=A.v+B.v) that are not in finalRequiredSlots or unused edges. 
Preserve
+        // them so the join output still contains the base columns needed to 
evaluate
+        // the alias expressions, both for aliases emitted at this stage and 
those
+        // deferred to a later join whose bitmap is a superset.
+        Set<Slot> aliasInputSlots = hyperGraph.getAllAliasInputSlotsForNodes(
+                LongBitmap.newBitmapUnion(left, right));
+        Set<Slot> requireSlots = new HashSet<>(parentRequireSlots);
+        requireSlots.addAll(aliasInputSlots);

Review Comment:
   [P2] Stop carrying alias inputs after materialization
   
   `getAllAliasInputSlotsForNodes` returns inputs for every overlapping alias 
even when that alias bitmap is already wholly contained in a completed child, 
and this set is copied into each rebuilt Project. For `Project[B.k, 
expensive(B.payload) AS dv]`, the leaf becomes `[dv, B.k, B.payload]`; every 
ancestor candidate still overlaps `{B}`, so DPHyp costs physical alternatives 
with the wide payload although the original Project dropped it. Since 
distribution and join costs use child output size, this can distort join-order 
selection and grow planning state (later ColumnPruning only removes the slot 
after DPHyp has chosen an alternative). Track inputs only until the alias is 
emitted, then retain just the alias and true parent/deferred-layer 
requirements; the current test also needs a plan-shape/output assertion because 
`assertNotNull` cannot catch this.



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