aboutsummaryrefslogtreecommitdiff
path: root/sql/catalyst
diff options
context:
space:
mode:
authorgatorsmile <gatorsmile@gmail.com>2016-04-05 11:19:46 +0200
committerHerman van Hovell <hvanhovell@questtec.nl>2016-04-05 11:19:46 +0200
commit78071736799b6c86b5c01b27395f4ab87075342b (patch)
tree08a4b18ebd0563c84cc8540ac81f511838e3810d /sql/catalyst
parent2715bc68bd1661d207b1af5f44ae8d02aec9d4ec (diff)
downloadspark-78071736799b6c86b5c01b27395f4ab87075342b.tar.gz
spark-78071736799b6c86b5c01b27395f4ab87075342b.tar.bz2
spark-78071736799b6c86b5c01b27395f4ab87075342b.zip
[SPARK-14349][SQL] Issue Error Messages for Unsupported Operators/DML/DDL in SQL Context.
#### What changes were proposed in this pull request? Currently, the weird error messages are issued if we use Hive Context-only operations in SQL Context. For example, - When calling `Drop Table` in SQL Context, we got the following message: ``` Expected exception org.apache.spark.sql.catalyst.parser.ParseException to be thrown, but java.lang.ClassCastException was thrown. ``` - When calling `Script Transform` in SQL Context, we got the message: ``` assertion failed: No plan for ScriptTransformation [key#9,value#10], cat, [tKey#155,tValue#156], null +- LogicalRDD [key#9,value#10], MapPartitionsRDD[3] at beforeAll at BeforeAndAfterAll.scala:187 ``` Updates: Based on the investigation from hvanhovell , the root cause is `visitChildren`, which is the default implementation. It always returns the result of the last defined context child. After merging the code changes from hvanhovell , it works! Thank you hvanhovell ! #### How was this patch tested? A few test cases are added. Not sure if the same issue exist for the other operators/DDL/DML. hvanhovell Author: gatorsmile <gatorsmile@gmail.com> Author: xiaoli <lixiao1983@gmail.com> Author: Herman van Hovell <hvanhovell@questtec.nl> Author: Xiao Li <xiaoli@Xiaos-MacBook-Pro.local> Closes #12134 from gatorsmile/hiveParserCommand.
Diffstat (limited to 'sql/catalyst')
-rw-r--r--sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g414
-rw-r--r--sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala118
-rw-r--r--sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala10
3 files changed, 68 insertions, 74 deletions
diff --git a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
index 27b01e0bed..96c170be3d 100644
--- a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
+++ b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
@@ -468,15 +468,15 @@ booleanExpression
// https://github.com/antlr/antlr4/issues/780
// https://github.com/antlr/antlr4/issues/781
predicated
- : valueExpression predicate[$valueExpression.ctx]?
+ : valueExpression predicate?
;
-predicate[ParserRuleContext value]
- : NOT? BETWEEN lower=valueExpression AND upper=valueExpression #between
- | NOT? IN '(' expression (',' expression)* ')' #inList
- | NOT? IN '(' query ')' #inSubquery
- | NOT? like=(RLIKE | LIKE) pattern=valueExpression #like
- | IS NOT? NULL #nullPredicate
+predicate
+ : NOT? kind=BETWEEN lower=valueExpression AND upper=valueExpression
+ | NOT? kind=IN '(' expression (',' expression)* ')'
+ | NOT? kind=IN '(' query ')'
+ | NOT? kind=(RLIKE | LIKE) pattern=valueExpression
+ | IS NOT? kind=NULL
;
valueExpression
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
index 61ea3e4010..14c90918e6 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
@@ -22,7 +22,7 @@ import scala.collection.JavaConverters._
import scala.collection.mutable.ArrayBuffer
import org.antlr.v4.runtime.{ParserRuleContext, Token}
-import org.antlr.v4.runtime.tree.{ParseTree, TerminalNode}
+import org.antlr.v4.runtime.tree.{ParseTree, RuleNode, TerminalNode}
import org.apache.spark.internal.Logging
import org.apache.spark.sql.catalyst.{InternalRow, TableIdentifier}
@@ -46,6 +46,19 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
ctx.accept(this).asInstanceOf[T]
}
+ /**
+ * Override the default behavior for all visit methods. This will only return a non-null result
+ * when the context has only one child. This is done because there is no generic method to
+ * combine the results of the context children. In all other cases null is returned.
+ */
+ override def visitChildren(node: RuleNode): AnyRef = {
+ if (node.getChildCount == 1) {
+ node.getChild(0).accept(this)
+ } else {
+ null
+ }
+ }
+
override def visitSingleStatement(ctx: SingleStatementContext): LogicalPlan = withOrigin(ctx) {
visit(ctx.statement).asInstanceOf[LogicalPlan]
}
@@ -351,7 +364,8 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
string(script),
attributes,
withFilter,
- withScriptIOSchema(inRowFormat, recordWriter, outRowFormat, recordReader, schemaLess))
+ withScriptIOSchema(
+ ctx, inRowFormat, recordWriter, outRowFormat, recordReader, schemaLess))
case SqlBaseParser.SELECT =>
// Regular select
@@ -398,11 +412,14 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
* Create a (Hive based) [[ScriptInputOutputSchema]].
*/
protected def withScriptIOSchema(
+ ctx: QuerySpecificationContext,
inRowFormat: RowFormatContext,
recordWriter: Token,
outRowFormat: RowFormatContext,
recordReader: Token,
- schemaLess: Boolean): ScriptInputOutputSchema = null
+ schemaLess: Boolean): ScriptInputOutputSchema = {
+ throw new ParseException("Script Transform is not supported", ctx)
+ }
/**
* Create a logical plan for a given 'FROM' clause. Note that we support multiple (comma
@@ -779,17 +796,6 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
}
/**
- * Invert a boolean expression if it has a valid NOT clause.
- */
- private def invertIfNotDefined(expression: Expression, not: TerminalNode): Expression = {
- if (not != null) {
- Not(expression)
- } else {
- expression
- }
- }
-
- /**
* Create a star (i.e. all) expression; this selects all elements (in the specified object).
* Both un-targeted (global) and targeted aliases are supported.
*/
@@ -909,57 +915,55 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with Logging {
}
/**
- * Create a BETWEEN expression. This tests if an expression lies with in the bounds set by two
- * other expressions. The inverse can also be created.
- */
- override def visitBetween(ctx: BetweenContext): Expression = withOrigin(ctx) {
- val value = expression(ctx.value)
- val between = And(
- GreaterThanOrEqual(value, expression(ctx.lower)),
- LessThanOrEqual(value, expression(ctx.upper)))
- invertIfNotDefined(between, ctx.NOT)
- }
-
- /**
- * Create an IN expression. This tests if the value of the left hand side expression is
- * contained by the sequence of expressions on the right hand side.
+ * Create a predicated expression. A predicated expression is a normal expression with a
+ * predicate attached to it, for example:
+ * {{{
+ * a + 1 IS NULL
+ * }}}
*/
- override def visitInList(ctx: InListContext): Expression = withOrigin(ctx) {
- val in = In(expression(ctx.value), ctx.expression().asScala.map(expression))
- invertIfNotDefined(in, ctx.NOT)
+ override def visitPredicated(ctx: PredicatedContext): Expression = withOrigin(ctx) {
+ val e = expression(ctx.valueExpression)
+ if (ctx.predicate != null) {
+ withPredicate(e, ctx.predicate)
+ } else {
+ e
+ }
}
/**
- * Create an IN expression, where the the right hand side is a query. This is unsupported.
+ * Add a predicate to the given expression. Supported expressions are:
+ * - (NOT) BETWEEN
+ * - (NOT) IN
+ * - (NOT) LIKE
+ * - (NOT) RLIKE
+ * - IS (NOT) NULL.
*/
- override def visitInSubquery(ctx: InSubqueryContext): Expression = {
- throw new ParseException("IN with a Sub-query is currently not supported.", ctx)
- }
+ private def withPredicate(e: Expression, ctx: PredicateContext): Expression = withOrigin(ctx) {
+ // Invert a predicate if it has a valid NOT clause.
+ def invertIfNotDefined(e: Expression): Expression = ctx.NOT match {
+ case null => e
+ case not => Not(e)
+ }
- /**
- * Create a (R)LIKE/REGEXP expression.
- */
- override def visitLike(ctx: LikeContext): Expression = {
- val left = expression(ctx.value)
- val right = expression(ctx.pattern)
- val like = ctx.like.getType match {
+ // Create the predicate.
+ ctx.kind.getType match {
+ case SqlBaseParser.BETWEEN =>
+ // BETWEEN is translated to lower <= e && e <= upper
+ invertIfNotDefined(And(
+ GreaterThanOrEqual(e, expression(ctx.lower)),
+ LessThanOrEqual(e, expression(ctx.upper))))
+ case SqlBaseParser.IN if ctx.query != null =>
+ throw new ParseException("IN with a Sub-query is currently not supported.", ctx)
+ case SqlBaseParser.IN =>
+ invertIfNotDefined(In(e, ctx.expression.asScala.map(expression)))
case SqlBaseParser.LIKE =>
- Like(left, right)
+ invertIfNotDefined(Like(e, expression(ctx.pattern)))
case SqlBaseParser.RLIKE =>
- RLike(left, right)
- }
- invertIfNotDefined(like, ctx.NOT)
- }
-
- /**
- * Create an IS (NOT) NULL expression.
- */
- override def visitNullPredicate(ctx: NullPredicateContext): Expression = withOrigin(ctx) {
- val value = expression(ctx.value)
- if (ctx.NOT != null) {
- IsNotNull(value)
- } else {
- IsNull(value)
+ invertIfNotDefined(RLike(e, expression(ctx.pattern)))
+ case SqlBaseParser.NULL if ctx.NOT != null =>
+ IsNotNull(e)
+ case SqlBaseParser.NULL =>
+ IsNull(e)
}
}
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
index 23f05ce846..9e1660df06 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
@@ -122,16 +122,6 @@ class PlanParserSuite extends PlanTest {
table("a").union(table("b")).as("c").select(star()))
}
- test("transform query spec") {
- val p = ScriptTransformation(Seq('a, 'b), "func", Seq.empty, table("e"), null)
- assertEqual("select transform(a, b) using 'func' from e where f < 10",
- p.copy(child = p.child.where('f < 10), output = Seq('key.string, 'value.string)))
- assertEqual("map a, b using 'func' as c, d from e",
- p.copy(output = Seq('c.string, 'd.string)))
- assertEqual("reduce a, b using 'func' as (c: int, d decimal(10, 0)) from e",
- p.copy(output = Seq('c.int, 'd.decimal(10, 0))))
- }
-
test("multi select query") {
assertEqual(
"from a select * select * where s < 10",