diff --git a/core/src/main/resources/error/error-classes.json b/core/src/main/resources/error/error-classes.json index a98cd6fc21189..26d75fa675e23 100644 --- a/core/src/main/resources/error/error-classes.json +++ b/core/src/main/resources/error/error-classes.json @@ -51,6 +51,9 @@ "FAILED_SET_ORIGINAL_PERMISSION_BACK" : { "message" : [ "Failed to set original permission %s back to the created path: %s. Exception: %s" ] }, + "FORBIDDEN_OPERATION" : { + "message" : [ "The operation %s is not allowed on %s: %s" ] + }, "GRAPHITE_SINK_INVALID_PROTOCOL" : { "message" : [ "Invalid Graphite protocol: %s" ] }, diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryCompilationErrors.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryCompilationErrors.scala index 6b32a08b6fd75..5ee64ad1b6c67 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryCompilationErrors.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryCompilationErrors.scala @@ -1931,11 +1931,17 @@ object QueryCompilationErrors extends QueryErrorsBase { } def descPartitionNotAllowedOnTempView(table: String): Throwable = { - new AnalysisException(s"DESC PARTITION is not allowed on a temporary view: $table") + new AnalysisException( + errorClass = "FORBIDDEN_OPERATION", + messageParameters = + Array(toSQLStmt("DESC PARTITION"), "the temporary view", toSQLId(table))) } def descPartitionNotAllowedOnView(table: String): Throwable = { - new AnalysisException(s"DESC PARTITION is not allowed on a view: $table") + new AnalysisException( + errorClass = "FORBIDDEN_OPERATION", + messageParameters = Array( + toSQLStmt("DESC PARTITION"), "the view", toSQLId(table))) } def showPartitionNotAllowedOnTableNotPartitionedError(tableIdentWithDB: String): Throwable = { @@ -1971,10 +1977,6 @@ object QueryCompilationErrors extends QueryErrorsBase { ) } - def descPartitionNotAllowedOnViewError(table: String): Throwable = { - new AnalysisException(s"DESC PARTITION is not allowed on a view: $table") - } - def showCreateTableAsSerdeNotAllowedOnSparkDataSourceTableError( table: TableIdentifier): Throwable = { new AnalysisException( diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryErrorsBase.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryErrorsBase.scala index 9b18b59c33dde..df38003d2ba52 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryErrorsBase.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryErrorsBase.scala @@ -46,6 +46,11 @@ trait QueryErrorsBase { litToErrorValue(Literal.create(v, t)) } + // Quote sql statements in error messages. + def toSQLStmt(text: String): String = { + s"'$text'" + } + def toSQLId(parts: Seq[String]): String = { parts.map(quoteIdentifier).mkString(".") } diff --git a/sql/core/src/test/resources/sql-tests/results/describe.sql.out b/sql/core/src/test/resources/sql-tests/results/describe.sql.out index 04259c0db857c..c0db04fa5c86b 100644 --- a/sql/core/src/test/resources/sql-tests/results/describe.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/describe.sql.out @@ -462,7 +462,7 @@ DESC temp_v PARTITION (c='Us', d=1) struct<> -- !query output org.apache.spark.sql.AnalysisException -DESC PARTITION is not allowed on a temporary view: temp_v +The operation 'DESC PARTITION' is not allowed on the temporary view: `temp_v` -- !query @@ -539,7 +539,7 @@ DESC v PARTITION (c='Us', d=1) struct<> -- !query output org.apache.spark.sql.AnalysisException -DESC PARTITION is not allowed on a view: v +The operation 'DESC PARTITION' is not allowed on the view: `v` -- !query diff --git a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryCompilationErrorsSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryCompilationErrorsSuite.scala index 9eb8f98ed55ed..297788db739f7 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryCompilationErrorsSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryCompilationErrorsSuite.scala @@ -310,6 +310,56 @@ class QueryCompilationErrorsSuite extends QueryTest with SharedSparkSession { } } } + + test("FORBIDDEN_OPERATION: desc partition on a temporary view") { + val tableName: String = "t" + val tempViewName: String = "tempView" + + withTable(tableName) { + sql( + s""" + |CREATE TABLE $tableName (a STRING, b INT, c STRING, d STRING) + |USING parquet + |PARTITIONED BY (c, d) + |""".stripMargin) + + withTempView(tempViewName) { + sql(s"CREATE TEMPORARY VIEW $tempViewName as SELECT * FROM $tableName") + + val e = intercept[AnalysisException]( + sql(s"DESC TABLE $tempViewName PARTITION (c='Us', d=1)") + ) + assert(e.getErrorClass === "FORBIDDEN_OPERATION") + assert(e.message === + s"The operation 'DESC PARTITION' is not allowed on the temporary view: `$tempViewName`") + } + } + } + + test("FORBIDDEN_OPERATION: desc partition on a view") { + val tableName: String = "t" + val viewName: String = "view" + + withTable(tableName) { + sql( + s""" + |CREATE TABLE $tableName (a STRING, b INT, c STRING, d STRING) + |USING parquet + |PARTITIONED BY (c, d) + |""".stripMargin) + + withView(viewName) { + sql(s"CREATE VIEW $viewName as SELECT * FROM $tableName") + + val e = intercept[AnalysisException]( + sql(s"DESC TABLE $viewName PARTITION (c='Us', d=1)") + ) + assert(e.getErrorClass === "FORBIDDEN_OPERATION") + assert(e.message === + s"The operation 'DESC PARTITION' is not allowed on the view: `$viewName`") + } + } + } } class MyCastToString extends SparkUserDefinedFunction(