You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by bl...@apache.org on 2020/04/17 21:35:27 UTC
[incubator-iceberg] branch master updated: Add SupportsNamespaces
extension to Catalog (#919)
This is an automated email from the ASF dual-hosted git repository.
blue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-iceberg.git
The following commit(s) were added to refs/heads/master by this push:
new 34126d2 Add SupportsNamespaces extension to Catalog (#919)
34126d2 is described below
commit 34126d20d29fa12848dd28b305a918f8a1219979
Author: XiaokunDing <di...@gmail.com>
AuthorDate: Sat Apr 18 05:35:19 2020 +0800
Add SupportsNamespaces extension to Catalog (#919)
This also adds implementations for HadoopCatalog and HiveCatalog.
---
.../apache/iceberg/catalog/SupportsNamespaces.java | 141 +++++++++++++
.../exceptions/NamespaceNotEmptyException.java | 33 +++
.../exceptions/NoSuchNamespaceException.java | 33 +++
.../org/apache/iceberg/hadoop/HadoopCatalog.java | 126 +++++++++++-
.../apache/iceberg/hadoop/TestHadoopCatalog.java | 168 +++++++++++++++-
.../java/org/apache/iceberg/hive/HiveCatalog.java | 221 ++++++++++++++++++++-
.../org/apache/iceberg/hive/TestHiveCatalog.java | 192 ++++++++++++++++++
7 files changed, 894 insertions(+), 20 deletions(-)
diff --git a/api/src/main/java/org/apache/iceberg/catalog/SupportsNamespaces.java b/api/src/main/java/org/apache/iceberg/catalog/SupportsNamespaces.java
new file mode 100644
index 0000000..0e3302a
--- /dev/null
+++ b/api/src/main/java/org/apache/iceberg/catalog/SupportsNamespaces.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iceberg.catalog;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.iceberg.exceptions.AlreadyExistsException;
+import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
+import org.apache.iceberg.exceptions.NoSuchNamespaceException;
+
+/**
+ * Catalog methods for working with namespaces.
+ * <p>
+ * If an object such as a table, view, or function exists, its parent namespaces must also exist
+ * and must be returned by the discovery methods {@link #listNamespaces()} and
+ * {@link #listNamespaces(Namespace namespace)}.
+ * <p>
+ * Catalog implementations are not required to maintain the existence of namespaces independent of
+ * objects in a namespace. For example, a function catalog that loads functions using reflection
+ * and uses Java packages as namespaces is not required to support the methods to create, alter, or
+ * drop a namespace. Implementations are allowed to discover the existence of objects or namespaces
+ * without throwing {@link NoSuchNamespaceException} when no namespace is found.
+ */
+public interface SupportsNamespaces {
+ /**
+ * Create a namespace in the catalog.
+ *
+ * @param namespace a namespace. {@link Namespace}.
+ * @throws AlreadyExistsException If the namespace already exists
+ * @throws UnsupportedOperationException If create is not a supported operation
+ */
+ default void createNamespace(Namespace namespace) {
+ createNamespace(namespace, ImmutableMap.of());
+ }
+
+ /**
+ * Create a namespace in the catalog.
+ *
+ * @param namespace a multi-part namespace
+ * @param metadata a string Map of properties for the given namespace
+ * @throws AlreadyExistsException If the namespace already exists
+ * @throws UnsupportedOperationException If create is not a supported operation
+ */
+ void createNamespace(Namespace namespace, Map<String, String> metadata);
+
+ /**
+ * List top-level namespaces from the catalog.
+ * <p>
+ * If an object such as a table, view, or function exists, its parent namespaces must also exist
+ * and must be returned by this discovery method. For example, if table a.b.t exists, this method
+ * must return ["a"] in the result array.
+ *
+ * @return an List of namespace {@link Namespace} names
+ */
+ default List<Namespace> listNamespaces() {
+ return listNamespaces(Namespace.empty());
+ }
+
+ /**
+ * List namespaces from the namespace.
+ * <p>
+ * For example, if table a.b.t exists, use 'SELECT NAMESPACE IN a' this method
+ * must return Namepace.of("a","b") {@link Namespace}.
+ *
+ * @return a List of namespace {@link Namespace} names
+ * @throws NoSuchNamespaceException If the namespace does not exist (optional)
+ */
+ List<Namespace> listNamespaces(Namespace namespace) throws NoSuchNamespaceException;
+
+ /**
+ * Load metadata properties for a namespace.
+ *
+ * @param namespace a namespace. {@link Namespace}
+ * @return a string map of properties for the given namespace
+ * @throws NoSuchNamespaceException If the namespace does not exist (optional)
+ */
+ Map<String, String> loadNamespaceMetadata(Namespace namespace) throws NoSuchNamespaceException;
+
+ /**
+ * Drop a namespace. If the namespace exists and was dropped, this will return true.
+ *
+ * @param namespace a namespace. {@link Namespace}
+ * @return true if the namespace was dropped, false otherwise.
+ * @throws NamespaceNotEmptyException If the namespace does not empty
+ */
+ boolean dropNamespace(Namespace namespace) throws NamespaceNotEmptyException;
+
+ /**
+ * Apply a set of metadata to a namespace in the catalog.
+ *
+ * @param namespace a namespace. {@link Namespace}
+ * @param properties a collection of metadata to apply to the namespace
+ * @throws NoSuchNamespaceException If the namespace does not exist (optional)
+ * @throws UnsupportedOperationException If namespace properties are not supported
+ */
+ boolean setProperties(Namespace namespace, Map<String, String> properties) throws NoSuchNamespaceException;
+
+ /**
+ * Remove a set of metadata from a namespace in the catalog.
+ *
+ * @param namespace a namespace. {@link Namespace}
+ * @param properties a collection of metadata to apply to the namespace
+ * @throws NoSuchNamespaceException If the namespace does not exist (optional)
+ * @throws UnsupportedOperationException If namespace properties are not supported
+ */
+ boolean removeProperties(Namespace namespace, Set<String> properties) throws NoSuchNamespaceException;
+
+ /**
+ * Checks whether the Namespace exists.
+ *
+ * @param namespace a namespace. {@link Namespace}
+ * @return true if the Namespace exists, false otherwise
+ */
+ default boolean namespaceExists(Namespace namespace) {
+ try {
+ loadNamespaceMetadata(namespace);
+ return true;
+ } catch (NoSuchNamespaceException e) {
+ return false;
+ }
+ }
+}
diff --git a/api/src/main/java/org/apache/iceberg/exceptions/NamespaceNotEmptyException.java b/api/src/main/java/org/apache/iceberg/exceptions/NamespaceNotEmptyException.java
new file mode 100644
index 0000000..130cd3e
--- /dev/null
+++ b/api/src/main/java/org/apache/iceberg/exceptions/NamespaceNotEmptyException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iceberg.exceptions;
+
+/**
+ * Exception raised when attempting to drop a namespace that is not empty.
+ */
+public class NamespaceNotEmptyException extends RuntimeException {
+ public NamespaceNotEmptyException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public NamespaceNotEmptyException(Throwable cause, String message, Object... args) {
+ super(String.format(message, args), cause);
+ }
+}
diff --git a/api/src/main/java/org/apache/iceberg/exceptions/NoSuchNamespaceException.java b/api/src/main/java/org/apache/iceberg/exceptions/NoSuchNamespaceException.java
new file mode 100644
index 0000000..f9348f7
--- /dev/null
+++ b/api/src/main/java/org/apache/iceberg/exceptions/NoSuchNamespaceException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iceberg.exceptions;
+
+/**
+ * Exception raised when attempting to load a namespace that does not exist.
+ */
+public class NoSuchNamespaceException extends RuntimeException {
+ public NoSuchNamespaceException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public NoSuchNamespaceException(Throwable cause, String message, Object... args) {
+ super(String.format(message, args), cause);
+ }
+}
diff --git a/core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java b/core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java
index 04b8747..200e979 100644
--- a/core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java
@@ -21,13 +21,17 @@ package org.apache.iceberg.hadoop;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.Closeable;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
@@ -40,9 +44,11 @@ import org.apache.iceberg.Table;
import org.apache.iceberg.TableMetadata;
import org.apache.iceberg.TableOperations;
import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.SupportsNamespaces;
import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.AlreadyExistsException;
+import org.apache.iceberg.exceptions.NoSuchNamespaceException;
import org.apache.iceberg.exceptions.NoSuchTableException;
-import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.exceptions.RuntimeIOException;
/**
@@ -58,12 +64,16 @@ import org.apache.iceberg.exceptions.RuntimeIOException;
*
* Note: The HadoopCatalog requires that the underlying file system supports atomic rename.
*/
-public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
+public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable, SupportsNamespaces {
+
private static final String ICEBERG_HADOOP_WAREHOUSE_BASE = "iceberg/warehouse";
private static final String TABLE_METADATA_FILE_EXTENSION = ".metadata.json";
+ private static final Joiner SLASH = Joiner.on("/");
private static final PathFilter TABLE_FILTER = path -> path.getName().endsWith(TABLE_METADATA_FILE_EXTENSION);
+
private final Configuration conf;
- private String warehouseLocation;
+ private final String warehouseLocation;
+ private final FileSystem fs;
/**
* The constructor of the HadoopCatalog. It uses the passed location as its warehouse directory.
@@ -77,6 +87,7 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
this.conf = conf;
this.warehouseLocation = warehouseLocation.replaceAll("/*$", "");
+ this.fs = Util.getFs(new Path(warehouseLocation), conf);
}
/**
@@ -89,6 +100,7 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
public HadoopCatalog(Configuration conf) {
this.conf = conf;
this.warehouseLocation = conf.get("fs.defaultFS") + "/" + ICEBERG_HADOOP_WAREHOUSE_BASE;
+ this.fs = Util.getFs(new Path(warehouseLocation), conf);
}
@Override
@@ -101,14 +113,12 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
Preconditions.checkArgument(namespace.levels().length >= 1,
"Missing database in table identifier: %s", namespace);
- Joiner slash = Joiner.on("/");
- Path nsPath = new Path(slash.join(warehouseLocation, slash.join(namespace.levels())));
- FileSystem fs = Util.getFs(nsPath, conf);
+ Path nsPath = new Path(warehouseLocation, SLASH.join(namespace.levels()));
Set<TableIdentifier> tblIdents = Sets.newHashSet();
try {
if (!fs.exists(nsPath) || !fs.isDirectory(nsPath)) {
- throw new NotFoundException("Unknown namespace " + namespace);
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
}
for (FileStatus s : fs.listStatus(nsPath)) {
@@ -128,7 +138,7 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
}
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to list tables under " + namespace, ioe);
+ throw new RuntimeIOException(ioe, "Failed to list tables under: %s", namespace);
}
return Lists.newArrayList(tblIdents);
@@ -180,7 +190,6 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
lastMetadata = null;
}
- FileSystem fs = Util.getFs(tablePath, conf);
try {
if (purge && lastMetadata != null) {
// Since the data files and the metadata files may store in different locations,
@@ -200,7 +209,104 @@ public class HadoopCatalog extends BaseMetastoreCatalog implements Closeable {
}
@Override
- public void close() throws IOException {
+ public void createNamespace(Namespace namespace, Map<String, String> meta) {
+ Preconditions.checkArgument(
+ !namespace.isEmpty(),
+ "Cannot create namespace with invalid name: %s", namespace);
+ if (!meta.isEmpty()) {
+ throw new UnsupportedOperationException("Cannot create namespace " + namespace + " : metadata is not supported");
+ }
+
+ Path nsPath = new Path(warehouseLocation, SLASH.join(namespace.levels()));
+
+ if (isNamespace(nsPath)) {
+ throw new AlreadyExistsException("Namespace '%s' already exists!", namespace);
+ }
+
+ try {
+ fs.mkdirs(nsPath);
+
+ } catch (IOException e) {
+ throw new RuntimeIOException(e, "Create namespace failed: %s", namespace);
+ }
}
+ @Override
+ public List<Namespace> listNamespaces(Namespace namespace) {
+ Path nsPath = namespace.isEmpty() ? new Path(warehouseLocation)
+ : new Path(warehouseLocation, SLASH.join(namespace.levels()));
+ if (!isNamespace(nsPath)) {
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
+ }
+
+ try {
+ return Stream.of(fs.listStatus(nsPath))
+ .map(FileStatus::getPath)
+ .filter(path -> isNamespace(path))
+ .map(path -> append(namespace, path.getName()))
+ .collect(Collectors.toList());
+ } catch (IOException ioe) {
+ throw new RuntimeIOException(ioe, "Failed to list namespace under: %s", namespace);
+ }
+ }
+
+ private Namespace append(Namespace ns, String name) {
+ String[] levels = Arrays.copyOfRange(ns.levels(), 0, ns.levels().length + 1);
+ levels[ns.levels().length] = name;
+ return Namespace.of(levels);
+ }
+
+ @Override
+ public boolean dropNamespace(Namespace namespace) {
+ Path nsPath = new Path(warehouseLocation, SLASH.join(namespace.levels()));
+
+ if (!isNamespace(nsPath) || namespace.isEmpty()) {
+ return false;
+ }
+
+ try {
+ return fs.delete(nsPath, false /* recursive */);
+
+ } catch (IOException e) {
+ throw new RuntimeIOException(e, "Namespace delete failed: %s", namespace);
+ }
+ }
+
+ @Override
+ public boolean setProperties(Namespace namespace, Map<String, String> properties) {
+ throw new UnsupportedOperationException(
+ "Cannot set namespace properties " + namespace + " : setProperties is not supported");
+ }
+
+ @Override
+ public boolean removeProperties(Namespace namespace, Set<String> properties) {
+ throw new UnsupportedOperationException(
+ "Cannot remove properties " + namespace + " : removeProperties is not supported");
+ }
+
+ @Override
+ public Map<String, String> loadNamespaceMetadata(Namespace namespace) {
+ Path nsPath = new Path(warehouseLocation, SLASH.join(namespace.levels()));
+
+ if (!isNamespace(nsPath) || namespace.isEmpty()) {
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
+ }
+
+ return ImmutableMap.of("location", nsPath.toString());
+ }
+
+ private boolean isNamespace(Path path) {
+ Path metadataPath = new Path(path, "metadata");
+ try {
+ return fs.isDirectory(path) && !(fs.exists(metadataPath) && fs.isDirectory(metadataPath) &&
+ (fs.listStatus(metadataPath, TABLE_FILTER).length >= 1));
+
+ } catch (IOException ioe) {
+ throw new RuntimeIOException(ioe, "Failed to list namespace info: %s ", path);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
}
diff --git a/core/src/test/java/org/apache/iceberg/hadoop/TestHadoopCatalog.java b/core/src/test/java/org/apache/iceberg/hadoop/TestHadoopCatalog.java
index 1e86720..1cafc25 100644
--- a/core/src/test/java/org/apache/iceberg/hadoop/TestHadoopCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/hadoop/TestHadoopCatalog.java
@@ -19,6 +19,7 @@
package org.apache.iceberg.hadoop;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.IOException;
@@ -33,11 +34,14 @@ import org.apache.iceberg.Table;
import org.apache.iceberg.Transaction;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
-import org.apache.iceberg.exceptions.NotFoundException;
+import org.apache.iceberg.exceptions.NoSuchNamespaceException;
+import org.apache.iceberg.exceptions.RuntimeIOException;
import org.junit.Assert;
import org.junit.Test;
public class TestHadoopCatalog extends HadoopTableTestBase {
+ private static ImmutableMap<String, String> meta = ImmutableMap.of();
+
@Test
public void testBasicCatalog() throws Exception {
Configuration conf = new Configuration();
@@ -129,8 +133,8 @@ public class TestHadoopCatalog extends HadoopTableTestBase {
Assert.assertEquals(tbls2.size(), 1);
Assert.assertTrue(tbls2.get(0).name().equals("tbl3"));
- AssertHelpers.assertThrows("should throw exception", NotFoundException.class,
- "Unknown namespace", () -> {
+ AssertHelpers.assertThrows("should throw exception", NoSuchNamespaceException.class,
+ "Namespace does not exist: ", () -> {
catalog.listTables(Namespace.of("db", "ns1", "ns2"));
});
}
@@ -149,4 +153,162 @@ public class TestHadoopCatalog extends HadoopTableTestBase {
Assert.assertEquals("1 table expected", 1, catalog.listTables(Namespace.of("ns1", "ns2")).size());
catalog.dropTable(tableIdent, true);
}
+
+ @Test
+ public void testCreateNamespace() throws Exception {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+
+ TableIdentifier tbl1 = TableIdentifier.of("db", "ns1", "ns2", "metadata");
+ TableIdentifier tbl2 = TableIdentifier.of("db", "ns2", "ns3", "tbl2");
+
+ Lists.newArrayList(tbl1, tbl2).forEach(t ->
+ catalog.createNamespace(t.namespace(), meta)
+ );
+
+ String metaLocation1 = warehousePath + "/" + "db/ns1/ns2";
+ FileSystem fs1 = Util.getFs(new Path(metaLocation1), conf);
+ Assert.assertTrue(fs1.isDirectory(new Path(metaLocation1)));
+
+ String metaLocation2 = warehousePath + "/" + "db/ns2/ns3";
+ FileSystem fs2 = Util.getFs(new Path(metaLocation2), conf);
+ Assert.assertTrue(fs2.isDirectory(new Path(metaLocation2)));
+
+ AssertHelpers.assertThrows("Should fail to create when namespace already exist: " + tbl1.namespace(),
+ org.apache.iceberg.exceptions.AlreadyExistsException.class,
+ "Namespace '" + tbl1.namespace() + "' already exists!", () -> {
+ catalog.createNamespace(tbl1.namespace());
+ });
+ }
+
+ @Test
+ public void testListNamespace() throws Exception {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+
+ TableIdentifier tbl1 = TableIdentifier.of("db", "ns1", "ns2", "metadata");
+ TableIdentifier tbl2 = TableIdentifier.of("db", "ns2", "ns3", "tbl2");
+ TableIdentifier tbl3 = TableIdentifier.of("db", "ns3", "tbl4");
+ TableIdentifier tbl4 = TableIdentifier.of("db", "metadata");
+ TableIdentifier tbl5 = TableIdentifier.of("db2", "metadata");
+
+ Lists.newArrayList(tbl1, tbl2, tbl3, tbl4, tbl5).forEach(t ->
+ catalog.createTable(t, SCHEMA, PartitionSpec.unpartitioned())
+ );
+
+ List<Namespace> nsp1 = catalog.listNamespaces(Namespace.of("db"));
+ Set<String> tblSet = Sets.newHashSet(nsp1.stream().map(t -> t.toString()).iterator());
+ Assert.assertEquals(tblSet.size(), 3);
+ Assert.assertTrue(tblSet.contains("db.ns1"));
+ Assert.assertTrue(tblSet.contains("db.ns2"));
+ Assert.assertTrue(tblSet.contains("db.ns3"));
+
+ List<Namespace> nsp2 = catalog.listNamespaces(Namespace.of("db", "ns1"));
+ Assert.assertEquals(nsp2.size(), 1);
+ Assert.assertTrue(nsp2.get(0).toString().equals("db.ns1.ns2"));
+
+ List<Namespace> nsp3 = catalog.listNamespaces();
+ Set<String> tblSet2 = Sets.newHashSet(nsp3.stream().map(t -> t.toString()).iterator());
+ Assert.assertEquals(tblSet2.size(), 2);
+ Assert.assertTrue(tblSet2.contains("db"));
+ Assert.assertTrue(tblSet2.contains("db2"));
+
+ List<Namespace> nsp4 = catalog.listNamespaces();
+ Set<String> tblSet3 = Sets.newHashSet(nsp4.stream().map(t -> t.toString()).iterator());
+ Assert.assertEquals(tblSet3.size(), 2);
+ Assert.assertTrue(tblSet3.contains("db"));
+ Assert.assertTrue(tblSet3.contains("db2"));
+
+ AssertHelpers.assertThrows("Should fail to list namespace doesn't exist", NoSuchNamespaceException.class,
+ "Namespace does not exist: ", () -> {
+ catalog.listNamespaces(Namespace.of("db", "db2", "ns2"));
+ });
+ }
+
+ @Test
+ public void testLoadNamespaceMeta() throws IOException {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+
+ TableIdentifier tbl1 = TableIdentifier.of("db", "ns1", "ns2", "metadata");
+ TableIdentifier tbl2 = TableIdentifier.of("db", "ns2", "ns3", "tbl2");
+ TableIdentifier tbl3 = TableIdentifier.of("db", "ns3", "tbl4");
+ TableIdentifier tbl4 = TableIdentifier.of("db", "metadata");
+
+ Lists.newArrayList(tbl1, tbl2, tbl3, tbl4).forEach(t ->
+ catalog.createTable(t, SCHEMA, PartitionSpec.unpartitioned())
+ );
+ catalog.loadNamespaceMetadata(Namespace.of("db"));
+
+ AssertHelpers.assertThrows("Should fail to load namespace doesn't exist", NoSuchNamespaceException.class,
+ "Namespace does not exist: ", () -> {
+ catalog.loadNamespaceMetadata(Namespace.of("db", "db2", "ns2"));
+ });
+ }
+
+ @Test
+ public void testNamespaceExists() throws IOException {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+
+ TableIdentifier tbl1 = TableIdentifier.of("db", "ns1", "ns2", "metadata");
+ TableIdentifier tbl2 = TableIdentifier.of("db", "ns2", "ns3", "tbl2");
+ TableIdentifier tbl3 = TableIdentifier.of("db", "ns3", "tbl4");
+ TableIdentifier tbl4 = TableIdentifier.of("db", "metadata");
+
+ Lists.newArrayList(tbl1, tbl2, tbl3, tbl4).forEach(t ->
+ catalog.createTable(t, SCHEMA, PartitionSpec.unpartitioned())
+ );
+ Assert.assertTrue("Should true to namespace exist",
+ catalog.namespaceExists(Namespace.of("db", "ns1", "ns2")));
+ Assert.assertTrue("Should false to namespace doesn't exist",
+ !catalog.namespaceExists(Namespace.of("db", "db2", "ns2")));
+ }
+
+ @Test
+ public void testAlterNamespaceMeta() throws IOException {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+ AssertHelpers.assertThrows("Should fail to change namespace", UnsupportedOperationException.class,
+ "Cannot set namespace properties db.db2.ns2 : setProperties is not supported", () -> {
+ catalog.setProperties(Namespace.of("db", "db2", "ns2"), ImmutableMap.of("property", "test"));
+ });
+ }
+
+ @Test
+ public void testDropNamespace() throws IOException {
+ Configuration conf = new Configuration();
+ String warehousePath = temp.newFolder().getAbsolutePath();
+ HadoopCatalog catalog = new HadoopCatalog(conf, warehousePath);
+ Namespace namespace1 = Namespace.of("db");
+ Namespace namespace2 = Namespace.of("db", "ns1");
+
+ TableIdentifier tbl1 = TableIdentifier.of(namespace1, "tbl1");
+ TableIdentifier tbl2 = TableIdentifier.of(namespace2, "tbl1");
+
+ Lists.newArrayList(tbl1, tbl2).forEach(t ->
+ catalog.createTable(t, SCHEMA, PartitionSpec.unpartitioned())
+ );
+
+ AssertHelpers.assertThrows("Should fail to drop namespace is not empty " + namespace1,
+ RuntimeIOException.class,
+ "Namespace delete failed: " + namespace1, () -> {
+ catalog.dropNamespace(Namespace.of("db"));
+ });
+ Assert.assertFalse("Should fail to drop namespace doesn't exist",
+ catalog.dropNamespace(Namespace.of("db2")));
+ Assert.assertTrue(catalog.dropTable(tbl1));
+ Assert.assertTrue(catalog.dropTable(tbl2));
+ Assert.assertTrue(catalog.dropNamespace(namespace2));
+ Assert.assertTrue(catalog.dropNamespace(namespace1));
+ String metaLocation = warehousePath + "/" + "db";
+ FileSystem fs = Util.getFs(new Path(metaLocation), conf);
+ Assert.assertFalse(fs.isDirectory(new Path(metaLocation)));
+ }
}
+
diff --git a/hive/src/main/java/org/apache/iceberg/hive/HiveCatalog.java b/hive/src/main/java/org/apache/iceberg/hive/HiveCatalog.java
index d00a151..4973b06 100644
--- a/hive/src/main/java/org/apache/iceberg/hive/HiveCatalog.java
+++ b/hive/src/main/java/org/apache/iceberg/hive/HiveCatalog.java
@@ -21,12 +21,20 @@ package org.apache.iceberg.hive;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
import java.io.Closeable;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
import org.apache.hadoop.hive.metastore.api.AlreadyExistsException;
+import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.InvalidOperationException;
import org.apache.hadoop.hive.metastore.api.NoSuchObjectException;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.hadoop.hive.metastore.api.UnknownDBException;
@@ -34,14 +42,16 @@ import org.apache.iceberg.BaseMetastoreCatalog;
import org.apache.iceberg.TableMetadata;
import org.apache.iceberg.TableOperations;
import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.SupportsNamespaces;
import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
+import org.apache.iceberg.exceptions.NoSuchNamespaceException;
import org.apache.iceberg.exceptions.NoSuchTableException;
-import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
+public class HiveCatalog extends BaseMetastoreCatalog implements Closeable, SupportsNamespaces {
private static final Logger LOG = LoggerFactory.getLogger(HiveCatalog.class);
private final HiveClientPool clients;
@@ -58,7 +68,7 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
@Override
public List<TableIdentifier> listTables(Namespace namespace) {
- Preconditions.checkArgument(namespace.levels().length == 1,
+ Preconditions.checkArgument(isValidateNamespace(namespace),
"Missing database in namespace: %s", namespace);
String database = namespace.level(0);
@@ -69,10 +79,10 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
.collect(Collectors.toList());
} catch (UnknownDBException e) {
- throw new NotFoundException(e, "Unknown namespace " + namespace.toString());
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
} catch (TException e) {
- throw new RuntimeException("Failed to list all tables under namespace " + namespace.toString(), e);
+ throw new RuntimeException("Failed to list all tables under namespace " + namespace, e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -119,7 +129,7 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
return false;
} catch (TException e) {
- throw new RuntimeException("Failed to drop " + identifier.toString(), e);
+ throw new RuntimeException("Failed to drop " + identifier, e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -155,7 +165,7 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
throw new org.apache.iceberg.exceptions.AlreadyExistsException("Table already exists: %s", to);
} catch (TException e) {
- throw new RuntimeException("Failed to rename " + from.toString() + " to " + to.toString(), e);
+ throw new RuntimeException("Failed to rename " + from + " to " + to, e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -164,10 +174,167 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
}
@Override
+ public void createNamespace(Namespace namespace, Map<String, String> meta) {
+ Preconditions.checkArgument(
+ !namespace.isEmpty(),
+ "Cannot create namespace with invalid name: %s", namespace);
+ Preconditions.checkArgument(isValidateNamespace(namespace),
+ "Cannot support multi part namespace in Hive MetaStore: %s", namespace);
+
+ try {
+ clients.run(client -> {
+ client.createDatabase(convertToDatabase(namespace, meta));
+ return null;
+ });
+
+ } catch (AlreadyExistsException e) {
+ throw new org.apache.iceberg.exceptions.AlreadyExistsException(e, "Namespace '%s' already exists!",
+ namespace);
+
+ } catch (TException e) {
+ throw new RuntimeException("Failed to create namespace " + namespace + " in Hive MataStore", e);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(
+ "Interrupted in call to createDatabase(name) " + namespace + " in Hive MataStore", e);
+ }
+ }
+
+ @Override
+ public List<Namespace> listNamespaces(Namespace namespace) {
+ if (!isValidateNamespace(namespace) && !namespace.isEmpty()) {
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
+ }
+ if (!namespace.isEmpty()) {
+ return ImmutableList.of();
+ }
+ try {
+ return clients.run(
+ HiveMetaStoreClient::getAllDatabases)
+ .stream()
+ .map(Namespace::of)
+ .collect(Collectors.toList());
+
+ } catch (TException e) {
+ throw new RuntimeException("Failed to list all namespace: " + namespace + " in Hive MataStore", e);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(
+ "Interrupted in call to getAllDatabases() " + namespace + " in Hive MataStore", e);
+ }
+ }
+
+ @Override
+ public boolean dropNamespace(Namespace namespace) {
+ if (!isValidateNamespace(namespace)) {
+ return false;
+ }
+
+ try {
+ clients.run(client -> {
+ client.dropDatabase(namespace.level(0),
+ false /* deleteData */,
+ false /* ignoreUnknownDb */,
+ false /* cascade */);
+ return null;
+ });
+
+ return true;
+
+ } catch (InvalidOperationException e) {
+ throw new NamespaceNotEmptyException("Namespace " + namespace + " is not empty. One or more tables exist.", e);
+
+ } catch (NoSuchObjectException e) {
+ return false;
+
+ } catch (TException e) {
+ throw new RuntimeException("Failed to drop namespace " + namespace + " in Hive MataStore", e);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(
+ "Interrupted in call to drop dropDatabase(name) " + namespace + " in Hive MataStore", e);
+ }
+ }
+
+ @Override
+ public boolean setProperties(Namespace namespace, Map<String, String> properties) {
+ Map<String, String> parameter = Maps.newHashMap();
+
+ parameter.putAll(loadNamespaceMetadata(namespace));
+ parameter.putAll(properties);
+ Database database = convertToDatabase(namespace, parameter);
+
+ return alterHiveDataBase(namespace, database);
+ }
+
+ @Override
+ public boolean removeProperties(Namespace namespace, Set<String> properties) {
+ Map<String, String> parameter = Maps.newHashMap();
+
+ parameter.putAll(loadNamespaceMetadata(namespace));
+ properties.forEach(key -> parameter.put(key, null));
+ Database database = convertToDatabase(namespace, parameter);
+
+ return alterHiveDataBase(namespace, database);
+ }
+
+ private boolean alterHiveDataBase(Namespace namespace, Database database) {
+ try {
+ clients.run(client -> {
+ client.alterDatabase(namespace.level(0), database);
+ return null;
+ });
+
+ return true;
+
+ } catch (NoSuchObjectException | UnknownDBException e) {
+ throw new NoSuchNamespaceException(e, "Namespace does not exist: %s", namespace);
+
+ } catch (TException e) {
+ throw new RuntimeException(
+ "Failed to list namespace under namespace: " + namespace + " in Hive MataStore", e);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Interrupted in call to getDatabase(name) " + namespace + " in Hive MataStore", e);
+ }
+ }
+
+ @Override
+ public Map<String, String> loadNamespaceMetadata(Namespace namespace) {
+ if (!isValidateNamespace(namespace)) {
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
+ }
+
+ try {
+ Database database = clients.run(client -> client.getDatabase(namespace.level(0)));
+ return convertToMetadata(database);
+
+ } catch (NoSuchObjectException | UnknownDBException e) {
+ throw new NoSuchNamespaceException(e, "Namespace does not exist: %s", namespace);
+
+ } catch (TException e) {
+ throw new RuntimeException("Failed to list namespace under namespace: " + namespace + " in Hive MataStore", e);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(
+ "Interrupted in call to getDatabase(name) " + namespace + " in Hive MataStore", e);
+ }
+ }
+
+ @Override
protected boolean isValidIdentifier(TableIdentifier tableIdentifier) {
return tableIdentifier.namespace().levels().length == 1;
}
+ private boolean isValidateNamespace(Namespace namespace) {
+ return namespace.levels().length == 1;
+ }
+
@Override
public TableOperations newTableOps(TableIdentifier tableIdentifier) {
String dbName = tableIdentifier.namespace().level(0);
@@ -188,6 +355,46 @@ public class HiveCatalog extends BaseMetastoreCatalog implements Closeable {
tableIdentifier.name());
}
+ private Map<String, String> convertToMetadata(Database database) {
+
+ Map<String, String> meta = Maps.newHashMap();
+
+ meta.putAll(database.getParameters());
+ meta.put("location", database.getLocationUri());
+ meta.put("comment", database.getDescription());
+
+ return meta;
+ }
+
+ Database convertToDatabase(Namespace namespace, Map<String, String> meta) {
+ String warehouseLocation = conf.get("hive.metastore.warehouse.dir");
+
+ if (!isValidateNamespace(namespace)) {
+ throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace);
+ }
+
+ Database database = new Database();
+ Map<String, String> parameter = Maps.newHashMap();
+
+ database.setName(namespace.level(0));
+ database.setLocationUri(new Path(warehouseLocation, namespace.level(0)).toString() + ".db");
+
+ meta.forEach((key, value) -> {
+ if (key.equals("comment")) {
+ database.setDescription(value);
+ } else if (key.equals("location")) {
+ database.setLocationUri(value);
+ } else {
+ if (value != null) {
+ parameter.put(key, value);
+ }
+ }
+ });
+ database.setParameters(parameter);
+
+ return database;
+ }
+
@Override
public void close() {
if (!closed) {
diff --git a/hive/src/test/java/org/apache/iceberg/hive/TestHiveCatalog.java b/hive/src/test/java/org/apache/iceberg/hive/TestHiveCatalog.java
new file mode 100644
index 0000000..744295c
--- /dev/null
+++ b/hive/src/test/java/org/apache/iceberg/hive/TestHiveCatalog.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iceberg.hive;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.iceberg.AssertHelpers;
+import org.apache.iceberg.Schema;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.AlreadyExistsException;
+import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
+import org.apache.iceberg.exceptions.NoSuchNamespaceException;
+import org.apache.iceberg.types.Types;
+import org.apache.thrift.TException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.apache.iceberg.types.Types.NestedField.required;
+
+public class TestHiveCatalog extends HiveMetastoreTest {
+ private static final String hiveLocalDir = "file:/tmp/hive/" + UUID.randomUUID().toString();
+ private static ImmutableMap meta = ImmutableMap.of(
+ "owner", "apache",
+ "group", "iceberg",
+ "comment", "iceberg hiveCatalog test");
+
+ @Test
+ public void testCreateNamespace() throws TException {
+ Namespace namespace1 = Namespace.of("noLocation");
+ catalog.createNamespace(namespace1, meta);
+ Database database1 = metastoreClient.getDatabase(namespace1.toString());
+
+ Assert.assertTrue(database1.getParameters().get("owner").equals("apache"));
+ Assert.assertTrue(database1.getParameters().get("group").equals("iceberg"));
+
+ Assert.assertEquals("There no same location for db and namespace",
+ database1.getLocationUri(), defaultUri(namespace1));
+
+ AssertHelpers.assertThrows("Should fail to create when namespace already exist " + namespace1,
+ AlreadyExistsException.class, "Namespace '" + namespace1 + "' already exists!", () -> {
+ catalog.createNamespace(namespace1);
+ });
+ ImmutableMap newMeta = ImmutableMap.<String, String>builder()
+ .putAll(meta)
+ .put("location", hiveLocalDir)
+ .build();
+ Namespace namespace2 = Namespace.of("haveLocation");
+
+ catalog.createNamespace(namespace2, newMeta);
+ Database database2 = metastoreClient.getDatabase(namespace2.toString());
+ Assert.assertEquals("There no same location for db and namespace",
+ database2.getLocationUri(), hiveLocalDir);
+ }
+
+ @Test
+ public void testListNamespace() throws TException {
+ List<Namespace> namespaces;
+ Namespace namespace1 = Namespace.of("dbname1");
+ catalog.createNamespace(namespace1, meta);
+ namespaces = catalog.listNamespaces(namespace1);
+ Assert.assertTrue("Hive db not hive the namespace 'dbname1'", namespaces.isEmpty());
+
+ Namespace namespace2 = Namespace.of("dbname2");
+ catalog.createNamespace(namespace2, meta);
+ namespaces = catalog.listNamespaces();
+
+ Assert.assertTrue("Hive db not hive the namespace 'dbname2'", namespaces.contains(namespace2));
+ }
+
+ @Test
+ public void testLoadNamespaceMeta() throws TException {
+ Namespace namespace = Namespace.of("dbname_load");
+
+ catalog.createNamespace(namespace, meta);
+
+ Map<String, String> nameMata = catalog.loadNamespaceMetadata(namespace);
+ Assert.assertTrue(nameMata.get("owner").equals("apache"));
+ Assert.assertTrue(nameMata.get("group").equals("iceberg"));
+ Assert.assertEquals("There no same location for db and namespace",
+ nameMata.get("location"), catalog.convertToDatabase(namespace, meta).getLocationUri());
+ }
+
+ @Test
+ public void testNamespaceExists() throws TException {
+ Namespace namespace = Namespace.of("dbname_exists");
+
+ catalog.createNamespace(namespace, meta);
+
+ Assert.assertTrue("Should true to namespace exist",
+ catalog.namespaceExists(namespace));
+ Assert.assertTrue("Should false to namespace doesn't exist",
+ !catalog.namespaceExists(Namespace.of("db2", "db2", "ns2")));
+ }
+
+ @Test
+ public void testSetNamespaceProperties() throws TException {
+ Namespace namespace = Namespace.of("dbname_set");
+
+ catalog.createNamespace(namespace, meta);
+ catalog.setProperties(namespace,
+ ImmutableMap.of(
+ "owner", "alter_apache",
+ "test", "test",
+ "location", "file:/data/tmp",
+ "comment", "iceberg test")
+ );
+
+ Database database = metastoreClient.getDatabase(namespace.level(0));
+ Assert.assertEquals(database.getParameters().get("owner"), "alter_apache");
+ Assert.assertEquals(database.getParameters().get("test"), "test");
+ Assert.assertEquals(database.getParameters().get("group"), "iceberg");
+ AssertHelpers.assertThrows("Should fail to namespace not exist" + namespace,
+ NoSuchNamespaceException.class, "Namespace does not exist: ", () -> {
+ catalog.setProperties(Namespace.of("db2", "db2", "ns2"), meta);
+ });
+ }
+
+ @Test
+ public void testRemoveNamespaceProperties() throws TException {
+ Namespace namespace = Namespace.of("dbname_remove");
+
+ catalog.createNamespace(namespace, meta);
+
+ catalog.removeProperties(namespace, ImmutableSet.of("comment", "owner"));
+
+ Database database = metastoreClient.getDatabase(namespace.level(0));
+
+ Assert.assertEquals(database.getParameters().get("owner"), null);
+ Assert.assertEquals(database.getParameters().get("group"), "iceberg");
+ AssertHelpers.assertThrows("Should fail to namespace not exist" + namespace,
+ NoSuchNamespaceException.class, "Namespace does not exist: ", () -> {
+ catalog.removeProperties(Namespace.of("db2", "db2", "ns2"), ImmutableSet.of("comment", "owner"));
+ });
+ }
+
+ @Test
+ public void testDropNamespace() throws TException {
+ Namespace namespace = Namespace.of("dbname_drop");
+ TableIdentifier identifier = TableIdentifier.of(namespace, "table");
+ Schema schema = new Schema(Types.StructType.of(
+ required(1, "id", Types.LongType.get())).fields());
+
+ catalog.createNamespace(namespace, meta);
+ catalog.createTable(identifier, schema);
+ Map<String, String> nameMata = catalog.loadNamespaceMetadata(namespace);
+ Assert.assertTrue(nameMata.get("owner").equals("apache"));
+ Assert.assertTrue(nameMata.get("group").equals("iceberg"));
+
+ AssertHelpers.assertThrows("Should fail to drop namespace is not empty" + namespace,
+ NamespaceNotEmptyException.class,
+ "Namespace dbname_drop is not empty. One or more tables exist.", () -> {
+ catalog.dropNamespace(namespace);
+ });
+ Assert.assertTrue(catalog.dropTable(identifier, true));
+ Assert.assertTrue("Should fail to drop namespace if it is not empty",
+ catalog.dropNamespace(namespace));
+ Assert.assertFalse("Should fail to drop when namespace doesn't exist",
+ catalog.dropNamespace(Namespace.of("db.ns1")));
+ AssertHelpers.assertThrows("Should fail to drop namespace exist" + namespace,
+ NoSuchNamespaceException.class, "Namespace does not exist: ", () -> {
+ catalog.loadNamespaceMetadata(namespace);
+ });
+ }
+
+ private String defaultUri(Namespace namespace) throws TException {
+ return metastoreClient.getConfigValue(
+ "hive.metastore.warehouse.dir", "") + "/" + namespace.level(0) + ".db";
+ }
+
+}