zhuqi-lucas commented on code in PR #23903:
URL: https://github.com/apache/datafusion/pull/23903#discussion_r3736272361


##########
datafusion/physical-plan/src/execution_plan.rs:
##########
@@ -1402,11 +1437,12 @@ pub fn with_new_children_if_necessary(
         }
         // Layer 2: same child properties → reuse `PlanProperties` cache.
         if has_same_children_properties(plan.as_ref(), &children)? {
-            return plan.with_new_children_and_same_properties(children);
+            return plan
+                .replace_children(children, 
ChildrenPropertiesHint::SameProperties);
         }
     }
     // Layer 3: full recompute.
-    plan.with_new_children(children)
+    plan.replace_children(children, ChildrenPropertiesHint::Recompute)

Review Comment:
   Naming-consistency nit (non-blocking): the PR moves everyone's mental model 
to `replace_children`, but the one entry point callers should actually use is 
still named `with_new_children_if_necessary` — embedding the now-deprecated 
`with_new_children`. That reads as "do I use `with_` or `replace_`?".
   
   Since this is a widely-used `pub fn` (~65 in-tree call sites + downstream), 
an outright rename would be another breaking change. A pure-additive move 
avoids that:
   
   ```rust
   pub fn replace_children_if_necessary(
       plan: Arc<dyn ExecutionPlan>,
       children: Vec<Arc<dyn ExecutionPlan>>,
   ) -> Result<Arc<dyn ExecutionPlan>> { /* current body */ }
   
   #[deprecated(since = "55.0.0", note = "Use `replace_children_if_necessary`")]
   pub fn with_new_children_if_necessary(
       plan: Arc<dyn ExecutionPlan>,
       children: Vec<Arc<dyn ExecutionPlan>>,
   ) -> Result<Arc<dyn ExecutionPlan>> {
       replace_children_if_necessary(plan, children)
   }
   ```
   
   One caveat if you do this: the in-tree call sites must move to the new name 
in the same PR, otherwise `-D warnings` fails CI on the self-deprecation. 
Downstream keeps compiling on the old name. Could also be a follow-up.



##########
datafusion/physical-plan/src/joins/hash_join/exec.rs:
##########
@@ -1335,13 +1335,21 @@ impl ExecutionPlan for HashJoinExec {
     /// This method is called during query optimization when the optimizer 
creates new
     /// plan nodes. Importantly, it creates a fresh bounds_accumulator via 
`try_new`
     /// rather than cloning the existing one because partitioning may have 
changed.
-    fn with_new_children(
+    fn replace_children(
         self: Arc<Self>,
         children: Vec<Arc<dyn ExecutionPlan>>,
+        _: ChildrenPropertiesHint,
     ) -> Result<Arc<dyn ExecutionPlan>> {
         self.builder().with_new_children(children)?.build_exec()

Review Comment:
   `HashJoinExec::replace_children` ignores `hint` and always rebuilds via the 
builder, so a `SameProperties` signal does no fast-path here. Behaviorally 
correct, but this is arguably the single most valuable place for the fast-path 
— join equivalence-property computation is exactly the "expensive 
`PlanProperties`" this PR set out to skip, yet it's rebuilt even when the 
children are proven property-identical.
   
   Is the always-rebuild intentional (builder / dynamic-filter lifecycle), or 
an oversight? If it's safe to reuse, a `SameProperties` arm that swaps the two 
children into a cloned `self` (like the other joins) would capture the win.



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