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 d886309b1d [core] Fix drop table when table directory is missing on 
disk (#8130)
d886309b1d is described below

commit d886309b1d650d5754658f54246b82f25b8b8baa
Author: Arnav Balyan <[email protected]>
AuthorDate: Tue Jun 23 02:59:39 2026 -0700

    [core] Fix drop table when table directory is missing on disk (#8130)
    
     - Drop fails when table exists in metastore but not available on disk
    - Ensure we can can check with the hms/jdbc catalog for the entry during
    deletion
    - Also delete the metadata when the table drop is requested but the
    table does not exist on disk.
    - This avoids leaking metadata, and manual risky operations on the
    metastore to do cleanup.
---
 .../org/apache/paimon/catalog/AbstractCatalog.java | 19 ++++++++-----
 .../java/org/apache/paimon/jdbc/JdbcCatalog.java   | 10 +++++++
 .../org/apache/paimon/jdbc/JdbcCatalogTest.java    | 22 +++++++++++++++
 .../java/org/apache/paimon/hive/HiveCatalog.java   | 21 ++++++++++++++
 .../org/apache/paimon/hive/HiveCatalogTest.java    | 32 ++++++++++++++++++++++
 5 files changed, 97 insertions(+), 7 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
index 4a8a8b1b91..d2db89e90f 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
@@ -364,15 +364,21 @@ public abstract class AbstractCatalog implements Catalog {
         checkNotBranch(identifier, "dropTable");
         checkNotSystemTable(identifier, "dropTable");
 
+        if (!tableExists(identifier)) {
+            if (ignoreIfNotExists) {
+                return;
+            }
+            throw new TableNotExistException(identifier);
+        }
+
         Set<Path> externalPaths = new HashSet<>();
-        try {
+        if (tableExistsInFileSystem(getTableLocation(identifier), 
DEFAULT_MAIN_BRANCH)) {
             Table table = getTable(identifier);
             if (table instanceof FileStoreTable) {
                 FileStoreTable fileStoreTable = (FileStoreTable) table;
                 List<Path> schemaExternalPaths =
                         
getSchemaExternalPaths(fileStoreTable.schemaManager().listAll());
                 externalPaths.addAll(schemaExternalPaths);
-                // get table branch external path
                 List<String> branches = 
fileStoreTable.branchManager().branches();
                 for (String branch : branches) {
                     SchemaManager schemaManager =
@@ -380,16 +386,15 @@ public abstract class AbstractCatalog implements Catalog {
                     
externalPaths.addAll(getSchemaExternalPaths(schemaManager.listAll()));
                 }
             }
-        } catch (TableNotExistException e) {
-            if (ignoreIfNotExists) {
-                return;
-            }
-            throw new TableNotExistException(identifier);
         }
 
         dropTableImpl(identifier, new ArrayList<>(externalPaths));
     }
 
+    protected boolean tableExists(Identifier identifier) {
+        return tableExistsInFileSystem(getTableLocation(identifier), 
DEFAULT_MAIN_BRANCH);
+    }
+
     private List<Path> getSchemaExternalPaths(List<TableSchema> schemas) {
         if (schemas == null) {
             return Collections.emptyList();
diff --git a/paimon-core/src/main/java/org/apache/paimon/jdbc/JdbcCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/jdbc/JdbcCatalog.java
index f765e5f88d..e26c2d013a 100644
--- a/paimon-core/src/main/java/org/apache/paimon/jdbc/JdbcCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/jdbc/JdbcCatalog.java
@@ -451,6 +451,16 @@ public class JdbcCatalog extends AbstractCatalog {
                         () -> new RuntimeException("There is no paimon table 
in " + tableLocation));
     }
 
+    @Override
+    protected boolean tableExists(Identifier identifier) {
+        return JdbcUtils.tableExists(
+                        connections,
+                        catalogKey,
+                        identifier.getDatabaseName(),
+                        identifier.getTableName())
+                || super.tableExists(identifier);
+    }
+
     @Override
     public boolean caseSensitive() {
         return false;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/jdbc/JdbcCatalogTest.java 
b/paimon-core/src/test/java/org/apache/paimon/jdbc/JdbcCatalogTest.java
index fd3c6fdc59..d3bf34f1eb 100644
--- a/paimon-core/src/test/java/org/apache/paimon/jdbc/JdbcCatalogTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/jdbc/JdbcCatalogTest.java
@@ -22,6 +22,7 @@ import org.apache.paimon.catalog.Catalog;
 import org.apache.paimon.catalog.CatalogContext;
 import org.apache.paimon.catalog.CatalogTestBase;
 import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.fs.Path;
 import org.apache.paimon.options.CatalogOptions;
 import org.apache.paimon.options.Options;
 import org.apache.paimon.schema.Schema;
@@ -86,6 +87,27 @@ public class JdbcCatalogTest extends CatalogTestBase {
     @Test
     public void testGetTable() throws Exception {}
 
+    @Test
+    public void testDropTableWhenTablePathMissing() throws Exception {
+        String databaseName = "test_db";
+        String tableName = "new_table";
+        catalog.createDatabase(databaseName, false);
+        Identifier identifier = Identifier.create(databaseName, tableName);
+        catalog.createTable(identifier, DEFAULT_TABLE_SCHEMA, false);
+
+        JdbcCatalog jdbcCatalog = (JdbcCatalog) catalog;
+        Path path = jdbcCatalog.getTableLocation(identifier);
+        jdbcCatalog.fileIO().deleteDirectoryQuietly(path);
+
+        assertThatThrownBy(() -> catalog.getTable(identifier))
+                .isInstanceOf(RuntimeException.class)
+                .hasMessage("There is no paimon table in " + path);
+        assertThat(jdbcCatalog.listTables(databaseName)).contains(tableName);
+
+        jdbcCatalog.dropTable(identifier, false);
+        
assertThat(jdbcCatalog.listTables(databaseName)).doesNotContain(tableName);
+    }
+
     @Test
     public void testAcquireLockFail() throws SQLException, 
InterruptedException {
         String lockId = "jdbc.testDb.testTable";
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
index 889850bf11..d21b961098 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
@@ -304,6 +304,27 @@ public class HiveCatalog extends AbstractCatalog {
         }
     }
 
+    @Override
+    protected boolean tableExists(Identifier identifier) {
+        try {
+            boolean inHms =
+                    clients()
+                            .run(
+                                    client ->
+                                            client.tableExists(
+                                                    
identifier.getDatabaseName(),
+                                                    
identifier.getTableName()));
+            return inHms || super.tableExists(identifier);
+        } catch (TException e) {
+            throw new RuntimeException(
+                    "Cannot determine if table " + identifier.getFullName() + 
" exists.", e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(
+                    "Interrupted in call to tableExists " + 
identifier.getFullName(), e);
+        }
+    }
+
     @Override
     protected void createDatabaseImpl(String name, Map<String, String> 
properties) {
         try {
diff --git 
a/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
 
b/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
index de04b0c838..a6f7dd7d1c 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
@@ -24,6 +24,7 @@ import org.apache.paimon.catalog.CatalogContext;
 import org.apache.paimon.catalog.CatalogTestBase;
 import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.client.ClientPool;
+import org.apache.paimon.fs.Path;
 import org.apache.paimon.options.CatalogOptions;
 import org.apache.paimon.options.Options;
 import org.apache.paimon.partition.Partition;
@@ -281,6 +282,37 @@ public class HiveCatalogTest extends CatalogTestBase {
         }
     }
 
+    @Test
+    public void testDropTableWhenTablePathMissing() throws Exception {
+        String databaseName = "test_db";
+        String tableName = "new_table";
+        catalog.createDatabase(databaseName, false);
+        Identifier identifier = Identifier.create(databaseName, tableName);
+
+        Schema schema =
+                new Schema(
+                        Lists.newArrayList(
+                                new DataField(0, "pk", DataTypes.INT()),
+                                new DataField(1, "col1", DataTypes.STRING()),
+                                new DataField(2, "col2", DataTypes.STRING())),
+                        Collections.emptyList(),
+                        Collections.emptyList(),
+                        new HashMap<>(),
+                        "");
+        catalog.createTable(identifier, schema, false);
+
+        HiveCatalog hiveCatalog = (HiveCatalog) catalog;
+        Path path = hiveCatalog.getTableLocation(identifier);
+        hiveCatalog.fileIO().deleteDirectoryQuietly(path);
+
+        assertThatThrownBy(() -> hiveCatalog.getTable(identifier))
+                .isInstanceOf(Catalog.TableNotExistException.class);
+        assertThat(hiveCatalog.listTables(databaseName)).contains(tableName);
+
+        hiveCatalog.dropTable(identifier, false);
+        
assertThat(hiveCatalog.listTables(databaseName)).doesNotContain(tableName);
+    }
+
     @Test
     public void testListTablesLock() {
         try {

Reply via email to