This is an automated email from the ASF dual-hosted git repository.
etudenhoefner pushed a commit to branch 1.10.x
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/1.10.x by this push:
new 8d050e222d Core: Prevent dropping namespace when it contains views
(#14456) (#14550)
8d050e222d is described below
commit 8d050e222d17eaa0325445972e141e6beb2749bd
Author: Eduard Tudenhoefner <[email protected]>
AuthorDate: Mon Nov 10 13:32:54 2025 +0100
Core: Prevent dropping namespace when it contains views (#14456) (#14550)
---
.../apache/iceberg/inmemory/InMemoryCatalog.java | 6 +++
.../java/org/apache/iceberg/jdbc/JdbcCatalog.java | 10 ++++-
.../org/apache/iceberg/jdbc/TestJdbcCatalog.java | 6 +--
.../org/apache/iceberg/view/ViewCatalogTests.java | 52 ++++++++++++++++++++++
4 files changed, 70 insertions(+), 4 deletions(-)
diff --git
a/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
b/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
index 067203b8d9..985127d651 100644
--- a/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
@@ -217,6 +217,12 @@ public class InMemoryCatalog extends
BaseMetastoreViewCatalog
"Namespace %s is not empty. Contains %d table(s).", namespace,
tableIdentifiers.size());
}
+ List<TableIdentifier> viewIdentifiers = listViews(namespace);
+ if (!viewIdentifiers.isEmpty()) {
+ throw new NamespaceNotEmptyException(
+ "Namespace %s is not empty. Contains %d view(s).", namespace,
viewIdentifiers.size());
+ }
+
return namespaces.remove(namespace) != null;
}
}
diff --git a/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
b/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
index 4d0aa08da2..0c8fbe41df 100644
--- a/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
@@ -535,7 +535,15 @@ public class JdbcCatalog extends BaseMetastoreViewCatalog
List<TableIdentifier> tableIdentifiers = listTables(namespace);
if (tableIdentifiers != null && !tableIdentifiers.isEmpty()) {
throw new NamespaceNotEmptyException(
- "Namespace %s is not empty. %s tables exist.", namespace,
tableIdentifiers.size());
+ "Namespace %s is not empty. Contains %d table(s).", namespace,
tableIdentifiers.size());
+ }
+
+ if (schemaVersion != JdbcUtil.SchemaVersion.V0) {
+ List<TableIdentifier> viewIdentifiers = listViews(namespace);
+ if (viewIdentifiers != null && !viewIdentifiers.isEmpty()) {
+ throw new NamespaceNotEmptyException(
+ "Namespace %s is not empty. Contains %d view(s).", namespace,
viewIdentifiers.size());
+ }
}
int deletedRows =
diff --git a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
index 0b7315787a..943b277f5c 100644
--- a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
@@ -848,15 +848,15 @@ public class TestJdbcCatalog extends
CatalogTests<JdbcCatalog> {
assertThatThrownBy(() -> catalog.dropNamespace(tbl1.namespace()))
.isInstanceOf(NamespaceNotEmptyException.class)
- .hasMessage("Namespace db.ns1.ns2 is not empty. 2 tables exist.");
+ .hasMessage("Namespace db.ns1.ns2 is not empty. Contains 2 table(s).");
assertThatThrownBy(() -> catalog.dropNamespace(tbl2.namespace()))
.isInstanceOf(NamespaceNotEmptyException.class)
- .hasMessage("Namespace db.ns1 is not empty. 1 tables exist.");
+ .hasMessage("Namespace db.ns1 is not empty. Contains 1 table(s).");
assertThatThrownBy(() -> catalog.dropNamespace(tbl4.namespace()))
.isInstanceOf(NamespaceNotEmptyException.class)
- .hasMessage("Namespace db is not empty. 1 tables exist.");
+ .hasMessage("Namespace db is not empty. Contains 1 table(s).");
}
@Test
diff --git a/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
b/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
index 676c187f77..71e6f32438 100644
--- a/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
+++ b/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
@@ -36,6 +36,7 @@ import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.catalog.ViewCatalog;
import org.apache.iceberg.exceptions.AlreadyExistsException;
import org.apache.iceberg.exceptions.CommitFailedException;
+import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
import org.apache.iceberg.exceptions.NoSuchNamespaceException;
import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.exceptions.NoSuchViewException;
@@ -1913,4 +1914,55 @@ public abstract class ViewCatalogTests<C extends
ViewCatalog & SupportsNamespace
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid dialect: (empty string)");
}
+
+ @Test
+ public void dropNonEmptyNamespace() {
+ TableIdentifier viewIdent = TableIdentifier.of("ns", "view");
+ TableIdentifier tableIdent = TableIdentifier.of("ns", "tbl");
+
+ assertThat(catalog().namespaceExists(viewIdent.namespace()))
+ .as("Namespace should not exist")
+ .isFalse();
+
+ if (requiresNamespaceCreate()) {
+ catalog().createNamespace(viewIdent.namespace());
+ }
+
+ assertThat(catalog().namespaceExists(viewIdent.namespace()))
+ .as("Namespace should exist")
+ .isTrue();
+
+ tableCatalog().buildTable(tableIdent, SCHEMA).create();
+ assertThat(tableCatalog().tableExists(tableIdent)).as("Table should
exist").isTrue();
+
+ catalog()
+ .buildView(viewIdent)
+ .withSchema(SCHEMA)
+ .withDefaultNamespace(viewIdent.namespace())
+ .withDefaultCatalog(catalog().name())
+ .withQuery("spark", "select * from ns.tbl")
+ .create();
+ assertThat(catalog().viewExists(viewIdent)).as("View should
exist").isTrue();
+
+ // dropping the namespace should fail, because the table & view hasn't
been dropped
+ assertThatThrownBy(() -> catalog().dropNamespace(viewIdent.namespace()))
+ .isInstanceOf(NamespaceNotEmptyException.class)
+ .hasMessageContaining("is not empty");
+
+ tableCatalog().dropTable(tableIdent);
+ assertThat(tableCatalog().tableExists(tableIdent)).as("Table should not
exist").isFalse();
+
+ // dropping the namespace should fail, because the view hasn't been dropped
+ assertThatThrownBy(() -> catalog().dropNamespace(viewIdent.namespace()))
+ .isInstanceOf(NamespaceNotEmptyException.class)
+ .hasMessageContaining("is not empty");
+
+ catalog().dropView(viewIdent);
+ assertThat(catalog().viewExists(viewIdent)).as("View should not
exist").isFalse();
+
+ assertThat(catalog().dropNamespace(viewIdent.namespace())).isTrue();
+ assertThat(catalog().namespaceExists(viewIdent.namespace()))
+ .as("Namespace should not exist")
+ .isFalse();
+ }
}