Skip to content

Commit 0407070

Browse files
committed
[SPARK-27444][SQL] multi-select can be used in subquery
## What changes were proposed in this pull request? This is a regression caused by apache#24150 `select * from (from a select * select *)` is supported in 2.4, and we should keep supporting it. This PR merges the parser rule for single and multi select statements, as they are very similar. ## How was this patch tested? a new test case Closes apache#24348 from cloud-fan/parser. Authored-by: Wenchen Fan <[email protected]> Signed-off-by: Wenchen Fan <[email protected]>
1 parent 4eb694c commit 0407070

File tree

3 files changed

+40
-51
lines changed

3 files changed

+40
-51
lines changed

sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ singleTableSchema
8181

8282
statement
8383
: query #statementDefault
84-
| insertStatement #insertStatementDefault
85-
| multiSelectStatement #multiSelectStatementDefault
84+
| ctes? dmlStatementNoWith #dmlStatement
8685
| USE db=identifier #use
8786
| CREATE database (IF NOT EXISTS)? identifier
8887
(COMMENT comment=STRING)? locationSpec?
@@ -356,14 +355,14 @@ resource
356355
: identifier STRING
357356
;
358357

359-
insertStatement
360-
: (ctes)? insertInto queryTerm queryOrganization #singleInsertQuery
361-
| (ctes)? fromClause multiInsertQueryBody+ #multiInsertQuery
358+
dmlStatementNoWith
359+
: insertInto queryTerm queryOrganization #singleInsertQuery
360+
| fromClause multiInsertQueryBody+ #multiInsertQuery
362361
;
363362

364363
queryNoWith
365364
: queryTerm queryOrganization #noWithQuery
366-
| fromClause selectStatement #queryWithFrom
365+
| fromClause selectStatement+ #queryWithFrom
367366
;
368367

369368
queryOrganization
@@ -379,10 +378,6 @@ multiInsertQueryBody
379378
: insertInto selectStatement
380379
;
381380

382-
multiSelectStatement
383-
: (ctes)? fromClause selectStatement+ #multiSelect
384-
;
385-
386381
selectStatement
387382
: querySpecification queryOrganization
388383
;

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
117117
query.optionalMap(ctx.ctes)(withCTE)
118118
}
119119

120+
override def visitDmlStatement(ctx: DmlStatementContext): AnyRef = withOrigin(ctx) {
121+
val dmlStmt = plan(ctx.dmlStatementNoWith)
122+
// Apply CTEs
123+
dmlStmt.optionalMap(ctx.ctes)(withCTE)
124+
}
125+
120126
private def withCTE(ctx: CtesContext, plan: LogicalPlan): LogicalPlan = {
121127
val ctes = ctx.namedQuery.asScala.map { nCtx =>
122128
val namedQuery = visitNamedQuery(nCtx)
@@ -129,11 +135,21 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
129135

130136
override def visitQueryWithFrom(ctx: QueryWithFromContext): LogicalPlan = withOrigin(ctx) {
131137
val from = visitFromClause(ctx.fromClause)
132-
validate(ctx.selectStatement.querySpecification.fromClause == null,
133-
"Individual select statement can not have FROM cause as its already specified in the" +
134-
" outer query block", ctx)
135-
withQuerySpecification(ctx.selectStatement.querySpecification, from).
136-
optionalMap(ctx.selectStatement.queryOrganization)(withQueryResultClauses)
138+
val selects = ctx.selectStatement.asScala.map { select =>
139+
validate(select.querySpecification.fromClause == null,
140+
"This select statement can not have FROM cause as its already specified upfront",
141+
select)
142+
143+
withQuerySpecification(select.querySpecification, from).
144+
// Add organization statements.
145+
optionalMap(select.queryOrganization)(withQueryResultClauses)
146+
}
147+
// If there are multiple SELECT just UNION them together into one query.
148+
if (selects.length == 1) {
149+
selects.head
150+
} else {
151+
Union(selects)
152+
}
137153
}
138154

139155
override def visitNoWithQuery(ctx: NoWithQueryContext): LogicalPlan = withOrigin(ctx) {
@@ -182,47 +198,21 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
182198
}
183199

184200
// If there are multiple INSERTS just UNION them together into one query.
185-
val insertPlan = inserts match {
186-
case Seq(query) => query
187-
case queries => Union(queries)
188-
}
189-
// Apply CTEs
190-
insertPlan.optionalMap(ctx.ctes)(withCTE)
191-
}
192-
193-
override def visitMultiSelect(ctx: MultiSelectContext): LogicalPlan = withOrigin(ctx) {
194-
val from = visitFromClause(ctx.fromClause)
195-
196-
// Build the insert clauses.
197-
val selects = ctx.selectStatement.asScala.map {
198-
body =>
199-
validate(body.querySpecification.fromClause == null,
200-
"Multi-select queries cannot have a FROM clause in their individual SELECT statements",
201-
body)
202-
203-
withQuerySpecification(body.querySpecification, from).
204-
// Add organization statements.
205-
optionalMap(body.queryOrganization)(withQueryResultClauses)
206-
}
207-
208-
// If there are multiple INSERTS just UNION them together into one query.
209-
val selectUnionPlan = selects match {
210-
case Seq(query) => query
211-
case queries => Union(queries)
201+
if (inserts.length == 1) {
202+
inserts.head
203+
} else {
204+
Union(inserts)
212205
}
213-
// Apply CTEs
214-
selectUnionPlan.optionalMap(ctx.ctes)(withCTE)
215206
}
216207

217208
/**
218209
* Create a logical plan for a regular (single-insert) query.
219210
*/
220211
override def visitSingleInsertQuery(
221212
ctx: SingleInsertQueryContext): LogicalPlan = withOrigin(ctx) {
222-
val insertPlan = withInsertInto(ctx.insertInto(),
223-
plan(ctx.queryTerm).optionalMap(ctx.queryOrganization)(withQueryResultClauses))
224-
// Apply CTEs
225-
insertPlan.optionalMap(ctx.ctes)(withCTE)
213+
withInsertInto(
214+
ctx.insertInto(),
215+
plan(ctx.queryTerm).optionalMap(ctx.queryOrganization)(withQueryResultClauses))
226216
}
227217

228218
/**

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,19 @@ class PlanParserSuite extends AnalysisTest {
132132
table("a").select(star()).union(table("a").where('s < 10).select(star())))
133133
intercept(
134134
"from a select * select * from x where a.s < 10",
135-
"Multi-select queries cannot have a FROM clause in their individual SELECT statements")
135+
"This select statement can not have FROM cause as its already specified upfront")
136136
intercept(
137137
"from a select * from b",
138-
"Individual select statement can not have FROM cause as its already specified in " +
139-
"the outer query block")
138+
"This select statement can not have FROM cause as its already specified upfront")
140139
assertEqual(
141140
"from a insert into tbl1 select * insert into tbl2 select * where s < 10",
142141
table("a").select(star()).insertInto("tbl1").union(
143142
table("a").where('s < 10).select(star()).insertInto("tbl2")))
143+
assertEqual(
144+
"select * from (from a select * select *)",
145+
table("a").select(star())
146+
.union(table("a").select(star()))
147+
.as("__auto_generated_subquery_name").select(star()))
144148
}
145149

146150
test("query organization") {

0 commit comments

Comments
 (0)