From e302962ceb3d57282400be14d8f4f35803e828a8 Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Sat, 5 Sep 2015 20:21:42 +0800 Subject: [PATCH] correctly parse NOT operator with comparison operations --- .../apache/spark/sql/catalyst/SqlParser.scala | 6 ++++-- .../spark/sql/catalyst/SqlParserSuite.scala | 21 ++++++++++++++----- .../spark/sql/catalyst/plans/PlanTest.scala | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/SqlParser.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/SqlParser.scala index dfab2398857e8..18446652c3d28 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/SqlParser.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/SqlParser.scala @@ -218,7 +218,10 @@ object SqlParser extends AbstractSparkSQLParser with DataTypeParser { andExpression * (OR ^^^ { (e1: Expression, e2: Expression) => Or(e1, e2) }) protected lazy val andExpression: Parser[Expression] = - comparisonExpression * (AND ^^^ { (e1: Expression, e2: Expression) => And(e1, e2) }) + notExpression * (AND ^^^ { (e1: Expression, e2: Expression) => And(e1, e2) }) + + protected lazy val notExpression: Parser[Expression] = + NOT.? ~ comparisonExpression ^^ { case not ~ e => not.map(_ => Not(e)).getOrElse(e) } protected lazy val comparisonExpression: Parser[Expression] = ( termExpression ~ ("=" ~> termExpression) ^^ { case e1 ~ e2 => EqualTo(e1, e2) } @@ -246,7 +249,6 @@ object SqlParser extends AbstractSparkSQLParser with DataTypeParser { } | termExpression <~ IS ~ NULL ^^ { case e => IsNull(e) } | termExpression <~ IS ~ NOT ~ NULL ^^ { case e => IsNotNull(e) } - | NOT ~> termExpression ^^ {e => Not(e)} | termExpression ) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/SqlParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/SqlParserSuite.scala index b93a3abc6ebd2..79b4846cb9544 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/SqlParserSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/SqlParserSuite.scala @@ -17,10 +17,10 @@ package org.apache.spark.sql.catalyst -import org.apache.spark.SparkFunSuite -import org.apache.spark.sql.catalyst.expressions.Attribute -import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan -import org.apache.spark.sql.catalyst.plans.logical.Command +import org.apache.spark.sql.catalyst.analysis.UnresolvedAlias +import org.apache.spark.sql.catalyst.expressions.{Literal, GreaterThan, Not, Attribute} +import org.apache.spark.sql.catalyst.plans.PlanTest +import org.apache.spark.sql.catalyst.plans.logical.{OneRowRelation, Project, LogicalPlan, Command} private[sql] case class TestCommand(cmd: String) extends LogicalPlan with Command { override def output: Seq[Attribute] = Seq.empty @@ -49,7 +49,7 @@ private[sql] class CaseInsensitiveTestParser extends AbstractSparkSQLParser { } } -class SqlParserSuite extends SparkFunSuite { +class SqlParserSuite extends PlanTest { test("test long keyword") { val parser = new SuperLongKeywordTestParser @@ -63,4 +63,15 @@ class SqlParserSuite extends SparkFunSuite { assert(TestCommand("NotRealCommand") === parser.parse("execute NotRealCommand")) assert(TestCommand("NotRealCommand") === parser.parse("exEcute NotRealCommand")) } + + test("test NOT operator with comparison operations") { + val parsed = SqlParser.parse("SELECT NOT TRUE > TRUE") + val expected = Project( + UnresolvedAlias( + Not( + GreaterThan(Literal(true), Literal(true))) + ) :: Nil, + OneRowRelation) + comparePlans(parsed, expected) + } } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/PlanTest.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/PlanTest.scala index f76a903dcc9cf..2efee1fc54706 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/PlanTest.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/PlanTest.scala @@ -25,7 +25,7 @@ import org.apache.spark.sql.catalyst.util._ /** * Provides helper methods for comparing plans. */ -class PlanTest extends SparkFunSuite { +abstract class PlanTest extends SparkFunSuite { /** * Since attribute references are given globally unique ids during analysis, * we must normalize them to check if two different queries are identical.