This is an automated email from the ASF dual-hosted git repository.
yuqi1129 pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 2894f13da3 [Cherry-pick to branch-1.3] [#11893] fix(lance): Add
external table guard to deregisterTable (#11896) (#11905)
2894f13da3 is described below
commit 2894f13da3f8ee1d4faf5a13b54a11e077337599
Author: StormSpirit <[email protected]>
AuthorDate: Mon Jul 6 11:37:57 2026 +0800
[Cherry-pick to branch-1.3] [#11893] fix(lance): Add external table guard
to deregisterTable (#11896) (#11905)
**Cherry-pick Information:**
- Original commit: 15778b188f10bbd8cd5698eb5788884611b09e0f
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)
Signed-off-by: jiangxt2 <[email protected]>
---
.../gravitino/GravitinoLanceTableOperations.java | 16 +++++++++-
.../rest/TestGravitinoLanceTableOperations.java | 34 ++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java
index cad7735118..50ba09df30 100644
---
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java
+++
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java
@@ -281,7 +281,21 @@ public class GravitinoLanceTableOperations implements
LanceTableOperations {
"Table not found: " + tableId, CommonUtil.formatCurrentStackTrace(),
tableId);
}
Map<String, String> properties = t.properties();
- // TODO Support real deregister API.
+
+ // Verify the table is external before deregistering. For non-external
(managed) tables,
+ // dropTable would delete the underlying Lance data, violating the
deregister contract
+ // ("It will not delete the underlying lance data"). The TableCatalog
interface does not
+ // expose a metadata-only removal path, so we fail fast rather than risk
silent data loss.
+ boolean external =
+ Optional.ofNullable(properties.get(Table.PROPERTY_EXTERNAL))
+ .map(Boolean::parseBoolean)
+ .orElse(false);
+ if (!external) {
+ throw new UnsupportedOperationException(
+ "deregisterTable only supports external tables, but table " +
tableId + " is managed");
+ }
+
+ // External tables: dropTable removes catalog metadata only, preserving
Lance data.
boolean result = catalog.asTableCatalog().dropTable(tableIdentifier);
if (!result) {
throw new TableNotFoundException(
diff --git
a/lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestGravitinoLanceTableOperations.java
b/lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestGravitinoLanceTableOperations.java
index 10c9b6bf32..84618a0932 100644
---
a/lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestGravitinoLanceTableOperations.java
+++
b/lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestGravitinoLanceTableOperations.java
@@ -21,11 +21,17 @@ package org.apache.gravitino.lance.service.rest;
import static
org.apache.gravitino.lance.common.utils.LanceConstants.LANCE_TABLE_VERSION;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.NameIdentifier;
+import
org.apache.gravitino.lance.common.ops.gravitino.GravitinoLanceNamespaceWrapper;
import
org.apache.gravitino.lance.common.ops.gravitino.GravitinoLanceTableAlterHandler.AlterColumnsGravitinoLance;
import
org.apache.gravitino.lance.common.ops.gravitino.GravitinoLanceTableAlterHandler.DropColumns;
+import
org.apache.gravitino.lance.common.ops.gravitino.GravitinoLanceTableOperations;
import org.apache.gravitino.rel.Table;
+import org.apache.gravitino.rel.TableCatalog;
import org.apache.gravitino.rel.TableChange;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -92,4 +98,32 @@ class TestGravitinoLanceTableOperations {
Assertions.assertEquals(
"Only RENAME alteration is supported currently.",
exception.getMessage());
}
+
+ @Test
+ void testDeregisterTableRejectsManagedTable() {
+ // Mock a managed table (no PROPERTY_EXTERNAL=true)
+ Table managedTable = Mockito.mock(Table.class);
+ Mockito.when(managedTable.properties()).thenReturn(new HashMap<>());
+
+ TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+ Mockito.when(tableCatalog.loadTable(Mockito.any(NameIdentifier.class)))
+ .thenReturn(managedTable);
+
+ Catalog catalog = Mockito.mock(Catalog.class);
+ Mockito.when(catalog.asTableCatalog()).thenReturn(tableCatalog);
+
+ GravitinoLanceNamespaceWrapper wrapper =
Mockito.mock(GravitinoLanceNamespaceWrapper.class);
+
Mockito.when(wrapper.loadAndValidateLakehouseCatalog(Mockito.anyString())).thenReturn(catalog);
+
+ GravitinoLanceTableOperations ops = new
GravitinoLanceTableOperations(wrapper);
+
+ UnsupportedOperationException exception =
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> ops.deregisterTable("catalog.schema.table", "."));
+ Assertions.assertTrue(exception.getMessage().contains("only supports
external tables"));
+
+ // Verify dropTable was never called — the guard must reject before
reaching the catalog layer.
+ Mockito.verify(tableCatalog, Mockito.never()).dropTable(Mockito.any());
+ }
}