This is an automated email from the ASF dual-hosted git repository.
gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git
The following commit(s) were added to refs/heads/main by this push:
new 37430083 feat(rest): implement load table and drop table (#438)
37430083 is described below
commit 3743008383e9407ec2fab10eb0d867d6108cd285
Author: Feiyang Li <[email protected]>
AuthorDate: Fri Jan 2 17:08:00 2026 +0800
feat(rest): implement load table and drop table (#438)
Co-authored-by: Gang Wu <[email protected]>
---
src/iceberg/catalog/rest/http_client.cc | 20 ++---
src/iceberg/catalog/rest/http_client.h | 5 +-
src/iceberg/catalog/rest/rest_catalog.cc | 129 ++++++++++++++++++++-----------
src/iceberg/catalog/rest/rest_catalog.h | 2 +
src/iceberg/catalog/rest/rest_util.cc | 8 --
src/iceberg/catalog/rest/rest_util.h | 8 --
src/iceberg/test/rest_catalog_test.cc | 120 +++++++++++++++++++++++-----
src/iceberg/test/rest_util_test.cc | 16 ----
8 files changed, 199 insertions(+), 109 deletions(-)
diff --git a/src/iceberg/catalog/rest/http_client.cc
b/src/iceberg/catalog/rest/http_client.cc
index d1138b78..7804ebd5 100644
--- a/src/iceberg/catalog/rest/http_client.cc
+++ b/src/iceberg/catalog/rest/http_client.cc
@@ -135,13 +135,12 @@ Status HandleFailureResponse(const cpr::Response&
response,
} // namespace
void HttpClient::PrepareSession(
- const std::string& path,
- const std::unordered_map<std::string, std::string>& request_headers,
- const std::unordered_map<std::string, std::string>& params) {
+ const std::string& path, const std::unordered_map<std::string,
std::string>& params,
+ const std::unordered_map<std::string, std::string>& headers) {
session_->SetUrl(cpr::Url{path});
session_->SetParameters(GetParameters(params));
session_->RemoveContent();
- auto final_headers = MergeHeaders(default_headers_, request_headers);
+ auto final_headers = MergeHeaders(default_headers_, headers);
session_->SetHeader(final_headers);
}
@@ -164,7 +163,7 @@ Result<HttpResponse> HttpClient::Get(
cpr::Response response;
{
std::lock_guard guard(session_mutex_);
- PrepareSession(path, headers, params);
+ PrepareSession(path, params, headers);
response = session_->Get();
}
@@ -181,7 +180,7 @@ Result<HttpResponse> HttpClient::Post(
cpr::Response response;
{
std::lock_guard guard(session_mutex_);
- PrepareSession(path, headers);
+ PrepareSession(path, /*params=*/{}, headers);
session_->SetBody(cpr::Body{body});
response = session_->Post();
}
@@ -206,7 +205,7 @@ Result<HttpResponse> HttpClient::PostForm(
auto form_headers = headers;
form_headers[kHeaderContentType] = kMimeTypeFormUrlEncoded;
- PrepareSession(path, form_headers);
+ PrepareSession(path, /*params=*/{}, form_headers);
std::vector<cpr::Pair> pair_list;
pair_list.reserve(form_data.size());
for (const auto& [key, val] : form_data) {
@@ -229,7 +228,7 @@ Result<HttpResponse> HttpClient::Head(
cpr::Response response;
{
std::lock_guard guard(session_mutex_);
- PrepareSession(path, headers);
+ PrepareSession(path, /*params=*/{}, headers);
response = session_->Head();
}
@@ -240,12 +239,13 @@ Result<HttpResponse> HttpClient::Head(
}
Result<HttpResponse> HttpClient::Delete(
- const std::string& path, const std::unordered_map<std::string,
std::string>& headers,
+ const std::string& path, const std::unordered_map<std::string,
std::string>& params,
+ const std::unordered_map<std::string, std::string>& headers,
const ErrorHandler& error_handler) {
cpr::Response response;
{
std::lock_guard guard(session_mutex_);
- PrepareSession(path, headers);
+ PrepareSession(path, params, headers);
response = session_->Delete();
}
diff --git a/src/iceberg/catalog/rest/http_client.h
b/src/iceberg/catalog/rest/http_client.h
index 56c9f290..a1401b63 100644
--- a/src/iceberg/catalog/rest/http_client.h
+++ b/src/iceberg/catalog/rest/http_client.h
@@ -104,13 +104,14 @@ class ICEBERG_REST_EXPORT HttpClient {
/// \brief Sends a DELETE request.
Result<HttpResponse> Delete(const std::string& path,
+ const std::unordered_map<std::string,
std::string>& params,
const std::unordered_map<std::string,
std::string>& headers,
const ErrorHandler& error_handler);
private:
void PrepareSession(const std::string& path,
- const std::unordered_map<std::string, std::string>&
request_headers,
- const std::unordered_map<std::string, std::string>&
params = {});
+ const std::unordered_map<std::string, std::string>&
params,
+ const std::unordered_map<std::string, std::string>&
headers);
std::unordered_map<std::string, std::string> default_headers_;
diff --git a/src/iceberg/catalog/rest/rest_catalog.cc
b/src/iceberg/catalog/rest/rest_catalog.cc
index eeffffd2..2c28691b 100644
--- a/src/iceberg/catalog/rest/rest_catalog.cc
+++ b/src/iceberg/catalog/rest/rest_catalog.cc
@@ -74,6 +74,33 @@ Result<CatalogConfig> FetchServerConfig(const ResourcePaths&
paths,
return CatalogConfigFromJson(json);
}
+#define ICEBERG_ENDPOINT_CHECK(endpoints, endpoint) \
+ do { \
+ if (!endpoints.contains(endpoint)) { \
+ return NotSupported("Not supported endpoint: {}", endpoint.ToString()); \
+ } \
+ } while (0)
+
+Result<bool> CaptureNoSuchObject(const auto& status, ErrorKind kind) {
+ ICEBERG_DCHECK(kind == ErrorKind::kNoSuchTable || kind ==
ErrorKind::kNoSuchNamespace,
+ "Invalid kind for CaptureNoSuchObject");
+ if (status.has_value()) {
+ return true;
+ }
+ if (status.error().kind == kind) {
+ return false;
+ }
+ return std::unexpected(status.error());
+}
+
+Result<bool> CaptureNoSuchTable(const auto& status) {
+ return CaptureNoSuchObject(status, ErrorKind::kNoSuchTable);
+}
+
+Result<bool> CaptureNoSuchNamespace(const auto& status) {
+ return CaptureNoSuchObject(status, ErrorKind::kNoSuchNamespace);
+}
+
} // namespace
RestCatalog::~RestCatalog() = default;
@@ -126,9 +153,7 @@
RestCatalog::RestCatalog(std::unique_ptr<RestCatalogProperties> config,
std::string_view RestCatalog::name() const { return name_; }
Result<std::vector<Namespace>> RestCatalog::ListNamespaces(const Namespace&
ns) const {
- ICEBERG_RETURN_UNEXPECTED(
- CheckEndpoint(supported_endpoints_, Endpoint::ListNamespaces()));
-
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::ListNamespaces());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespaces());
std::vector<Namespace> result;
std::string next_token;
@@ -157,9 +182,7 @@ Result<std::vector<Namespace>>
RestCatalog::ListNamespaces(const Namespace& ns)
Status RestCatalog::CreateNamespace(
const Namespace& ns, const std::unordered_map<std::string, std::string>&
properties) {
- ICEBERG_RETURN_UNEXPECTED(
- CheckEndpoint(supported_endpoints_, Endpoint::CreateNamespace()));
-
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::CreateNamespace());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespaces());
CreateNamespaceRequest request{.namespace_ = ns, .properties = properties};
ICEBERG_ASSIGN_OR_RAISE(auto json_request, ToJsonString(ToJson(request)));
@@ -173,9 +196,7 @@ Status RestCatalog::CreateNamespace(
Result<std::unordered_map<std::string, std::string>>
RestCatalog::GetNamespaceProperties(
const Namespace& ns) const {
- ICEBERG_RETURN_UNEXPECTED(
- CheckEndpoint(supported_endpoints_, Endpoint::GetNamespaceProperties()));
-
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_,
Endpoint::GetNamespaceProperties());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns));
ICEBERG_ASSIGN_OR_RAISE(const auto response,
client_->Get(path, /*params=*/{}, /*headers=*/{},
@@ -186,48 +207,29 @@ Result<std::unordered_map<std::string, std::string>>
RestCatalog::GetNamespacePr
}
Status RestCatalog::DropNamespace(const Namespace& ns) {
- ICEBERG_RETURN_UNEXPECTED(
- CheckEndpoint(supported_endpoints_, Endpoint::DropNamespace()));
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::DropNamespace());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns));
- ICEBERG_ASSIGN_OR_RAISE(
- const auto response,
- client_->Delete(path, /*headers=*/{},
*DropNamespaceErrorHandler::Instance()));
+ ICEBERG_ASSIGN_OR_RAISE(const auto response,
+ client_->Delete(path, /*params=*/{}, /*headers=*/{},
+
*DropNamespaceErrorHandler::Instance()));
return {};
}
Result<bool> RestCatalog::NamespaceExists(const Namespace& ns) const {
- auto check = CheckEndpoint(supported_endpoints_,
Endpoint::NamespaceExists());
- if (!check.has_value()) {
+ if (!supported_endpoints_.contains(Endpoint::NamespaceExists())) {
// Fall back to GetNamespaceProperties
- auto result = GetNamespaceProperties(ns);
- if (!result.has_value() && result.error().kind ==
ErrorKind::kNoSuchNamespace) {
- return false;
- }
- ICEBERG_RETURN_UNEXPECTED(result);
- // GET succeeded, namespace exists
- return true;
+ return CaptureNoSuchNamespace(GetNamespaceProperties(ns));
}
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns));
- auto response_or_error =
- client_->Head(path, /*headers=*/{}, *NamespaceErrorHandler::Instance());
- if (!response_or_error.has_value()) {
- const auto& error = response_or_error.error();
- // catch NoSuchNamespaceException/404 and return false
- if (error.kind == ErrorKind::kNoSuchNamespace) {
- return false;
- }
- ICEBERG_RETURN_UNEXPECTED(response_or_error);
- }
- return true;
+ return CaptureNoSuchNamespace(
+ client_->Head(path, /*headers=*/{}, *NamespaceErrorHandler::Instance()));
}
Status RestCatalog::UpdateNamespaceProperties(
const Namespace& ns, const std::unordered_map<std::string, std::string>&
updates,
const std::unordered_set<std::string>& removals) {
- ICEBERG_RETURN_UNEXPECTED(
- CheckEndpoint(supported_endpoints_, Endpoint::UpdateNamespace()));
-
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::UpdateNamespace());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->NamespaceProperties(ns));
UpdateNamespacePropertiesRequest request{
.removals = std::vector<std::string>(removals.begin(), removals.end()),
@@ -252,7 +254,7 @@ Result<std::shared_ptr<Table>> RestCatalog::CreateTable(
const std::shared_ptr<PartitionSpec>& spec, const
std::shared_ptr<SortOrder>& order,
const std::string& location,
const std::unordered_map<std::string, std::string>& properties) {
- ICEBERG_RETURN_UNEXPECTED(CheckEndpoint(supported_endpoints_,
Endpoint::CreateTable()));
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::CreateTable());
ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Tables(identifier.ns));
CreateTableRequest request{
@@ -294,14 +296,29 @@ Result<std::shared_ptr<Transaction>>
RestCatalog::StageCreateTable(
return NotImplemented("Not implemented");
}
-Status RestCatalog::DropTable([[maybe_unused]] const TableIdentifier&
identifier,
- [[maybe_unused]] bool purge) {
- return NotImplemented("Not implemented");
+Status RestCatalog::DropTable(const TableIdentifier& identifier, bool purge) {
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::DeleteTable());
+ ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Table(identifier));
+
+ std::unordered_map<std::string, std::string> params;
+ if (purge) {
+ params["purgeRequested"] = "true";
+ }
+ ICEBERG_ASSIGN_OR_RAISE(
+ const auto response,
+ client_->Delete(path, params, /*headers=*/{},
*TableErrorHandler::Instance()));
+ return {};
}
-Result<bool> RestCatalog::TableExists(
- [[maybe_unused]] const TableIdentifier& identifier) const {
- return NotImplemented("Not implemented");
+Result<bool> RestCatalog::TableExists(const TableIdentifier& identifier) const
{
+ if (!supported_endpoints_.contains(Endpoint::TableExists())) {
+ // Fall back to call LoadTable
+ return CaptureNoSuchTable(LoadTableInternal(identifier));
+ }
+
+ ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Table(identifier));
+ return CaptureNoSuchTable(
+ client_->Head(path, /*headers=*/{}, *TableErrorHandler::Instance()));
}
Status RestCatalog::RenameTable([[maybe_unused]] const TableIdentifier& from,
@@ -309,9 +326,27 @@ Status RestCatalog::RenameTable([[maybe_unused]] const
TableIdentifier& from,
return NotImplemented("Not implemented");
}
-Result<std::shared_ptr<Table>> RestCatalog::LoadTable(
- [[maybe_unused]] const TableIdentifier& identifier) {
- return NotImplemented("Not implemented");
+Result<std::string> RestCatalog::LoadTableInternal(
+ const TableIdentifier& identifier) const {
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::LoadTable());
+ ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Table(identifier));
+ ICEBERG_ASSIGN_OR_RAISE(
+ const auto response,
+ client_->Get(path, /*params=*/{}, /*headers=*/{},
*TableErrorHandler::Instance()));
+ return response.body();
+}
+
+Result<std::shared_ptr<Table>> RestCatalog::LoadTable(const TableIdentifier&
identifier) {
+ ICEBERG_ENDPOINT_CHECK(supported_endpoints_, Endpoint::LoadTable());
+ ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Table(identifier));
+
+ ICEBERG_ASSIGN_OR_RAISE(const auto body, LoadTableInternal(identifier));
+ ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(body));
+ ICEBERG_ASSIGN_OR_RAISE(auto load_result, LoadTableResultFromJson(json));
+
+ return Table::Make(identifier, std::move(load_result.metadata),
+ std::move(load_result.metadata_location), file_io_,
+ shared_from_this());
}
Result<std::shared_ptr<Table>> RestCatalog::RegisterTable(
diff --git a/src/iceberg/catalog/rest/rest_catalog.h
b/src/iceberg/catalog/rest/rest_catalog.h
index a8096521..41928cf7 100644
--- a/src/iceberg/catalog/rest/rest_catalog.h
+++ b/src/iceberg/catalog/rest/rest_catalog.h
@@ -108,6 +108,8 @@ class ICEBERG_REST_EXPORT RestCatalog : public Catalog,
std::shared_ptr<FileIO> file_io, std::unique_ptr<ResourcePaths>
paths,
std::unordered_set<Endpoint> endpoints);
+ Result<std::string> LoadTableInternal(const TableIdentifier& identifier)
const;
+
std::unique_ptr<RestCatalogProperties> config_;
std::shared_ptr<FileIO> file_io_;
std::unique_ptr<HttpClient> client_;
diff --git a/src/iceberg/catalog/rest/rest_util.cc
b/src/iceberg/catalog/rest/rest_util.cc
index 62b9bfc3..18b94195 100644
--- a/src/iceberg/catalog/rest/rest_util.cc
+++ b/src/iceberg/catalog/rest/rest_util.cc
@@ -253,12 +253,4 @@ std::string GetStandardReasonPhrase(int32_t status_code) {
}
}
-Status CheckEndpoint(const std::unordered_set<Endpoint>& supported_endpoints,
- const Endpoint& endpoint) {
- if (!supported_endpoints.contains(endpoint)) {
- return NotSupported("Server does not support endpoint: {}",
endpoint.ToString());
- }
- return {};
-}
-
} // namespace iceberg::rest
diff --git a/src/iceberg/catalog/rest/rest_util.h
b/src/iceberg/catalog/rest/rest_util.h
index 5734bbc7..ffc941ea 100644
--- a/src/iceberg/catalog/rest/rest_util.h
+++ b/src/iceberg/catalog/rest/rest_util.h
@@ -93,12 +93,4 @@ ICEBERG_REST_EXPORT std::unordered_map<std::string,
std::string> MergeConfigs(
/// Error").
ICEBERG_REST_EXPORT std::string GetStandardReasonPhrase(int32_t status_code);
-/// \brief Check whether the given endpoint is in the set of supported
endpoints.
-///
-/// \param supported_endpoints Set of endpoints advertised by the server
-/// \param endpoint Endpoint to validate
-/// \return Status::OK if supported, NotSupported error otherwise
-ICEBERG_REST_EXPORT Status CheckEndpoint(
- const std::unordered_set<Endpoint>& supported_endpoints, const Endpoint&
endpoint);
-
} // namespace iceberg::rest
diff --git a/src/iceberg/test/rest_catalog_test.cc
b/src/iceberg/test/rest_catalog_test.cc
index fb1f7061..52969040 100644
--- a/src/iceberg/test/rest_catalog_test.cc
+++ b/src/iceberg/test/rest_catalog_test.cc
@@ -136,6 +136,14 @@ class RestCatalogIntegrationTest : public ::testing::Test {
return RestCatalog::Make(*config, std::make_shared<test::StdFileIO>());
}
+ // Helper function to create a default schema for testing
+ std::shared_ptr<Schema> CreateDefaultSchema() {
+ return std::make_shared<Schema>(
+ std::vector<SchemaField>{SchemaField::MakeRequired(1, "id", int32()),
+ SchemaField::MakeOptional(2, "data",
string())},
+ /*schema_id=*/1);
+ }
+
static inline std::unique_ptr<DockerCompose> docker_compose_;
};
@@ -288,7 +296,7 @@ TEST_F(RestCatalogIntegrationTest, NamespaceExists) {
// Check it now exists
exists_result = catalog->NamespaceExists(ns);
ASSERT_THAT(exists_result, IsOk());
- EXPECT_TRUE(*exists_result);
+ EXPECT_TRUE(exists_result.value());
}
TEST_F(RestCatalogIntegrationTest, UpdateNamespaceProperties) {
@@ -340,7 +348,19 @@ TEST_F(RestCatalogIntegrationTest, DropNamespace) {
// Verify it no longer exists
exists_result = catalog->NamespaceExists(ns);
ASSERT_THAT(exists_result, IsOk());
- EXPECT_FALSE(*exists_result);
+ EXPECT_FALSE(exists_result.value());
+}
+
+TEST_F(RestCatalogIntegrationTest, DropNonExistentNamespace) {
+ auto catalog_result = CreateCatalog();
+ ASSERT_THAT(catalog_result, IsOk());
+ auto& catalog = catalog_result.value();
+
+ Namespace ns{.levels = {"nonexistent_namespace"}};
+ auto status = catalog->DropNamespace(ns);
+
+ // Should return NoSuchNamespace error
+ EXPECT_THAT(status, IsError(ErrorKind::kNoSuchNamespace));
}
TEST_F(RestCatalogIntegrationTest, CreateTable) {
@@ -362,22 +382,9 @@ TEST_F(RestCatalogIntegrationTest, CreateTable) {
status = catalog->CreateNamespace(ns, ns_properties);
EXPECT_THAT(status, IsOk());
- // Create schema
- auto schema = std::make_shared<Schema>(
- std::vector<SchemaField>{SchemaField::MakeOptional(1, "foo", string()),
- SchemaField::MakeRequired(2, "bar", int32()),
- SchemaField::MakeOptional(3, "baz", boolean())},
- /*schema_id=*/1);
-
- // Create partition spec and sort order (unpartitioned and unsorted)
- auto partition_spec_result =
PartitionSpec::Make(PartitionSpec::kInitialSpecId, {}, 0);
- ASSERT_THAT(partition_spec_result, IsOk());
- auto partition_spec =
std::shared_ptr<PartitionSpec>(std::move(*partition_spec_result));
-
- auto sort_order_result =
- SortOrder::Make(SortOrder::kUnsortedOrderId, std::vector<SortField>{});
- ASSERT_THAT(sort_order_result, IsOk());
- auto sort_order = std::shared_ptr<SortOrder>(std::move(*sort_order_result));
+ auto schema = CreateDefaultSchema();
+ auto partition_spec = PartitionSpec::Unpartitioned();
+ auto sort_order = SortOrder::Unsorted();
// Create table
TableIdentifier table_id{.ns = ns, .name = "t1"};
@@ -400,4 +407,81 @@ TEST_F(RestCatalogIntegrationTest, CreateTable) {
HasErrorMessage("Table already exists:
test_create_table.apple.ios.t1"));
}
+TEST_F(RestCatalogIntegrationTest, LoadTable) {
+ auto catalog_result = CreateCatalog();
+ ASSERT_THAT(catalog_result, IsOk());
+ auto& catalog = catalog_result.value();
+
+ // Create namespace and table first
+ Namespace ns{.levels = {"test_load_table"}};
+ auto status = catalog->CreateNamespace(ns, {});
+ EXPECT_THAT(status, IsOk());
+
+ // Create schema, partition spec, and sort order using helper functions
+ auto schema = CreateDefaultSchema();
+ auto partition_spec = PartitionSpec::Unpartitioned();
+ auto sort_order = SortOrder::Unsorted();
+
+ // Create table
+ TableIdentifier table_id{.ns = ns, .name = "test_table"};
+ std::unordered_map<std::string, std::string> table_properties{{"key1",
"value1"}};
+ auto create_result = catalog->CreateTable(table_id, schema, partition_spec,
sort_order,
+ "", table_properties);
+ ASSERT_THAT(create_result, IsOk());
+
+ // Load the table
+ auto load_result = catalog->LoadTable(table_id);
+ ASSERT_THAT(load_result, IsOk());
+ auto& loaded_table = load_result.value();
+
+ // Verify loaded table properties
+ EXPECT_EQ(loaded_table->name().ns.levels,
std::vector<std::string>{"test_load_table"});
+ EXPECT_EQ(loaded_table->name().name, "test_table");
+ EXPECT_NE(loaded_table->metadata(), nullptr);
+
+ // Verify schema
+ auto loaded_schema_result = loaded_table->schema();
+ ASSERT_THAT(loaded_schema_result, IsOk());
+ auto loaded_schema = loaded_schema_result.value();
+ EXPECT_EQ(loaded_schema->fields().size(), 2);
+ EXPECT_EQ(loaded_schema->fields()[0].name(), "id");
+ EXPECT_EQ(loaded_schema->fields()[1].name(), "data");
+}
+
+TEST_F(RestCatalogIntegrationTest, DropTable) {
+ auto catalog_result = CreateCatalog();
+ ASSERT_THAT(catalog_result, IsOk());
+ auto& catalog = catalog_result.value();
+
+ // Create namespace and table first
+ Namespace ns{.levels = {"test_drop_table"}};
+ auto status = catalog->CreateNamespace(ns, {});
+ EXPECT_THAT(status, IsOk());
+
+ // Create table
+ auto schema = CreateDefaultSchema();
+ auto partition_spec = PartitionSpec::Unpartitioned();
+ auto sort_order = SortOrder::Unsorted();
+
+ TableIdentifier table_id{.ns = ns, .name = "table_to_drop"};
+ std::unordered_map<std::string, std::string> table_properties;
+ auto create_result = catalog->CreateTable(table_id, schema, partition_spec,
sort_order,
+ "", table_properties);
+ ASSERT_THAT(create_result, IsOk());
+
+ // Verify table exists
+ auto load_result = catalog->TableExists(table_id);
+ ASSERT_THAT(load_result, IsOk());
+ EXPECT_TRUE(load_result.value());
+
+ // Drop the table
+ status = catalog->DropTable(table_id, /*purge=*/false);
+ ASSERT_THAT(status, IsOk());
+
+ // Verify table no longer exists
+ load_result = catalog->TableExists(table_id);
+ ASSERT_THAT(load_result, IsOk());
+ EXPECT_FALSE(load_result.value());
+}
+
} // namespace iceberg::rest
diff --git a/src/iceberg/test/rest_util_test.cc
b/src/iceberg/test/rest_util_test.cc
index 5d2abf55..e11f0015 100644
--- a/src/iceberg/test/rest_util_test.cc
+++ b/src/iceberg/test/rest_util_test.cc
@@ -154,20 +154,4 @@ TEST(RestUtilTest, MergeConfigs) {
EXPECT_EQ(merged_empty["key"], "value");
}
-TEST(RestUtilTest, CheckEndpointSupported) {
- std::unordered_set<Endpoint> supported = {
- Endpoint::ListNamespaces(), Endpoint::LoadTable(),
Endpoint::CreateTable()};
-
- // Supported endpoints should pass
- EXPECT_THAT(CheckEndpoint(supported, Endpoint::ListNamespaces()), IsOk());
- EXPECT_THAT(CheckEndpoint(supported, Endpoint::LoadTable()), IsOk());
- EXPECT_THAT(CheckEndpoint(supported, Endpoint::CreateTable()), IsOk());
-
- // Unsupported endpoints should fail
- EXPECT_THAT(CheckEndpoint(supported, Endpoint::DeleteTable()),
- IsError(ErrorKind::kNotSupported));
- EXPECT_THAT(CheckEndpoint(supported, Endpoint::UpdateTable()),
- IsError(ErrorKind::kNotSupported));
-}
-
} // namespace iceberg::rest