diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt index 75ac9c84..e3709504 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 03bd0f6c..83ea677a 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> UpdateTable( const TableIdentifier& identifier, - const std::vector>& requirements, - const std::vector>& updates) = 0; + const std::vector>& requirements, + const std::vector>& 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 08a9822c..c024aac2 100644 --- a/src/iceberg/catalog/memory/in_memory_catalog.cc +++ b/src/iceberg/catalog/memory/in_memory_catalog.cc @@ -392,8 +392,8 @@ Result> InMemoryCatalog::CreateTable( Result> InMemoryCatalog::UpdateTable( const TableIdentifier& identifier, - const std::vector>& requirements, - const std::vector>& updates) { + const std::vector>& requirements, + const std::vector>& 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 bde97bae..59c6d3ad 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> UpdateTable( const TableIdentifier& identifier, - const std::vector>& requirements, - const std::vector>& updates) override; + const std::vector>& requirements, + const std::vector>& updates) override; Result> StageCreateTable( const TableIdentifier& identifier, const Schema& schema, const PartitionSpec& spec, diff --git a/src/iceberg/meson.build b/src/iceberg/meson.build index df64ae02..25bfdc64 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 79dd52b9..99df3724 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; } 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 e58d06ae..e32e75ee 100644 --- a/src/iceberg/table_metadata.cc +++ b/src/iceberg/table_metadata.cc @@ -19,11 +19,13 @@ #include "iceberg/table_metadata.h" +#include #include #include #include +#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()) {} + +TableMetadataBuilder::TableMetadataBuilder(const TableMetadata* base) + : impl_(std::make_unique()) {} + +TableMetadataBuilder::~TableMetadataBuilder() = default; + +TableMetadataBuilder::TableMetadataBuilder(TableMetadataBuilder&&) noexcept = default; + +TableMetadataBuilder& TableMetadataBuilder::operator=(TableMetadataBuilder&&) noexcept = + default; + +std::unique_ptr TableMetadataBuilder::BuildFromEmpty( + int8_t format_version) { + return std::unique_ptr( + new TableMetadataBuilder(format_version)); // NOLINT +} + +std::unique_ptr TableMetadataBuilder::BuildFrom( + const TableMetadata* base) { + return std::unique_ptr(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, 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) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::SetDefaultPartitionSpec( + std::shared_ptr 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 spec) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::RemovePartitionSpecs( + const std::vector& spec_ids) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::RemoveSchemas( + const std::vector& schema_ids) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::SetDefaultSortOrder( + std::shared_ptr 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 order) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::AddSnapshot( + std::shared_ptr 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 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>& snapshots_to_remove) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::RemoveSnapshots( + const std::vector& 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& 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& 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& updated) { + throw IcebergError(std::format("{} not implemented", __FUNCTION__)); +} + +TableMetadataBuilder& TableMetadataBuilder::RemoveProperties( + const std::vector& 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 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> 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 427447a1..6f7a8190 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 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 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, + 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); + + /// \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 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 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& 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& 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 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 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); + + /// \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 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>& 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& 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& 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& 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& 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& 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 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> 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_; +}; + /// \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 00000000..4ca4b915 --- /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 00000000..c054532a --- /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 +#include + +#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 snapshot_id) + : ref_name_(std::move(ref_name)), snapshot_id_(snapshot_id) {} + + const std::string& ref_name() const { return ref_name_; } + + const std::optional& snapshot_id() const { return snapshot_id_; } + + Status Validate(const TableMetadata* base) const override; + + private: + std::string ref_name_; + std::optional 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 00000000..1eb870cb --- /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 requirement) { + throw IcebergError("TableUpdateContext::AddRequirement not implemented"); +} + +Result>> TableUpdateContext::Build() { + return NotImplemented("TableUpdateContext::Build not implemented"); +} + +Result>> TableRequirements::ForCreateTable( + const std::vector>& table_updates) { + return NotImplemented("TableRequirements::ForCreateTable not implemented"); +} + +Result>> TableRequirements::ForReplaceTable( + const TableMetadata& base, + const std::vector>& table_updates) { + return NotImplemented("TableRequirements::ForReplaceTable not implemented"); +} + +Result>> TableRequirements::ForUpdateTable( + const TableMetadata& base, + const std::vector>& 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 00000000..327e7d53 --- /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 +#include + +#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 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>> Build(); + + private: + const TableMetadata* base_; + const bool is_replace_; + + std::vector> 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>> ForCreateTable( + const std::vector>& 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>> ForReplaceTable( + const TableMetadata& base, + const std::vector>& 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>> ForUpdateTable( + const TableMetadata& base, + const std::vector>& table_updates); +}; + +} // namespace iceberg diff --git a/src/iceberg/table_update.cc b/src/iceberg/table_update.cc new file mode 100644 index 00000000..7d81dd8f --- /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 00000000..445295a4 --- /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 +#include +#include +#include +#include + +#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, int32_t last_column_id) + : schema_(std::move(schema)), last_column_id_(last_column_id) {} + + const std::shared_ptr& 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_; + 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 spec) + : spec_(std::move(spec)) {} + + const std::shared_ptr& spec() const { return spec_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::shared_ptr 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 spec_ids) + : spec_ids_(std::move(spec_ids)) {} + + const std::vector& spec_ids() const { return spec_ids_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::vector spec_ids_; +}; + +/// \brief Represents removing schemas from the table +class ICEBERG_EXPORT RemoveSchemas : public TableUpdate { + public: + explicit RemoveSchemas(std::vector schema_ids) + : schema_ids_(std::move(schema_ids)) {} + + const std::vector& schema_ids() const { return schema_ids_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::vector schema_ids_; +}; + +/// \brief Represents adding a new sort order to the table +class ICEBERG_EXPORT AddSortOrder : public TableUpdate { + public: + explicit AddSortOrder(std::shared_ptr sort_order) + : sort_order_(std::move(sort_order)) {} + + const std::shared_ptr& sort_order() const { return sort_order_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::shared_ptr 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_(std::move(snapshot)) {} + + const std::shared_ptr& snapshot() const { return snapshot_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::shared_ptr snapshot_; +}; + +/// \brief Represents removing snapshots from the table +class ICEBERG_EXPORT RemoveSnapshots : public TableUpdate { + public: + explicit RemoveSnapshots(std::vector snapshot_ids) + : snapshot_ids_(std::move(snapshot_ids)) {} + + const std::vector& snapshot_ids() const { return snapshot_ids_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::vector 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 min_snapshots_to_keep = std::nullopt, + std::optional max_snapshot_age_ms = std::nullopt, + std::optional 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& min_snapshots_to_keep() const { + return min_snapshots_to_keep_; + } + const std::optional& max_snapshot_age_ms() const { + return max_snapshot_age_ms_; + } + const std::optional& 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 min_snapshots_to_keep_; + std::optional max_snapshot_age_ms_; + std::optional max_ref_age_ms_; +}; + +/// \brief Represents setting table properties +class ICEBERG_EXPORT SetProperties : public TableUpdate { + public: + explicit SetProperties(std::unordered_map updated) + : updated_(std::move(updated)) {} + + const std::unordered_map& updated() const { return updated_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::unordered_map updated_; +}; + +/// \brief Represents removing table properties +class ICEBERG_EXPORT RemoveProperties : public TableUpdate { + public: + explicit RemoveProperties(std::vector removed) + : removed_(std::move(removed)) {} + + const std::vector& removed() const { return removed_; } + + void ApplyTo(TableMetadataBuilder& builder) const override; + + Status GenerateRequirements(TableUpdateContext& context) const override; + + private: + std::vector 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 5363f1c7..f54982bb 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>), UpdateTable, (const TableIdentifier&, - (const std::vector>&), - (const std::vector>&)), + (const std::vector>&), + (const std::vector>&)), (override)); MOCK_METHOD((Result>), StageCreateTable, diff --git a/src/iceberg/type_fwd.h b/src/iceberg/type_fwd.h index bdc5c1e3..3bd067d0 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 a0fccfd3..a22aa7a5 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