Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPARK-38106][SQL] Use error classes in the parsing errors of functions #35865

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ object QueryParsingErrors {
}

def functionNameUnsupportedError(functionName: String, ctx: ParserRuleContext): Throwable = {
new ParseException(s"Unsupported function name '$functionName'", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array(s"Unsupported function name '$functionName'"), ctx)
}

def cannotParseValueTypeError(
Expand Down Expand Up @@ -293,12 +294,13 @@ object QueryParsingErrors {
}

def showFunctionsUnsupportedError(identifier: String, ctx: IdentifierContext): Throwable = {
new ParseException(s"SHOW $identifier FUNCTIONS not supported", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array(s"SHOW $identifier FUNCTIONS not supported"), ctx)
}

def showFunctionsInvalidPatternError(pattern: String, ctx: ParserRuleContext): Throwable = {
new ParseException(s"Invalid pattern in SHOW FUNCTIONS: $pattern. It must be " +
"a string literal.", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array(s"Invalid pattern in SHOW FUNCTIONS: $pattern. It must be a string literal."), ctx)
}

def duplicateCteDefinitionNamesError(duplicateNames: String, ctx: CtesContext): Throwable = {
Expand Down Expand Up @@ -403,22 +405,27 @@ object QueryParsingErrors {
}

def createFuncWithBothIfNotExistsAndReplaceError(ctx: CreateFunctionContext): Throwable = {
new ParseException("CREATE FUNCTION with both IF NOT EXISTS and REPLACE is not allowed.", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array("CREATE FUNCTION with both IF NOT EXISTS and REPLACE is not allowed."), ctx)
}

def defineTempFuncWithIfNotExistsError(ctx: CreateFunctionContext): Throwable = {
new ParseException("It is not allowed to define a TEMPORARY function with IF NOT EXISTS.", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array("It is not allowed to define a TEMPORARY function with IF NOT EXISTS."), ctx)
}

def unsupportedFunctionNameError(quoted: String, ctx: CreateFunctionContext): Throwable = {
new ParseException(s"Unsupported function name '$quoted'", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array(s"Unsupported function name '$quoted'"), ctx)
}

def specifyingDBInCreateTempFuncError(
databaseName: String,
ctx: CreateFunctionContext): Throwable = {
new ParseException(
s"Specifying a database in CREATE TEMPORARY FUNCTION is not allowed: '$databaseName'", ctx)
"INVALID_SQL_SYNTAX",
Array(s"Specifying a database in CREATE TEMPORARY FUNCTION is not allowed: '$databaseName'"),
ctx)
}

def unclosedBracketedCommentError(command: String, position: Origin): Throwable = {
Expand All @@ -430,8 +437,8 @@ object QueryParsingErrors {
}

def invalidNameForDropTempFunc(name: Seq[String], ctx: ParserRuleContext): Throwable = {
new ParseException(
s"DROP TEMPORARY FUNCTION requires a single part name but got: ${name.quoted}", ctx)
new ParseException("INVALID_SQL_SYNTAX",
Array(s"DROP TEMPORARY FUNCTION requires a single part name but got: ${name.quoted}"), ctx)
}

def defaultColumnNotImplementedYetError(ctx: ParserRuleContext): Throwable = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,176 @@ class QueryParsingErrorsSuite extends QueryTest with SharedSparkSession {
|--------------------------------------------^^^
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Invalid table value function name") {
validateParsingError(
sqlText = "SELECT * FROM ns.db.func()",
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
"""
|Invalid SQL syntax: Unsupported function name 'ns.db.func'(line 1, pos 14)
|
|== SQL ==
|SELECT * FROM ns.db.func()
|--------------^^^
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Invalid scope in show functions") {
validateParsingError(
sqlText = "SHOW sys FUNCTIONS",
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
"""
|Invalid SQL syntax: SHOW sys FUNCTIONS not supported(line 1, pos 5)
|
|== SQL ==
|SHOW sys FUNCTIONS
|-----^^^
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Invalid pattern in show functions") {
val errorDesc =
"Invalid pattern in SHOW FUNCTIONS: f1. It must be a string literal.(line 1, pos 21)"

validateParsingError(
sqlText = "SHOW FUNCTIONS IN db f1",
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
s"""
|Invalid SQL syntax: $errorDesc
|
|== SQL ==
|SHOW FUNCTIONS IN db f1
|---------------------^^^
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Create function with both if not exists and replace") {
val sqlText =
"""
|CREATE OR REPLACE FUNCTION IF NOT EXISTS func1 as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin
val errorDesc =
"CREATE FUNCTION with both IF NOT EXISTS and REPLACE is not allowed.(line 2, pos 0)"

validateParsingError(
sqlText = sqlText,
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
s"""
|Invalid SQL syntax: $errorDesc
|
|== SQL ==
|
|CREATE OR REPLACE FUNCTION IF NOT EXISTS func1 as
|^^^
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Create temporary function with if not exists") {
val sqlText =
"""
|CREATE TEMPORARY FUNCTION IF NOT EXISTS func1 as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin
val errorDesc =
"It is not allowed to define a TEMPORARY function with IF NOT EXISTS.(line 2, pos 0)"

validateParsingError(
sqlText = sqlText,
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
s"""
|Invalid SQL syntax: $errorDesc
|
|== SQL ==
|
|CREATE TEMPORARY FUNCTION IF NOT EXISTS func1 as
|^^^
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Create temporary function with multi-part name") {
val sqlText =
"""
|CREATE TEMPORARY FUNCTION ns.db.func as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin

validateParsingError(
sqlText = sqlText,
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
"""
|Invalid SQL syntax: Unsupported function name 'ns.db.func'(line 2, pos 0)
|
|== SQL ==
|
|CREATE TEMPORARY FUNCTION ns.db.func as
|^^^
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Specifying database while creating temporary function") {
val sqlText =
"""
|CREATE TEMPORARY FUNCTION db.func as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin
val errorDesc =
"Specifying a database in CREATE TEMPORARY FUNCTION is not allowed: 'db'(line 2, pos 0)"

validateParsingError(
sqlText = sqlText,
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
s"""
|Invalid SQL syntax: $errorDesc
|
|== SQL ==
|
|CREATE TEMPORARY FUNCTION db.func as
|^^^
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
|""".stripMargin)
}

test("INVALID_SQL_SYNTAX: Drop temporary function requires a single part name") {
val errorDesc =
"DROP TEMPORARY FUNCTION requires a single part name but got: db.func(line 1, pos 0)"

validateParsingError(
sqlText = "DROP TEMPORARY FUNCTION db.func",
errorClass = "INVALID_SQL_SYNTAX",
sqlState = "42000",
message =
s"""
|Invalid SQL syntax: $errorDesc
|
|== SQL ==
|DROP TEMPORARY FUNCTION db.func
|^^^
|""".stripMargin)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2103,57 +2103,6 @@ abstract class DDLSuite extends QueryTest with SQLTestUtils {
}
}

test("create temporary function with if not exists") {
withUserDefinedFunction("func1" -> true) {
val sql1 =
"""
|CREATE TEMPORARY FUNCTION IF NOT EXISTS func1 as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
""".stripMargin
val e = intercept[AnalysisException] {
sql(sql1)
}.getMessage
assert(e.contains("It is not allowed to define a TEMPORARY function with IF NOT EXISTS"))
}
}

test("create function with both if not exists and replace") {
withUserDefinedFunction("func1" -> false) {
val sql1 =
"""
|CREATE OR REPLACE FUNCTION IF NOT EXISTS func1 as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
""".stripMargin
val e = intercept[AnalysisException] {
sql(sql1)
}.getMessage
assert(e.contains("CREATE FUNCTION with both IF NOT EXISTS and REPLACE is not allowed"))
}
}

test("create temporary function by specifying a database") {
val dbName = "mydb"
withDatabase(dbName) {
sql(s"CREATE DATABASE $dbName")
sql(s"USE $dbName")
withUserDefinedFunction("func1" -> true) {
val sql1 =
s"""
|CREATE TEMPORARY FUNCTION $dbName.func1 as
|'com.matthewrathbone.example.SimpleUDFExample' USING JAR '/path/to/jar1',
|JAR '/path/to/jar2'
""".stripMargin
val e = intercept[AnalysisException] {
sql(sql1)
}.getMessage
assert(e.contains(s"Specifying a database in CREATE TEMPORARY FUNCTION " +
s"is not allowed: '$dbName'"))
}
}
}

Seq(true, false).foreach { caseSensitive =>
test(s"alter table add columns with existing column name - caseSensitive $caseSensitive") {
withSQLConf(SQLConf.CASE_SENSITIVE.key -> s"$caseSensitive") {
Expand Down