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 b2b38af feat: add table update and requirement interface (#257)
b2b38af is described below
commit b2b38af03504de97a6405a636dc2135723b80e70
Author: Guotao Yu <[email protected]>
AuthorDate: Fri Oct 17 22:02:08 2025 +0800
feat: add table update and requirement interface (#257)
---
src/iceberg/CMakeLists.txt | 3 +
src/iceberg/catalog.h | 4 +-
src/iceberg/catalog/memory/in_memory_catalog.cc | 4 +-
src/iceberg/catalog/memory/in_memory_catalog.h | 4 +-
src/iceberg/meson.build | 6 +
src/iceberg/result.h | 2 +
src/iceberg/table_metadata.cc | 189 +++++++++++++
src/iceberg/table_metadata.h | 267 ++++++++++++++++++
src/iceberg/table_requirement.cc | 60 ++++
src/iceberg/table_requirement.h | 189 +++++++++++++
src/iceberg/table_requirements.cc | 53 ++++
src/iceberg/table_requirements.h | 118 ++++++++
src/iceberg/table_update.cc | 199 +++++++++++++
src/iceberg/table_update.h | 360 ++++++++++++++++++++++++
src/iceberg/test/mock_catalog.h | 4 +-
src/iceberg/type_fwd.h | 8 +-
src/iceberg/util/string_util.h | 5 +
17 files changed, 1465 insertions(+), 10 deletions(-)
diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt
index 75ac9c8..e370950 100644
--- a/src/iceberg/CMakeLists.txt
+++ b/src/iceberg/CMakeLists.txt
@@ -52,7 +52,10 @@ set(ICEBERG_SOURCES
table.cc
table_metadata.cc
table_properties.cc
+ table_requirement.cc
+ table_requirements.cc
table_scan.cc
+ table_update.cc
transform.cc
transform_function.cc
type.cc
diff --git a/src/iceberg/catalog.h b/src/iceberg/catalog.h
index 03bd0f6..83ea677 100644
--- a/src/iceberg/catalog.h
+++ b/src/iceberg/catalog.h
@@ -123,8 +123,8 @@ class ICEBERG_EXPORT Catalog {
/// \return a Table instance or ErrorKind::kAlreadyExists if the table
already exists
virtual Result<std::unique_ptr<Table>> UpdateTable(
const TableIdentifier& identifier,
- const std::vector<std::unique_ptr<UpdateRequirement>>& requirements,
- const std::vector<std::unique_ptr<MetadataUpdate>>& updates) = 0;
+ const std::vector<std::unique_ptr<TableRequirement>>& requirements,
+ const std::vector<std::unique_ptr<TableUpdate>>& updates) = 0;
/// \brief Start a transaction to create a table
///
diff --git a/src/iceberg/catalog/memory/in_memory_catalog.cc
b/src/iceberg/catalog/memory/in_memory_catalog.cc
index 08a9822..c024aac 100644
--- a/src/iceberg/catalog/memory/in_memory_catalog.cc
+++ b/src/iceberg/catalog/memory/in_memory_catalog.cc
@@ -392,8 +392,8 @@ Result<std::unique_ptr<Table>> InMemoryCatalog::CreateTable(
Result<std::unique_ptr<Table>> InMemoryCatalog::UpdateTable(
const TableIdentifier& identifier,
- const std::vector<std::unique_ptr<UpdateRequirement>>& requirements,
- const std::vector<std::unique_ptr<MetadataUpdate>>& updates) {
+ const std::vector<std::unique_ptr<TableRequirement>>& requirements,
+ const std::vector<std::unique_ptr<TableUpdate>>& updates) {
return NotImplemented("update table");
}
diff --git a/src/iceberg/catalog/memory/in_memory_catalog.h
b/src/iceberg/catalog/memory/in_memory_catalog.h
index bde97ba..59c6d3a 100644
--- a/src/iceberg/catalog/memory/in_memory_catalog.h
+++ b/src/iceberg/catalog/memory/in_memory_catalog.h
@@ -77,8 +77,8 @@ class ICEBERG_EXPORT InMemoryCatalog
Result<std::unique_ptr<Table>> UpdateTable(
const TableIdentifier& identifier,
- const std::vector<std::unique_ptr<UpdateRequirement>>& requirements,
- const std::vector<std::unique_ptr<MetadataUpdate>>& updates) override;
+ const std::vector<std::unique_ptr<TableRequirement>>& requirements,
+ const std::vector<std::unique_ptr<TableUpdate>>& updates) override;
Result<std::shared_ptr<Transaction>> StageCreateTable(
const TableIdentifier& identifier, const Schema& schema, const
PartitionSpec& spec,
diff --git a/src/iceberg/meson.build b/src/iceberg/meson.build
index df64ae0..25bfdc6 100644
--- a/src/iceberg/meson.build
+++ b/src/iceberg/meson.build
@@ -74,7 +74,10 @@ iceberg_sources = files(
'table.cc',
'table_metadata.cc',
'table_properties.cc',
+ 'table_requirement.cc',
+ 'table_requirements.cc',
'table_scan.cc',
+ 'table_update.cc',
'transform.cc',
'transform_function.cc',
'type.cc',
@@ -169,7 +172,10 @@ install_headers(
'table.h',
'table_identifier.h',
'table_metadata.h',
+ 'table_requirement.h',
+ 'table_requirements.h',
'table_scan.h',
+ 'table_update.h',
'transaction.h',
'transform_function.h',
'transform.h',
diff --git a/src/iceberg/result.h b/src/iceberg/result.h
index 79dd52b..99df372 100644
--- a/src/iceberg/result.h
+++ b/src/iceberg/result.h
@@ -30,6 +30,7 @@ namespace iceberg {
/// \brief Error types for iceberg.
enum class ErrorKind {
kAlreadyExists,
+ kCommitFailed,
kCommitStateUnknown,
kDecompressError,
kInvalid, // For general invalid errors
@@ -78,6 +79,7 @@ using Status = Result<void>;
}
DEFINE_ERROR_FUNCTION(AlreadyExists)
+DEFINE_ERROR_FUNCTION(CommitFailed)
DEFINE_ERROR_FUNCTION(CommitStateUnknown)
DEFINE_ERROR_FUNCTION(DecompressError)
DEFINE_ERROR_FUNCTION(Invalid)
diff --git a/src/iceberg/table_metadata.cc b/src/iceberg/table_metadata.cc
index e58d06a..e32e75e 100644
--- a/src/iceberg/table_metadata.cc
+++ b/src/iceberg/table_metadata.cc
@@ -19,11 +19,13 @@
#include "iceberg/table_metadata.h"
+#include <algorithm>
#include <format>
#include <string>
#include <nlohmann/json.hpp>
+#include "iceberg/exception.h"
#include "iceberg/file_io.h"
#include "iceberg/json_internal.h"
#include "iceberg/partition_spec.h"
@@ -31,6 +33,7 @@
#include "iceberg/schema.h"
#include "iceberg/snapshot.h"
#include "iceberg/sort_order.h"
+#include "iceberg/table_update.h"
#include "iceberg/util/gzip_internal.h"
#include "iceberg/util/macros.h"
@@ -196,4 +199,190 @@ Status TableMetadataUtil::Write(FileIO& io, const
std::string& location,
return io.WriteFile(location, json_string);
}
+// TableMetadataBuilder implementation
+
+struct TableMetadataBuilder::Impl {};
+
+TableMetadataBuilder::TableMetadataBuilder(int8_t format_version)
+ : impl_(std::make_unique<Impl>()) {}
+
+TableMetadataBuilder::TableMetadataBuilder(const TableMetadata* base)
+ : impl_(std::make_unique<Impl>()) {}
+
+TableMetadataBuilder::~TableMetadataBuilder() = default;
+
+TableMetadataBuilder::TableMetadataBuilder(TableMetadataBuilder&&) noexcept =
default;
+
+TableMetadataBuilder& TableMetadataBuilder::operator=(TableMetadataBuilder&&)
noexcept =
+ default;
+
+std::unique_ptr<TableMetadataBuilder> TableMetadataBuilder::BuildFromEmpty(
+ int8_t format_version) {
+ return std::unique_ptr<TableMetadataBuilder>(
+ new TableMetadataBuilder(format_version)); // NOLINT
+}
+
+std::unique_ptr<TableMetadataBuilder> TableMetadataBuilder::BuildFrom(
+ const TableMetadata* base) {
+ return std::unique_ptr<TableMetadataBuilder>(new
TableMetadataBuilder(base)); // NOLINT
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetMetadataLocation(
+ std::string_view metadata_location) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetPreviousMetadataLocation(
+ std::string_view previous_metadata_location) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AssignUUID() {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AssignUUID(std::string_view uuid) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+ ;
+}
+
+TableMetadataBuilder& TableMetadataBuilder::UpgradeFormatVersion(
+ int8_t new_format_version) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetCurrentSchema(
+ std::shared_ptr<Schema> schema, int32_t new_last_column_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetCurrentSchema(int32_t
schema_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AddSchema(std::shared_ptr<Schema>
schema) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetDefaultPartitionSpec(
+ std::shared_ptr<PartitionSpec> spec) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetDefaultPartitionSpec(int32_t
spec_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AddPartitionSpec(
+ std::shared_ptr<PartitionSpec> spec) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemovePartitionSpecs(
+ const std::vector<int32_t>& spec_ids) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveSchemas(
+ const std::vector<int32_t>& schema_ids) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetDefaultSortOrder(
+ std::shared_ptr<SortOrder> order) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetDefaultSortOrder(int32_t
order_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AddSortOrder(
+ std::shared_ptr<SortOrder> order) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AddSnapshot(
+ std::shared_ptr<Snapshot> snapshot) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetBranchSnapshot(int64_t
snapshot_id,
+ const
std::string& branch) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetRef(const std::string& name,
+
std::shared_ptr<SnapshotRef> ref) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveRef(const std::string& name)
{
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveSnapshots(
+ const std::vector<std::shared_ptr<Snapshot>>& snapshots_to_remove) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveSnapshots(
+ const std::vector<int64_t>& snapshot_ids) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::suppressHistoricalSnapshots() {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetStatistics(
+ const std::shared_ptr<StatisticsFile>& statistics_file) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveStatistics(int64_t
snapshot_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetPartitionStatistics(
+ const std::shared_ptr<PartitionStatisticsFile>& partition_statistics_file)
{
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemovePartitionStatistics(
+ int64_t snapshot_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetProperties(
+ const std::unordered_map<std::string, std::string>& updated) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::RemoveProperties(
+ const std::vector<std::string>& removed) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::SetLocation(std::string_view
location) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::AddEncryptionKey(
+ std::shared_ptr<EncryptedKey> key) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder&
TableMetadataBuilder::RemoveEncryptionKey(std::string_view key_id) {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+TableMetadataBuilder& TableMetadataBuilder::DiscardChanges() {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Result<std::unique_ptr<TableMetadata>> TableMetadataBuilder::Build() {
+ return NotImplemented("TableMetadataBuilder::Build not implemented");
+}
+
} // namespace iceberg
diff --git a/src/iceberg/table_metadata.h b/src/iceberg/table_metadata.h
index 427447a..6f7a819 100644
--- a/src/iceberg/table_metadata.h
+++ b/src/iceberg/table_metadata.h
@@ -144,6 +144,273 @@ ICEBERG_EXPORT std::string ToString(const
SnapshotLogEntry& entry);
/// \brief Returns a string representation of a MetadataLogEntry
ICEBERG_EXPORT std::string ToString(const MetadataLogEntry& entry);
+/// \brief Builder class for constructing TableMetadata objects
+///
+/// This builder provides a fluent interface for creating and modifying table
metadata.
+/// It supports both creating new tables and building from existing metadata.
+///
+/// Each modification method generates a corresponding MetadataUpdate that is
tracked
+/// in a changes list. This allows the builder to maintain a complete history
of all
+/// modifications made to the table metadata, which is important for tracking
table
+/// evolution and for serialization purposes.
+///
+/// If a modification violates Iceberg table constraints (e.g., setting a
current
+/// schema ID that does not exist), an error will be recorded and returned when
+/// Build() is called.
+class ICEBERG_EXPORT TableMetadataBuilder {
+ public:
+ /// \brief Create a builder for a new table
+ ///
+ /// \param format_version The format version for the table
+ /// \return A new TableMetadataBuilder instance
+ static std::unique_ptr<TableMetadataBuilder> BuildFromEmpty(
+ int8_t format_version = TableMetadata::kDefaultTableFormatVersion);
+
+ /// \brief Create a builder from existing table metadata
+ ///
+ /// \param base The base table metadata to build from
+ /// \return A new TableMetadataBuilder instance initialized with base
metadata
+ static std::unique_ptr<TableMetadataBuilder> BuildFrom(const TableMetadata*
base);
+
+ /// \brief Set the metadata location of the table
+ ///
+ /// \param metadata_location The new metadata location
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetMetadataLocation(std::string_view
metadata_location);
+
+ /// \brief Set the previous metadata location of the table
+ ///
+ /// \param previous_metadata_location The previous metadata location
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetPreviousMetadataLocation(
+ std::string_view previous_metadata_location);
+
+ /// \brief Assign a UUID to the table
+ ///
+ /// If no UUID is provided, a random UUID will be generated.
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AssignUUID();
+
+ /// \brief Assign a specific UUID to the table
+ ///
+ /// \param uuid The UUID string to assign
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AssignUUID(std::string_view uuid);
+
+ /// \brief Upgrade the format version of the table
+ ///
+ /// \param new_format_version The new format version (must be >= current
version)
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& UpgradeFormatVersion(int8_t new_format_version);
+
+ /// \brief Set the current schema for the table
+ ///
+ /// \param schema The schema to set as current
+ /// \param new_last_column_id The highest column ID in the schema
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetCurrentSchema(std::shared_ptr<Schema> schema,
+ int32_t new_last_column_id);
+
+ /// \brief Set the current schema by schema ID
+ ///
+ /// \param schema_id The ID of the schema to set as current
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetCurrentSchema(int32_t schema_id);
+
+ /// \brief Add a schema to the table
+ ///
+ /// \param schema The schema to add
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AddSchema(std::shared_ptr<Schema> schema);
+
+ /// \brief Set the default partition spec for the table
+ ///
+ /// \param spec The partition spec to set as default
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetDefaultPartitionSpec(std::shared_ptr<PartitionSpec>
spec);
+
+ /// \brief Set the default partition spec by spec ID
+ ///
+ /// \param spec_id The ID of the partition spec to set as default
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetDefaultPartitionSpec(int32_t spec_id);
+
+ /// \brief Add a partition spec to the table
+ ///
+ /// \param spec The partition spec to add
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AddPartitionSpec(std::shared_ptr<PartitionSpec> spec);
+
+ /// \brief Remove partition specs from the table
+ ///
+ /// \param spec_ids The IDs of partition specs to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemovePartitionSpecs(const std::vector<int32_t>&
spec_ids);
+
+ /// \brief Remove schemas from the table
+ ///
+ /// \param schema_ids The IDs of schemas to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveSchemas(const std::vector<int32_t>& schema_ids);
+
+ /// \brief Set the default sort order for the table
+ ///
+ /// \param order The sort order to set as default
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetDefaultSortOrder(std::shared_ptr<SortOrder> order);
+
+ /// \brief Set the default sort order by order ID
+ ///
+ /// \param order_id The ID of the sort order to set as default
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetDefaultSortOrder(int32_t order_id);
+
+ /// \brief Add a sort order to the table
+ ///
+ /// \param order The sort order to add
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AddSortOrder(std::shared_ptr<SortOrder> order);
+
+ /// \brief Add a snapshot to the table
+ ///
+ /// \param snapshot The snapshot to add
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AddSnapshot(std::shared_ptr<Snapshot> snapshot);
+
+ /// \brief Set a branch to point to a specific snapshot
+ ///
+ /// \param snapshot_id The snapshot ID the branch should reference
+ /// \param branch The name of the branch
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetBranchSnapshot(int64_t snapshot_id, const
std::string& branch);
+
+ /// \brief Set a snapshot reference
+ ///
+ /// \param name The name of the reference
+ /// \param ref The snapshot reference to set
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetRef(const std::string& name,
std::shared_ptr<SnapshotRef> ref);
+
+ /// \brief Remove a snapshot reference
+ ///
+ /// \param name The name of the reference to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveRef(const std::string& name);
+
+ /// \brief Remove snapshots from the table
+ ///
+ /// \param snapshots_to_remove The snapshots to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveSnapshots(
+ const std::vector<std::shared_ptr<Snapshot>>& snapshots_to_remove);
+
+ /// \brief Remove snapshots from the table
+ ///
+ /// \param snapshot_ids The IDs of snapshots to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveSnapshots(const std::vector<int64_t>&
snapshot_ids);
+
+ /// \brief Suppresses snapshots that are historical, removing the metadata
for lazy
+ /// snapshot loading.
+ ///
+ /// Note that the snapshots are not considered removed from metadata and no
+ /// RemoveSnapshot changes are created. A snapshot is historical if no ref
directly
+ /// references its ID.
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& suppressHistoricalSnapshots();
+
+ /// \brief Set table statistics
+ ///
+ /// \param statistics_file The statistics file to set
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetStatistics(
+ const std::shared_ptr<StatisticsFile>& statistics_file);
+
+ /// \brief Remove table statistics by snapshot ID
+ ///
+ /// \param snapshot_id The snapshot ID whose statistics to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveStatistics(int64_t snapshot_id);
+
+ /// \brief Set partition statistics
+ ///
+ /// \param partition_statistics_file The partition statistics file to set
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetPartitionStatistics(
+ const std::shared_ptr<PartitionStatisticsFile>&
partition_statistics_file);
+
+ /// \brief Remove partition statistics by snapshot ID
+ ///
+ /// \param snapshot_id The snapshot ID whose partition statistics to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemovePartitionStatistics(int64_t snapshot_id);
+
+ /// \brief Set table properties
+ ///
+ /// \param updated Map of properties to set or update
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetProperties(
+ const std::unordered_map<std::string, std::string>& updated);
+
+ /// \brief Remove table properties
+ ///
+ /// \param removed Set of property keys to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveProperties(const std::vector<std::string>&
removed);
+
+ /// \brief Set the table location
+ ///
+ /// \param location The table base location
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& SetLocation(std::string_view location);
+
+ /// \brief Add an encryption key to the table
+ ///
+ /// \param key The encryption key to add
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& AddEncryptionKey(std::shared_ptr<EncryptedKey> key);
+
+ /// \brief Remove an encryption key from the table by key ID
+ ///
+ /// \param key_id The ID of the encryption key to remove
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& RemoveEncryptionKey(std::string_view key_id);
+
+ /// \brief Discard all accumulated changes
+ ///
+ /// This is useful when you want to reset the builder state without
+ /// creating a new builder instance.
+ /// \return Reference to this builder for method chaining
+ TableMetadataBuilder& DiscardChanges();
+
+ /// \brief Build the TableMetadata object
+ ///
+ /// \return A Result containing the constructed TableMetadata or an error
+ Result<std::unique_ptr<TableMetadata>> Build();
+
+ /// \brief Destructor
+ ~TableMetadataBuilder();
+
+ // Delete copy operations (use BuildFrom to create a new builder)
+ TableMetadataBuilder(const TableMetadataBuilder&) = delete;
+ TableMetadataBuilder& operator=(const TableMetadataBuilder&) = delete;
+
+ // Enable move operations
+ TableMetadataBuilder(TableMetadataBuilder&&) noexcept;
+ TableMetadataBuilder& operator=(TableMetadataBuilder&&) noexcept;
+
+ private:
+ /// \brief Private constructor for building from empty state
+ explicit TableMetadataBuilder(int8_t format_version);
+
+ /// \brief Private constructor for building from existing metadata
+ explicit TableMetadataBuilder(const TableMetadata* base);
+
+ /// Internal state members
+ struct Impl;
+ std::unique_ptr<Impl> impl_;
+};
+
/// \brief The codec type of the table metadata file.
enum class ICEBERG_EXPORT MetadataFileCodecType {
kNone,
diff --git a/src/iceberg/table_requirement.cc b/src/iceberg/table_requirement.cc
new file mode 100644
index 0000000..4ca4b91
--- /dev/null
+++ b/src/iceberg/table_requirement.cc
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "iceberg/table_requirement.h"
+
+#include "iceberg/table_metadata.h"
+
+namespace iceberg::table {
+
+Status AssertDoesNotExist::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertTableDoesNotExist::Validate not implemented");
+}
+
+Status AssertUUID::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertTableUUID::Validate not implemented");
+}
+
+Status AssertRefSnapshotID::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertTableRefSnapshotID::Validate not implemented");
+}
+
+Status AssertLastAssignedFieldId::Validate(const TableMetadata* base) const {
+ return NotImplemented(
+ "AssertCurrentTableLastAssignedFieldId::Validate not implemented");
+}
+
+Status AssertCurrentSchemaID::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertCurrentTableSchemaID::Validate not
implemented");
+}
+
+Status AssertLastAssignedPartitionId::Validate(const TableMetadata* base)
const {
+ return NotImplemented(
+ "AssertCurrentTableLastAssignedPartitionId::Validate not implemented");
+}
+
+Status AssertDefaultSpecID::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertDefaultTableSpecID::Validate not implemented");
+}
+
+Status AssertDefaultSortOrderID::Validate(const TableMetadata* base) const {
+ return NotImplemented("AssertDefaultTableSortOrderID::Validate not
implemented");
+}
+
+} // namespace iceberg::table
diff --git a/src/iceberg/table_requirement.h b/src/iceberg/table_requirement.h
new file mode 100644
index 0000000..c054532
--- /dev/null
+++ b/src/iceberg/table_requirement.h
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/// \file iceberg/table_requirement.h
+/// Update requirements for Iceberg table operations.
+///
+/// Table requirements are conditions that must be satisfied before
+/// applying metadata updates to a table. They are used for optimistic
+/// concurrency control in table operations.
+
+#include <optional>
+#include <string>
+
+#include "iceberg/iceberg_export.h"
+#include "iceberg/result.h"
+#include "iceberg/type_fwd.h"
+
+namespace iceberg {
+
+/// \brief Base class for update requirement operations
+///
+/// Represents a requirement that must be validated before applying
+/// metadata updates to a table. Each concrete subclass represents
+/// a specific type of requirement check.
+class ICEBERG_EXPORT TableRequirement {
+ public:
+ virtual ~TableRequirement() = default;
+
+ /// \brief Validate this requirement against table metadata
+ ///
+ /// \param base The base table metadata to validate against (may be nullptr)
+ /// \return Status indicating success or failure with error details
+ virtual Status Validate(const TableMetadata* base) const = 0;
+};
+
+namespace table {
+
+/// \brief Requirement that the table does not exist
+///
+/// This requirement is used when creating a new table to ensure
+/// it doesn't already exist.
+class ICEBERG_EXPORT AssertDoesNotExist : public TableRequirement {
+ public:
+ AssertDoesNotExist() = default;
+
+ Status Validate(const TableMetadata* base) const override;
+};
+
+/// \brief Requirement that the table UUID matches the expected value
+///
+/// This ensures the table hasn't been replaced or recreated between
+/// reading the metadata and attempting to update it.
+class ICEBERG_EXPORT AssertUUID : public TableRequirement {
+ public:
+ explicit AssertUUID(std::string uuid) : uuid_(std::move(uuid)) {}
+
+ const std::string& uuid() const { return uuid_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ std::string uuid_;
+};
+
+/// \brief Requirement that a reference (branch or tag) points to a specific
snapshot
+///
+/// This requirement validates that a named reference (branch or tag) either:
+/// - Points to the expected snapshot ID
+/// - Does not exist (if snapshot_id is nullopt)
+class ICEBERG_EXPORT AssertRefSnapshotID : public TableRequirement {
+ public:
+ AssertRefSnapshotID(std::string ref_name, std::optional<int64_t> snapshot_id)
+ : ref_name_(std::move(ref_name)), snapshot_id_(snapshot_id) {}
+
+ const std::string& ref_name() const { return ref_name_; }
+
+ const std::optional<int64_t>& snapshot_id() const { return snapshot_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ std::string ref_name_;
+ std::optional<int64_t> snapshot_id_;
+};
+
+/// \brief Requirement that the last assigned field ID matches
+///
+/// This ensures the schema hasn't been modified (by adding fields)
+/// since the metadata was read.
+class ICEBERG_EXPORT AssertLastAssignedFieldId : public TableRequirement {
+ public:
+ explicit AssertLastAssignedFieldId(int32_t last_assigned_field_id)
+ : last_assigned_field_id_(last_assigned_field_id) {}
+
+ int32_t last_assigned_field_id() const { return last_assigned_field_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ int32_t last_assigned_field_id_;
+};
+
+/// \brief Requirement that the current schema ID matches
+///
+/// This ensures the active schema hasn't changed since the
+/// metadata was read.
+class ICEBERG_EXPORT AssertCurrentSchemaID : public TableRequirement {
+ public:
+ explicit AssertCurrentSchemaID(int32_t schema_id) : schema_id_(schema_id) {}
+
+ int32_t schema_id() const { return schema_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ int32_t schema_id_;
+};
+
+/// \brief Requirement that the last assigned partition ID matches
+///
+/// This ensures partition specs haven't been modified since the
+/// metadata was read.
+class ICEBERG_EXPORT AssertLastAssignedPartitionId : public TableRequirement {
+ public:
+ explicit AssertLastAssignedPartitionId(int32_t last_assigned_partition_id)
+ : last_assigned_partition_id_(last_assigned_partition_id) {}
+
+ int32_t last_assigned_partition_id() const { return
last_assigned_partition_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ int32_t last_assigned_partition_id_;
+};
+
+/// \brief Requirement that the default partition spec ID matches
+///
+/// This ensures the default partition spec hasn't changed since
+/// the metadata was read.
+class ICEBERG_EXPORT AssertDefaultSpecID : public TableRequirement {
+ public:
+ explicit AssertDefaultSpecID(int32_t spec_id) : spec_id_(spec_id) {}
+
+ int32_t spec_id() const { return spec_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ int32_t spec_id_;
+};
+
+/// \brief Requirement that the default sort order ID matches
+///
+/// This ensures the default sort order hasn't changed since
+/// the metadata was read.
+class ICEBERG_EXPORT AssertDefaultSortOrderID : public TableRequirement {
+ public:
+ explicit AssertDefaultSortOrderID(int32_t sort_order_id)
+ : sort_order_id_(sort_order_id) {}
+
+ int32_t sort_order_id() const { return sort_order_id_; }
+
+ Status Validate(const TableMetadata* base) const override;
+
+ private:
+ int32_t sort_order_id_;
+};
+
+} // namespace table
+
+} // namespace iceberg
diff --git a/src/iceberg/table_requirements.cc
b/src/iceberg/table_requirements.cc
new file mode 100644
index 0000000..1eb870c
--- /dev/null
+++ b/src/iceberg/table_requirements.cc
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include "iceberg/table_requirements.h"
+
+#include "iceberg/exception.h"
+#include "iceberg/table_metadata.h"
+#include "iceberg/table_update.h"
+
+namespace iceberg {
+
+void TableUpdateContext::AddRequirement(std::unique_ptr<TableRequirement>
requirement) {
+ throw IcebergError("TableUpdateContext::AddRequirement not implemented");
+}
+
+Result<std::vector<std::unique_ptr<TableRequirement>>>
TableUpdateContext::Build() {
+ return NotImplemented("TableUpdateContext::Build not implemented");
+}
+
+Result<std::vector<std::unique_ptr<TableRequirement>>>
TableRequirements::ForCreateTable(
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates) {
+ return NotImplemented("TableRequirements::ForCreateTable not implemented");
+}
+
+Result<std::vector<std::unique_ptr<TableRequirement>>>
TableRequirements::ForReplaceTable(
+ const TableMetadata& base,
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates) {
+ return NotImplemented("TableRequirements::ForReplaceTable not implemented");
+}
+
+Result<std::vector<std::unique_ptr<TableRequirement>>>
TableRequirements::ForUpdateTable(
+ const TableMetadata& base,
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates) {
+ return NotImplemented("TableRequirements::ForUpdateTable not implemented");
+}
+
+} // namespace iceberg
diff --git a/src/iceberg/table_requirements.h b/src/iceberg/table_requirements.h
new file mode 100644
index 0000000..327e7d5
--- /dev/null
+++ b/src/iceberg/table_requirements.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/// \file iceberg/table_requirements.h
+/// Factory for generating table requirements from metadata updates.
+///
+/// This utility class generates the appropriate TableRequirement instances
+/// based on a list of TableUpdate operations. The requirements are used
+/// for optimistic concurrency control when committing table changes.
+
+#include <memory>
+#include <vector>
+
+#include "iceberg/iceberg_export.h"
+#include "iceberg/table_requirement.h"
+#include "iceberg/type_fwd.h"
+
+namespace iceberg {
+
+/// \brief Context for generating table requirements
+///
+/// This context is passed to each TableUpdate's GenerateRequirements method
+/// and maintains state about what requirements have already been added to
avoid
+/// duplicates.
+class ICEBERG_EXPORT TableUpdateContext {
+ public:
+ /// \brief Construct a context for requirement generation
+ ///
+ /// \param base The base table metadata (maybe nullptr for table creation)
+ /// \param is_replace Whether this is a replace operation (more permissive)
+ TableUpdateContext(const TableMetadata* base, bool is_replace)
+ : base_(base), is_replace_(is_replace) {}
+
+ // Delete copy operations (contains unique_ptr members)
+ TableUpdateContext(const TableUpdateContext&) = delete;
+ TableUpdateContext& operator=(const TableUpdateContext&) = delete;
+
+ // Enable move construction only (assignment deleted due to const members)
+ TableUpdateContext(TableUpdateContext&&) noexcept = default;
+
+ /// \brief Add a requirement to the list
+ void AddRequirement(std::unique_ptr<TableRequirement> requirement);
+
+ /// \brief Get the base table metadata
+ const TableMetadata* base() const { return base_; }
+
+ /// \brief Check if this is a replace operation
+ bool is_replace() const { return is_replace_; }
+
+ /// \brief Build and return the list of requirements
+ Result<std::vector<std::unique_ptr<TableRequirement>>> Build();
+
+ private:
+ const TableMetadata* base_;
+ const bool is_replace_;
+
+ std::vector<std::unique_ptr<TableRequirement>> requirements_;
+};
+
+/// \brief Factory class for generating table requirements
+///
+/// This class analyzes a sequence of table updates and generates the
+/// appropriate table requirements to ensure safe concurrent modifications.
+class ICEBERG_EXPORT TableRequirements {
+ public:
+ /// \brief Generate requirements for creating a new table
+ ///
+ /// For table creation, this requires that the table does not already exist.
+ ///
+ /// \param table_updates The list of table updates for table creation
+ /// \return A list of table requirements to validate before creation
+ static Result<std::vector<std::unique_ptr<TableRequirement>>> ForCreateTable(
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates);
+
+ /// \brief Generate requirements for replacing an existing table
+ ///
+ /// For table replacement, this requires that the table UUID matches but
+ /// allows more aggressive changes than a regular update.
+ ///
+ /// \param base The base table metadata
+ /// \param table_updates The list of table updates for replacement
+ /// \return A list of table requirements to validate before replacement
+ static Result<std::vector<std::unique_ptr<TableRequirement>>>
ForReplaceTable(
+ const TableMetadata& base,
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates);
+
+ /// \brief Generate requirements for updating an existing table
+ ///
+ /// For table updates, this generates requirements to ensure that key
+ /// metadata properties haven't changed concurrently.
+ ///
+ /// \param base The base table metadata
+ /// \param table_updates The list of table updates
+ /// \return A list of table requirements to validate before update
+ static Result<std::vector<std::unique_ptr<TableRequirement>>> ForUpdateTable(
+ const TableMetadata& base,
+ const std::vector<std::unique_ptr<TableUpdate>>& table_updates);
+};
+
+} // namespace iceberg
diff --git a/src/iceberg/table_update.cc b/src/iceberg/table_update.cc
new file mode 100644
index 0000000..7d81dd8
--- /dev/null
+++ b/src/iceberg/table_update.cc
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+#include "iceberg/table_update.h"
+
+#include "iceberg/exception.h"
+#include "iceberg/table_metadata.h"
+#include "iceberg/table_requirements.h"
+
+namespace iceberg::table {
+
+// AssignUUID
+
+void AssignUUID::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status AssignUUID::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("AssignTableUUID::GenerateRequirements not
implemented");
+}
+
+// UpgradeFormatVersion
+
+void UpgradeFormatVersion::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status UpgradeFormatVersion::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented(
+ "UpgradeTableFormatVersion::GenerateRequirements not implemented");
+}
+
+// AddSchema
+
+void AddSchema::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status AddSchema::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("AddTableSchema::GenerateRequirements not
implemented");
+}
+
+// SetCurrentSchema
+
+void SetCurrentSchema::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetCurrentSchema::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("SetCurrentTableSchema::GenerateRequirements not
implemented");
+}
+
+// AddPartitionSpec
+
+void AddPartitionSpec::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status AddPartitionSpec::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("AddTablePartitionSpec::GenerateRequirements not
implemented");
+}
+
+// SetDefaultPartitionSpec
+
+void SetDefaultPartitionSpec::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetDefaultPartitionSpec::GenerateRequirements(TableUpdateContext&
context) const {
+ return NotImplemented(
+ "SetDefaultTablePartitionSpec::GenerateRequirements not implemented");
+}
+
+// RemovePartitionSpecs
+
+void RemovePartitionSpecs::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status RemovePartitionSpecs::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented(
+ "RemoveTablePartitionSpecs::GenerateRequirements not implemented");
+}
+
+// RemoveSchemas
+
+void RemoveSchemas::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status RemoveSchemas::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("RemoveTableSchemas::GenerateRequirements not
implemented");
+}
+
+// AddSortOrder
+
+void AddSortOrder::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status AddSortOrder::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("AddTableSortOrder::GenerateRequirements not
implemented");
+}
+
+// SetDefaultSortOrder
+
+void SetDefaultSortOrder::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetDefaultSortOrder::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("SetDefaultTableSortOrder::GenerateRequirements not
implemented");
+}
+
+// AddSnapshot
+
+void AddSnapshot::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status AddSnapshot::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("AddTableSnapshot::GenerateRequirements not
implemented");
+}
+
+// RemoveSnapshots
+
+void RemoveSnapshots::ApplyTo(TableMetadataBuilder& builder) const {}
+
+Status RemoveSnapshots::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("RemoveTableSnapshots::GenerateRequirements not
implemented");
+}
+
+// RemoveSnapshotRef
+
+void RemoveSnapshotRef::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status RemoveSnapshotRef::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("RemoveTableSnapshotRef::GenerateRequirements not
implemented");
+}
+
+// SetSnapshotRef
+
+void SetSnapshotRef::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetSnapshotRef::GenerateRequirements(TableUpdateContext& context) const
{
+ return NotImplemented("SetTableSnapshotRef::GenerateRequirements not
implemented");
+}
+
+// SetProperties
+
+void SetProperties::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetProperties::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("SetTableProperties::GenerateRequirements not
implemented");
+}
+
+// RemoveProperties
+
+void RemoveProperties::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status RemoveProperties::GenerateRequirements(TableUpdateContext& context)
const {
+ return NotImplemented("RemoveTableProperties::GenerateRequirements not
implemented");
+}
+
+// SetLocation
+
+void SetLocation::ApplyTo(TableMetadataBuilder& builder) const {
+ throw IcebergError(std::format("{} not implemented", __FUNCTION__));
+}
+
+Status SetLocation::GenerateRequirements(TableUpdateContext& context) const {
+ return NotImplemented("SetTableLocation::GenerateRequirements not
implemented");
+}
+
+} // namespace iceberg::table
diff --git a/src/iceberg/table_update.h b/src/iceberg/table_update.h
new file mode 100644
index 0000000..445295a
--- /dev/null
+++ b/src/iceberg/table_update.h
@@ -0,0 +1,360 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/// \file iceberg/table_update.h
+/// Table metadata update operations for Iceberg tables.
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "iceberg/iceberg_export.h"
+#include "iceberg/snapshot.h"
+#include "iceberg/type_fwd.h"
+
+namespace iceberg {
+
+/// \brief Base class for metadata update operations
+///
+/// Represents a change to table metadata. Each concrete subclass
+/// represents a specific type of update operation.
+class ICEBERG_EXPORT TableUpdate {
+ public:
+ virtual ~TableUpdate() = default;
+
+ /// \brief Apply this update to a TableMetadataBuilder
+ ///
+ /// This method modifies the builder by applying the update operation
+ /// it represents. Each subclass implements this to apply its specific
+ /// type of update.
+ ///
+ /// \param builder The builder to apply this update to
+ virtual void ApplyTo(TableMetadataBuilder& builder) const = 0;
+
+ /// \brief Generate update requirements for this metadata update
+ ///
+ /// This method generates the appropriate UpdateRequirement instances
+ /// that must be validated before this update can be applied. The context
+ /// provides information about the base metadata and operation mode.
+ ///
+ /// \param context The context containing base metadata and operation state
+ /// \return Status indicating success or failure with error details
+ virtual Status GenerateRequirements(TableUpdateContext& context) const = 0;
+};
+
+namespace table {
+
+/// \brief Represents an assignment of a UUID to the table
+class ICEBERG_EXPORT AssignUUID : public TableUpdate {
+ public:
+ explicit AssignUUID(std::string uuid) : uuid_(std::move(uuid)) {}
+
+ const std::string& uuid() const { return uuid_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::string uuid_;
+};
+
+/// \brief Represents an upgrade of the table format version
+class ICEBERG_EXPORT UpgradeFormatVersion : public TableUpdate {
+ public:
+ explicit UpgradeFormatVersion(int8_t format_version)
+ : format_version_(format_version) {}
+
+ int8_t format_version() const { return format_version_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ int8_t format_version_;
+};
+
+/// \brief Represents adding a new schema to the table
+class ICEBERG_EXPORT AddSchema : public TableUpdate {
+ public:
+ explicit AddSchema(std::shared_ptr<Schema> schema, int32_t last_column_id)
+ : schema_(std::move(schema)), last_column_id_(last_column_id) {}
+
+ const std::shared_ptr<Schema>& schema() const { return schema_; }
+
+ int32_t last_column_id() const { return last_column_id_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::shared_ptr<Schema> schema_;
+ int32_t last_column_id_;
+};
+
+/// \brief Represents setting the current schema
+class ICEBERG_EXPORT SetCurrentSchema : public TableUpdate {
+ public:
+ explicit SetCurrentSchema(int32_t schema_id) : schema_id_(schema_id) {}
+
+ int32_t schema_id() const { return schema_id_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ int32_t schema_id_;
+};
+
+/// \brief Represents adding a new partition spec to the table
+class ICEBERG_EXPORT AddPartitionSpec : public TableUpdate {
+ public:
+ explicit AddPartitionSpec(std::shared_ptr<PartitionSpec> spec)
+ : spec_(std::move(spec)) {}
+
+ const std::shared_ptr<PartitionSpec>& spec() const { return spec_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::shared_ptr<PartitionSpec> spec_;
+};
+
+/// \brief Represents setting the default partition spec
+class ICEBERG_EXPORT SetDefaultPartitionSpec : public TableUpdate {
+ public:
+ explicit SetDefaultPartitionSpec(int32_t spec_id) : spec_id_(spec_id) {}
+
+ int32_t spec_id() const { return spec_id_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ int32_t spec_id_;
+};
+
+/// \brief Represents removing partition specs from the table
+class ICEBERG_EXPORT RemovePartitionSpecs : public TableUpdate {
+ public:
+ explicit RemovePartitionSpecs(std::vector<int32_t> spec_ids)
+ : spec_ids_(std::move(spec_ids)) {}
+
+ const std::vector<int32_t>& spec_ids() const { return spec_ids_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::vector<int32_t> spec_ids_;
+};
+
+/// \brief Represents removing schemas from the table
+class ICEBERG_EXPORT RemoveSchemas : public TableUpdate {
+ public:
+ explicit RemoveSchemas(std::vector<int32_t> schema_ids)
+ : schema_ids_(std::move(schema_ids)) {}
+
+ const std::vector<int32_t>& schema_ids() const { return schema_ids_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::vector<int32_t> schema_ids_;
+};
+
+/// \brief Represents adding a new sort order to the table
+class ICEBERG_EXPORT AddSortOrder : public TableUpdate {
+ public:
+ explicit AddSortOrder(std::shared_ptr<SortOrder> sort_order)
+ : sort_order_(std::move(sort_order)) {}
+
+ const std::shared_ptr<SortOrder>& sort_order() const { return sort_order_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::shared_ptr<SortOrder> sort_order_;
+};
+
+/// \brief Represents setting the default sort order
+class ICEBERG_EXPORT SetDefaultSortOrder : public TableUpdate {
+ public:
+ explicit SetDefaultSortOrder(int32_t sort_order_id) :
sort_order_id_(sort_order_id) {}
+
+ int32_t sort_order_id() const { return sort_order_id_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ int32_t sort_order_id_;
+};
+
+/// \brief Represents adding a snapshot to the table
+class ICEBERG_EXPORT AddSnapshot : public TableUpdate {
+ public:
+ explicit AddSnapshot(std::shared_ptr<Snapshot> snapshot)
+ : snapshot_(std::move(snapshot)) {}
+
+ const std::shared_ptr<Snapshot>& snapshot() const { return snapshot_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::shared_ptr<Snapshot> snapshot_;
+};
+
+/// \brief Represents removing snapshots from the table
+class ICEBERG_EXPORT RemoveSnapshots : public TableUpdate {
+ public:
+ explicit RemoveSnapshots(std::vector<int64_t> snapshot_ids)
+ : snapshot_ids_(std::move(snapshot_ids)) {}
+
+ const std::vector<int64_t>& snapshot_ids() const { return snapshot_ids_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::vector<int64_t> snapshot_ids_;
+};
+
+/// \brief Represents removing a snapshot reference
+class ICEBERG_EXPORT RemoveSnapshotRef : public TableUpdate {
+ public:
+ explicit RemoveSnapshotRef(std::string ref_name) :
ref_name_(std::move(ref_name)) {}
+
+ const std::string& ref_name() const { return ref_name_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::string ref_name_;
+};
+
+/// \brief Represents setting a snapshot reference
+class ICEBERG_EXPORT SetSnapshotRef : public TableUpdate {
+ public:
+ SetSnapshotRef(std::string ref_name, int64_t snapshot_id, SnapshotRefType
type,
+ std::optional<int32_t> min_snapshots_to_keep = std::nullopt,
+ std::optional<int64_t> max_snapshot_age_ms = std::nullopt,
+ std::optional<int64_t> max_ref_age_ms = std::nullopt)
+ : ref_name_(std::move(ref_name)),
+ snapshot_id_(snapshot_id),
+ type_(type),
+ min_snapshots_to_keep_(min_snapshots_to_keep),
+ max_snapshot_age_ms_(max_snapshot_age_ms),
+ max_ref_age_ms_(max_ref_age_ms) {}
+
+ const std::string& ref_name() const { return ref_name_; }
+ int64_t snapshot_id() const { return snapshot_id_; }
+ SnapshotRefType type() const { return type_; }
+ const std::optional<int32_t>& min_snapshots_to_keep() const {
+ return min_snapshots_to_keep_;
+ }
+ const std::optional<int64_t>& max_snapshot_age_ms() const {
+ return max_snapshot_age_ms_;
+ }
+ const std::optional<int64_t>& max_ref_age_ms() const { return
max_ref_age_ms_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::string ref_name_;
+ int64_t snapshot_id_;
+ SnapshotRefType type_;
+ std::optional<int32_t> min_snapshots_to_keep_;
+ std::optional<int64_t> max_snapshot_age_ms_;
+ std::optional<int64_t> max_ref_age_ms_;
+};
+
+/// \brief Represents setting table properties
+class ICEBERG_EXPORT SetProperties : public TableUpdate {
+ public:
+ explicit SetProperties(std::unordered_map<std::string, std::string> updated)
+ : updated_(std::move(updated)) {}
+
+ const std::unordered_map<std::string, std::string>& updated() const { return
updated_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::unordered_map<std::string, std::string> updated_;
+};
+
+/// \brief Represents removing table properties
+class ICEBERG_EXPORT RemoveProperties : public TableUpdate {
+ public:
+ explicit RemoveProperties(std::vector<std::string> removed)
+ : removed_(std::move(removed)) {}
+
+ const std::vector<std::string>& removed() const { return removed_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::vector<std::string> removed_;
+};
+
+/// \brief Represents setting the table location
+class ICEBERG_EXPORT SetLocation : public TableUpdate {
+ public:
+ explicit SetLocation(std::string location) : location_(std::move(location))
{}
+
+ const std::string& location() const { return location_; }
+
+ void ApplyTo(TableMetadataBuilder& builder) const override;
+
+ Status GenerateRequirements(TableUpdateContext& context) const override;
+
+ private:
+ std::string location_;
+};
+
+} // namespace table
+
+} // namespace iceberg
diff --git a/src/iceberg/test/mock_catalog.h b/src/iceberg/test/mock_catalog.h
index 5363f1c..f54982b 100644
--- a/src/iceberg/test/mock_catalog.h
+++ b/src/iceberg/test/mock_catalog.h
@@ -62,8 +62,8 @@ class MockCatalog : public Catalog {
MOCK_METHOD((Result<std::unique_ptr<Table>>), UpdateTable,
(const TableIdentifier&,
- (const std::vector<std::unique_ptr<UpdateRequirement>>&),
- (const std::vector<std::unique_ptr<MetadataUpdate>>&)),
+ (const std::vector<std::unique_ptr<TableRequirement>>&),
+ (const std::vector<std::unique_ptr<TableUpdate>>&)),
(override));
MOCK_METHOD((Result<std::shared_ptr<Transaction>>), StageCreateTable,
diff --git a/src/iceberg/type_fwd.h b/src/iceberg/type_fwd.h
index bdc5c1e..3bd067d 100644
--- a/src/iceberg/type_fwd.h
+++ b/src/iceberg/type_fwd.h
@@ -142,12 +142,16 @@ class StructLike;
class ArrayLike;
class MapLike;
+class TableUpdate;
+class TableRequirement;
+class TableMetadataBuilder;
+class TableUpdateContext;
+
///
----------------------------------------------------------------------------
/// TODO: Forward declarations below are not added yet.
///
----------------------------------------------------------------------------
-class MetadataUpdate;
-class UpdateRequirement;
class AppendFiles;
+class EncryptedKey;
} // namespace iceberg
diff --git a/src/iceberg/util/string_util.h b/src/iceberg/util/string_util.h
index a0fccfd..a22aa7a 100644
--- a/src/iceberg/util/string_util.h
+++ b/src/iceberg/util/string_util.h
@@ -44,6 +44,11 @@ class ICEBERG_EXPORT StringUtils {
[](char c) { return std::toupper(c); }); // NOLINT
return input;
}
+
+ static bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs)
{
+ return std::ranges::equal(
+ lhs, rhs, [](char lc, char rc) { return std::tolower(lc) ==
std::tolower(rc); });
+ }
};
/// \brief Transparent hash function that supports std::string_view as lookup
key