From 448c088641af409ffeff59d1c3a326631a6bd599 Mon Sep 17 00:00:00 2001 From: Michael Armbrust Date: Sun, 29 Jun 2014 11:32:12 -0700 Subject: [PATCH 1/2] Add analysis checks --- .../spark/sql/catalyst/analysis/Analyzer.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 4ebc0e70d946b..6898a80a6e3b8 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 @@ -20,6 +20,7 @@ package org.apache.spark.sql.catalyst.analysis import org.apache.spark.sql.catalyst.expressions._ import org.apache.spark.sql.catalyst.plans.logical._ import org.apache.spark.sql.catalyst.rules._ +import org.apache.spark.sql.catalyst.errors.TreeNodeException /** @@ -54,10 +55,22 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool ResolveFunctions :: GlobalAggregates :: typeCoercionRules :_*), + Batch("Check Analysis", Once, + CheckResolution), Batch("AnalysisOperators", fixedPoint, EliminateAnalysisOperators) ) + object CheckResolution extends Rule[LogicalPlan] { + def apply(plan: LogicalPlan): LogicalPlan = { + plan.transform { + case p if p.expressions.filterNot(_.resolved).nonEmpty => + throw new TreeNodeException(p, + s"Unresolved attributes: ${p.expressions.filterNot(_.resolved).mkString(",")}") + } + } + } + /** * Replaces [[UnresolvedRelation]]s with concrete relations from the catalog. */ From a639e01952381aeb22467798c52536d1fffd5518 Mon Sep 17 00:00:00 2001 From: Reynold Xin Date: Sun, 29 Jun 2014 17:07:56 -0700 Subject: [PATCH 2/2] Added a test case for unresolved attribute analysis. --- .../org/apache/spark/sql/catalyst/analysis/Analyzer.scala | 7 +++++-- .../spark/sql/catalyst/analysis/AnalysisSuite.scala | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) 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 6898a80a6e3b8..c7188469bfb86 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 @@ -17,10 +17,10 @@ package org.apache.spark.sql.catalyst.analysis +import org.apache.spark.sql.catalyst.errors.TreeNodeException import org.apache.spark.sql.catalyst.expressions._ import org.apache.spark.sql.catalyst.plans.logical._ import org.apache.spark.sql.catalyst.rules._ -import org.apache.spark.sql.catalyst.errors.TreeNodeException /** @@ -61,10 +61,13 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool EliminateAnalysisOperators) ) + /** + * Makes sure all attributes have been resolved. + */ object CheckResolution extends Rule[LogicalPlan] { def apply(plan: LogicalPlan): LogicalPlan = { plan.transform { - case p if p.expressions.filterNot(_.resolved).nonEmpty => + case p if p.expressions.exists(!_.resolved) => throw new TreeNodeException(p, s"Unresolved attributes: ${p.expressions.filterNot(_.resolved).mkString(",")}") } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnalysisSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnalysisSuite.scala index 4c313585c6386..f14df8137683b 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnalysisSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnalysisSuite.scala @@ -19,6 +19,7 @@ package org.apache.spark.sql.catalyst.analysis import org.scalatest.FunSuite +import org.apache.spark.sql.catalyst.errors.TreeNodeException import org.apache.spark.sql.catalyst.plans.logical._ /* Implicit conversions */ @@ -34,4 +35,11 @@ class AnalysisSuite extends FunSuite { analyze(Project(Seq(UnresolvedAttribute("a")), testRelation)) === Project(testRelation.output, testRelation)) } + + test("throw errors for unresolved attributes during analysis") { + val e = intercept[TreeNodeException[_]] { + analyze(Project(Seq(UnresolvedAttribute("abcd")), testRelation)) + } + assert(e.getMessage().toLowerCase.contains("unresolved")) + } }