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 6365ffeba9 [core] Only require the <db>.db warehouse layout for 
catalog-storage Iceberg metadata (#8911)
6365ffeba9 is described below

commit 6365ffeba91caf1fafcfe8d77db72f943c1ea036
Author: Tejansh <[email protected]>
AuthorDate: Thu Jul 30 05:00:16 2026 +0100

    [core] Only require the <db>.db warehouse layout for catalog-storage 
Iceberg metadata (#8911)
---
 .../paimon/iceberg/IcebergCommitCallback.java      | 22 ++++++++++-----
 .../paimon/iceberg/IcebergCommitCallbackTest.java  | 33 ++++++++++++++++++++++
 2 files changed, 48 insertions(+), 7 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/iceberg/IcebergCommitCallback.java
 
b/paimon-core/src/main/java/org/apache/paimon/iceberg/IcebergCommitCallback.java
index d8fa8457db..336b0fe865 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/iceberg/IcebergCommitCallback.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/iceberg/IcebergCommitCallback.java
@@ -198,13 +198,6 @@ public class IcebergCommitCallback implements 
CommitCallback, TagCallback {
         IcebergOptions.StorageType storageType =
                 
table.coreOptions().toConfiguration().get(IcebergOptions.METADATA_ICEBERG_STORAGE);
 
-        if (!dbPath.getName().endsWith(dbSuffix)) {
-            throw new UnsupportedOperationException(
-                    String.format(
-                            "Storage type %s can only be used on Paimon tables 
in a Paimon warehouse.",
-                            storageType.name()));
-        }
-
         IcebergOptions.StorageLocation storageLocation =
                 table.coreOptions()
                         .toConfiguration()
@@ -213,8 +206,23 @@ public class IcebergCommitCallback implements 
CommitCallback, TagCallback {
 
         switch (storageLocation) {
             case TABLE_LOCATION:
+                // Iceberg metadata is written beside the table, under the 
database's own location,
+                // so no warehouse (<db>.db) layout is required. This lets the 
table register in any
+                // catalog, including a database whose location is not a 
Paimon warehouse path (e.g.
+                // an externally-provisioned / cross-account catalog database).
                 return dbPath;
             case CATALOG_STORAGE:
+                // Catalog-storage derives a warehouse-relative iceberg/<db>/ 
path by stripping the
+                // ".db" suffix, so it only applies under the Paimon <db>.db 
warehouse layout.
+                if (!dbPath.getName().endsWith(dbSuffix)) {
+                    throw new UnsupportedOperationException(
+                            String.format(
+                                    "Storage type %s with catalog-location 
Iceberg metadata requires a "
+                                            + "Paimon warehouse database (a 
<db>.db location); set "
+                                            + 
"metadata.iceberg.storage-location=table-location for a "
+                                            + "database with a non-warehouse 
location.",
+                                    storageType.name()));
+                }
                 String dbName =
                         dbPath.getName()
                                 .substring(0, dbPath.getName().length() - 
dbSuffix.length());
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/iceberg/IcebergCommitCallbackTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/iceberg/IcebergCommitCallbackTest.java
index cb8a83c2a2..e1ecb772db 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/iceberg/IcebergCommitCallbackTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/iceberg/IcebergCommitCallbackTest.java
@@ -38,6 +38,7 @@ import java.util.stream.Stream;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -164,6 +165,38 @@ public class IcebergCommitCallbackTest {
         assertThat(result.toString()).isEqualTo(expectedPath);
     }
 
+    @Test
+    void testCatalogDatabasePathTableLocationAllowsNonWarehouseDatabase() {
+        // A database whose location is not a Paimon <db>.db warehouse path 
(e.g. an externally
+        // provisioned / cross-account catalog database).
+        when(mockTable.location()).thenReturn(new 
Path("s3://bucket/ingest/mydb/mytable"));
+        when(mockConfig.get(IcebergOptions.METADATA_ICEBERG_STORAGE))
+                .thenReturn(IcebergOptions.StorageType.REST_CATALOG);
+        
when(mockConfig.getOptional(IcebergOptions.METADATA_ICEBERG_STORAGE_LOCATION))
+                
.thenReturn(Optional.of(IcebergOptions.StorageLocation.TABLE_LOCATION));
+
+        // table-location writes Iceberg metadata beside the table, so the 
database path is used
+        // as-is with no <db>.db requirement.
+        
assertThat(IcebergCommitCallback.catalogDatabasePath(mockTable).toString())
+                .isEqualTo("s3://bucket/ingest/mydb");
+    }
+
+    @Test
+    void testCatalogDatabasePathCatalogStorageRejectsNonWarehouseDatabase() {
+        when(mockTable.location()).thenReturn(new 
Path("s3://bucket/ingest/mydb/mytable"));
+        when(mockConfig.get(IcebergOptions.METADATA_ICEBERG_STORAGE))
+                .thenReturn(IcebergOptions.StorageType.REST_CATALOG);
+        
when(mockConfig.getOptional(IcebergOptions.METADATA_ICEBERG_STORAGE_LOCATION))
+                
.thenReturn(Optional.of(IcebergOptions.StorageLocation.CATALOG_STORAGE));
+
+        // catalog-storage derives a warehouse-relative iceberg/<db>/ path by 
stripping ".db", so a
+        // non-".db" database is still rejected (with a message pointing at 
the table-location fix).
+        assertThatThrownBy(() -> 
IcebergCommitCallback.catalogDatabasePath(mockTable))
+                .isInstanceOf(UnsupportedOperationException.class)
+                .hasMessageContaining("requires a Paimon warehouse database")
+                .hasMessageContaining("table-location");
+    }
+
     private static Stream<Arguments> provideMetadataPathsWithStorageType() {
         return Stream.of(
                 Arguments.of(

Reply via email to