From 3053540c5dc7199d84a6a9bfbe0d3e37efe87990 Mon Sep 17 00:00:00 2001 From: Eduard Tudenhoefner Date: Thu, 12 Dec 2024 18:07:04 +0100 Subject: [PATCH] Core: Use HEAD request to check if view exists (#11760) --- .../apache/iceberg/rest/CatalogHandlers.java | 6 ++++ .../iceberg/rest/RESTSessionCatalog.java | 12 +++++++ .../iceberg/rest/RESTCatalogAdapter.java | 10 ++++++ .../iceberg/rest/TestRESTViewCatalog.java | 32 +++++++++++++++++++ 4 files changed, 60 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 563853e3f033..344308e4caf0 100644 --- a/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java +++ b/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java @@ -507,6 +507,12 @@ private static LoadViewResponse viewResponse(View view) { .build(); } + public static void viewExists(ViewCatalog catalog, TableIdentifier viewIdentifier) { + if (!catalog.viewExists(viewIdentifier)) { + throw new NoSuchViewException("View does not exist: %s", viewIdentifier); + } + } + public static LoadViewResponse loadView(ViewCatalog catalog, TableIdentifier viewIdentifier) { View view = catalog.loadView(viewIdentifier); return viewResponse(view); 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 5c6fc49984a5..8e8bd2bb70c7 100644 --- a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java +++ b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java @@ -1212,6 +1212,18 @@ public List listViews(SessionContext context, Namespace namespa return views.build(); } + @Override + public boolean viewExists(SessionContext context, TableIdentifier identifier) { + checkViewIdentifierIsValid(identifier); + + try { + client.head(paths.view(identifier), headers(context), ErrorHandlers.viewErrorHandler()); + return true; + } catch (NoSuchViewException e) { + return false; + } + } + @Override public View loadView(SessionContext context, TableIdentifier identifier) { Endpoint.check( 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 87b693e206ae..94dd45d4f23d 100644 --- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java +++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java @@ -156,6 +156,7 @@ enum Route { CommitTransactionRequest.class, null), LIST_VIEWS(HTTPMethod.GET, ResourcePaths.V1_VIEWS, null, ListTablesResponse.class), + VIEW_EXISTS(HTTPMethod.HEAD, ResourcePaths.V1_VIEW), LOAD_VIEW(HTTPMethod.GET, ResourcePaths.V1_VIEW, null, LoadViewResponse.class), CREATE_VIEW( HTTPMethod.POST, ResourcePaths.V1_VIEWS, CreateViewRequest.class, LoadViewResponse.class), @@ -471,6 +472,15 @@ public T handleRequest( break; } + case VIEW_EXISTS: + { + if (null != asViewCatalog) { + CatalogHandlers.viewExists(asViewCatalog, viewIdentFromPathVars(vars)); + return null; + } + break; + } + case LOAD_VIEW: { if (null != asViewCatalog) { diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java b/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java index 85ccdc8f5ddd..40a206896be4 100644 --- a/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java +++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java @@ -50,6 +50,7 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -226,6 +227,37 @@ public void testPaginationForListViews(int numberOfItems) { eq(ListTablesResponse.class)); } + @Test + public void viewExistsViaHEADRequest() { + RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); + RESTCatalog catalog = + new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter); + catalog.initialize("test", ImmutableMap.of()); + + catalog.createNamespace(Namespace.of("ns")); + + assertThat(catalog.viewExists(TableIdentifier.of("ns", "view"))).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/ns/views/view"), + any(), + any(), + any(), + any(), + any()); + } + @Override protected RESTCatalog catalog() { return restCatalog;