This is an automated email from the ASF dual-hosted git repository.

etudenhoefner 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 1e126e24e2 Core: Use HEAD request to check if namespace exists (#11761)
1e126e24e2 is described below

commit 1e126e24e2c4b639e2509d3f8194e5ceaace6d56
Author: Eduard Tudenhoefner <[email protected]>
AuthorDate: Thu Dec 12 18:17:14 2024 +0100

    Core: Use HEAD request to check if namespace exists (#11761)
---
 .../org/apache/iceberg/rest/CatalogHandlers.java   |  6 +++++
 .../java/org/apache/iceberg/rest/RESTCatalog.java  |  5 ++++
 .../apache/iceberg/rest/RESTSessionCatalog.java    | 13 ++++++++++
 .../apache/iceberg/rest/RESTCatalogAdapter.java    |  8 ++++++
 .../org/apache/iceberg/rest/TestRESTCatalog.java   | 29 ++++++++++++++++++++++
 5 files changed, 61 insertions(+)

diff --git a/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java 
b/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java
index 344308e4ca..aeb3108547 100644
--- a/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java
+++ b/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java
@@ -163,6 +163,12 @@ public class CatalogHandlers {
         .build();
   }
 
+  public static void namespaceExists(SupportsNamespaces catalog, Namespace 
namespace) {
+    if (!catalog.namespaceExists(namespace)) {
+      throw new NoSuchNamespaceException("Namespace does not exist: %s", 
namespace);
+    }
+  }
+
   public static GetNamespaceResponse loadNamespace(
       SupportsNamespaces catalog, Namespace namespace) {
     Map<String, String> properties = catalog.loadNamespaceMetadata(namespace);
diff --git a/core/src/main/java/org/apache/iceberg/rest/RESTCatalog.java 
b/core/src/main/java/org/apache/iceberg/rest/RESTCatalog.java
index 61a7eca272..73a53de906 100644
--- a/core/src/main/java/org/apache/iceberg/rest/RESTCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/rest/RESTCatalog.java
@@ -228,6 +228,11 @@ public class RESTCatalog
     return nsDelegate.listNamespaces(ns);
   }
 
+  @Override
+  public boolean namespaceExists(Namespace namespace) {
+    return nsDelegate.namespaceExists(namespace);
+  }
+
   @Override
   public Map<String, String> loadNamespaceMetadata(Namespace ns) throws 
NoSuchNamespaceException {
     return nsDelegate.loadNamespaceMetadata(ns);
diff --git a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java 
b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
index 8e8bd2bb70..37b70aff3d 100644
--- a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
@@ -652,6 +652,19 @@ public class RESTSessionCatalog extends 
BaseViewSessionCatalog
     return namespaces.build();
   }
 
+  @Override
+  public boolean namespaceExists(SessionContext context, Namespace namespace) {
+    checkNamespaceIsValid(namespace);
+
+    try {
+      client.head(
+          paths.namespace(namespace), headers(context), 
ErrorHandlers.namespaceErrorHandler());
+      return true;
+    } catch (NoSuchNamespaceException e) {
+      return false;
+    }
+  }
+
   @Override
   public Map<String, String> loadNamespaceMetadata(SessionContext context, 
Namespace ns) {
     Endpoint.check(endpoints, Endpoint.V1_LOAD_NAMESPACE);
diff --git a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java 
b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
index 94dd45d4f2..2fb4defd12 100644
--- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
+++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
@@ -124,6 +124,7 @@ public class RESTCatalogAdapter implements RESTClient {
         ResourcePaths.V1_NAMESPACES,
         CreateNamespaceRequest.class,
         CreateNamespaceResponse.class),
+    NAMESPACE_EXISTS(HTTPMethod.HEAD, ResourcePaths.V1_NAMESPACE),
     LOAD_NAMESPACE(HTTPMethod.GET, ResourcePaths.V1_NAMESPACE, null, 
GetNamespaceResponse.class),
     DROP_NAMESPACE(HTTPMethod.DELETE, ResourcePaths.V1_NAMESPACE),
     UPDATE_NAMESPACE(
@@ -331,6 +332,13 @@ public class RESTCatalogAdapter implements RESTClient {
         }
         break;
 
+      case NAMESPACE_EXISTS:
+        if (asNamespaceCatalog != null) {
+          CatalogHandlers.namespaceExists(asNamespaceCatalog, 
namespaceFromPathVars(vars));
+          return null;
+        }
+        break;
+
       case LOAD_NAMESPACE:
         if (asNamespaceCatalog != null) {
           Namespace namespace = namespaceFromPathVars(vars);
diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java 
b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
index 973e394b30..768d6c3777 100644
--- a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
@@ -2634,6 +2634,35 @@ public class TestRESTCatalog extends 
CatalogTests<RESTCatalog> {
         .isTrue();
   }
 
+  @Test
+  public void testNamespaceExistsViaHEADRequest() {
+    RESTCatalogAdapter adapter = Mockito.spy(new 
RESTCatalogAdapter(backendCatalog));
+    RESTCatalog catalog =
+        new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) 
-> adapter);
+    catalog.initialize("test", ImmutableMap.of());
+
+    
assertThat(catalog.namespaceExists(Namespace.of("non-existing"))).isFalse();
+
+    Mockito.verify(adapter)
+        .execute(
+            eq(HTTPMethod.GET),
+            eq("v1/config"),
+            any(),
+            any(),
+            eq(ConfigResponse.class),
+            any(),
+            any());
+    Mockito.verify(adapter)
+        .execute(
+            eq(HTTPMethod.HEAD),
+            eq("v1/namespaces/non-existing"),
+            any(),
+            any(),
+            any(),
+            any(),
+            any());
+  }
+
   private RESTCatalog catalog(RESTCatalogAdapter adapter) {
     RESTCatalog catalog =
         new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) 
-> adapter);

Reply via email to