| /* | |
| * 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 <algorithm> | |
| #include <memory> | |
| #include <ranges> | |
| #include "iceberg/snapshot.h" | |
| #include "iceberg/table_metadata.h" | |
| #include "iceberg/table_requirement.h" | |
| #include "iceberg/table_update.h" | |
| namespace iceberg { | |
| void TableUpdateContext::AddRequirement(std::unique_ptr<TableRequirement> requirement) { | |
| requirements_.emplace_back(std::move(requirement)); | |
| } | |
| Result<std::vector<std::unique_ptr<TableRequirement>>> TableUpdateContext::Build() { | |
| return std::move(requirements_); | |
| } | |
| void TableUpdateContext::RequireLastAssignedFieldIdUnchanged() { | |
| if (!added_last_assigned_field_id_) { | |
| if (base_ != nullptr) { | |
| AddRequirement( | |
| std::make_unique<table::AssertLastAssignedFieldId>(base_->last_column_id)); | |
| } | |
| added_last_assigned_field_id_ = true; | |
| } | |
| } | |
| void TableUpdateContext::RequireCurrentSchemaIdUnchanged() { | |
| if (!added_current_schema_id_) { | |
| if (base_ != nullptr && !is_replace_) { | |
| AddRequirement( | |
| std::make_unique<table::AssertCurrentSchemaID>(base_->current_schema_id)); | |
| } | |
| added_current_schema_id_ = true; | |
| } | |
| } | |
| void TableUpdateContext::RequireLastAssignedPartitionIdUnchanged() { | |
| if (!added_last_assigned_partition_id_) { | |
| if (base_ != nullptr) { | |
| AddRequirement(std::make_unique<table::AssertLastAssignedPartitionId>( | |
| base_->last_partition_id)); | |
| } | |
| added_last_assigned_partition_id_ = true; | |
| } | |
| } | |
| void TableUpdateContext::RequireDefaultSpecIdUnchanged() { | |
| if (!added_default_spec_id_) { | |
| if (base_ != nullptr && !is_replace_) { | |
| AddRequirement( | |
| std::make_unique<table::AssertDefaultSpecID>(base_->default_spec_id)); | |
| } | |
| added_default_spec_id_ = true; | |
| } | |
| } | |
| void TableUpdateContext::RequireDefaultSortOrderIdUnchanged() { | |
| if (!added_default_sort_order_id_) { | |
| if (base_ != nullptr && !is_replace_) { | |
| AddRequirement(std::make_unique<table::AssertDefaultSortOrderID>( | |
| base_->default_sort_order_id)); | |
| } | |
| added_default_sort_order_id_ = true; | |
| } | |
| } | |
| void TableUpdateContext::RequireNoBranchesChanged() { | |
| if (base_ != nullptr && !is_replace_) { | |
| for (const auto& [name, ref] : base_->refs) { | |
| if (ref->type() == SnapshotRefType::kBranch && name != SnapshotRef::kMainBranch) { | |
| AddRequirement( | |
| std::make_unique<table::AssertRefSnapshotID>(name, ref->snapshot_id)); | |
| } | |
| } | |
| } | |
| } | |
| bool TableUpdateContext::AddChangedRef(const std::string& ref_name) { | |
| auto [_, inserted] = changed_refs_.insert(ref_name); | |
| return inserted; | |
| } | |
| Result<std::vector<std::unique_ptr<TableRequirement>>> TableRequirements::ForCreateTable( | |
| const std::vector<std::unique_ptr<TableUpdate>>& table_updates) { | |
| TableUpdateContext context(nullptr, false); | |
| context.AddRequirement(std::make_unique<table::AssertDoesNotExist>()); | |
| for (const auto& update : table_updates) { | |
| update->GenerateRequirements(context); | |
| } | |
| return context.Build(); | |
| } | |
| Result<std::vector<std::unique_ptr<TableRequirement>>> TableRequirements::ForReplaceTable( | |
| const TableMetadata& base, | |
| const std::vector<std::unique_ptr<TableUpdate>>& table_updates) { | |
| TableUpdateContext context(&base, true); | |
| context.AddRequirement(std::make_unique<table::AssertUUID>(base.table_uuid)); | |
| for (const auto& update : table_updates) { | |
| update->GenerateRequirements(context); | |
| } | |
| return context.Build(); | |
| } | |
| Result<std::vector<std::unique_ptr<TableRequirement>>> TableRequirements::ForUpdateTable( | |
| const TableMetadata& base, | |
| const std::vector<std::unique_ptr<TableUpdate>>& table_updates) { | |
| TableUpdateContext context(&base, false); | |
| context.AddRequirement(std::make_unique<table::AssertUUID>(base.table_uuid)); | |
| for (const auto& update : table_updates) { | |
| update->GenerateRequirements(context); | |
| } | |
| return context.Build(); | |
| } | |
| Result<bool> TableRequirements::IsCreate( | |
| const std::vector<std::unique_ptr<TableRequirement>>& requirements) { | |
| bool is_create = std::ranges::any_of(requirements, [](const auto& req) { | |
| return req->kind() == TableRequirement::Kind::kAssertDoesNotExist; | |
| }); | |
| if (is_create) { | |
| ICEBERG_PRECHECK( | |
| requirements.size() == 1, | |
| "Cannot have other requirements than AssertDoesNotExist in a table creation"); | |
| } | |
| return is_create; | |
| } | |
| } // namespace iceberg |