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 f6e1df7a4b [spark] Reject SET LOCATION for format tables (#9435)
f6e1df7a4b is described below

commit f6e1df7a4b8d6406ec9a8cf559a927f30c3ac3e3
Author: Dapeng Sun(孙大鹏) <[email protected]>
AuthorDate: Fri Aug 28 13:48:20 2026 +0800

    [spark] Reject SET LOCATION for format tables (#9435)
---
 .../spark/catalyst/analysis/PaimonAnalysis.scala   |  6 +++
 .../sql/CatalogManagedPartitionDdlParityTest.scala | 55 +++++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonAnalysis.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonAnalysis.scala
index 40276aca1c..6353422342 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonAnalysis.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/catalyst/analysis/PaimonAnalysis.scala
@@ -84,6 +84,12 @@ class PaimonAnalysis(session: SparkSession) extends 
Rule[LogicalPlan] {
         throw new UnsupportedOperationException(
           "ALTER TABLE ... SET LOCATION is not supported for Paimon tables.")
 
+      // Only the table-level form. Spark's own analyzer already rejects the 
partition form with a
+      // structured AnalysisException, and replacing it here would be a worse 
error.
+      case SetTableLocation(ResolvedTable(_, _, _: PaimonFormatTable, _), 
None, _) =>
+        throw new UnsupportedOperationException(
+          "ALTER TABLE ... SET LOCATION is not supported for Paimon tables.")
+
       case r: ReplaceColumns if r.resolved && isPaimonTable(r.table) =>
         // Spark rewrites REPLACE COLUMNS into a batch that drops every 
existing column and re-adds
         // the new set. Re-adding columns assigns brand-new field ids while 
existing data files keep
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/CatalogManagedPartitionDdlParityTest.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/CatalogManagedPartitionDdlParityTest.scala
index 4ce81c53db..fb77ab834b 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/CatalogManagedPartitionDdlParityTest.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/CatalogManagedPartitionDdlParityTest.scala
@@ -19,10 +19,11 @@
 package org.apache.paimon.spark.sql
 
 import org.apache.paimon.catalog.Identifier
+import org.apache.paimon.fs.Path
 import org.apache.paimon.spark.PaimonSparkTestWithRestCatalogBase
 import org.apache.paimon.table.FormatTable
 
-import org.apache.spark.sql.Row
+import org.apache.spark.sql.{AnalysisException, Row}
 
 import scala.collection.JavaConverters._
 
@@ -154,6 +155,58 @@ class CatalogManagedPartitionDdlParityTest extends 
PaimonSparkTestWithRestCatalo
     }
   }
 
+  test("SET LOCATION is rejected without changing a catalog-managed Format 
Table") {
+    val tableName = "ddl_set_location"
+    withTable(tableName) {
+      createTable(tableName)
+      val table = formatTable(tableName)
+      val originalLocation = table.location().toString
+      sql(s"ALTER TABLE ${qualified(tableName)} ADD PARTITION (dt = 
'20260101', hour = '00')")
+      table
+        .fileIO()
+        .writeFile(new Path(table.location(), 
"dt=20260101/hour=00/part-00001.csv"), "1,a\n", false)
+
+      val error = intercept[UnsupportedOperationException] {
+        sql(s"ALTER TABLE ${qualified(tableName)} SET LOCATION 
'${originalLocation}_relocated'")
+      }
+
+      assert(error.getMessage == "ALTER TABLE ... SET LOCATION is not 
supported for Paimon tables.")
+      assert(formatTable(tableName).location().toString == originalLocation)
+      assert(registered(tableName) == Set("20260101/00"))
+      checkAnswer(
+        sql(s"SELECT id, payload, dt, hour FROM ${qualified(tableName)}"),
+        Seq(Row(1, "a", "20260101", "00")))
+    }
+  }
+
+  test("partition SET LOCATION keeps Spark's structured rejection and table 
state") {
+    val tableName = "ddl_partition_set_location"
+    withTable(tableName) {
+      createTable(tableName)
+      val table = formatTable(tableName)
+      val originalLocation = table.location().toString
+      sql(s"ALTER TABLE ${qualified(tableName)} ADD PARTITION (dt = 
'20260101', hour = '00')")
+      table
+        .fileIO()
+        .writeFile(new Path(table.location(), 
"dt=20260101/hour=00/part-00001.csv"), "1,a\n", false)
+
+      val error = intercept[AnalysisException] {
+        sql(
+          s"ALTER TABLE ${qualified(tableName)} PARTITION " +
+            s"(dt = '20260101', hour = '00') SET LOCATION 
'${originalLocation}_relocated'")
+      }
+
+      // Spark 4 renamed getErrorClass to getCondition and is still moving 
these legacy ids to
+      // named conditions, so match the message instead of the id.
+      assert(error.getMessage.contains("does not support partition"))
+      assert(formatTable(tableName).location().toString == originalLocation)
+      assert(registered(tableName) == Set("20260101/00"))
+      checkAnswer(
+        sql(s"SELECT id, payload, dt, hour FROM ${qualified(tableName)}"),
+        Seq(Row(1, "a", "20260101", "00")))
+    }
+  }
+
   private def qualified(tableName: String): String = 
s"paimon.$dbName0.$tableName"
 
   private def createTable(tableName: String): Unit =

Reply via email to