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

Reply via email to