diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala index 1db44496e67c13fdaa80aa4dcb06c2572e8403e9..e03cae8ecd6c0aa73a3cca7b347adebb16f58cb7 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala @@ -1027,7 +1027,8 @@ class Analyzer( def failOnOuterReference(p: LogicalPlan): Unit = { if (p.expressions.exists(containsOuter)) { failAnalysis( - s"Correlated predicates are not supported outside of WHERE/HAVING clauses: $p") + "Expressions referencing the outer query are not supported outside of WHERE/HAVING " + + s"clauses: $p") } } diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/namedExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/namedExpressions.scala index 1274757136051356f360b8a6cb319b5d6c651708..c842f85af693caf3e15530be9ef9e833b09e7a7b 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/namedExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/namedExpressions.scala @@ -356,10 +356,17 @@ case class PrettyAttribute( * A place holder used to hold a reference that has been resolved to a field outside of the current * plan. This is used for correlated subqueries. */ -case class OuterReference(e: NamedExpression) extends LeafExpression with Unevaluable { +case class OuterReference(e: NamedExpression) + extends LeafExpression with NamedExpression with Unevaluable { override def dataType: DataType = e.dataType override def nullable: Boolean = e.nullable override def prettyName: String = "outer" + + override def name: String = e.name + override def qualifier: Option[String] = e.qualifier + override def exprId: ExprId = e.exprId + override def toAttribute: Attribute = e.toAttribute + override def newInstance(): NamedExpression = OuterReference(e.newInstance()) } object VirtualColumn { diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveSubquerySuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveSubquerySuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..4aafb2b83fb695c7f64234791cdfc4b37d234cee --- /dev/null +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveSubquerySuite.scala @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.spark.sql.catalyst.analysis + +import org.apache.spark.sql.AnalysisException +import org.apache.spark.sql.catalyst.dsl.expressions._ +import org.apache.spark.sql.catalyst.expressions.{In, ListQuery, OuterReference} +import org.apache.spark.sql.catalyst.plans.logical.{Filter, LocalRelation, Project} + +/** + * Unit tests for [[ResolveSubquery]]. + */ +class ResolveSubquerySuite extends AnalysisTest { + + val a = 'a.int + val b = 'b.int + val t1 = LocalRelation(a) + val t2 = LocalRelation(b) + + test("SPARK-17251 Improve `OuterReference` to be `NamedExpression`") { + val expr = Filter(In(a, Seq(ListQuery(Project(Seq(OuterReference(a)), t2)))), t1) + val m = intercept[AnalysisException] { + SimpleAnalyzer.ResolveSubquery(expr) + }.getMessage + assert(m.contains( + "Expressions referencing the outer query are not supported outside of WHERE/HAVING clauses")) + } +}