sunchao commented on code in PR #58345:
URL: https://github.com/apache/spark/pull/58345#discussion_r3874207269


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/exchange/ShuffleExchangeExec.scala:
##########
@@ -463,8 +465,22 @@ object ShuffleExchangeExec {
         val projection = 
UnsafeProjection.create(sortingExpressions.map(_.child), outputAttributes)
         row => projection(row)
       case SinglePartition => identity
-      case KeyedPartitioning(expressions, _, _, _) =>
-        row => bindReferences(expressions, outputAttributes).map(_.eval(row))
+      case k: KeyedPartitioning =>
+        val expressions = bindReferences(k.expressions, 
outputAttributes).toArray
+        // Wrap the evaluated partition key so it compares equal to the 
KeyGroupedPartitioner's
+        // map keys (the partitioning's own wrappers) under `RowOrdering` 
semantics. The wrapped
+        // row is reused across records; KeyGroupedPartitioner does not retain 
lookup keys.
+        val wrapperFactory = InternalRowComparableWrapper
+          .getInternalRowComparableWrapperFactory(k.expressionDataTypes)

Review Comment:
   **[P1] Use the same schema for stored and lookup partition wrappers**
   
   With `spark.sql.sources.v2.bucketing.shuffle.enabled=true`, a legal join 
between an identity-partitioned `STRUCT<a:INT>` column and an unpartitioned 
`STRUCT<b:INT>` column can silently lose matches. Spark's 
`BinaryComparison.sameType` ignores struct field names, and common-type 
inference leaves this differently named pair uncast, so the one-sided shuffle 
remains eligible.
   
   `KeyedShuffleSpec.createPartitioning` substitutes the shuffled side's 
expressions but retains the partitioned side's `partitionKeys` wrappers. The 
map at line 414 therefore contains wrappers with `STRUCT<a:INT>`, while this 
factory creates lookup wrappers with `STRUCT<b:INT>`. 
`InternalRowComparableWrapper.equals` requires exact `dataTypes` equality and 
rejects every lookup before comparing values. Matching keys then use hash 
fallback instead of their declared partition IDs, losing matches whenever those 
IDs differ. The previous `Seq[Any]` lookup did not compare the schemas of the 
nested row values.
   
   Both sets of wrappers need a shared compatible comparison schema. A 
regression test should join matching struct values whose field names differ, 
with multiple keyed partitions, and compare the results with bucketing shuffle 
enabled and disabled.
   
   This finding is based on source analysis; the regression test was not 
executed during this review.



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