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

huaxingao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new f17f34bce6 Core: Check table UUID in RESTTableOperations (#14363)
f17f34bce6 is described below

commit f17f34bce65567fd1479607038221bcccb594b29
Author: Yuya Ebihara <[email protected]>
AuthorDate: Tue Nov 4 10:45:05 2025 +0900

    Core: Check table UUID in RESTTableOperations (#14363)
---
 .../apache/iceberg/rest/RESTTableOperations.java   | 15 +++++++-
 .../org/apache/iceberg/rest/TestRESTCatalog.java   | 43 ++++++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/iceberg/rest/RESTTableOperations.java 
b/core/src/main/java/org/apache/iceberg/rest/RESTTableOperations.java
index 00522fd6e1..38dabc8ae5 100644
--- a/core/src/main/java/org/apache/iceberg/rest/RESTTableOperations.java
+++ b/core/src/main/java/org/apache/iceberg/rest/RESTTableOperations.java
@@ -247,12 +247,25 @@ class RESTTableOperations implements TableOperations {
     // safely ignored. there is no requirement to update config on refresh or 
commit.
     if (current == null
         || !Objects.equals(current.metadataFileLocation(), 
response.metadataLocation())) {
-      this.current = response.tableMetadata();
+      this.current = checkUUID(current, response.tableMetadata());
     }
 
     return current;
   }
 
+  private static TableMetadata checkUUID(TableMetadata currentMetadata, 
TableMetadata newMetadata) {
+    String newUUID = newMetadata.uuid();
+    if (currentMetadata != null && currentMetadata.uuid() != null && newUUID 
!= null) {
+      Preconditions.checkState(
+          newUUID.equals(currentMetadata.uuid()),
+          "Table UUID does not match: current=%s != refreshed=%s",
+          currentMetadata.uuid(),
+          newUUID);
+    }
+
+    return newMetadata;
+  }
+
   private static String metadataFileLocation(TableMetadata metadata, String 
filename) {
     String metadataLocation = 
metadata.properties().get(TableProperties.WRITE_METADATA_LOCATION);
 
diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java 
b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
index 6f7af7ae75..e4e80348f9 100644
--- a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
@@ -56,6 +56,7 @@ import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.Snapshot;
 import org.apache.iceberg.Table;
+import org.apache.iceberg.TableMetadata;
 import org.apache.iceberg.Transaction;
 import org.apache.iceberg.UpdatePartitionSpec;
 import org.apache.iceberg.UpdateSchema;
@@ -2955,6 +2956,48 @@ public class TestRESTCatalog extends 
CatalogTests<RESTCatalog> {
             any());
   }
 
+  @Test
+  void testDifferentTableUUID() {
+    RESTCatalogAdapter adapter = Mockito.spy(new 
RESTCatalogAdapter(backendCatalog));
+    RESTCatalog catalog = catalog(adapter);
+
+    catalog.createNamespace(TABLE.namespace());
+    catalog.createTable(TABLE, SCHEMA);
+
+    // simulate drop and re-create the table with same name
+    String newUUID = "386b9f01-002b-4d8c-b77f-42c3fd3b7c9b";
+    Answer<LoadTableResponse> updateTable =
+        invocation -> {
+          LoadTableResponse loadTable = (LoadTableResponse) 
invocation.callRealMethod();
+          TableMetadata current = loadTable.tableMetadata();
+          assertThat(current.uuid()).isNotEqualTo(newUUID);
+          TableMetadata newMetadata = 
TableMetadata.buildFrom(current).assignUUID(newUUID).build();
+          return LoadTableResponse.builder()
+              .withTableMetadata(newMetadata)
+              .addAllConfig(loadTable.config())
+              .build();
+        };
+
+    Mockito.doAnswer(updateTable)
+        .when(adapter)
+        .execute(
+            reqMatcher(HTTPMethod.POST, RESOURCE_PATHS.table(TABLE)),
+            eq(LoadTableResponse.class),
+            any(),
+            any());
+
+    DataFile file =
+        DataFiles.builder(PartitionSpec.unpartitioned())
+            .withPath("/path/to/data-a.parquet")
+            .withFileSizeInBytes(10)
+            .withRecordCount(2)
+            .build();
+
+    assertThatThrownBy(() -> 
catalog.loadTable(TABLE).newFastAppend().appendFile(file).commit())
+        .isInstanceOf(IllegalStateException.class)
+        .hasMessageMatching("Table UUID does not match: current=.* != 
refreshed=" + newUUID);
+  }
+
   private RESTCatalog catalogWithResponseHeaders(Map<String, String> 
respHeaders) {
     RESTCatalogAdapter adapter =
         new RESTCatalogAdapter(backendCatalog) {

Reply via email to