Skip to content

Commit f0498f0

Browse files
mrk-andreevMaxGekk
authored andcommitted
[SPARK-49549][SQL] Assign a name to the error conditions _LEGACY_ERROR_TEMP_3055, 3146
### What changes were proposed in this pull request? Choose a proper name for the error conditions _LEGACY_ERROR_TEMP_3055 and _LEGACY_ERROR_TEMP_3146 defined in core/src/main/resources/error/error-conditions.json. The name should be short but complete (look at the example in error-conditions.json). Add a test which triggers the error from user code if such test still doesn't exist. Check exception fields by using checkError(). The last function checks valuable error fields only, and avoids dependencies from error text message. In this way, tech editors can modify error format in error-conditions.json, and don't worry of Spark's internal tests. Migrate other tests that might trigger the error onto checkError(). ### Why are the changes needed? This changes needed because spark 4 introduce new approach with user friendly error messages. ### Does this PR introduce _any_ user-facing change? Yes, if user's code depends on error condition names. ### How was this patch tested? Unit tests ### Was this patch authored or co-authored using generative AI tooling? No Closes #48288 from mrk-andreev/SPARK-49549. Authored-by: Mark Andreev <mark.andreev@gmail.com> Signed-off-by: Max Gekk <max.gekk@gmail.com>
1 parent d7772f2 commit f0498f0

File tree

5 files changed

+55
-17
lines changed

5 files changed

+55
-17
lines changed

common/utils/src/main/resources/error/error-conditions.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,6 +3996,18 @@
39963996
],
39973997
"sqlState" : "22023"
39983998
},
3999+
"SCALAR_FUNCTION_NOT_COMPATIBLE" : {
4000+
"message" : [
4001+
"ScalarFunction <scalarFunc> not overrides method 'produceResult(InternalRow)' with custom implementation."
4002+
],
4003+
"sqlState" : "42K0O"
4004+
},
4005+
"SCALAR_FUNCTION_NOT_FULLY_IMPLEMENTED" : {
4006+
"message" : [
4007+
"ScalarFunction <scalarFunc> not implements or overrides method 'produceResult(InternalRow)'."
4008+
],
4009+
"sqlState" : "42K0P"
4010+
},
39994011
"SCALAR_SUBQUERY_IS_IN_GROUP_BY_OR_AGGREGATE_FUNCTION" : {
40004012
"message" : [
40014013
"The correlated scalar subquery '<sqlExpr>' is neither present in GROUP BY, nor in an aggregate function.",
@@ -7946,11 +7958,6 @@
79467958
"<expr> is not currently supported"
79477959
]
79487960
},
7949-
"_LEGACY_ERROR_TEMP_3055" : {
7950-
"message" : [
7951-
"ScalarFunction <scalarFunc> neither implement magic method nor override 'produceResult'"
7952-
]
7953-
},
79547961
"_LEGACY_ERROR_TEMP_3056" : {
79557962
"message" : [
79567963
"Unexpected row-level read relations (allow multiple = <allowMultipleReads>): <other>"
@@ -8309,11 +8316,6 @@
83098316
"Partitions truncate is not supported"
83108317
]
83118318
},
8312-
"_LEGACY_ERROR_TEMP_3146" : {
8313-
"message" : [
8314-
"Cannot find a compatible ScalarFunction#produceResult"
8315-
]
8316-
},
83178319
"_LEGACY_ERROR_TEMP_3147" : {
83188320
"message" : [
83198321
"<description>: Batch scan are not supported"

common/utils/src/main/resources/error/error-states.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4631,6 +4631,18 @@
46314631
"standard": "N",
46324632
"usedBy": ["Spark"]
46334633
},
4634+
"42K0O": {
4635+
"description": "ScalarFunction not overrides method 'produceResult(InternalRow)' with custom implementation.",
4636+
"origin": "Spark",
4637+
"standard": "N",
4638+
"usedBy": ["Spark"]
4639+
},
4640+
"42K0P": {
4641+
"description": "ScalarFunction not implements or overrides method 'produceResult(InternalRow)'.",
4642+
"origin": "Spark",
4643+
"standard": "N",
4644+
"usedBy": ["Spark"]
4645+
},
46344646
"42KD0": {
46354647
"description": "Ambiguous name reference.",
46364648
"origin": "Databricks",

sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/functions/ScalarFunction.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
import org.apache.spark.SparkUnsupportedOperationException;
2121
import org.apache.spark.annotation.Evolving;
2222
import org.apache.spark.sql.catalyst.InternalRow;
23+
import org.apache.spark.sql.catalyst.util.QuotingUtils;
2324
import org.apache.spark.sql.types.DataType;
2425

26+
import java.util.Map;
27+
2528
/**
2629
* Interface for a function that produces a result value for each input row.
2730
* <p>
@@ -149,7 +152,10 @@ public interface ScalarFunction<R> extends BoundFunction {
149152
* @return a result value
150153
*/
151154
default R produceResult(InternalRow input) {
152-
throw new SparkUnsupportedOperationException("_LEGACY_ERROR_TEMP_3146");
155+
throw new SparkUnsupportedOperationException(
156+
"SCALAR_FUNCTION_NOT_COMPATIBLE",
157+
Map.of("scalarFunc", QuotingUtils.quoteIdentifier(name()))
158+
);
153159
}
154160

155161
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/V2ExpressionUtils.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.apache.spark.sql.connector.catalog.{FunctionCatalog, Identifier}
3131
import org.apache.spark.sql.connector.catalog.functions._
3232
import org.apache.spark.sql.connector.catalog.functions.ScalarFunction.MAGIC_METHOD_NAME
3333
import org.apache.spark.sql.connector.expressions.{BucketTransform, Expression => V2Expression, FieldReference, IdentityTransform, Literal => V2Literal, NamedReference, NamedTransform, NullOrdering => V2NullOrdering, SortDirection => V2SortDirection, SortOrder => V2SortOrder, SortValue, Transform}
34+
import org.apache.spark.sql.errors.DataTypeErrors.toSQLId
3435
import org.apache.spark.sql.errors.QueryCompilationErrors
3536
import org.apache.spark.sql.types._
3637
import org.apache.spark.util.ArrayImplicits._
@@ -182,8 +183,8 @@ object V2ExpressionUtils extends SQLConfHelper with Logging {
182183
ApplyFunctionExpression(scalarFunc, arguments)
183184
case _ =>
184185
throw new AnalysisException(
185-
errorClass = "_LEGACY_ERROR_TEMP_3055",
186-
messageParameters = Map("scalarFunc" -> scalarFunc.name()))
186+
errorClass = "SCALAR_FUNCTION_NOT_FULLY_IMPLEMENTED",
187+
messageParameters = Map("scalarFunc" -> toSQLId(scalarFunc.name())))
187188
}
188189
}
189190
}

sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2FunctionSuite.scala

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,8 @@ class DataSourceV2FunctionSuite extends DatasourceV2SQLBase {
414414
new JavaStrLen(new JavaStrLenNoImpl))
415415
checkError(
416416
exception = intercept[AnalysisException](sql("SELECT testcat.ns.strlen('abc')").collect()),
417-
condition = "_LEGACY_ERROR_TEMP_3055",
418-
parameters = Map("scalarFunc" -> "strlen"),
417+
condition = "SCALAR_FUNCTION_NOT_FULLY_IMPLEMENTED",
418+
parameters = Map("scalarFunc" -> "`strlen`"),
419419
context = ExpectedContext(
420420
fragment = "testcat.ns.strlen('abc')",
421421
start = 7,
@@ -448,8 +448,8 @@ class DataSourceV2FunctionSuite extends DatasourceV2SQLBase {
448448
addFunction(Identifier.of(Array("ns"), "add"), new JavaLongAdd(new JavaLongAddMismatchMagic))
449449
checkError(
450450
exception = intercept[AnalysisException](sql("SELECT testcat.ns.add(1L, 2L)").collect()),
451-
condition = "_LEGACY_ERROR_TEMP_3055",
452-
parameters = Map("scalarFunc" -> "long_add_mismatch_magic"),
451+
condition = "SCALAR_FUNCTION_NOT_FULLY_IMPLEMENTED",
452+
parameters = Map("scalarFunc" -> "`long_add_mismatch_magic`"),
453453
context = ExpectedContext(
454454
fragment = "testcat.ns.add(1L, 2L)",
455455
start = 7,
@@ -458,6 +458,23 @@ class DataSourceV2FunctionSuite extends DatasourceV2SQLBase {
458458
)
459459
}
460460

461+
test("SPARK-49549: scalar function w/ mismatch a compatible ScalarFunction#produceResult") {
462+
case object CharLength extends ScalarFunction[Int] {
463+
override def inputTypes(): Array[DataType] = Array(StringType)
464+
override def resultType(): DataType = IntegerType
465+
override def name(): String = "CHAR_LENGTH"
466+
}
467+
468+
catalog("testcat").asInstanceOf[SupportsNamespaces].createNamespace(Array("ns"), emptyProps)
469+
addFunction(Identifier.of(Array("ns"), "my_strlen"), StrLen(CharLength))
470+
checkError(
471+
exception = intercept[SparkUnsupportedOperationException]
472+
(sql("SELECT testcat.ns.my_strlen('abc')").collect()),
473+
condition = "SCALAR_FUNCTION_NOT_COMPATIBLE",
474+
parameters = Map("scalarFunc" -> "`CHAR_LENGTH`")
475+
)
476+
}
477+
461478
test("SPARK-35390: scalar function w/ type coercion") {
462479
catalog("testcat").asInstanceOf[SupportsNamespaces].createNamespace(Array("ns"), emptyProps)
463480
addFunction(Identifier.of(Array("ns"), "add"), new JavaLongAdd(new JavaLongAddDefault(false)))

0 commit comments

Comments
 (0)