@@ -28,6 +28,7 @@ class GroupPartitionsExecSuite extends SharedSparkSession {
2828
2929 private val exprA = AttributeReference (" a" , IntegerType )()
3030 private val exprB = AttributeReference (" b" , IntegerType )()
31+ private val exprC = AttributeReference (" c" , IntegerType )()
3132
3233 private def row (a : Int ): InternalRow = InternalRow .fromSeq(Seq (a))
3334 private def row (a : Int , b : Int ): InternalRow = InternalRow .fromSeq(Seq (a, b))
@@ -45,10 +46,12 @@ class GroupPartitionsExecSuite extends SharedSparkSession {
4546 assert(gpe.outputOrdering === childOrdering)
4647 }
4748
48- test(" SPARK-56241: coalescing without reducers returns key-derived ordering " ) {
49+ test(" SPARK-56241: coalescing without reducers keeps key-expression orders from child " ) {
4950 // Key 1 appears on partitions 0 and 2, causing coalescing.
5051 val partitionKeys = Seq (row(1 ), row(2 ), row(1 ))
51- val child = DummySparkPlan (outputPartitioning = KeyedPartitioning (Seq (exprA), partitionKeys))
52+ val child = DummySparkPlan (
53+ outputPartitioning = KeyedPartitioning (Seq (exprA), partitionKeys),
54+ outputOrdering = Seq (SortOrder (exprA, Ascending )))
5255 val gpe = GroupPartitionsExec (child)
5356
5457 assert(! gpe.groupedPartitions.forall(_._2.size <= 1 ), " expected coalescing" )
@@ -59,11 +62,12 @@ class GroupPartitionsExecSuite extends SharedSparkSession {
5962 assert(ordering.head.sameOrderExpressions.isEmpty)
6063 }
6164
62- test(" SPARK-56241: coalescing without reducers returns one SortOrder per key expression" ) {
65+ test(" SPARK-56241: coalescing without reducers keeps one SortOrder per key expression" ) {
6366 // Multi-key partition: key (1,10) appears on partitions 0 and 2, causing coalescing.
6467 val partitionKeys = Seq (row(1 , 10 ), row(2 , 20 ), row(1 , 10 ))
6568 val child = DummySparkPlan (
66- outputPartitioning = KeyedPartitioning (Seq (exprA, exprB), partitionKeys))
69+ outputPartitioning = KeyedPartitioning (Seq (exprA, exprB), partitionKeys),
70+ outputOrdering = Seq (SortOrder (exprA, Ascending ), SortOrder (exprB, Ascending )))
6771 val gpe = GroupPartitionsExec (child)
6872
6973 assert(! gpe.groupedPartitions.forall(_._2.size <= 1 ), " expected coalescing" )
@@ -75,13 +79,16 @@ class GroupPartitionsExecSuite extends SharedSparkSession {
7579 assert(ordering(1 ).sameOrderExpressions.isEmpty)
7680 }
7781
78- test(" SPARK-56241: coalescing join case exposes sameOrderExpressions across join sides " ) {
82+ test(" SPARK-56241: coalescing join case preserves sameOrderExpressions from child " ) {
7983 // PartitioningCollection wraps two KeyedPartitionings (one per join side), sharing the same
80- // partition keys. Key 1 coalesces partitions 0 and 2.
84+ // partition keys. Key 1 coalesces partitions 0 and 2. The child (e.g. SortMergeJoinExec)
85+ // already carries sameOrderExpressions linking both sides' key expressions.
8186 val partitionKeys = Seq (row(1 ), row(2 ), row(1 ))
8287 val leftKP = KeyedPartitioning (Seq (exprA), partitionKeys)
8388 val rightKP = KeyedPartitioning (Seq (exprB), partitionKeys)
84- val child = DummySparkPlan (outputPartitioning = PartitioningCollection (Seq (leftKP, rightKP)))
89+ val child = DummySparkPlan (
90+ outputPartitioning = PartitioningCollection (Seq (leftKP, rightKP)),
91+ outputOrdering = Seq (SortOrder (exprA, Ascending , sameOrderExpressions = Seq (exprB))))
8592 val gpe = GroupPartitionsExec (child)
8693
8794 assert(! gpe.groupedPartitions.forall(_._2.size <= 1 ), " expected coalescing" )
@@ -91,6 +98,22 @@ class GroupPartitionsExecSuite extends SharedSparkSession {
9198 assert(ordering.head.sameOrderExpressions === Seq (exprB))
9299 }
93100
101+ test(" SPARK-56241: coalescing drops non-key sort orders from child" ) {
102+ // exprA is the partition key; exprC is a non-key sort order the child also reports
103+ // (e.g. a secondary sort within each partition). After coalescing, exprC ordering is lost
104+ // by concatenation, so only the exprA order should survive.
105+ val partitionKeys = Seq (row(1 ), row(2 ), row(1 ))
106+ val child = DummySparkPlan (
107+ outputPartitioning = KeyedPartitioning (Seq (exprA), partitionKeys),
108+ outputOrdering = Seq (SortOrder (exprA, Ascending ), SortOrder (exprC, Ascending )))
109+ val gpe = GroupPartitionsExec (child)
110+
111+ assert(! gpe.groupedPartitions.forall(_._2.size <= 1 ), " expected coalescing" )
112+ val ordering = gpe.outputOrdering
113+ assert(ordering.length === 1 )
114+ assert(ordering.head.child === exprA)
115+ }
116+
94117 test(" SPARK-56241: coalescing with reducers returns empty ordering" ) {
95118 // When reducers are present, the original key expressions are not constant within the merged
96119 // partition, so outputOrdering falls back to the default (empty).
0 commit comments