Skip to content

Commit

Permalink
[SPARK-17832][SQL] TableIdentifier.quotedString creates un-parseable …
Browse files Browse the repository at this point in the history
…names when name contains a backtick

## What changes were proposed in this pull request?

The `quotedString` method in `TableIdentifier` and `FunctionIdentifier` produce an illegal (un-parseable) name when the name contains a backtick. For example:
```
import org.apache.spark.sql.catalyst.parser.CatalystSqlParser._
import org.apache.spark.sql.catalyst.TableIdentifier
import org.apache.spark.sql.catalyst.analysis.UnresolvedAttribute
val complexName = TableIdentifier("`weird`table`name", Some("`d`b`1"))
parseTableIdentifier(complexName.unquotedString) // Does not work
parseTableIdentifier(complexName.quotedString) // Does not work
parseExpression(complexName.unquotedString) // Does not work
parseExpression(complexName.quotedString) // Does not work
```
We should handle the backtick properly to make `quotedString` parseable.

## How was this patch tested?
Add new testcases in `TableIdentifierParserSuite` and `ExpressionParserSuite`.

Author: jiangxingbo <jiangxb1987@gmail.com>

Closes #15403 from jiangxb1987/backtick.

(cherry picked from commit 26fbca4)
Signed-off-by: Herman van Hovell <hvanhovell@databricks.com>
  • Loading branch information
jiangxb1987 authored and hvanhovell committed Oct 10, 2016
1 parent 6d056c1 commit d27df35
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.apache.spark.sql.catalyst


/**
* An identifier that optionally specifies a database.
*
Expand All @@ -29,8 +28,16 @@ sealed trait IdentifierWithDatabase {

def database: Option[String]

/*
* Escapes back-ticks within the identifier name with double-back-ticks.
*/
private def quoteIdentifier(name: String): String = name.replace("`", "``")

def quotedString: String = {
if (database.isDefined) s"`${database.get}`.`$identifier`" else s"`$identifier`"
val replacedId = quoteIdentifier(identifier)
val replacedDb = database.map(quoteIdentifier(_))

if (replacedDb.isDefined) s"`${replacedDb.get}`.`$replacedId`" else s"`$replacedId`"
}

def unquotedString: String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package org.apache.spark.sql.catalyst.parser

import java.sql.{Date, Timestamp}

import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
import org.apache.spark.sql.catalyst.FunctionIdentifier
import org.apache.spark.sql.catalyst.analysis.{UnresolvedAttribute, _}
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.plans.PlanTest
Expand Down Expand Up @@ -534,4 +534,13 @@ class ExpressionParserSuite extends PlanTest {
// ".123BD" should not be treated as token of type BIGDECIMAL_LITERAL
assertEqual("a.123BD_column", UnresolvedAttribute("a.123BD_column"))
}

test("SPARK-17832 function identifier contains backtick") {
val complexName = FunctionIdentifier("`ba`r", Some("`fo`o"))
assertEqual(complexName.quotedString, UnresolvedAttribute("`fo`o.`ba`r"))
intercept(complexName.unquotedString, "mismatched input")
// Function identifier contains countious backticks should be treated correctly.
val complexName2 = FunctionIdentifier("ba``r", Some("fo``o"))
assertEqual(complexName2.quotedString, UnresolvedAttribute("fo``o.ba``r"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,14 @@ class TableIdentifierParserSuite extends SparkFunSuite {
// ".123BD" should not be treated as token of type BIGDECIMAL_LITERAL
assert(parseTableIdentifier("a.123BD_LIST") == TableIdentifier("123BD_LIST", Some("a")))
}

test("SPARK-17832 table identifier - contains backtick") {
val complexName = TableIdentifier("`weird`table`name", Some("`d`b`1"))
assert(complexName === parseTableIdentifier("```d``b``1`.```weird``table``name`"))
assert(complexName === parseTableIdentifier(complexName.quotedString))
intercept[ParseException](parseTableIdentifier(complexName.unquotedString))
// Table identifier contains countious backticks should be treated correctly.
val complexName2 = TableIdentifier("x``y", Some("d``b"))
assert(complexName2 === parseTableIdentifier(complexName2.quotedString))
}
}

0 comments on commit d27df35

Please sign in to comment.