From dab5439a083b5f771d5d5b462d0d517fa8e9aaf2 Mon Sep 17 00:00:00 2001 From: Matei Zaharia Date: Fri, 21 Mar 2014 16:53:18 -0700 Subject: Make SQL keywords case-insensitive This is a bit of a hack that allows all variations of a keyword, but it still seems to produce valid error messages and such. Author: Matei Zaharia Closes #193 from mateiz/case-insensitive-sql and squashes the following commits: 0ee4ace [Matei Zaharia] Removed unnecessary `+ ""` e3ed773 [Matei Zaharia] Make SQL keywords case-insensitive --- .../org/apache/spark/sql/catalyst/SqlParser.scala | 16 ++++++++++++-- .../scala/org/apache/spark/sql/SQLQuerySuite.scala | 25 ++++++++++++++++++---- 2 files changed, 35 insertions(+), 6 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 d3b1070a58..919bf4dbc8 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 @@ -51,7 +51,9 @@ class SqlParser extends StandardTokenParsers { } protected case class Keyword(str: String) - protected implicit def asParser(k: Keyword): Parser[String] = k.str + + protected implicit def asParser(k: Keyword): Parser[String] = + allCaseVersions(k.str).map(x => x : Parser[String]).reduce(_ | _) protected class SqlLexical extends StdLexical { case class FloatLit(chars: String) extends Token { @@ -133,7 +135,17 @@ class SqlParser extends StandardTokenParsers { .filter(_.getReturnType == classOf[Keyword]) .map(_.invoke(this).asInstanceOf[Keyword]) - lexical.reserved ++= reservedWords.map(_.str) + /** Generate all variations of upper and lower case of a given string */ + private def allCaseVersions(s: String, prefix: String = ""): Stream[String] = { + if (s == "") { + Stream(prefix) + } else { + allCaseVersions(s.tail, prefix + s.head.toLower) ++ + allCaseVersions(s.tail, prefix + s.head.toUpper) + } + } + + lexical.reserved ++= reservedWords.flatMap(w => allCaseVersions(w.str)) lexical.delimiters += ( "@", "*", "+", "-", "<", "=", "<>", "!=", "<=", ">=", ">", "/", "(", ")", diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala index 656d89c644..6371fa296a 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala @@ -200,9 +200,9 @@ class SQLQuerySuite extends QueryTest { sql( """ |SELECT * FROM - | (SELECT * FROM upperCaseData WHERE N <= 4) left FULL OUTER JOIN - | (SELECT * FROM upperCaseData WHERE N >= 3) right - | ON left.N = right.N + | (SELECT * FROM upperCaseData WHERE N <= 4) leftTable FULL OUTER JOIN + | (SELECT * FROM upperCaseData WHERE N >= 3) rightTable + | ON leftTable.N = rightTable.N """.stripMargin), (1, "A", null, null) :: (2, "B", null, null) :: @@ -211,4 +211,21 @@ class SQLQuerySuite extends QueryTest { (null, null, 5, "E") :: (null, null, 6, "F") :: Nil) } -} \ No newline at end of file + + test("mixed-case keywords") { + checkAnswer( + sql( + """ + |SeleCT * from + | (select * from upperCaseData WherE N <= 4) leftTable fuLL OUtER joiN + | (sElEcT * FROM upperCaseData whERe N >= 3) rightTable + | oN leftTable.N = rightTable.N + """.stripMargin), + (1, "A", null, null) :: + (2, "B", null, null) :: + (3, "C", 3, "C") :: + (4, "D", 4, "D") :: + (null, null, 5, "E") :: + (null, null, 6, "F") :: Nil) + } +} -- cgit v1.2.3