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]