[SPARK-35789][SQL] Refine lateral join syntax to only allow subqueries

### What changes were proposed in this pull request?
This PR is a follow-up for SPARK-34382. It refines the lateral join syntax to only allow the LATERAL keyword to be in front of subqueries, instead of all `relationPriamry`. For example, `SELECT * FROM t1, LATERAL t2` should not be allowed.

### Why are the changes needed?
To be consistent with Postgres.

### Does this PR introduce _any_ user-facing change?
Yes. After this PR, the LATERAL keyword can only be in front of subqueries.

```scala
sql("SELECT * FROM t1, LATERAL t2")

org.apache.spark.sql.catalyst.parser.ParseException:
LATERAL can only be used with subquery(line 1, pos 26)

== SQL ==
select * from t1, lateral t2
--------------------------^^^
```

### How was this patch tested?
New unit tests.

Closes #32937 from allisonwang-db/spark-35789-lateral-join-parser.

Authored-by: allisonwang-db <allison.wang@databricks.com>
Signed-off-by: Wenchen Fan <wenchen@databricks.com>
This commit is contained in:
allisonwang-db 2021-06-17 16:47:30 +00:00 committed by Wenchen Fan
parent 509c076bc0
commit 0d900b6cfa
3 changed files with 21 additions and 0 deletions

View file

@ -873,6 +873,9 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
val right = plan(relation.relationPrimary)
val join = right.optionalMap(left) { (left, right) =>
if (relation.LATERAL != null) {
if (!relation.relationPrimary.isInstanceOf[AliasedQueryContext]) {
throw QueryParsingErrors.invalidLateralJoinRelationError(relation.relationPrimary)
}
LateralJoin(left, LateralSubquery(right), Inner, None)
} else {
Join(left, right, Inner, None, JoinHint.NONE)
@ -1130,6 +1133,10 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
case _ => Inner
}
if (join.LATERAL != null && !join.right.isInstanceOf[AliasedQueryContext]) {
throw QueryParsingErrors.invalidLateralJoinRelationError(join.right)
}
// Resolve the join type and join condition
val (joinType, condition) = Option(join.joinCriteria) match {
case Some(c) if c.USING != null =>

View file

@ -112,6 +112,10 @@ object QueryParsingErrors {
new ParseException(s"Unsupported LATERAL join type $joinType", ctx)
}
def invalidLateralJoinRelationError(ctx: RelationPrimaryContext): Throwable = {
new ParseException(s"LATERAL can only be used with subquery", ctx)
}
def repetitiveWindowDefinitionError(name: String, ctx: WindowClauseContext): Throwable = {
new ParseException(s"The definition of window '$name' is repetitive", ctx)
}

View file

@ -208,4 +208,14 @@ class ErrorParserSuite extends AnalysisTest {
|SELECT b
""".stripMargin, 2, 9, 10, msg + " test-table")
}
test("SPARK-35789: lateral join with non-subquery relations") {
val msg = "LATERAL can only be used with subquery"
intercept("SELECT * FROM t1, LATERAL t2", msg)
intercept("SELECT * FROM t1 JOIN LATERAL t2", msg)
intercept("SELECT * FROM t1, LATERAL (t2 JOIN t3)", msg)
intercept("SELECT * FROM t1, LATERAL (LATERAL t2)", msg)
intercept("SELECT * FROM t1, LATERAL VALUES (0, 1)", msg)
intercept("SELECT * FROM t1, LATERAL RANGE(0, 1)", msg)
}
}