andygrove commented on code in PR #6835:
URL: https://github.com/apache/datafusion-comet/pull/6835#discussion_r4249879841


##########
native/core/src/execution/planner.rs:
##########
@@ -2937,6 +2951,54 @@ impl PhysicalPlanner {
         Ok(Arc::new(prepared))
     }
 
+    /// `plan`, declaring `sort_orders` as the order its rows arrive in, 
unless it already
+    /// declares one. Spark's plan knows the order of an input that enters the 
native plan from
+    /// the JVM, but the `ScanExec` it arrives through declares none. Only 
that order is declared:
+    /// when an operator in between may have moved rows, `plan` is left as it 
is.
+    fn with_declared_order(
+        &self,
+        plan: Arc<dyn ExecutionPlan>,
+        sort_orders: &[Expr],
+    ) -> Result<Arc<dyn ExecutionPlan>, ExecutionError> {
+        if plan.output_ordering().is_some() || 
!Self::keeps_jvm_input_order(&plan) {
+            return Ok(plan);
+        }
+        let exprs = sort_orders
+            .iter()
+            .map(|expr| self.create_sort_expr(expr, plan.schema()))
+            .collect::<Result<Vec<_>, _>>()?;
+        match LexOrdering::new(exprs) {
+            Some(ordering) => Ok(Arc::new(SortedInputExec::new(plan, 
ordering))),
+            None => Ok(plan),
+        }
+    }
+
+    /// Whether `plan`'s rows come from an input outside the native plan 
through operators that
+    /// keep their input's order, so that the order Spark reports for them 
still holds. Any other
+    /// operator may have moved rows, such as the null-aware anti join, whose 
streamed side Comet
+    /// builds on, and declaring Spark's order over it would be wrong.
+    fn keeps_jvm_input_order(plan: &Arc<dyn ExecutionPlan>) -> bool {
+        if plan.is::<ScanExec>() || plan.is::<ShuffleScanExec>() {

Review Comment:
   Confirmed, thanks. I reproduced it in a Comet test with AQE off, 83 groups 
for 37 keys. From Spark 3.5 a cached plan with a join runs under AQE and 
reports no order, but Spark 3.4 runs every cached plan without AQE, so there it 
only takes enabling Comet's cache, which is off by default on 3.4. The NOT IN 
isn't the only join that can write a cache like that. One of this PR's own 
outer joins can, when the native planner declares no order on its probe input 
(an IN subquery underneath is enough), and so can a native nested loop join, 
which DataFusion runs build row by build row.
   
   Fixed in 2e0fb63469. Before it sends `streamed_sort_orders`, `doConvert` now 
walks down the streamed side, through the `CometScanWrapper` that hides a 
native plan's JVM inputs during conversion and into the plan that wrote any 
cache it reads, and sends no order when one of those joins produces it with no 
sort in between. A sort above such a join still lets the declaration through. 
New tests cover the cached NOT IN, a cache written by an undeclared outer join, 
one written by a nested loop join, and one sorted above an outer join, and each 
fails against a mutant of the case it covers.
   



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