| /* |
| * 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 <memory> |
| #include <ranges> |
| #include <string> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include "iceberg/partition_spec.h" |
| #include "iceberg/schema.h" |
| #include "iceberg/snapshot.h" |
| #include "iceberg/sort_order.h" |
| #include "iceberg/table_metadata.h" |
| #include "iceberg/table_requirement.h" |
| #include "iceberg/table_update.h" |
| #include "iceberg/test/matchers.h" |
| #include "iceberg/type.h" |
| |
| namespace iceberg { |
| |
| namespace { |
| |
| // Helper function to create base metadata for tests |
| std::unique_ptr<TableMetadata> CreateBaseMetadata( |
| const std::string& uuid = "test-uuid-1234") { |
| auto metadata = std::make_unique<TableMetadata>(); |
| metadata->format_version = 2; |
| metadata->table_uuid = uuid; |
| metadata->location = "s3://bucket/test"; |
| metadata->last_sequence_number = 0; |
| metadata->last_updated_ms = TimePointMs{std::chrono::milliseconds(1000)}; |
| metadata->last_column_id = 0; |
| metadata->current_schema_id = Schema::kInitialSchemaId; |
| metadata->default_spec_id = PartitionSpec::kInitialSpecId; |
| metadata->last_partition_id = 0; |
| metadata->current_snapshot_id = kInvalidSnapshotId; |
| metadata->default_sort_order_id = SortOrder::kUnsortedOrderId; |
| metadata->next_row_id = TableMetadata::kInitialRowId; |
| return metadata; |
| } |
| |
| // Helper function to create a simple schema for tests |
| std::shared_ptr<Schema> CreateTestSchema(int32_t schema_id = 0) { |
| std::vector<SchemaField> fields; |
| fields.emplace_back(SchemaField::MakeRequired(1, "id", int32())); |
| return std::make_shared<Schema>(std::move(fields), schema_id); |
| } |
| |
| // Helper function to count requirements of a specific type |
| template <typename T> |
| int CountRequirementsOfType( |
| const std::vector<std::unique_ptr<TableRequirement>>& requirements) { |
| return std::ranges::count_if(requirements, [](const auto& req) { |
| return dynamic_cast<T*>(req.get()) != nullptr; |
| }); |
| } |
| |
| // Helper function to add a branch to metadata |
| void AddBranch(TableMetadata& metadata, const std::string& name, int64_t snapshot_id) { |
| auto ref = std::make_shared<SnapshotRef>(); |
| ref->snapshot_id = snapshot_id; |
| ref->retention = SnapshotRef::Branch{}; |
| metadata.refs[name] = ref; |
| } |
| |
| } // namespace |
| |
| // Empty Updates Tests |
| |
| TEST(TableRequirementsTest, EmptyUpdatesForCreateTable) { |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto result = TableRequirements::ForCreateTable(updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Should have only AssertDoesNotExist requirement |
| auto* assert_does_not_exist = |
| dynamic_cast<table::AssertDoesNotExist*>(requirements[0].get()); |
| EXPECT_NE(assert_does_not_exist, nullptr); |
| } |
| |
| TEST(TableRequirementsTest, IsCreate) { |
| // Should have only AssertDoesNotExist requirement |
| std::vector<std::unique_ptr<TableRequirement>> requirements; |
| requirements.push_back(std::make_unique<table::AssertDoesNotExist>()); |
| EXPECT_TRUE(TableRequirements::IsCreate(requirements)); |
| |
| // Not only have AssertDoesNotExist requirement |
| requirements.push_back(std::make_unique<table::AssertCurrentSchemaID>(0)); |
| auto res = TableRequirements::IsCreate(requirements); |
| EXPECT_THAT(res, IsError(ErrorKind::kInvalidArgument)); |
| EXPECT_THAT(res, |
| HasErrorMessage("Cannot have other requirements than AssertDoesNotExist")); |
| } |
| |
| TEST(TableRequirementsTest, EmptyUpdatesForUpdateTable) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Should have only AssertUUID requirement |
| auto* assert_uuid = dynamic_cast<table::AssertUUID*>(requirements[0].get()); |
| ASSERT_NE(assert_uuid, nullptr); |
| EXPECT_EQ(assert_uuid->uuid(), metadata->table_uuid); |
| } |
| |
| TEST(TableRequirementsTest, EmptyUpdatesForReplaceTable) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Should have only AssertUUID requirement |
| auto* assert_uuid = dynamic_cast<table::AssertUUID*>(requirements[0].get()); |
| ASSERT_NE(assert_uuid, nullptr); |
| EXPECT_EQ(assert_uuid->uuid(), metadata->table_uuid); |
| } |
| |
| // Table Existence Tests |
| |
| TEST(TableRequirementsTest, TableAlreadyExists) { |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto result = TableRequirements::ForCreateTable(updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Validate against existing metadata (should fail) |
| auto metadata = CreateBaseMetadata(); |
| auto status = requirements[0]->Validate(metadata.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("table already exists")); |
| } |
| |
| TEST(TableRequirementsTest, TableDoesNotExist) { |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto result = TableRequirements::ForCreateTable(updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Validate against null metadata (should succeed) |
| auto status = requirements[0]->Validate(nullptr); |
| EXPECT_THAT(status, IsOk()); |
| } |
| |
| // AssignUUID Tests |
| |
| TEST(TableRequirementsTest, AssignUUID) { |
| auto metadata = CreateBaseMetadata("original-uuid"); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| // Add multiple AssignUUID updates |
| updates.push_back(std::make_unique<table::AssignUUID>(metadata->table_uuid)); |
| updates.push_back(std::make_unique<table::AssignUUID>("new-uuid-1")); |
| updates.push_back(std::make_unique<table::AssignUUID>("new-uuid-2")); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // After deduplication: only 1 AssertUUID from ForUpdateTable |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Should have AssertUUID requirement with original UUID |
| auto* assert_uuid = dynamic_cast<table::AssertUUID*>(requirements[0].get()); |
| ASSERT_NE(assert_uuid, nullptr); |
| EXPECT_EQ(assert_uuid->uuid(), "original-uuid"); |
| |
| auto status = requirements[0]->Validate(metadata.get()); |
| EXPECT_THAT(status, IsOk()); |
| } |
| |
| TEST(TableRequirementsTest, AssignUUIDFailure) { |
| auto metadata = CreateBaseMetadata("original-uuid"); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::AssignUUID>(metadata->table_uuid)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // After deduplication: only 1 AssertUUID from ForUpdateTable |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Create updated metadata with different UUID |
| auto updated = CreateBaseMetadata("different-uuid"); |
| |
| // Validate against updated metadata (should fail) |
| auto status = requirements[0]->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("UUID does not match")); |
| } |
| |
| TEST(TableRequirementsTest, AssignUUIDForReplaceTable) { |
| auto metadata = CreateBaseMetadata("original-uuid"); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| // Add multiple AssignUUID updates |
| updates.push_back(std::make_unique<table::AssignUUID>(metadata->table_uuid)); |
| updates.push_back(std::make_unique<table::AssignUUID>("new-uuid-1")); |
| updates.push_back(std::make_unique<table::AssignUUID>("new-uuid-2")); |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // After deduplication: only 1 AssertUUID from ForReplaceTable |
| ASSERT_EQ(requirements.size(), 1); |
| |
| // Should have AssertUUID requirement |
| auto* assert_uuid = dynamic_cast<table::AssertUUID*>(requirements[0].get()); |
| ASSERT_NE(assert_uuid, nullptr); |
| EXPECT_EQ(assert_uuid->uuid(), "original-uuid"); |
| |
| // Validate against base metadata (should succeed) |
| auto status = requirements[0]->Validate(metadata.get()); |
| EXPECT_THAT(status, IsOk()); |
| } |
| |
| // UpgradeFormatVersion Tests |
| |
| TEST(TableRequirementsTest, UpgradeFormatVersion) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::UpgradeFormatVersion>(2)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // UpgradeFormatVersion doesn't add additional requirements |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // AddSchema Tests |
| |
| TEST(TableRequirementsTest, AddSchema) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->last_column_id = 1; |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| auto schema = CreateTestSchema(); |
| // Add multiple AddSchema updates |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 1)); |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 1)); |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 1)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertLastAssignedFieldId (deduplicated) |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertLastAssignedFieldId>(requirements), 1); |
| |
| // Verify the last assigned field ID value |
| auto* assert_field_id = |
| dynamic_cast<table::AssertLastAssignedFieldId*>(requirements[1].get()); |
| ASSERT_NE(assert_field_id, nullptr); |
| EXPECT_EQ(assert_field_id->last_assigned_field_id(), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, AddSchemaFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->last_column_id = 2; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| auto schema = CreateTestSchema(); |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 2)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different last_column_id |
| auto updated = CreateBaseMetadata(); |
| updated->last_column_id = 3; |
| |
| // Find and validate the AssertLastAssignedFieldId requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertLastAssignedFieldId*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("last assigned field ID does not match")); |
| break; |
| } |
| } |
| } |
| |
| // SetCurrentSchema Tests |
| |
| TEST(TableRequirementsTest, SetCurrentSchema) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| // Add multiple SetCurrentSchema updates |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(3)); |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(4)); |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(5)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertCurrentSchemaID (deduplicated) |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertCurrentSchemaID>(requirements), 1); |
| |
| // Verify the current schema ID value |
| auto* assert_schema_id = |
| dynamic_cast<table::AssertCurrentSchemaID*>(requirements[1].get()); |
| ASSERT_NE(assert_schema_id, nullptr); |
| EXPECT_EQ(assert_schema_id->schema_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, SetCurrentSchemaFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(3)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different current_schema_id |
| auto updated = CreateBaseMetadata(); |
| updated->current_schema_id = 4; |
| |
| // Find and validate the AssertCurrentSchemaID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertCurrentSchemaID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("current schema ID does not match")); |
| break; |
| } |
| } |
| } |
| |
| // AddPartitionSpec Tests |
| |
| TEST(TableRequirementsTest, AddPartitionSpec) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->last_partition_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::AddPartitionSpec>(PartitionSpec::Unpartitioned())); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertLastAssignedPartitionId |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertLastAssignedPartitionId>(requirements), |
| 1); |
| |
| // Verify the last assigned partition ID value |
| auto* assert_partition_id = |
| dynamic_cast<table::AssertLastAssignedPartitionId*>(requirements[1].get()); |
| ASSERT_NE(assert_partition_id, nullptr); |
| EXPECT_EQ(assert_partition_id->last_assigned_partition_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, AddPartitionSpecFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->last_partition_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::AddPartitionSpec>(PartitionSpec::Unpartitioned())); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different last_partition_id |
| auto updated = CreateBaseMetadata(); |
| updated->last_partition_id = 4; |
| |
| // Find and validate the AssertLastAssignedPartitionId requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertLastAssignedPartitionId*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("last assigned partition ID does not match")); |
| break; |
| } |
| } |
| } |
| |
| // SetDefaultPartitionSpec Tests |
| |
| TEST(TableRequirementsTest, SetDefaultPartitionSpec) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| // Add multiple SetDefaultPartitionSpec updates |
| updates.push_back(std::make_unique<table::SetDefaultPartitionSpec>(3)); |
| updates.push_back(std::make_unique<table::SetDefaultPartitionSpec>(4)); |
| updates.push_back(std::make_unique<table::SetDefaultPartitionSpec>(5)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertDefaultSpecID (deduplicated) |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSpecID>(requirements), 1); |
| |
| // Verify the default spec ID value |
| auto* assert_spec_id = dynamic_cast<table::AssertDefaultSpecID*>(requirements[1].get()); |
| ASSERT_NE(assert_spec_id, nullptr); |
| EXPECT_EQ(assert_spec_id->spec_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, SetDefaultPartitionSpecFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = PartitionSpec::kInitialSpecId; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::SetDefaultPartitionSpec>(PartitionSpec::kInitialSpecId)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different default_spec_id |
| auto updated = CreateBaseMetadata(); |
| updated->default_spec_id = PartitionSpec::kInitialSpecId + 1; |
| |
| // Find and validate the AssertDefaultSpecID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertDefaultSpecID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("default partition spec changed")); |
| break; |
| } |
| } |
| } |
| |
| // RemovePartitionSpecs Tests |
| |
| TEST(TableRequirementsTest, RemovePartitionSpecs) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemovePartitionSpecs>(std::vector<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertDefaultSpecID |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSpecID>(requirements), 1); |
| |
| // Verify the default spec ID value |
| auto* assert_spec_id = dynamic_cast<table::AssertDefaultSpecID*>(requirements[1].get()); |
| ASSERT_NE(assert_spec_id, nullptr); |
| EXPECT_EQ(assert_spec_id->spec_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemovePartitionSpecsWithBranch) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| AddBranch(*metadata, "branch", 42); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemovePartitionSpecs>(std::vector<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertDefaultSpecID + AssertRefSnapshotID |
| ASSERT_EQ(requirements.size(), 3); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSpecID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertRefSnapshotID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemovePartitionSpecsWithSpecChangedFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemovePartitionSpecs>(std::vector<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different default_spec_id |
| auto updated = CreateBaseMetadata(); |
| updated->default_spec_id = 4; |
| |
| // Find and validate the AssertDefaultSpecID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertDefaultSpecID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("default partition spec changed")); |
| break; |
| } |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemovePartitionSpecsWithBranchChangedFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| AddBranch(*metadata, "test", 42); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemovePartitionSpecs>(std::vector<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with changed branch |
| auto updated = CreateBaseMetadata(); |
| updated->default_spec_id = 3; |
| AddBranch(*updated, "test", 43); |
| |
| // Find and validate the AssertRefSnapshotID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertRefSnapshotID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("has changed")); |
| break; |
| } |
| } |
| } |
| |
| // RemoveSchemas Tests |
| |
| TEST(TableRequirementsTest, RemoveSchemas) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemoveSchemas>(std::unordered_set<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertCurrentSchemaID |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertCurrentSchemaID>(requirements), 1); |
| |
| // Verify the current schema ID value |
| auto* assert_schema_id = |
| dynamic_cast<table::AssertCurrentSchemaID*>(requirements[1].get()); |
| ASSERT_NE(assert_schema_id, nullptr); |
| EXPECT_EQ(assert_schema_id->schema_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemoveSchemasWithBranch) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| AddBranch(*metadata, "branch", 42); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemoveSchemas>(std::unordered_set<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertCurrentSchemaID + AssertRefSnapshotID |
| ASSERT_EQ(requirements.size(), 3); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertCurrentSchemaID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertRefSnapshotID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemoveSchemasWithSchemaChangedFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemoveSchemas>(std::unordered_set<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different current_schema_id |
| auto updated = CreateBaseMetadata(); |
| updated->current_schema_id = 4; |
| |
| // Find and validate the AssertCurrentSchemaID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertCurrentSchemaID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("current schema ID does not match")); |
| break; |
| } |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemoveSchemasWithBranchChangedFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| AddBranch(*metadata, "test", 42); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemoveSchemas>(std::unordered_set<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with changed branch |
| auto updated = CreateBaseMetadata(); |
| updated->current_schema_id = 3; |
| AddBranch(*updated, "test", 43); |
| |
| // Find and validate the AssertRefSnapshotID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertRefSnapshotID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("has changed")); |
| break; |
| } |
| } |
| } |
| |
| // AddSortOrder Tests |
| |
| TEST(TableRequirementsTest, AddSortOrder) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| updates.push_back(std::make_unique<table::AddSortOrder>(SortOrder::Unsorted())); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // AddSortOrder doesn't add additional requirements |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // SetDefaultSortOrder Tests |
| |
| TEST(TableRequirementsTest, SetDefaultSortOrder) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_sort_order_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| // Add multiple SetDefaultSortOrder updates |
| updates.push_back(std::make_unique<table::SetDefaultSortOrder>(3)); |
| updates.push_back(std::make_unique<table::SetDefaultSortOrder>(4)); |
| updates.push_back(std::make_unique<table::SetDefaultSortOrder>(5)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have AssertUUID + AssertDefaultSortOrderID (deduplicated) |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSortOrderID>(requirements), 1); |
| |
| // Verify the default sort order ID value |
| auto* assert_sort_order_id = |
| dynamic_cast<table::AssertDefaultSortOrderID*>(requirements[1].get()); |
| ASSERT_NE(assert_sort_order_id, nullptr); |
| EXPECT_EQ(assert_sort_order_id->sort_order_id(), 3); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, SetDefaultSortOrderFailure) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_sort_order_id = SortOrder::kUnsortedOrderId; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::SetDefaultSortOrder>(SortOrder::kUnsortedOrderId)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| |
| // Create updated metadata with different default_sort_order_id |
| auto updated = CreateBaseMetadata(); |
| updated->default_sort_order_id = SortOrder::kUnsortedOrderId + 1; |
| |
| // Find and validate the AssertDefaultSortOrderID requirement |
| for (const auto& req : requirements) { |
| if (dynamic_cast<table::AssertDefaultSortOrderID*>(req.get()) != nullptr) { |
| auto status = req->Validate(updated.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("default sort order changed")); |
| break; |
| } |
| } |
| } |
| |
| // AddSnapshot Tests |
| |
| TEST(TableRequirementsTest, AddSnapshot) { |
| auto metadata = CreateBaseMetadata(); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| auto snapshot = std::make_shared<Snapshot>(); |
| snapshot->snapshot_id = 1; |
| snapshot->sequence_number = 1; |
| snapshot->timestamp_ms = TimePointMs{std::chrono::milliseconds(1000)}; |
| snapshot->manifest_list = "s3://bucket/manifest_list"; |
| updates.push_back(std::make_unique<table::AddSnapshot>(snapshot)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // RemoveSnapshots Tests |
| |
| TEST(TableRequirementsTest, RemoveSnapshots) { |
| auto metadata = CreateBaseMetadata(); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::RemoveSnapshots>(std::vector<int64_t>{0})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // SetSnapshotRef Tests |
| |
| TEST(TableRequirementsTest, SetSnapshotRef) { |
| constexpr int64_t kSnapshotId = 14; |
| const std::string kRefName = "branch"; |
| |
| auto metadata = CreateBaseMetadata(); |
| AddBranch(*metadata, kRefName, kSnapshotId); |
| |
| // Multiple updates to same ref should deduplicate |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| ICEBERG_UNWRAP_OR_FAIL(auto ref1, SnapshotRef::MakeBranch(kSnapshotId)); |
| updates.push_back(std::make_unique<table::SetSnapshotRef>(kRefName, *ref1)); |
| ICEBERG_UNWRAP_OR_FAIL(auto ref2, SnapshotRef::MakeBranch(kSnapshotId + 1)); |
| updates.push_back(std::make_unique<table::SetSnapshotRef>(kRefName, *ref2)); |
| ICEBERG_UNWRAP_OR_FAIL(auto ref3, SnapshotRef::MakeBranch(kSnapshotId + 2)); |
| updates.push_back(std::make_unique<table::SetSnapshotRef>(kRefName, *ref3)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| |
| ASSERT_EQ(requirements.size(), 2); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertRefSnapshotID>(requirements), 1); |
| |
| auto* assert_ref = dynamic_cast<table::AssertRefSnapshotID*>(requirements[1].get()); |
| ASSERT_NE(assert_ref, nullptr); |
| EXPECT_EQ(assert_ref->snapshot_id(), kSnapshotId); |
| EXPECT_EQ(assert_ref->ref_name(), kRefName); |
| } |
| |
| // RemoveSnapshotRef Tests |
| |
| TEST(TableRequirementsTest, RemoveSnapshotRef) { |
| auto metadata = CreateBaseMetadata(); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::RemoveSnapshotRef>("branch")); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // SetAndRemoveProperties Tests |
| |
| TEST(TableRequirementsTest, SetProperties) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| std::unordered_map<std::string, std::string> props; |
| props["test"] = "value"; |
| updates.push_back(std::make_unique<table::SetProperties>(props)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // SetProperties doesn't add additional requirements |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| TEST(TableRequirementsTest, RemoveProperties) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| updates.push_back( |
| std::make_unique<table::RemoveProperties>(std::unordered_set<std::string>{"test"})); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // RemoveProperties doesn't add additional requirements |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // SetLocation Tests |
| |
| TEST(TableRequirementsTest, SetLocation) { |
| auto metadata = CreateBaseMetadata(); |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| |
| updates.push_back(std::make_unique<table::SetLocation>("s3://new-bucket/test")); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // SetLocation doesn't add additional requirements |
| ASSERT_EQ(requirements.size(), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| |
| // Validate against base metadata |
| for (const auto& req : requirements) { |
| EXPECT_THAT(req->Validate(metadata.get()), IsOk()); |
| } |
| } |
| |
| // AssertRefSnapshotID Tests |
| |
| TEST(TableRequirementsTest, AssertRefSnapshotIDSuccess) { |
| auto metadata = CreateBaseMetadata(); |
| AddBranch(*metadata, "branch", 14); |
| |
| table::AssertRefSnapshotID requirement("branch", 14); |
| auto status = requirement.Validate(metadata.get()); |
| EXPECT_THAT(status, IsOk()); |
| } |
| |
| TEST(TableRequirementsTest, AssertRefSnapshotIDCreatedConcurrently) { |
| auto metadata = CreateBaseMetadata(); |
| AddBranch(*metadata, "random_branch", 14); |
| |
| // Requirement expects ref doesn't exist (nullopt snapshot_id) |
| table::AssertRefSnapshotID requirement("random_branch", std::nullopt); |
| auto status = requirement.Validate(metadata.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("was created concurrently")); |
| } |
| |
| TEST(TableRequirementsTest, AssertRefSnapshotIDMissing) { |
| auto metadata = CreateBaseMetadata(); |
| // No branch added |
| |
| // Requirement expects a snapshot ID that doesn't exist |
| table::AssertRefSnapshotID requirement("random_branch", 14); |
| auto status = requirement.Validate(metadata.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("is missing")); |
| } |
| |
| TEST(TableRequirementsTest, AssertRefSnapshotIDChanged) { |
| auto metadata = CreateBaseMetadata(); |
| AddBranch(*metadata, "random_branch", 15); |
| |
| // Requirement expects snapshot ID 14, but actual is 15 |
| table::AssertRefSnapshotID requirement("random_branch", 14); |
| auto status = requirement.Validate(metadata.get()); |
| EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed)); |
| EXPECT_THAT(status, HasErrorMessage("has changed")); |
| } |
| |
| // Replace Table Tests (less restrictive than Update) |
| |
| TEST(TableRequirementsTest, ReplaceTableDoesNotRequireCurrentSchemaID) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(5)); |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Replace table should NOT add AssertCurrentSchemaID |
| EXPECT_EQ(CountRequirementsOfType<table::AssertCurrentSchemaID>(requirements), 0); |
| } |
| |
| TEST(TableRequirementsTest, ReplaceTableDoesNotRequireDefaultSpecID) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_spec_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::SetDefaultPartitionSpec>(5)); |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Replace table should NOT add AssertDefaultSpecID |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSpecID>(requirements), 0); |
| } |
| |
| TEST(TableRequirementsTest, ReplaceTableDoesNotRequireDefaultSortOrderID) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->default_sort_order_id = 3; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back(std::make_unique<table::SetDefaultSortOrder>(5)); |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Replace table should NOT add AssertDefaultSortOrderID |
| EXPECT_EQ(CountRequirementsOfType<table::AssertDefaultSortOrderID>(requirements), 0); |
| } |
| |
| TEST(TableRequirementsTest, ReplaceTableDoesNotAddBranchRequirements) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->current_schema_id = 3; |
| AddBranch(*metadata, "branch", 42); |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| updates.push_back( |
| std::make_unique<table::RemoveSchemas>(std::unordered_set<int32_t>{1, 2})); |
| |
| auto result = TableRequirements::ForReplaceTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Replace table should NOT add AssertRefSnapshotID for branches |
| EXPECT_EQ(CountRequirementsOfType<table::AssertRefSnapshotID>(requirements), 0); |
| } |
| |
| // Combined Updates Tests |
| |
| TEST(TableRequirementsTest, MultipleUpdatesDeduplication) { |
| auto metadata = CreateBaseMetadata(); |
| metadata->last_column_id = 1; |
| metadata->current_schema_id = 0; |
| |
| std::vector<std::unique_ptr<TableUpdate>> updates; |
| auto schema = CreateTestSchema(); |
| // Add multiple AddSchema updates - should only generate one requirement |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 1)); |
| updates.push_back(std::make_unique<table::AddSchema>(schema, 1)); |
| // Add multiple SetCurrentSchema updates - should only generate one requirement |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(0)); |
| updates.push_back(std::make_unique<table::SetCurrentSchema>(1)); |
| |
| auto result = TableRequirements::ForUpdateTable(*metadata, updates); |
| ASSERT_THAT(result, IsOk()); |
| |
| auto& requirements = result.value(); |
| // Should have: 1 AssertUUID + 1 AssertLastAssignedFieldId + 1 AssertCurrentSchemaID |
| ASSERT_EQ(requirements.size(), 3); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertUUID>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertLastAssignedFieldId>(requirements), 1); |
| EXPECT_EQ(CountRequirementsOfType<table::AssertCurrentSchemaID>(requirements), 1); |
| } |
| |
| } // namespace iceberg |