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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FuncDepsDG.java:
##########
@@ -219,6 +219,25 @@ public void addDeps(FuncDepsDG funcDepsDG) {
             }
         }
 
+        /**
+         * Add FD edges from the nullable side of an outer join. Only keep 
edges whose
+         * determinant slots are all NOT NULL in the original child: matched 
rows always
+         * carry a non-null determinant, while unmatched rows contribute 
(NULL, NULL),
+         * so they cannot collide. Edges with a nullable determinant are 
dropped because
+         * a matched row with determinant=NULL could conflict with an 
unmatched (NULL, NULL).
+         */
+        public void addDepsForOuterJoinNullableSide(FuncDepsDG funcDepsDG) {
+            for (DGItem dgItem : funcDepsDG.dgItems) {

Review Comment:
   [P2] Preserve safe transitive dependencies before filtering direct edges
   
   This loop copies only direct edges. For a real projection chain that 
produces `a NOT NULL -> b nullable -> c` while forwarding all three slots, the 
graph has no separate `a -> c` edge: this helper keeps `a -> b` and drops `b -> 
c`, so recursive FD lookup loses `a -> c`. That dependency is still valid after 
null-extension because no original row can have `a=NULL`, while synthetic rows 
are all NULL. This makes group/order-key elimination miss a safe reduction. 
Please derive/materialize the reachable dependencies for each safe determinant 
before rebuilding the filtered graph, and add a nullable-intermediate chain 
test.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FuncDepsDG.java:
##########
@@ -219,6 +219,25 @@ public void addDeps(FuncDepsDG funcDepsDG) {
             }
         }
 
+        /**
+         * Add FD edges from the nullable side of an outer join. Only keep 
edges whose
+         * determinant slots are all NOT NULL in the original child: matched 
rows always
+         * carry a non-null determinant, while unmatched rows contribute 
(NULL, NULL),
+         * so they cannot collide. Edges with a nullable determinant are 
dropped because
+         * a matched row with determinant=NULL could conflict with an 
unmatched (NULL, NULL).
+         */
+        public void addDepsForOuterJoinNullableSide(FuncDepsDG funcDepsDG) {
+            for (DGItem dgItem : funcDepsDG.dgItems) {
+                boolean allNotNull = dgItem.slots.stream().allMatch(slot -> 
!slot.nullable());

Review Comment:
   [P1] Check determinant nullability against the current child output
   
   `dgItem.slots` can retain an older non-nullable `SlotReference` even when 
the immediate child now outputs a nullable slot with the same ExprId: slot 
equality/hash use only ExprId, and `getOrCreateNode` never replaces the stored 
object. A reduced failing tree is:
   
   ```
   Aggregate(group by r_id, c; output r_id, count(*))
     RightOuterJoin
       Project(l_id, r_id, coalesce(r_id, 1) AS c)
         LeftOuterJoin
           Scan L
           Scan R(r_id NOT NULL UNIQUE)
       Scan V
   ```
   
   The first outer join/project can emit `(r_id,c)=(NULL,1)`; an unmatched row 
from the second join emits `(NULL,NULL)`. This line still sees the historical 
non-null `r_id`, keeps `r_id -> c`, and lets `EliminateGroupByKey` drop `c`, 
merging two groups. Please canonicalize determinants against the immediate 
child's current output/nullability before filtering, and cover this nested 
logical and physical path.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java:
##########
@@ -704,11 +704,46 @@ public void computeEqualSet(Builder builder) {
 
     @Override
     public void computeFd(Builder builder) {
-        if (!joinType.isLeftSemiOrAntiJoin()) {
-            builder.addFuncDepsDG(right().getLogicalProperties().getTrait());
-        }
-        if (!joinType.isRightSemiOrAntiJoin()) {
-            builder.addFuncDepsDG(left().getLogicalProperties().getTrait());
+        switch (joinType) {
+            case INNER_JOIN:
+            case ASOF_LEFT_INNER_JOIN:
+            case ASOF_RIGHT_INNER_JOIN:
+            case CROSS_JOIN:
+                
builder.addFuncDepsDG(left().getLogicalProperties().getTrait());
+                
builder.addFuncDepsDG(right().getLogicalProperties().getTrait());
+                break;
+            case LEFT_SEMI_JOIN:
+            case LEFT_ANTI_JOIN:
+            case NULL_AWARE_LEFT_ANTI_JOIN:
+                // Semi/anti joins only output the left side; right-side FDs 
are irrelevant.
+                
builder.addFuncDepsDG(left().getLogicalProperties().getTrait());
+                break;
+            case LEFT_OUTER_JOIN:
+            case ASOF_LEFT_OUTER_JOIN:
+                // Left side preserved; right side nullable — keep only FDs 
whose
+                // determinant is NOT NULL in the original child.
+                
builder.addFuncDepsDG(left().getLogicalProperties().getTrait());
+                
builder.addFuncDepsDGForOuterJoinNullableSide(right().getLogicalProperties().getTrait());

Review Comment:
   [P2] Preserve FDs proven safe by a null-rejecting join key
   
   Passing only the child trait loses join-domain information. For example, a 
right child `Project(r_a, coalesce(r_a, 1) AS r_c)` has the direct FD `r_a -> 
r_c` even when `r_a` is nullable. In `L LEFT OUTER JOIN R ON l_k = r_a`, 
ordinary equality prevents every `r_a=NULL` child row from matching, those 
unmatched right rows are not preserved, and synthetic left-unmatched rows 
contribute `(r_a,r_c)=(NULL,NULL)`. Thus the FD remains valid, but this call 
drops it based only on schema nullability. Please include nullable-side keys 
proven null-rejected by the one-sided join predicate when filtering (without 
extending that assumption to full outer or null-safe/unproven predicates), and 
mirror it in physical recomputation.



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