diff --git a/core/src/main/resources/error/error-classes.json b/core/src/main/resources/error/error-classes.json index cd1d8c3a8129d..b87c6c91bfe1e 100644 --- a/core/src/main/resources/error/error-classes.json +++ b/core/src/main/resources/error/error-classes.json @@ -133,6 +133,12 @@ "message" : [ "The value of parameter(s) '' in is invalid: " ], "sqlState" : "22023" }, + "INVALID_PROPERTY_KEY" : { + "message" : [ " is an invalid property key, please use quotes, e.g. SET =" ] + }, + "INVALID_PROPERTY_VALUE" : { + "message" : [ " is an invalid property value, please use quotes, e.g. SET =" ] + }, "INVALID_SQL_SYNTAX" : { "message" : [ "Invalid SQL syntax: " ], "sqlState" : "42000" @@ -262,6 +268,15 @@ "REPEATED_PIVOT" : { "message" : [ "Repeated PIVOT operation." ] }, + "SET_NAMESPACE_PROPERTY" : { + "message" : [ " is a reserved namespace property, ." ] + }, + "SET_PROPERTIES_AND_DBPROPERTIES" : { + "message" : [ "set PROPERTIES and DBPROPERTIES at the same time." ] + }, + "SET_TABLE_PROPERTY" : { + "message" : [ " is a reserved table property, ." ] + }, "TOO_MANY_TYPE_ARGUMENTS_FOR_UDF_CLASS" : { "message" : [ "UDF class with type arguments." ] }, diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryParsingErrors.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryParsingErrors.scala index 1d15557c9d014..3bb0970afeb17 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryParsingErrors.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryParsingErrors.scala @@ -267,16 +267,26 @@ object QueryParsingErrors extends QueryErrorsBase { def cannotCleanReservedNamespacePropertyError( property: String, ctx: ParserRuleContext, msg: String): Throwable = { - new ParseException(s"$property is a reserved namespace property, $msg.", ctx) + new ParseException( + errorClass = "UNSUPPORTED_FEATURE", + messageParameters = Array("SET_NAMESPACE_PROPERTY", property, msg), + ctx) } def propertiesAndDbPropertiesBothSpecifiedError(ctx: CreateNamespaceContext): Throwable = { - new ParseException("Either PROPERTIES or DBPROPERTIES is allowed.", ctx) + new ParseException( + errorClass = "UNSUPPORTED_FEATURE", + messageParameters = Array("SET_PROPERTIES_AND_DBPROPERTIES"), + ctx + ) } def cannotCleanReservedTablePropertyError( property: String, ctx: ParserRuleContext, msg: String): Throwable = { - new ParseException(s"$property is a reserved table property, $msg.", ctx) + new ParseException( + errorClass = "UNSUPPORTED_FEATURE", + messageParameters = Array("SET_TABLE_PROPERTY", property, msg), + ctx) } def duplicatedTablePathsFoundError( @@ -380,14 +390,18 @@ object QueryParsingErrors extends QueryErrorsBase { def invalidPropertyKeyForSetQuotedConfigurationError( keyCandidate: String, valueStr: String, ctx: ParserRuleContext): Throwable = { - new ParseException(s"'$keyCandidate' is an invalid property key, please " + - s"use quotes, e.g. SET `$keyCandidate`=`$valueStr`", ctx) + new ParseException(errorClass = "INVALID_PROPERTY_KEY", + messageParameters = Array(toSQLConf(keyCandidate), + toSQLConf(keyCandidate), toSQLConf(valueStr)), + ctx) } def invalidPropertyValueForSetQuotedConfigurationError( valueCandidate: String, keyStr: String, ctx: ParserRuleContext): Throwable = { - new ParseException(s"'$valueCandidate' is an invalid property value, please " + - s"use quotes, e.g. SET `$keyStr`=`$valueCandidate`", ctx) + new ParseException(errorClass = "INVALID_PROPERTY_VALUE", + messageParameters = Array(toSQLConf(valueCandidate), + toSQLConf(keyStr), toSQLConf(valueCandidate)), + ctx) } def unexpectedFormatForResetConfigurationError(ctx: ResetConfigurationContext): Throwable = { diff --git a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryParsingErrorsSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryParsingErrorsSuite.scala index 50966db2a213a..6dc40c3eadb6a 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryParsingErrorsSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryParsingErrorsSuite.scala @@ -642,4 +642,92 @@ class QueryParsingErrorsSuite extends QueryTest with QueryErrorsSuiteBase { |^^^ |""".stripMargin) } + + test("UNSUPPORTED_FEATURE: cannot set reserved namespace property") { + val sql = "CREATE NAMESPACE IF NOT EXISTS a.b.c WITH PROPERTIES ('location'='/home/user/db')" + validateParsingError( + sqlText = sql, + errorClass = "UNSUPPORTED_FEATURE", + errorSubClass = Some("SET_NAMESPACE_PROPERTY"), + sqlState = "0A000", + message = + """The feature is not supported: location is a reserved namespace property, """ + + """please use the LOCATION clause to specify it.(line 1, pos 0)""" + + s""" + | + |== SQL == + |$sql + |^^^ + |""".stripMargin) + } + + test("UNSUPPORTED_FEATURE: cannot set reserved table property") { + val sql = "CREATE TABLE student (id INT, name STRING, age INT) " + + "USING PARQUET TBLPROPERTIES ('provider'='parquet')" + validateParsingError( + sqlText = sql, + errorClass = "UNSUPPORTED_FEATURE", + errorSubClass = Some("SET_TABLE_PROPERTY"), + sqlState = "0A000", + message = + """The feature is not supported: provider is a reserved table property, """ + + """please use the USING clause to specify it.(line 1, pos 66)""" + + s""" + | + |== SQL == + |$sql + |------------------------------------------------------------------^^^ + |""".stripMargin) + } + + test("INVALID_PROPERTY_KEY: invalid property key for set quoted configuration") { + val sql = "set =`value`" + validateParsingError( + sqlText = sql, + errorClass = "INVALID_PROPERTY_KEY", + sqlState = null, + message = + s""""" is an invalid property key, please use quotes, e.g. SET ""="value"(line 1, pos 0) + | + |== SQL == + |$sql + |^^^ + |""".stripMargin) + } + + test("INVALID_PROPERTY_VALUE: invalid property value for set quoted configuration") { + val sql = "set `key`=1;2;;" + validateParsingError( + sqlText = sql, + errorClass = "INVALID_PROPERTY_VALUE", + sqlState = null, + message = + """"1;2;;" is an invalid property value, please use quotes, """ + + """e.g. SET "key"="1;2;;"(line 1, pos 0)""" + + s""" + | + |== SQL == + |$sql + |^^^ + |""".stripMargin) + } + + test("UNSUPPORTED_FEATURE: cannot set Properties and DbProperties at the same time") { + val sql = "CREATE NAMESPACE IF NOT EXISTS a.b.c WITH PROPERTIES ('a'='a', 'b'='b', 'c'='c') " + + "WITH DBPROPERTIES('a'='a', 'b'='b', 'c'='c')" + validateParsingError( + sqlText = sql, + errorClass = "UNSUPPORTED_FEATURE", + errorSubClass = Some("SET_PROPERTIES_AND_DBPROPERTIES"), + sqlState = "0A000", + message = + """The feature is not supported: set PROPERTIES and DBPROPERTIES at the same time.""" + + """(line 1, pos 0)""" + + s""" + | + |== SQL == + |$sql + |^^^ + |""".stripMargin) + } } diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkSqlParserSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkSqlParserSuite.scala index 050c27ede1539..86cf08e672224 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkSqlParserSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkSqlParserSuite.scala @@ -168,11 +168,11 @@ class SparkSqlParserSuite extends AnalysisTest { intercept("SET a=1;2;;", expectedErrMsg) intercept("SET a b=`1;;`", - "'a b' is an invalid property key, please use quotes, e.g. SET `a b`=`1;;`") + "\"a b\" is an invalid property key, please use quotes, e.g. SET \"a b\"=\"1;;\"") intercept("SET `a`=1;2;;", - "'1;2;;' is an invalid property value, please use quotes, e.g." + - " SET `a`=`1;2;;`") + "\"1;2;;\" is an invalid property value, please use quotes, e.g." + + " SET \"a\"=\"1;2;;\"") } test("refresh resource") { diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceParserSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceParserSuite.scala index 69a208b942429..6c59512148a53 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceParserSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/CreateNamespaceParserSuite.scala @@ -84,7 +84,8 @@ class CreateNamespaceParserSuite extends AnalysisTest { |WITH PROPERTIES ('a'='a', 'b'='b', 'c'='c') |WITH DBPROPERTIES ('a'='a', 'b'='b', 'c'='c') """.stripMargin - intercept(sql, "Either PROPERTIES or DBPROPERTIES is allowed") + intercept(sql, "The feature is not supported: " + + "set PROPERTIES and DBPROPERTIES at the same time.") } test("create namespace - support for other types in PROPERTIES") {