This is an automated email from the ASF dual-hosted git repository.

wenchen pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 3c8e04d72520 [SPARK-54558][SQL] Fix Internal Exception when Exception 
Handlers with no BEGIN/END are used
3c8e04d72520 is described below

commit 3c8e04d72520657d02005eb3aa11f031ed830985
Author: Milan Dankovic <[email protected]>
AuthorDate: Mon Dec 1 16:38:59 2025 -0800

    [SPARK-54558][SQL] Fix Internal Exception when Exception Handlers with no 
BEGIN/END are used
    
    ### What changes were proposed in this pull request?
    When Exception Handlers which don't have BEGIN-END body are triggered, 
internal exception `java.util.NoSuchElementException` was thrown instead of 
executing properly or propagating/raising the new error if it happens in 
handler.
    
    ```
    BEGIN
      DECLARE EXIT HANDLER FOR SQLEXCEPTION
        SELECT 1;
    
      SELECT 1/0;
    END
    ```
    
    ### Why are the changes needed?
    Code was encountering a bug which throws internal error for what should be 
valid user code.
    
    ### Does this PR introduce _any_ user-facing change?
    No.
    
    ### How was this patch tested?
    New unit tests in `SqlScriptingExecutionSuite`.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    No.
    
    Closes #53271 from miland-db/milan-dankovic_data/fix-no-body-handlers.
    
    Authored-by: Milan Dankovic <[email protected]>
    Signed-off-by: Wenchen Fan <[email protected]>
    (cherry picked from commit 4435a3a7f0883559de3bd13ba84f311200943622)
    Signed-off-by: Wenchen Fan <[email protected]>
---
 .../spark/sql/catalyst/parser/AstBuilder.scala     | 11 ++-
 .../sql/scripting/SqlScriptingExecutionSuite.scala | 83 ++++++++++++++++++++++
 2 files changed, 93 insertions(+), 1 deletion(-)

diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
index d1d4a6b8c980..d9202f69fcea 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
@@ -300,12 +300,21 @@ class AstBuilder extends DataTypeAstBuilder
         parsingCtx)
     } else {
       // If there is no compound body, then there must be a statement or set 
statement.
+      // Single-statement handler bodies need a label for the CompoundBody, 
just like
+      // BEGIN-END blocks do (see visitBeginEndCompoundBlockImpl). Generate a 
random UUID
+      // label since no explicit label is defined.
+      val labelText = parsingCtx.labelContext.enterLabeledScope(
+        beginLabelCtx = None,
+        endLabelCtx = None
+      )
       val statement = Option(ctx.statement().asInstanceOf[ParserRuleContext])
         
.orElse(Option(ctx.setStatementInsideSqlScript().asInstanceOf[ParserRuleContext]))
         .map { s =>
           SingleStatement(parsedPlan = visit(s).asInstanceOf[LogicalPlan])
         }
-      CompoundBody(Seq(statement.get), None, isScope = false)
+      val compoundBody = CompoundBody(Seq(statement.get), Some(labelText), 
isScope = false)
+      parsingCtx.labelContext.exitLabeledScope(None)
+      compoundBody
     }
 
     ExceptionHandler(exceptionHandlerTriggers, body, handlerType)
diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/scripting/SqlScriptingExecutionSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/scripting/SqlScriptingExecutionSuite.scala
index e26815c6c01e..d080e1f05014 100644
--- 
a/sql/core/src/test/scala/org/apache/spark/sql/scripting/SqlScriptingExecutionSuite.scala
+++ 
b/sql/core/src/test/scala/org/apache/spark/sql/scripting/SqlScriptingExecutionSuite.scala
@@ -1324,6 +1324,89 @@ class SqlScriptingExecutionSuite extends QueryTest with 
SharedSparkSession {
     verifySqlScriptResult(sqlScript, expected = expected)
   }
 
+  test("exit handler body without BEGIN-END propagates error properly") {
+    val sqlScript =
+      """
+        |BEGIN
+        |  DECLARE EXIT HANDLER FOR SQLEXCEPTION
+        |    INSERT INTO test_table_non_existing VALUES(1, 2, 3);
+        |
+        |  SELECT 1/0;
+        |END
+        |""".stripMargin
+    val exception = intercept[AnalysisException] {
+      verifySqlScriptResult(sqlScript, Seq.empty)
+    }
+    checkError(
+      exception = exception,
+      condition = "TABLE_OR_VIEW_NOT_FOUND",
+      sqlState = Some("42P01"),
+      parameters = Map("relationName" -> toSQLId("test_table_non_existing")),
+      context = ExpectedContext(
+        fragment = "test_table_non_existing",
+        start = 63,
+        stop = 85)
+    )
+  }
+
+  test("continue handler body without BEGIN-END propagates error properly") {
+    val sqlScript =
+      """
+        |BEGIN
+        |  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+        |    INSERT INTO test_table_non_existing VALUES(1, 2, 3);
+        |
+        |  SELECT 1/0;
+        |END
+        |""".stripMargin
+    val exception = intercept[AnalysisException] {
+      verifySqlScriptResult(sqlScript, Seq.empty)
+    }
+    checkError(
+      exception = exception,
+      condition = "TABLE_OR_VIEW_NOT_FOUND",
+      sqlState = Some("42P01"),
+      parameters = Map("relationName" -> toSQLId("test_table_non_existing")),
+      context = ExpectedContext(
+        fragment = "test_table_non_existing",
+        start = 67,
+        stop = 89)
+    )
+  }
+
+  test("exit handler body without BEGIN-END executes properly") {
+    val sqlScript =
+      """
+        |BEGIN
+        |  DECLARE EXIT HANDLER FOR SQLEXCEPTION
+        |    SELECT 1;
+        |
+        |  SELECT 1/0;
+        |  SELECT 2;
+        |END
+        |""".stripMargin
+    val expected = Seq(Seq(Row(1)))
+    verifySqlScriptResult(sqlScript, expected)
+  }
+
+  test("continue handler body without BEGIN-END executes properly") {
+    val sqlScript =
+      """
+        |BEGIN
+        |  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+        |    SELECT 1;
+        |
+        |  SELECT 1/0;
+        |  SELECT 2;
+        |END
+        |""".stripMargin
+    val expected = Seq(
+      Seq(Row(1)), // select from handler
+      Seq(Row(2)) // select
+    )
+    verifySqlScriptResult(sqlScript, expected)
+  }
+
   // Tests
   test("multi statement - simple") {
     withTable("t") {


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

Reply via email to