This is an automated email from the ASF dual-hosted git repository.
fokko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push:
new 472ec6cbdc Core: Add tests for catalogs supporting empty namespaces
(#9890)
472ec6cbdc is described below
commit 472ec6cbdcaa24773ef5c64c9444ae933805381d
Author: Eduard Tudenhoefner <[email protected]>
AuthorDate: Mon Jan 13 16:41:07 2025 +0100
Core: Add tests for catalogs supporting empty namespaces (#9890)
---
.../apache/iceberg/inmemory/InMemoryCatalog.java | 5 +-
.../java/org/apache/iceberg/jdbc/JdbcUtil.java | 4 ++
.../org/apache/iceberg/catalog/CatalogTests.java | 73 ++++++++++++++++++++++
.../iceberg/inmemory/TestInMemoryCatalog.java | 5 ++
.../iceberg/inmemory/TestInMemoryViewCatalog.java | 5 ++
.../org/apache/iceberg/jdbc/TestJdbcCatalog.java | 8 +--
.../java/org/apache/iceberg/jdbc/TestJdbcUtil.java | 6 ++
.../apache/iceberg/jdbc/TestJdbcViewCatalog.java | 5 ++
.../org/apache/iceberg/view/ViewCatalogTests.java | 38 +++++++++++
.../apache/iceberg/spark/extensions/TestViews.java | 43 +++++++------
.../apache/iceberg/spark/extensions/TestViews.java | 34 +++++-----
11 files changed, 178 insertions(+), 48 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 ff71bde71f..f5b7c7bb5f 100644
--- a/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/inmemory/InMemoryCatalog.java
@@ -146,7 +146,7 @@ public class InMemoryCatalog extends
BaseMetastoreViewCatalog
}
return tables.keySet().stream()
- .filter(t -> namespace.isEmpty() || t.namespace().equals(namespace))
+ .filter(t -> t.namespace().equals(namespace))
.sorted(Comparator.comparing(TableIdentifier::toString))
.collect(Collectors.toList());
}
@@ -290,6 +290,7 @@ public class InMemoryCatalog extends
BaseMetastoreViewCatalog
List<Namespace> filteredNamespaces =
namespaces.keySet().stream()
+ .filter(n -> !n.isEmpty())
.filter(n ->
DOT.join(n.levels()).startsWith(searchNamespaceString))
.collect(Collectors.toList());
@@ -323,7 +324,7 @@ public class InMemoryCatalog extends
BaseMetastoreViewCatalog
}
return views.keySet().stream()
- .filter(v -> namespace.isEmpty() || v.namespace().equals(namespace))
+ .filter(v -> v.namespace().equals(namespace))
.sorted(Comparator.comparing(TableIdentifier::toString))
.collect(Collectors.toList());
}
diff --git a/core/src/main/java/org/apache/iceberg/jdbc/JdbcUtil.java
b/core/src/main/java/org/apache/iceberg/jdbc/JdbcUtil.java
index c9bd2b78a6..544e9f39c7 100644
--- a/core/src/main/java/org/apache/iceberg/jdbc/JdbcUtil.java
+++ b/core/src/main/java/org/apache/iceberg/jdbc/JdbcUtil.java
@@ -500,6 +500,10 @@ final class JdbcUtil {
static Namespace stringToNamespace(String namespace) {
Preconditions.checkArgument(namespace != null, "Invalid namespace %s",
namespace);
+ if (namespace.isEmpty()) {
+ return Namespace.empty();
+ }
+
return Namespace.of(Iterables.toArray(SPLITTER_DOT.split(namespace),
String.class));
}
diff --git a/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
b/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
index f032928e03..51d3cbb933 100644
--- a/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
+++ b/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
@@ -22,6 +22,7 @@ import static
org.apache.iceberg.types.Types.NestedField.required;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static
org.assertj.core.api.Assertions.setMaxStackTraceElementsDisplayed;
+import static org.assertj.core.api.Assumptions.assumeThat;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -183,6 +184,10 @@ public abstract class CatalogTests<C extends Catalog &
SupportsNamespaces> {
return true;
}
+ protected boolean supportsEmptyNamespace() {
+ return false;
+ }
+
@Test
public void testCreateNamespace() {
C catalog = catalog();
@@ -978,6 +983,74 @@ public abstract class CatalogTests<C extends Catalog &
SupportsNamespaces> {
assertEmpty("Should not contain ns_2.table_1 after drop", catalog, ns2);
}
+ @Test
+ public void listNamespacesWithEmptyNamespace() {
+ catalog().createNamespace(NS);
+
+ assertThat(catalog().namespaceExists(Namespace.empty())).isFalse();
+
assertThat(catalog().listNamespaces()).contains(NS).doesNotContain(Namespace.empty());
+ assertThat(catalog().listNamespaces(Namespace.empty()))
+ .contains(NS)
+ .doesNotContain(Namespace.empty());
+ }
+
+ @Test
+ public void createAndDropEmptyNamespace() {
+ assumeThat(supportsEmptyNamespace())
+ .as("Only valid for catalogs that support creating/dropping empty
namespaces")
+ .isTrue();
+
+ assertThat(catalog().namespaceExists(Namespace.empty())).isFalse();
+ catalog().createNamespace(Namespace.empty());
+ assertThat(catalog().namespaceExists(Namespace.empty())).isTrue();
+
+ // TODO: if a catalog supports creating an empty namespace, what should be
the expected behavior
+ // when listing all namespaces?
+ assertThat(catalog().listNamespaces()).isEmpty();
+ assertThat(catalog().listNamespaces(Namespace.empty())).isEmpty();
+
+ catalog().dropNamespace(Namespace.empty());
+ assertThat(catalog().namespaceExists(Namespace.empty())).isFalse();
+ }
+
+ @Test
+ public void namespacePropertiesOnEmptyNamespace() {
+ assumeThat(supportsEmptyNamespace())
+ .as("Only valid for catalogs that support properties on empty
namespaces")
+ .isTrue();
+
+ catalog().createNamespace(Namespace.empty());
+
+ Map<String, String> properties = ImmutableMap.of("owner", "user",
"created-at", "sometime");
+ catalog().setProperties(Namespace.empty(), properties);
+
+
assertThat(catalog().loadNamespaceMetadata(Namespace.empty())).containsAllEntriesOf(properties);
+
+ catalog().removeProperties(Namespace.empty(), ImmutableSet.of("owner"));
+ assertThat(catalog().loadNamespaceMetadata(Namespace.empty()))
+ .containsAllEntriesOf(ImmutableMap.of("created-at", "sometime"));
+ }
+
+ @Test
+ public void listTablesInEmptyNamespace() {
+ assumeThat(supportsEmptyNamespace())
+ .as("Only valid for catalogs that support listing tables in empty
namespaces")
+ .isTrue();
+
+ if (requiresNamespaceCreate()) {
+ catalog().createNamespace(Namespace.empty());
+ catalog().createNamespace(NS);
+ }
+
+ TableIdentifier table1 = TableIdentifier.of(Namespace.empty(), "table_1");
+ TableIdentifier table2 = TableIdentifier.of(NS, "table_2");
+
+ catalog().buildTable(table1, SCHEMA).create();
+ catalog().buildTable(table2, SCHEMA).create();
+
+
assertThat(catalog().listTables(Namespace.empty())).containsExactly(table1);
+ }
+
@Test
public void testUpdateTableSchema() {
C catalog = catalog();
diff --git
a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
index 2c8650d635..6386e3189f 100644
--- a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
@@ -48,4 +48,9 @@ public class TestInMemoryCatalog extends
CatalogTests<InMemoryCatalog> {
protected boolean requiresNamespaceCreate() {
return true;
}
+
+ @Override
+ protected boolean supportsEmptyNamespace() {
+ return true;
+ }
}
diff --git
a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryViewCatalog.java
b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryViewCatalog.java
index 65e4e26315..79f187b1f6 100644
---
a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryViewCatalog.java
+++
b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryViewCatalog.java
@@ -52,4 +52,9 @@ public class TestInMemoryViewCatalog extends
ViewCatalogTests<InMemoryCatalog> {
protected boolean requiresNamespaceCreate() {
return true;
}
+
+ @Override
+ protected boolean supportsEmptyNamespace() {
+ return true;
+ }
}
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 2d4eb2f157..a1363ac508 100644
--- a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
@@ -103,12 +103,12 @@ public class TestJdbcCatalog extends
CatalogTests<JdbcCatalog> {
}
@Override
- protected boolean supportsNamespaceProperties() {
+ protected boolean supportsNestedNamespaces() {
return true;
}
@Override
- protected boolean supportsNestedNamespaces() {
+ protected boolean supportsEmptyNamespace() {
return true;
}
@@ -763,11 +763,11 @@ public class TestJdbcCatalog extends
CatalogTests<JdbcCatalog> {
List<Namespace> nsp3 = catalog.listNamespaces();
Set<String> tblSet2 =
Sets.newHashSet(nsp3.stream().map(Namespace::toString).iterator());
- assertThat(tblSet2).hasSize(5).contains("db", "db2", "d_", "d%", "");
+ assertThat(tblSet2).hasSize(4).contains("db", "db2", "d_", "d%");
List<Namespace> nsp4 = catalog.listNamespaces();
Set<String> tblSet3 =
Sets.newHashSet(nsp4.stream().map(Namespace::toString).iterator());
- assertThat(tblSet3).hasSize(5).contains("db", "db2", "d_", "d%", "");
+ assertThat(tblSet3).hasSize(4).contains("db", "db2", "d_", "d%");
List<Namespace> nsp5 = catalog.listNamespaces(Namespace.of("d_"));
assertThat(nsp5).hasSize(1);
diff --git a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcUtil.java
b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcUtil.java
index 44c21113c3..4ac3a9301b 100644
--- a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcUtil.java
+++ b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcUtil.java
@@ -143,4 +143,10 @@ public class TestJdbcUtil {
assertThat(updated).isEqualTo(1);
}
}
+
+ @Test
+ public void emptyNamespaceInIdentifier() {
+ assertThat(JdbcUtil.stringToTableIdentifier("", "tblName"))
+ .isEqualTo(TableIdentifier.of(Namespace.empty(), "tblName"));
+ }
}
diff --git
a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcViewCatalog.java
b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcViewCatalog.java
index 9b8ca3a906..2ac12015ab 100644
--- a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcViewCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcViewCatalog.java
@@ -80,6 +80,11 @@ public class TestJdbcViewCatalog extends
ViewCatalogTests<JdbcCatalog> {
return true;
}
+ @Override
+ protected boolean supportsEmptyNamespace() {
+ return true;
+ }
+
@Test
public void testCommitExceptionWithoutMessage() {
TableIdentifier identifier = TableIdentifier.of("namespace1", "view");
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 d365defb29..561d0cd580 100644
--- a/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
+++ b/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java
@@ -21,6 +21,7 @@ package org.apache.iceberg.view;
import static org.apache.iceberg.types.Types.NestedField.required;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assumptions.assumeThat;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -73,6 +74,10 @@ public abstract class ViewCatalogTests<C extends ViewCatalog
& SupportsNamespace
return false;
}
+ protected boolean supportsEmptyNamespace() {
+ return false;
+ }
+
@Test
public void basicCreateView() {
TableIdentifier identifier = TableIdentifier.of("ns", "view");
@@ -814,6 +819,39 @@ public abstract class ViewCatalogTests<C extends
ViewCatalog & SupportsNamespace
assertThat(catalog().listViews(ns2)).isEmpty();
}
+ @Test
+ public void listViewsInEmptyNamespace() {
+ assumeThat(supportsEmptyNamespace())
+ .as("Only valid for catalogs that support listing views in empty
namespaces")
+ .isTrue();
+
+ Namespace namespace = Namespace.of("ns");
+
+ if (requiresNamespaceCreate()) {
+ catalog().createNamespace(Namespace.empty());
+ catalog().createNamespace(namespace);
+ }
+
+ TableIdentifier view1 = TableIdentifier.of(Namespace.empty(), "view1");
+ TableIdentifier view2 = TableIdentifier.of(namespace, "view2");
+
+ catalog()
+ .buildView(view1)
+ .withSchema(SCHEMA)
+ .withDefaultNamespace(view1.namespace())
+ .withQuery("spark", "select * from ns.tbl")
+ .create();
+
+ catalog()
+ .buildView(view2)
+ .withSchema(SCHEMA)
+ .withDefaultNamespace(view2.namespace())
+ .withQuery("spark", "select * from ns.tbl")
+ .create();
+
+ assertThat(catalog().listViews(Namespace.empty())).containsExactly(view1);
+ }
+
@Test
public void listViewsAndTables() {
Assumptions.assumeThat(tableCatalog())
diff --git
a/spark/v3.4/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
b/spark/v3.4/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
index a9160832aa..0fe4600297 100644
---
a/spark/v3.4/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
+++
b/spark/v3.4/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import org.apache.iceberg.CatalogUtil;
import org.apache.iceberg.IcebergBuild;
import org.apache.iceberg.Schema;
import org.apache.iceberg.catalog.Catalog;
@@ -1532,39 +1533,37 @@ public class TestViews extends SparkExtensionsTestBase {
// spark stores temp views case-insensitive by default
Object[] tempView = row("", tempViewForListing.toLowerCase(Locale.ROOT),
true);
- assertThat(sql("SHOW VIEWS"))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
-
- assertThat(sql("SHOW VIEWS IN %s", catalogName))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
+ Object[] v1Row = row(NAMESPACE.toString(), v1, false);
+ Object[] v2Row = row(NAMESPACE.toString(), prefixV2, false);
+ Object[] v3Row = row(NAMESPACE.toString(), prefixV3, false);
+ assertThat(sql("SHOW VIEWS")).contains(v2Row, v3Row, v1Row, tempView);
+
+ if (!"rest".equals(catalogConfig.get(CatalogUtil.ICEBERG_CATALOG_TYPE))) {
+ // REST catalog requires a namespace
+ assertThat(sql("SHOW VIEWS IN %s", catalogName))
+ .contains(tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
+ }
assertThat(sql("SHOW VIEWS IN %s.%s", catalogName, NAMESPACE))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
+ .contains(v2Row, v3Row, v1Row, tempView);
assertThat(sql("SHOW VIEWS LIKE 'pref*'"))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
row(NAMESPACE.toString(), prefixV3, false));
+ .contains(v2Row, v3Row)
+ .doesNotContain(v1Row, tempView);
assertThat(sql("SHOW VIEWS LIKE 'non-existing'")).isEmpty();
- assertThat(sql("SHOW VIEWS IN spark_catalog.default")).contains(tempView);
+ sql("CREATE NAMESPACE IF NOT EXISTS spark_catalog.%s", NAMESPACE);
+ assertThat(sql("SHOW VIEWS IN spark_catalog.%s", NAMESPACE))
+ .contains(tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
assertThat(sql("SHOW VIEWS IN global_temp"))
.contains(
// spark stores temp views case-insensitive by default
- row("global_temp", globalViewForListing.toLowerCase(Locale.ROOT),
true), tempView);
+ row("global_temp", globalViewForListing.toLowerCase(Locale.ROOT),
true), tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
sql("USE spark_catalog");
assertThat(sql("SHOW VIEWS")).contains(tempView);
diff --git
a/spark/v3.5/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
b/spark/v3.5/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
index f22fbf9369..2a1f1c7eae 100644
---
a/spark/v3.5/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
+++
b/spark/v3.5/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
@@ -1561,45 +1561,39 @@ public class TestViews extends ExtensionsTestBase {
// spark stores temp views case-insensitive by default
Object[] tempView = row("", tempViewForListing.toLowerCase(Locale.ROOT),
true);
- assertThat(sql("SHOW VIEWS"))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
+ Object[] v1Row = row(NAMESPACE.toString(), v1, false);
+ Object[] v2Row = row(NAMESPACE.toString(), prefixV2, false);
+ Object[] v3Row = row(NAMESPACE.toString(), prefixV3, false);
+ assertThat(sql("SHOW VIEWS")).contains(v2Row, v3Row, v1Row, tempView);
if (!"rest".equals(catalogConfig.get(CatalogUtil.ICEBERG_CATALOG_TYPE))) {
// REST catalog requires a namespace
assertThat(sql("SHOW VIEWS IN %s", catalogName))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
+ .contains(tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
}
assertThat(sql("SHOW VIEWS IN %s.%s", catalogName, NAMESPACE))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
- row(NAMESPACE.toString(), prefixV3, false),
- row(NAMESPACE.toString(), v1, false),
- tempView);
+ .contains(v2Row, v3Row, v1Row, tempView);
assertThat(sql("SHOW VIEWS LIKE 'pref*'"))
- .contains(
- row(NAMESPACE.toString(), prefixV2, false),
row(NAMESPACE.toString(), prefixV3, false));
+ .contains(v2Row, v3Row)
+ .doesNotContain(v1Row, tempView);
assertThat(sql("SHOW VIEWS LIKE 'non-existing'")).isEmpty();
if (!catalogName.equals(SPARK_CATALOG)) {
sql("CREATE NAMESPACE IF NOT EXISTS spark_catalog.%s", NAMESPACE);
- assertThat(sql("SHOW VIEWS IN spark_catalog.%s",
NAMESPACE)).contains(tempView);
+ assertThat(sql("SHOW VIEWS IN spark_catalog.%s", NAMESPACE))
+ .contains(tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
}
assertThat(sql("SHOW VIEWS IN global_temp"))
.contains(
// spark stores temp views case-insensitive by default
- row("global_temp", globalViewForListing.toLowerCase(Locale.ROOT),
true), tempView);
+ row("global_temp", globalViewForListing.toLowerCase(Locale.ROOT),
true), tempView)
+ .doesNotContain(v1Row, v2Row, v3Row);
sql("USE spark_catalog");
assertThat(sql("SHOW VIEWS")).contains(tempView);