diff --git a/connector/connect/client/jvm/src/main/scala/org/apache/spark/sql/functions.scala b/connector/connect/client/jvm/src/main/scala/org/apache/spark/sql/functions.scala index ba6fe725ab2c2..061fca276a3da 100644 --- a/connector/connect/client/jvm/src/main/scala/org/apache/spark/sql/functions.scala +++ b/connector/connect/client/jvm/src/main/scala/org/apache/spark/sql/functions.scala @@ -3318,14 +3318,6 @@ object functions { */ def raise_error(c: Column): Column = Column.fn("raise_error", c) - /** - * Throws an exception with the provided error message. - * - * @group misc_funcs - * @since 4.0.0 - */ - def raise_error(c: Column, e: Column): Column = Column.fn("raise_error", c, e) - /** * Returns the estimated number of unique values given the binary representation of a * Datasketches HllSketch. diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala index 4fb8d88f6eab1..e72ce91b063cf 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala @@ -722,7 +722,7 @@ object FunctionRegistry { // misc functions expression[AssertTrue]("assert_true"), - expression[RaiseError]("raise_error"), + expressionBuilder("raise_error", RaiseErrorExpressionBuilder), expression[Crc32]("crc32"), expression[Md5]("md5"), expression[Uuid]("uuid"), diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/misc.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/misc.scala index 60bf5c603d905..1ae8b19ff63eb 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/misc.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/misc.scala @@ -19,12 +19,13 @@ package org.apache.spark.sql.catalyst.expressions import org.apache.spark.{SPARK_REVISION, SPARK_VERSION_SHORT} import org.apache.spark.sql.catalyst.InternalRow -import org.apache.spark.sql.catalyst.analysis.UnresolvedSeed +import org.apache.spark.sql.catalyst.analysis.{ExpressionBuilder, UnresolvedSeed} import org.apache.spark.sql.catalyst.expressions.codegen._ import org.apache.spark.sql.catalyst.expressions.codegen.Block._ import org.apache.spark.sql.catalyst.expressions.objects.StaticInvoke import org.apache.spark.sql.catalyst.trees.TreePattern.{CURRENT_LIKE, TreePattern} import org.apache.spark.sql.catalyst.util.{MapData, RandomUUIDGenerator} +import org.apache.spark.sql.errors.QueryCompilationErrors import org.apache.spark.sql.errors.QueryExecutionErrors.raiseError import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.types._ @@ -61,21 +62,9 @@ case class PrintToStderr(child: Expression) extends UnaryExpression { /** * Throw with the result of an expression (used for debugging). + * Caller can specify the errorClass to be thrown and parameters to be passed to this error class. + * Default is to throw USER_RAISED_EXCEPTION with provided string literal. */ -// scalastyle:off line.size.limit -@ExpressionDescription( - usage = "_FUNC_( expr [, errorParams ]) - Throws a USER_RAISED_EXCEPTION with `expr` as message, or a defined error class in `expr` with a parameter map. A `null` errorParms is equivalent to an empty map.", - examples = """ - Examples: - > SELECT _FUNC_('custom error message'); - [USER_RAISED_EXCEPTION] custom error message - - > SELECT _FUNC_('VIEW_NOT_FOUND', Map('relationName' -> '`V1`')); - [VIEW_NOT_FOUND] The view `V1` cannot be found. ... - """, - since = "3.1.0", - group = "misc_funcs") -// scalastyle:on line.size.limit case class RaiseError(errorClass: Expression, errorParms: Expression, dataType: DataType) extends BinaryExpression with ImplicitCastInputTypes { @@ -139,6 +128,30 @@ object RaiseError { new RaiseError(errorClass, parms) } +/** + * Throw with the result of an expression (used for debugging). + */ +// scalastyle:off line.size.limit +@ExpressionDescription( + usage = "_FUNC_( expr ) - Throws a USER_RAISED_EXCEPTION with `expr` as message.", + examples = """ + Examples: + > SELECT _FUNC_('custom error message'); + [USER_RAISED_EXCEPTION] custom error message + """, + since = "3.1.0", + group = "misc_funcs") +// scalastyle:on line.size.limit +object RaiseErrorExpressionBuilder extends ExpressionBuilder { + override def build(funcName: String, expressions: Seq[Expression]): Expression = { + if (expressions.length != 1) { + throw QueryCompilationErrors.wrongNumArgsError(funcName, Seq(1), expressions.length) + } else { + RaiseError(expressions.head) + } + } +} + /** * A function that throws an exception if 'condition' is not true. */ diff --git a/sql/core/src/main/scala/org/apache/spark/sql/functions.scala b/sql/core/src/main/scala/org/apache/spark/sql/functions.scala index a52412e94ee52..d8b5a4b416c98 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/functions.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/functions.scala @@ -3269,14 +3269,6 @@ object functions { */ def raise_error(c: Column): Column = Column.fn("raise_error", c) - /** - * Throws an exception with the provided error class and parameter map. - * - * @group misc_funcs - * @since 4.0.0 - */ - def raise_error(c: Column, e: Column): Column = Column.fn("raise_error", c, e) - /** * Returns the estimated number of unique values given the binary representation * of a Datasketches HllSketch. diff --git a/sql/core/src/test/resources/sql-tests/analyzer-results/misc-functions.sql.out b/sql/core/src/test/resources/sql-tests/analyzer-results/misc-functions.sql.out index 1fd6e55a4d2f8..539a348584217 100644 --- a/sql/core/src/test/resources/sql-tests/analyzer-results/misc-functions.sql.out +++ b/sql/core/src/test/resources/sql-tests/analyzer-results/misc-functions.sql.out @@ -120,58 +120,85 @@ Project [if ((v#x > 5)) cast(raise_error(USER_RAISED_EXCEPTION, map(errorMessage -- !query -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`')) --- !query analysis -Project [raise_error(VIEW_NOT_FOUND, map(relationName, `v`), NullType) AS raise_error(VIEW_NOT_FOUND, map(relationName, `v`))#x] -+- OneRowRelation - - --- !query -SELECT raise_error('VIEW_NOT_FOund', Map('relationName', '`v`')) --- !query analysis -Project [raise_error(VIEW_NOT_FOund, map(relationName, `v`), NullType) AS raise_error(VIEW_NOT_FOund, map(relationName, `v`))#x] -+- OneRowRelation - - --- !query -SELECT raise_error('VIEW_NOT_FOund', Map('relationNAME', '`v`')) --- !query analysis -Project [raise_error(VIEW_NOT_FOund, map(relationNAME, `v`), NullType) AS raise_error(VIEW_NOT_FOund, map(relationNAME, `v`))#x] -+- OneRowRelation - - --- !query -SELECT raise_error('VIEW_NOT_FOUND', Map()) --- !query analysis -Project [raise_error(VIEW_NOT_FOUND, cast(map() as map), NullType) AS raise_error(VIEW_NOT_FOUND, map())#x] -+- OneRowRelation - - --- !query -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`', 'totallymadeup', '5')) --- !query analysis -Project [raise_error(VIEW_NOT_FOUND, map(relationName, `v`, totallymadeup, 5), NullType) AS raise_error(VIEW_NOT_FOUND, map(relationName, `v`, totallymadeup, 5))#x] -+- OneRowRelation - - --- !query -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', Map()) --- !query analysis -Project [raise_error(ALL_PARTITION_COLUMNS_NOT_ALLOWED, cast(map() as map), NullType) AS raise_error(ALL_PARTITION_COLUMNS_NOT_ALLOWED, map())#x] -+- OneRowRelation - - --- !query -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', NULL) --- !query analysis -Project [raise_error(ALL_PARTITION_COLUMNS_NOT_ALLOWED, cast(null as map), NullType) AS raise_error(ALL_PARTITION_COLUMNS_NOT_ALLOWED, NULL)#x] -+- OneRowRelation - - --- !query -SELECT raise_error(NULL, NULL) --- !query analysis -Project [raise_error(cast(null as string), cast(null as map), NullType) AS raise_error(NULL, NULL)#x] +SELECT raise_error('error message', Map()) +-- !query analysis +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", + "messageParameters" : { + "actualNum" : "2", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 42, + "fragment" : "raise_error('error message', Map())" + } ] +} + + +-- !query +SELECT raise_error('error message', 'some args') +-- !query analysis +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", + "messageParameters" : { + "actualNum" : "2", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 48, + "fragment" : "raise_error('error message', 'some args')" + } ] +} + + +-- !query +SELECT raise_error() +-- !query analysis +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", + "messageParameters" : { + "actualNum" : "0", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 20, + "fragment" : "raise_error()" + } ] +} + + +-- !query +SELECT raise_error(NULL) +-- !query analysis +Project [raise_error(USER_RAISED_EXCEPTION, cast(map(errorMessage, null) as map), NullType) AS raise_error(USER_RAISED_EXCEPTION, map(errorMessage, NULL))#x] ++- OneRowRelation + + +-- !query +SELECT raise_error(1) +-- !query analysis +Project [raise_error(USER_RAISED_EXCEPTION, cast(map(errorMessage, 1) as map), NullType) AS raise_error(USER_RAISED_EXCEPTION, map(errorMessage, 1))#x] +- OneRowRelation diff --git a/sql/core/src/test/resources/sql-tests/inputs/misc-functions.sql b/sql/core/src/test/resources/sql-tests/inputs/misc-functions.sql index 12e6a36db7735..f7c58a0dc5add 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/misc-functions.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/misc-functions.sql @@ -21,21 +21,20 @@ CREATE TEMPORARY VIEW tbl_misc AS SELECT * FROM (VALUES (1), (8), (2)) AS T(v); SELECT raise_error('error message'); SELECT if(v > 5, raise_error('too big: ' || v), v + 1) FROM tbl_misc; -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`')); --- Error class is case insensitive -SELECT raise_error('VIEW_NOT_FOund', Map('relationName', '`v`')); --- parameters are case sensitive -SELECT raise_error('VIEW_NOT_FOund', Map('relationNAME', '`v`')); --- Too few parameters -SELECT raise_error('VIEW_NOT_FOUND', Map()); -- Too many parameters -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`', 'totallymadeup', '5')); +SELECT raise_error('error message', Map()); + +-- Too many parameters +SELECT raise_error('error message', 'some args'); + +-- Too few parameters +SELECT raise_error(); --- Empty parameter list -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', Map()); -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', NULL); +-- Passing null as message +SELECT raise_error(NULL); -SELECT raise_error(NULL, NULL); +-- Passing non-string type +SELECT raise_error(1); -- Check legacy config disables printing of [USER_RAISED_EXCEPTION] SET spark.sql.legacy.raiseErrorWithoutErrorClass=true; diff --git a/sql/core/src/test/resources/sql-tests/results/misc-functions.sql.out b/sql/core/src/test/resources/sql-tests/results/misc-functions.sql.out index c9bdd7d2640bd..7316c3136ce0e 100644 --- a/sql/core/src/test/resources/sql-tests/results/misc-functions.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/misc-functions.sql.out @@ -177,121 +177,106 @@ org.apache.spark.SparkRuntimeException -- !query -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`')) +SELECT raise_error('error message', Map()) -- !query schema struct<> -- !query output -org.apache.spark.SparkRuntimeException -{ - "errorClass" : "VIEW_NOT_FOUND", - "sqlState" : "42P01", - "messageParameters" : { - "relationName" : "`v`" - } -} - - --- !query -SELECT raise_error('VIEW_NOT_FOund', Map('relationName', '`v`')) --- !query schema -struct<> --- !query output -org.apache.spark.SparkRuntimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "VIEW_NOT_FOUND", - "sqlState" : "42P01", + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", "messageParameters" : { - "relationName" : "`v`" - } + "actualNum" : "2", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 42, + "fragment" : "raise_error('error message', Map())" + } ] } -- !query -SELECT raise_error('VIEW_NOT_FOund', Map('relationNAME', '`v`')) +SELECT raise_error('error message', 'some args') -- !query schema struct<> -- !query output -org.apache.spark.SparkRuntimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "USER_RAISED_EXCEPTION_PARAMETER_MISMATCH", - "sqlState" : "P0001", + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", "messageParameters" : { - "errorClass" : "'VIEW_NOT_FOUND'", - "expectedParms" : "'relationName'", - "providedParms" : "'relationNAME'" - } + "actualNum" : "2", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 48, + "fragment" : "raise_error('error message', 'some args')" + } ] } -- !query -SELECT raise_error('VIEW_NOT_FOUND', Map()) +SELECT raise_error() -- !query schema struct<> -- !query output -org.apache.spark.SparkRuntimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "USER_RAISED_EXCEPTION_PARAMETER_MISMATCH", - "sqlState" : "P0001", + "errorClass" : "WRONG_NUM_ARGS.WITHOUT_SUGGESTION", + "sqlState" : "42605", "messageParameters" : { - "errorClass" : "'VIEW_NOT_FOUND'", - "expectedParms" : "'relationName'", - "providedParms" : "" - } + "actualNum" : "0", + "docroot" : "https://spark.apache.org/docs/latest", + "expectedNum" : "1", + "functionName" : "`raise_error`" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 20, + "fragment" : "raise_error()" + } ] } -- !query -SELECT raise_error('VIEW_NOT_FOUND', Map('relationName', '`v`', 'totallymadeup', '5')) +SELECT raise_error(NULL) -- !query schema struct<> -- !query output org.apache.spark.SparkRuntimeException { - "errorClass" : "USER_RAISED_EXCEPTION_PARAMETER_MISMATCH", + "errorClass" : "USER_RAISED_EXCEPTION", "sqlState" : "P0001", "messageParameters" : { - "errorClass" : "'VIEW_NOT_FOUND'", - "expectedParms" : "'relationName'", - "providedParms" : "'relationName','totallymadeup'" + "errorMessage" : "null" } } -- !query -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', Map()) +SELECT raise_error(1) -- !query schema struct<> -- !query output org.apache.spark.SparkRuntimeException { - "errorClass" : "ALL_PARTITION_COLUMNS_NOT_ALLOWED", - "sqlState" : "KD005" -} - - --- !query -SELECT raise_error('ALL_PARTITION_COLUMNS_NOT_ALLOWED', NULL) --- !query schema -struct<> --- !query output -org.apache.spark.SparkRuntimeException -{ - "errorClass" : "ALL_PARTITION_COLUMNS_NOT_ALLOWED", - "sqlState" : "KD005" -} - - --- !query -SELECT raise_error(NULL, NULL) --- !query schema -struct<> --- !query output -org.apache.spark.SparkRuntimeException -{ - "errorClass" : "USER_RAISED_EXCEPTION_UNKNOWN_ERROR_CLASS", + "errorClass" : "USER_RAISED_EXCEPTION", "sqlState" : "P0001", "messageParameters" : { - "errorClass" : "'null'" + "errorMessage" : "1" } } diff --git a/sql/core/src/test/scala/org/apache/spark/sql/expressions/ExpressionInfoSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/expressions/ExpressionInfoSuite.scala index 0f1a45319af78..b28a72f7c201a 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/expressions/ExpressionInfoSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/expressions/ExpressionInfoSuite.scala @@ -193,7 +193,7 @@ class ExpressionInfoSuite extends SparkFunSuite with SharedSparkSession { "org.apache.spark.sql.catalyst.expressions.TryReflect", "org.apache.spark.sql.catalyst.expressions.SparkVersion", // Throws an error - "org.apache.spark.sql.catalyst.expressions.RaiseError", + "org.apache.spark.sql.catalyst.expressions.RaiseErrorExpressionBuilder", "org.apache.spark.sql.catalyst.expressions.AssertTrue", classOf[CurrentUser].getName, // The encrypt expression includes a random initialization vector to its encrypted result