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 {