@@ -23,7 +23,8 @@ import org.apache.spark.sql.catalyst.dsl.expressions._
2323import org .apache .spark .sql .catalyst .dsl .plans ._
2424import org .apache .spark .sql .catalyst .expressions .Literal
2525import org .apache .spark .sql .catalyst .plans ._
26- import org .apache .spark .sql .catalyst .plans .logical .{LocalRelation , LogicalPlan , Project }
26+ import org .apache .spark .sql .catalyst .plans .logical .{Distinct , GlobalLimit , LocalLimit ,
27+ LocalRelation , LogicalPlan , Project }
2728import org .apache .spark .sql .catalyst .rules .RuleExecutor
2829import org .apache .spark .sql .types .{IntegerType , StructType }
2930
@@ -221,4 +222,79 @@ class PropagateEmptyRelationSuite extends PlanTest {
221222 val optimized = Optimize .execute(query.analyze)
222223 assert(optimized.resolved)
223224 }
225+
226+ test(" Limit 0: return empty local relation" ) {
227+ val query = testRelation1.limit(0 )
228+
229+ val optimized = Optimize .execute(query.analyze)
230+ val correctAnswer = LocalRelation (' a .int)
231+
232+ comparePlans(optimized, correctAnswer)
233+ }
234+
235+ test(" Limit 0: individual LocalLimit 0 node" ) {
236+ val query = LocalLimit (0 , testRelation1)
237+
238+ val optimized = Optimize .execute(query.analyze)
239+ val correctAnswer = LocalRelation (' a .int)
240+
241+ comparePlans(optimized, correctAnswer)
242+ }
243+
244+ test(" Limit 0: individual GlobalLimit 0 node" ) {
245+ val query = GlobalLimit (0 , testRelation1)
246+
247+ val optimized = Optimize .execute(query.analyze)
248+ val correctAnswer = LocalRelation (' a .int)
249+
250+ comparePlans(optimized, correctAnswer)
251+ }
252+
253+ test(" Limit 0: Joins" ) {
254+ val testcases = Seq (
255+ (Inner , Some (LocalRelation (' a .int, ' b .int))),
256+ (LeftOuter ,
257+ Some (Project (Seq (' a , Literal (null ).cast(IntegerType ).as(' b )), testRelation1).analyze)),
258+ (RightOuter , Some (LocalRelation (' a .int, ' b .int))),
259+ (FullOuter ,
260+ Some (Project (Seq (' a , Literal (null ).cast(IntegerType ).as(' b )), testRelation1).analyze))
261+ )
262+
263+ testcases.foreach { case (jt, answer) =>
264+ val query = testRelation1
265+ .join(testRelation2.limit(0 ), joinType = jt, condition = Some (' a .attr == ' b .attr))
266+
267+ val optimized = Optimize .execute(query.analyze)
268+ val correctAnswer =
269+ answer.getOrElse(OptimizeWithoutPropagateEmptyRelation .execute(query.analyze))
270+
271+ comparePlans(optimized, correctAnswer)
272+ }
273+ }
274+
275+ test(" Limit 0: 3-way join" ) {
276+ val testRelation3 = LocalRelation .fromExternalRows(Seq (' c .int), data = Seq (Row (1 )))
277+
278+ val subJoinQuery = testRelation1
279+ .join(testRelation2, joinType = Inner , condition = Some (' a .attr == ' b .attr))
280+ val query = subJoinQuery
281+ .join(testRelation3.limit(0 ), joinType = Inner , condition = Some (' a .attr == ' c .attr))
282+
283+ val optimized = Optimize .execute(query.analyze)
284+ val correctAnswer = Some (LocalRelation (' a .int, ' b .int, ' c .int))
285+ .getOrElse(OptimizeWithoutPropagateEmptyRelation .execute(query.analyze))
286+
287+ comparePlans(optimized, correctAnswer)
288+ }
289+
290+ test(" Limit 0: intersect" ) {
291+ val query = testRelation1
292+ .intersect(testRelation1.limit(0 ), isAll = false )
293+
294+ val optimized = Optimize .execute(query.analyze)
295+ val correctAnswer = Distinct (Some (LocalRelation (' a .int))
296+ .getOrElse(OptimizeWithoutPropagateEmptyRelation .execute(query.analyze)))
297+
298+ comparePlans(optimized, correctAnswer)
299+ }
224300}
0 commit comments