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


##########
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:
   [P1] [P1] Verify ordering across cache boundaries before trusting a scan
   
   A `ScanExec` can read a cache populated by a native null-aware anti join. 
Materialize `SELECT k FROM sorted_cached WHERE k NOT IN (...)` into another 
cache, then use it as the streamed input of a broadcast left outer join 
followed by `GROUP BY k` without an exchange. The anti join reverses build 
batches, but `CometBroadcastHashJoinExec.outputOrdering` and Spark’s cache scan 
retain `k ASC`. This check sees only the cache scan and returns `true`, so 
`SortedInputExec` enables sorted aggregation over unsorted rows. A focused 
reproduction returns 49 groups for 37 keys, splitting their counts, whereas the 
pre-PR behavior returns 37 correct groups. The existing regression test leaves 
the anti join in the same native plan and therefore misses this boundary. 
Restore the anti join’s claimed order before caching, or validate the cached 
producer’s ordering before applying the declaration, and cover this cached 
variant.
   
   Evidence: Recompiled `/tmp/review6835-pinned-8hg9r0yc/cache_boundary.rs` 
using the exact-head `sorted_input.rs`. Input: 100,000 sorted rows with `k = (i 
% 40) * 1,000,000`, batches of 8,192, excluding keys 1,000,000, 10,000,000, 
20,000,000 and an absent 100,000,000 through the native null-aware anti join. 
Materialized and replayed its output, outer-joined against the even keys, then 
grouped by `k`. Without declaration: 37 groups, count 2,500 each. With the PR’s 
declaration: 49 groups containing only 37 distinct keys; key 3,000,000 split 
into counts 692 and 1,808. An actual-sort control returned 37 correct groups. 
The Spark-only `spark_metadata.py` run confirmed the cached anti result retains 
`k ASC` and `SinglePartition`, and the final plan has two aggregates above the 
outer join without an exchange. DataFusion 55.1.0 harness dependencies have 
byte-identical relevant join and aggregate sources to locked 55.2.0. This was 
bounded native execution plus Spark metadata validation, no
 t a full Comet JVM run.



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