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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 10480976c0 [spark] Improve unresolved expression errors in MERGE INTO 
(#9068)
10480976c0 is described below

commit 10480976c04d0f64e7eb215b8ab87028ff2e199c
Author: Zouxxyy <[email protected]>
AuthorDate: Thu Aug 6 22:14:14 2026 +0800

    [spark] Improve unresolved expression errors in MERGE INTO (#9068)
---
 .../analysis/expressions/ExpressionHelper.scala    | 13 +++++----
 .../paimon/spark/sql/MergeIntoTableTestBase.scala  | 33 +++++++++++++++++++++-
 2 files changed, 39 insertions(+), 7 deletions(-)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/expressions/ExpressionHelper.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/expressions/ExpressionHelper.scala
index 82dcb594a2..7a3fce7fe0 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/expressions/ExpressionHelper.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/expressions/ExpressionHelper.scala
@@ -124,12 +124,13 @@ trait ExpressionHelperBase extends PredicateHelper {
     } else {
       val newPlan = FakeLogicalPlan(Seq(expr), plan.children)
       spark.sessionState.analyzer.execute(newPlan) match {
-        case FakeLogicalPlan(resolvedExpr, _) =>
-          resolvedExpr.foreach {
-            expr =>
-              if (!expr.resolved) {
-                throw new RuntimeException(s"cannot resolve ${expr.sql} from 
$plan")
-              }
+        case analyzedPlan @ FakeLogicalPlan(resolvedExpr, _) =>
+          resolvedExpr.find(expr => !expr.resolved).foreach {
+            unresolvedExpr =>
+              // Let Spark report the concrete unresolved column or type error 
with its structured
+              // AnalysisException before falling back to Paimon's generic 
error.
+              spark.sessionState.analyzer.checkAnalysis(analyzedPlan)
+              throw new RuntimeException(s"cannot resolve 
${unresolvedExpr.sql} from $plan")
           }
           resolvedExpr.head
         case _ =>
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/MergeIntoTableTestBase.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/MergeIntoTableTestBase.scala
index bc0924ac17..05804740a2 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/MergeIntoTableTestBase.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/MergeIntoTableTestBase.scala
@@ -21,7 +21,7 @@ package org.apache.paimon.spark.sql
 import org.apache.paimon.Snapshot
 import org.apache.paimon.spark.{PaimonAppendTable, PaimonPrimaryKeyTable, 
PaimonSparkTestBase, PaimonTableTest}
 
-import org.apache.spark.sql.Row
+import org.apache.spark.sql.{AnalysisException, Row}
 
 import java.util.UUID
 import java.util.concurrent.Executors
@@ -270,6 +270,37 @@ abstract class MergeIntoTableTestBase extends 
PaimonSparkTestBase with PaimonTab
     }
   }
 
+  test("Paimon MergeInto: unresolved target column reports Spark analysis 
error") {
+    withTable("source", "target") {
+      Seq((1, "new-oneid", "new-customer"))
+        .toDF("id", "oneid", "customer_id")
+        .createOrReplaceTempView("source")
+
+      createTable("target", "id INT, oneid STRING", Seq("id"))
+
+      val error = intercept[AnalysisException] {
+        spark.sql(s"""
+                     |MERGE INTO target
+                     |USING source
+                     |ON target.id = source.id
+                     |WHEN MATCHED AND (
+                     |  NOT (target.oneid <=> source.oneid)
+                     |  OR NOT (target.customer_id <=> source.customer_id)
+                     |) THEN UPDATE SET oneid = source.oneid
+                     |""".stripMargin)
+      }
+
+      // Spark versions use different error classes for an unresolved column.
+      Option(error.getErrorClass).foreach {
+        errorClass =>
+          assert(Set("MISSING_COLUMN", 
"UNRESOLVED_COLUMN.WITH_SUGGESTION").contains(errorClass))
+      }
+      val normalizedMessage = error.getMessage.replace("`", "")
+      assert(normalizedMessage.contains("target.customer_id"))
+      assert(normalizedMessage.contains("source.customer_id"))
+    }
+  }
+
   test(s"Paimon MergeInto: conditional insert") {
     withTable("source", "target") {
 

Reply via email to