@@ -97,12 +97,34 @@ class EliminateSortsSuite extends PlanTest {
97
97
comparePlans(optimized, correctAnswer)
98
98
}
99
99
100
- test(" remove redundant order by" ) {
100
+ test(" SPARK-33183: remove consecutive no-op sorts" ) {
101
+ val plan = testRelation.orderBy().orderBy().orderBy()
102
+ val optimized = Optimize .execute(plan.analyze)
103
+ val correctAnswer = testRelation.analyze
104
+ comparePlans(optimized, correctAnswer)
105
+ }
106
+
107
+ test(" SPARK-33183: remove redundant sort by" ) {
101
108
val orderedPlan = testRelation.select(' a , ' b ).orderBy(' a .asc, ' b .desc_nullsFirst)
102
- val unnecessaryReordered = orderedPlan.limit(2 ).select(' a ).orderBy (' a .asc, ' b .desc_nullsFirst)
109
+ val unnecessaryReordered = orderedPlan.limit(2 ).select(' a ).sortBy (' a .asc, ' b .desc_nullsFirst)
103
110
val optimized = Optimize .execute(unnecessaryReordered.analyze)
104
111
val correctAnswer = orderedPlan.limit(2 ).select(' a ).analyze
105
- comparePlans(Optimize .execute(optimized), correctAnswer)
112
+ comparePlans(optimized, correctAnswer)
113
+ }
114
+
115
+ test(" SPARK-33183: remove all redundant local sorts" ) {
116
+ val orderedPlan = testRelation.sortBy(' a .asc).orderBy(' a .asc).sortBy(' a .asc)
117
+ val optimized = Optimize .execute(orderedPlan.analyze)
118
+ val correctAnswer = testRelation.orderBy(' a .asc).analyze
119
+ comparePlans(optimized, correctAnswer)
120
+ }
121
+
122
+ test(" SPARK-33183: should not remove global sort" ) {
123
+ val orderedPlan = testRelation.select(' a , ' b ).orderBy(' a .asc, ' b .desc_nullsFirst)
124
+ val reordered = orderedPlan.limit(2 ).select(' a ).orderBy(' a .asc, ' b .desc_nullsFirst)
125
+ val optimized = Optimize .execute(reordered.analyze)
126
+ val correctAnswer = reordered.analyze
127
+ comparePlans(optimized, correctAnswer)
106
128
}
107
129
108
130
test(" do not remove sort if the order is different" ) {
@@ -113,22 +135,39 @@ class EliminateSortsSuite extends PlanTest {
113
135
comparePlans(optimized, correctAnswer)
114
136
}
115
137
116
- test(" filters don't affect order " ) {
138
+ test(" SPARK-33183: remove top level local sort with filter operators " ) {
117
139
val orderedPlan = testRelation.select(' a , ' b ).orderBy(' a .asc, ' b .desc)
118
- val filteredAndReordered = orderedPlan.where(' a > Literal (10 )).orderBy (' a .asc, ' b .desc)
140
+ val filteredAndReordered = orderedPlan.where(' a > Literal (10 )).sortBy (' a .asc, ' b .desc)
119
141
val optimized = Optimize .execute(filteredAndReordered.analyze)
120
142
val correctAnswer = orderedPlan.where(' a > Literal (10 )).analyze
121
143
comparePlans(optimized, correctAnswer)
122
144
}
123
145
124
- test(" limits don't affect order" ) {
146
+ test(" SPARK-33183: keep top level global sort with filter operators" ) {
147
+ val projectPlan = testRelation.select(' a , ' b )
148
+ val orderedPlan = projectPlan.orderBy(' a .asc, ' b .desc)
149
+ val filteredAndReordered = orderedPlan.where(' a > Literal (10 )).orderBy(' a .asc, ' b .desc)
150
+ val optimized = Optimize .execute(filteredAndReordered.analyze)
151
+ val correctAnswer = projectPlan.where(' a > Literal (10 )).orderBy(' a .asc, ' b .desc).analyze
152
+ comparePlans(optimized, correctAnswer)
153
+ }
154
+
155
+ test(" SPARK-33183: limits should not affect order for local sort" ) {
125
156
val orderedPlan = testRelation.select(' a , ' b ).orderBy(' a .asc, ' b .desc)
126
- val filteredAndReordered = orderedPlan.limit(Literal (10 )).orderBy (' a .asc, ' b .desc)
157
+ val filteredAndReordered = orderedPlan.limit(Literal (10 )).sortBy (' a .asc, ' b .desc)
127
158
val optimized = Optimize .execute(filteredAndReordered.analyze)
128
159
val correctAnswer = orderedPlan.limit(Literal (10 )).analyze
129
160
comparePlans(optimized, correctAnswer)
130
161
}
131
162
163
+ test(" SPARK-33183: should not remove global sort with limit operators" ) {
164
+ val orderedPlan = testRelation.select(' a , ' b ).orderBy(' a .asc, ' b .desc)
165
+ val filteredAndReordered = orderedPlan.limit(Literal (10 )).orderBy(' a .asc, ' b .desc)
166
+ val optimized = Optimize .execute(filteredAndReordered.analyze)
167
+ val correctAnswer = filteredAndReordered.analyze
168
+ comparePlans(optimized, correctAnswer)
169
+ }
170
+
132
171
test(" different sorts are not simplified if limit is in between" ) {
133
172
val orderedPlan = testRelation.select(' a , ' b ).orderBy(' b .desc).limit(Literal (10 ))
134
173
.orderBy(' a .asc)
@@ -137,11 +176,11 @@ class EliminateSortsSuite extends PlanTest {
137
176
comparePlans(optimized, correctAnswer)
138
177
}
139
178
140
- test(" range is already sorted " ) {
179
+ test(" SPARK-33183: should not remove global sort with range operator " ) {
141
180
val inputPlan = Range (1L , 1000L , 1 , 10 )
142
181
val orderedPlan = inputPlan.orderBy(' id .asc)
143
182
val optimized = Optimize .execute(orderedPlan.analyze)
144
- val correctAnswer = inputPlan .analyze
183
+ val correctAnswer = orderedPlan .analyze
145
184
comparePlans(optimized, correctAnswer)
146
185
147
186
val reversedPlan = inputPlan.orderBy(' id .desc)
@@ -152,10 +191,18 @@ class EliminateSortsSuite extends PlanTest {
152
191
val negativeStepInputPlan = Range (10L , 1L , - 1 , 10 )
153
192
val negativeStepOrderedPlan = negativeStepInputPlan.orderBy(' id .desc)
154
193
val negativeStepOptimized = Optimize .execute(negativeStepOrderedPlan.analyze)
155
- val negativeStepCorrectAnswer = negativeStepInputPlan .analyze
194
+ val negativeStepCorrectAnswer = negativeStepOrderedPlan .analyze
156
195
comparePlans(negativeStepOptimized, negativeStepCorrectAnswer)
157
196
}
158
197
198
+ test(" SPARK-33183: remove local sort with range operator" ) {
199
+ val inputPlan = Range (1L , 1000L , 1 , 10 )
200
+ val orderedPlan = inputPlan.sortBy(' id .asc)
201
+ val optimized = Optimize .execute(orderedPlan.analyze)
202
+ val correctAnswer = inputPlan.analyze
203
+ comparePlans(optimized, correctAnswer)
204
+ }
205
+
159
206
test(" sort should not be removed when there is a node which doesn't guarantee any order" ) {
160
207
val orderedPlan = testRelation.select(' a , ' b )
161
208
val groupedAndResorted = orderedPlan.groupBy(' a )(sum(' a )).orderBy(' a .asc)
@@ -319,4 +366,39 @@ class EliminateSortsSuite extends PlanTest {
319
366
val correctAnswer = PushDownOptimizer .execute(noOrderByPlan.analyze)
320
367
comparePlans(optimized, correctAnswer)
321
368
}
369
+
370
+ test(" SPARK-33183: remove consecutive global sorts with the same ordering" ) {
371
+ Seq (
372
+ (testRelation.orderBy(' a .asc).orderBy(' a .asc), testRelation.orderBy(' a .asc)),
373
+ (testRelation.orderBy(' a .asc, ' b .desc).orderBy(' a .asc), testRelation.orderBy(' a .asc))
374
+ ).foreach { case (ordered, answer) =>
375
+ val optimized = Optimize .execute(ordered.analyze)
376
+ comparePlans(optimized, answer.analyze)
377
+ }
378
+ }
379
+
380
+ test(" SPARK-33183: remove consecutive local sorts with the same ordering" ) {
381
+ val orderedPlan = testRelation.sortBy(' a .asc).sortBy(' a .asc).sortBy(' a .asc)
382
+ val optimized = Optimize .execute(orderedPlan.analyze)
383
+ val correctAnswer = testRelation.sortBy(' a .asc).analyze
384
+ comparePlans(optimized, correctAnswer)
385
+ }
386
+
387
+ test(" SPARK-33183: remove consecutive local sorts with different ordering" ) {
388
+ val orderedPlan = testRelation.sortBy(' b .asc).sortBy(' a .desc).sortBy(' a .asc)
389
+ val optimized = Optimize .execute(orderedPlan.analyze)
390
+ val correctAnswer = testRelation.sortBy(' a .asc).analyze
391
+ comparePlans(optimized, correctAnswer)
392
+ }
393
+
394
+ test(" SPARK-33183: should keep global sort when child is a local sort with the same ordering" ) {
395
+ val correctAnswer = testRelation.orderBy(' a .asc).analyze
396
+ Seq (
397
+ testRelation.sortBy(' a .asc).orderBy(' a .asc),
398
+ testRelation.orderBy(' a .asc).sortBy(' a .asc).orderBy(' a .asc)
399
+ ).foreach { ordered =>
400
+ val optimized = Optimize .execute(ordered.analyze)
401
+ comparePlans(optimized, correctAnswer)
402
+ }
403
+ }
322
404
}
0 commit comments