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

CalvinKirs pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 9b5200a7598 [fix](catalog) Reject DROP TEMPORARY TABLE on external 
catalogs (#68474)
9b5200a7598 is described below

commit 9b5200a7598acd2cfcd675f3b9e69c37cbd9e86d
Author: 924060929 <[email protected]>
AuthorDate: Thu Sep 24 18:04:22 2026 +0800

    [fix](catalog) Reject DROP TEMPORARY TABLE on external catalogs (#68474)
    
    ### What problem does this PR solve?
    
    Issue Number: N/A
    
    Related PR: N/A
    
    Problem Summary:
    
    `DROP TEMPORARY TABLE` issued against a plugin-driven external catalog
    (for example an
    Iceberg catalog) deleted the permanent external table of the same name.
    
    An external catalog has no temporary-table namespace: `CREATE TEMPORARY
    TABLE` is
    rejected for every non-internal catalog, so when the statement names a
    table that is not a
    session temporary table, name resolution falls back to the permanent
    table of that name.
    `PluginDrivenExternalCatalog.dropTable` accepted the `mustTemporary`
    flag but never
    inspected it, forwarded the statement to `ConnectorMetadata.dropTable`,
    and removed the
    permanent table from the external metastore.
    
    Reproduction: create a permanent table `ext.db.t`, then run
    `DROP TEMPORARY TABLE ext.db.t`. Before the fix, `ext.db.t` disappears
    from the external
    metastore. After the fix, the statement reports `Unknown table 't' in
    db` (or is a no-op
    with `IF EXISTS`) and `ext.db.t` is untouched.
    
    The fix mirrors `InternalCatalog.dropTable`: when `mustTemporary` is
    set, the permanent
    table is never handed to the connector. `IF EXISTS` turns the missing
    temporary table into
    a no-op; otherwise the statement raises `ERR_UNKNOWN_TABLE`.
    
    ### Release note
    
    Fix `DROP TEMPORARY TABLE` on an external catalog deleting the permanent
    table of the same
    name. It now reports that the temporary table does not exist (a no-op
    with `IF EXISTS`) and
    leaves the permanent table untouched.
---
 .../plugin/PluginDrivenExternalCatalog.java        | 12 ++++++++
 .../PluginDrivenExternalCatalogDdlRoutingTest.java | 36 ++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java
index 124de50bf66..85959852cf5 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java
@@ -800,6 +800,18 @@ public class PluginDrivenExternalCatalog extends 
ExternalCatalog {
             }
             throw new DdlException("Failed to get table: '" + tableName + "' 
in database: " + dbName);
         }
+        // External catalogs have no temporary-table namespace: CREATE 
TEMPORARY TABLE is rejected for
+        // every non-internal catalog, so DROP TEMPORARY TABLE can never name 
a live temporary table
+        // here. getTableNullable above falls back to the permanent table of 
the same name, which must
+        // NOT be dropped. Mirror InternalCatalog: IF EXISTS turns the missing 
temporary table into a
+        // no-op, otherwise report the unknown table instead of silently 
destroying the permanent one.
+        if (mustTemporary) {
+            if (ifExists) {
+                LOG.info("drop temporary table[{}.{}.{}] which does not 
exist", getName(), dbName, tableName);
+                return;
+            }
+            ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TABLE, 
tableName, dbName);
+        }
         ConnectorSession session = buildConnectorSession();
         ConnectorMetadata metadata = PluginDrivenMetadata.get(session, 
connector);
         // Route a DROP on a VIEW to dropView, mirroring legacy 
IcebergMetadataOps.dropTableImpl's
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalogDdlRoutingTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalogDdlRoutingTest.java
index a35cfa9a991..57900ae241d 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalogDdlRoutingTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalogDdlRoutingTest.java
@@ -428,6 +428,42 @@ public class PluginDrivenExternalCatalogDdlRoutingTest {
         Mockito.verifyNoInteractions(metadata);
     }
 
+    @Test
+    public void testDropTemporaryTableNeverDropsPermanentExternalTable() {
+        // An external catalog hosts no temporary tables, so the name resolves 
to the permanent table.
+        // DROP TEMPORARY TABLE must report the missing temporary table and 
leave the permanent one alone.
+        ExternalDatabase<? extends ExternalTable> db = mockExternalDatabase();
+        ExternalTable table = Mockito.mock(ExternalTable.class);
+        Mockito.when(table.getName()).thenReturn("t1");
+        Mockito.when(table.getRemoteDbName()).thenReturn("DB1");
+        Mockito.when(table.getRemoteName()).thenReturn("TBL1");
+        Mockito.doReturn(table).when(db).getTableNullable("t1");
+        catalog.dbNullableResult = db;
+
+        DdlException ex = Assertions.assertThrows(DdlException.class,
+                () -> catalog.dropTable("db1", "t1", false, false, false, 
false, true, false));
+        Assertions.assertTrue(ex.getMessage().contains("Unknown table"), 
ex.getMessage());
+        Mockito.verifyNoInteractions(metadata);
+        Mockito.verify(mockEditLog, 
Mockito.never()).logDropTable(Mockito.any());
+        Mockito.verify(connector, 
Mockito.never()).invalidateTable(Mockito.any(), Mockito.any());
+    }
+
+    @Test
+    public void 
testDropTemporaryTableIfExistsIsNoopAndKeepsPermanentExternalTable() throws 
Exception {
+        // With IF EXISTS the missing temporary table is a no-op; the 
permanent table must survive.
+        ExternalDatabase<? extends ExternalTable> db = mockExternalDatabase();
+        ExternalTable table = Mockito.mock(ExternalTable.class);
+        Mockito.when(table.getName()).thenReturn("t1");
+        Mockito.doReturn(table).when(db).getTableNullable("t1");
+        catalog.dbNullableResult = db;
+
+        catalog.dropTable("db1", "t1", false, false, false, true, true, false);
+
+        Mockito.verifyNoInteractions(metadata);
+        Mockito.verify(mockEditLog, 
Mockito.never()).logDropTable(Mockito.any());
+        Mockito.verify(connector, 
Mockito.never()).invalidateTable(Mockito.any(), Mockito.any());
+    }
+
     @Test
     public void 
testDropTableHandleAbsentAfterLocalResolveCleansLocalStateWithIfExists() throws 
Exception {
         ExternalDatabase<? extends ExternalTable> db = mockExternalDatabase();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to