Skip to content

Commit

Permalink
REST: Use HEAD request to check table existence
Browse files Browse the repository at this point in the history
  • Loading branch information
ebyhr committed Sep 24, 2024
1 parent 72fd9ab commit 8a775da
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,13 @@ public static void purgeTable(Catalog catalog, TableIdentifier ident) {
}
}

public static void tableExists(Catalog catalog, TableIdentifier ident) {
boolean exists = catalog.tableExists(ident);
if (!exists) {
throw new NoSuchTableException("Table does not exist: %s", ident);
}
}

public static LoadTableResponse loadTable(Catalog catalog, TableIdentifier ident) {
Table table = catalog.loadTable(ident);

Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,18 @@ public void renameTable(SessionContext context, TableIdentifier from, TableIdent
client.post(paths.rename(), request, null, headers(context), ErrorHandlers.tableErrorHandler());
}

@Override
public boolean tableExists(SessionContext context, TableIdentifier identifier) {
checkIdentifierIsValid(identifier);

try {
client.head(paths.table(identifier), headers(context), ErrorHandlers.tableErrorHandler());
return true;
} catch (NoSuchTableException e) {
return false;
}
}

private LoadTableResponse loadInternal(
SessionContext context, TableIdentifier identifier, SnapshotMode mode) {
Endpoint.check(endpoints, Endpoint.V1_LOAD_TABLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ enum Route {
ResourcePaths.V1_TABLES,
CreateTableRequest.class,
LoadTableResponse.class),
TABLE_EXISTS(HTTPMethod.HEAD, "v1/namespaces/{namespace}/tables/{name}"),
LOAD_TABLE(HTTPMethod.GET, ResourcePaths.V1_TABLE, null, LoadTableResponse.class),
REGISTER_TABLE(
HTTPMethod.POST,
Expand Down Expand Up @@ -388,6 +389,13 @@ public <T extends RESTResponse> T handleRequest(
return null;
}

case TABLE_EXISTS:
{
TableIdentifier ident = identFromPathVars(vars);
CatalogHandlers.tableExists(catalog, ident);
return null;
}

case LOAD_TABLE:
{
TableIdentifier ident = tableIdentFromPathVars(vars);
Expand Down
54 changes: 27 additions & 27 deletions core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,11 @@ public void testCatalogBasicBearerToken() {
any());
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(catalogHeaders),
any());
}
Expand Down Expand Up @@ -387,11 +387,11 @@ public void testCatalogCredentialNoOauth2ServerUri() {
// use the catalog token for all interactions
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(catalogHeaders),
any());
}
Expand Down Expand Up @@ -442,11 +442,11 @@ public void testCatalogCredential(String oauth2ServerUri) {
// use the catalog token for all interactions
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(catalogHeaders),
any());
}
Expand Down Expand Up @@ -500,14 +500,14 @@ public void testCatalogBearerTokenWithClientCredential(String oauth2ServerUri) {
eq(OAuthTokenResponse.class),
eq(catalogHeaders),
any());
// use the context token for table load
// use the context token for table existence check
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(contextHeaders),
any());
}
Expand Down Expand Up @@ -573,14 +573,14 @@ public void testCatalogCredentialWithClientCredential(String oauth2ServerUri) {
eq(OAuthTokenResponse.class),
eq(catalogHeaders),
any());
// use the context token for table load
// use the context token for table existence check
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(contextHeaders),
any());
}
Expand Down Expand Up @@ -648,14 +648,14 @@ public void testCatalogBearerTokenAndCredentialWithClientCredential(String oauth
eq(OAuthTokenResponse.class),
eq(catalogHeaders),
any());
// use the context token for table load
// use the context token for table existence check
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(contextHeaders),
any());
}
Expand Down Expand Up @@ -839,11 +839,11 @@ private void testClientAuth(
}
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(expectedHeaders),
any());
if (!optionalOAuthParams.isEmpty()) {
Expand Down Expand Up @@ -1606,18 +1606,18 @@ public void testCatalogRefreshedTokenIsUsed(String oauth2ServerUri) {
eq(catalogHeaders),
any());

// use the refreshed context token for table load
// use the refreshed context token for table existence check
Map<String, String> refreshedCatalogHeader =
ImmutableMap.of(
"Authorization",
"Bearer token-exchange-token:sub=client-credentials-token:sub=catalog");
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(refreshedCatalogHeader),
any());
});
Expand Down Expand Up @@ -1784,11 +1784,11 @@ public void testCatalogExpiredBearerTokenIsRefreshedWithCredential(String oauth2

Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(ImmutableMap.of("Authorization", "Bearer token-exchange-token:sub=" + token)),
any());
}
Expand Down Expand Up @@ -1826,11 +1826,11 @@ public void testCatalogValidBearerTokenIsNotRefreshed() {

Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(OAuth2Util.authHeaders(token)),
any());
}
Expand Down Expand Up @@ -1961,18 +1961,18 @@ public void testCatalogTokenRefreshFailsAndUsesCredentialForRefresh(String oauth
eq(basicHeaders),
any());

// use the refreshed context token for table load
// use the refreshed context token for table existence check
Map<String, String> refreshedCatalogHeader =
ImmutableMap.of(
"Authorization",
"Bearer token-exchange-token:sub=client-credentials-token:sub=catalog");
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.GET),
eq(HTTPMethod.HEAD),
eq("v1/namespaces/ns/tables/table"),
any(),
any(),
eq(LoadTableResponse.class),
any(),
eq(refreshedCatalogHeader),
any());
});
Expand Down

0 comments on commit 8a775da

Please sign in to comment.