| /* | |
| * 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 |