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


##########
spark/src/main/scala/org/apache/spark/sql/comet/CometWindowExec.scala:
##########
@@ -81,10 +81,55 @@ object CometWindowExec extends 
CometOperatorSerde[WindowExec] {
 
     if (windowExprProto.forall(_.isDefined) && 
partitionExprs.forall(_.isDefined)
       && sortOrders.forall(_.isDefined)) {
+      // Spark plans no sort below the window when its input reports the order 
the window needs,
+      // a sorted cache for instance, but rows from the JVM arrive through a 
ScanExec that
+      // declares none, and the native window rejects them. Send the order for 
the planner to
+      // declare (see SortedInputExec), unless a native sort below declares it 
or the reported
+      // order may be false. A key Comet cannot express or a collated string 
key, which the
+      // native side would order by raw bytes, sends the window back to Spark.
+      val requiredOrdering = op.requiredChildOrdering.head
+      val inputSortOrders =
+        if (requiredOrdering.nonEmpty && !op.child.isInstanceOf[CometSortExec] 
&&
+          SortOrder.orderingSatisfies(op.child.outputOrdering, 
requiredOrdering) &&
+          !CometHashJoinExec.mayReportFalseInputOrder(op.child)) {
+          val orders = requiredOrdering.map { order =>
+            if (hasNonDefaultStringCollation(order.dataType)) None
+            else exprToProto(order, op.child.output)
+          }
+          if (!orders.forall(_.isDefined)) {
+            withFallbackReason(
+              op,
+              "the order of the window's input, which Spark relies on, cannot 
be expressed " +
+                "natively")
+            return None
+          }
+          // The keys the child reports past the required ones hold too, and 
this window reports
+          // them, so a window above in the same native plan may rely on them. 
Send them up to the
+          // first one that cannot be expressed, in a scope of their own, so 
that a key left out
+          // records no fallback reason.
+          val rest = op.child.outputOrdering
+            .drop(requiredOrdering.length)
+            .iterator
+            .map { order =>
+              if (hasNonDefaultStringCollation(order.dataType) ||
+                !order.references.subsetOf(op.child.outputSet)) {

Review Comment:
   There is also a projection-sensitive version of the alias case. On a cache 
sorted by `(k, x)`, expose `x` twice as `a` and `b`, then run a window 
partitioned by `k` under one partitioned by `(k, b)`. With both aliases in the 
scan output, the lower window declares `(k, b)` and Comet returns all 24 rows. 
When the query drops unused `a`, Spark still reports `b` as the suffix key and 
lists `a` in `sameOrderExpressions`. `order.references` includes both names, 
although only `b` is serialized. This check therefore drops the suffix, the 
lower window declares just `k`, and the upper window fails with two partition 
keys required and only one ordered. Spark 4.1.3 returned all 24 rows in both 
projections.



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