uros-b commented on code in PR #58862:
URL: https://github.com/apache/spark/pull/58862#discussion_r4035157496


##########
common/utils/src/main/resources/error/error-conditions.json:
##########
@@ -5741,7 +5741,7 @@
   },
   "JDBC_EXTERNAL_ENGINE_SYNTAX_ERROR" : {
     "message" : [
-      "JDBC external engine syntax error. The error was caused by the query 
<jdbcQuery>. <externalEngineError>."
+      "JDBC external engine syntax error with SQLSTATE 
<externalEngineSqlState>. The error was caused by the query <jdbcQuery>. 
<externalEngineError>."

Review Comment:
   The change adds externalEngineSqlState to both 
JDBC_EXTERNAL_ENGINE_SYNTAX_ERROR throw sites in JDBCRDD and templates it as:
   ```
   JDBC external engine syntax error with SQLSTATE <externalEngineSqlState>. 
The error was caused by the query <jdbcQuery>. <externalEngineError>.
   ```
   
   Keeping Spark’s own SQLSTATE at 42000 and putting the remote value in 
messageParameters is the correct design. Putting that value in the message as 
bare “SQLSTATE” fights Spark’s existing pretty-printer, which already appends 
Spark’s SQLSTATE:
   ```
   SparkThrowableHelper.scala
   Ln 78–86
   def formatErrorMessage(
       errorClass: String,
       displayMessage: String,
       sqlState: String,
       context: String): String = {
     val displaySqlState = if (sqlState == null) "" else s" SQLSTATE: $sqlState"
     val displayQueryContext = (if (context.isEmpty) "" else "\n") + context
     val prefix = if (errorClass.startsWith("_LEGACY_ERROR_")) "" else 
s"[$errorClass] "
     s"$prefix$displayMessage$displaySqlState$displayQueryContext"
   }
   ```
   
   On Postgres (42601) the user-visible line becomes two different SQLSTATEs:
   ```
   [JDBC_EXTERNAL_ENGINE_SYNTAX_ERROR.DURING_OUTPUT_SCHEMA_RESOLUTION] JDBC 
external engine 
   syntax error with SQLSTATE 42601. The error was caused by the query .... 
<engine message>. The error occurred during output schema resolution. SQLSTATE: 
42000
   ```
   
   That undercuts the grouping/observability goal: log parsers and humans 
cannot tell which SQLSTATE is Spark’s and which is the engine’s.
   
   Suggested template:
   ```
   JDBC external engine syntax error. The error was caused by the query 
<jdbcQuery>. <externalEngineError>. External engine SQLSTATE: 
<externalEngineSqlState>.
   ```
   
   The placeholder has to stay in the template (tests reject unused parameters 
unless the class is allowlisted). Only the label needs to change.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/JDBCRDD.scala:
##########
@@ -87,7 +87,8 @@ object JDBCRDD extends Logging {
           errorClass = 
"JDBC_EXTERNAL_ENGINE_SYNTAX_ERROR.DURING_OUTPUT_SCHEMA_RESOLUTION",
           messageParameters = Map(
             "jdbcQuery" -> fullQuery,
-            "externalEngineError" -> e.getMessage.replaceAll("\\.+$", "")
+            "externalEngineError" -> e.getMessage.replaceAll("\\.+$", ""),
+            "externalEngineSqlState" -> 
Option(e.getSQLState).getOrElse("unknown")

Review Comment:
   Treat blank SQLSTATE as missing. Option(e.getSQLState).getOrElse("unknown") 
leaves "" in the message (with SQLSTATE . ...). Prefer 
Option(e.getSQLState).filter(_.nonEmpty).getOrElse("unknown"). Most dialects 
that wrap this error already have a class-42 SQLSTATE; SQL Server is the 
outlier (message-based detection), so this fallback actually matters.



##########
connector/docker-integration-tests/src/test/scala/org/apache/spark/sql/jdbc/SharedJDBCIntegrationSuite.scala:
##########
@@ -68,9 +68,12 @@ abstract class SharedJDBCIntegrationSuite extends 
DockerJDBCIntegrationSuite {
       condition = 
"JDBC_EXTERNAL_ENGINE_SYNTAX_ERROR.DURING_OUTPUT_SCHEMA_RESOLUTION",
       parameters = Map(
         "jdbcQuery" -> "SELECT \\* FROM \\(.*",
-        "externalEngineError" -> "[\\s\\S]*"
+        "externalEngineError" -> "[\\s\\S]*",
+        "externalEngineSqlState" -> "[\\s\\S]*"
       )
     )
+    assert(ex.getMessageParameters.get("externalEngineSqlState") ===
+      
Option(ex.getCause.asInstanceOf[SQLException].getSQLState).getOrElse("unknown"))
   }

Review Comment:
   Test is in the right suite, but weak. SharedJDBCIntegrationSuite is the 
existing SPARK-52184 coverage and will run in CI because this PR touches 
docker-integration-tests. Please tighten it:
   
   - assert Spark SQLSTATE is still 42000 (sqlState = Some("42000"))
   - avoid asInstanceOf[SQLException] on getCause
   - [\\s\\S]* accepts an empty value; the extra assert is what actually checks 
the parameter
   
   An H2 test in JDBCSuite would cover this without Docker; not required if the 
Docker job is green.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to