Skip to content

Commit b147683

Browse files
committed
feat: implement metadata updates and update requirements
This commit introduces the metadata update and update requirements system for table metadata modifications.
1 parent 046f149 commit b147683

12 files changed

+1723
-2
lines changed

src/iceberg/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ set(ICEBERG_SOURCES
3131
manifest_entry.cc
3232
manifest_list.cc
3333
metadata_columns.cc
34+
metadata_update.cc
3435
name_mapping.cc
3536
partition_field.cc
3637
partition_spec.cc
@@ -55,6 +56,8 @@ set(ICEBERG_SOURCES
5556
manifest_reader_internal.cc
5657
manifest_writer.cc
5758
arrow_c_data_guard_internal.cc
59+
update_requirement.cc
60+
update_requirements.cc
5861
util/conversions.cc
5962
util/decimal.cc
6063
util/gzip_internal.cc

src/iceberg/metadata_update.cc

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/metadata_update.h"
21+
22+
#include "iceberg/table_metadata.h"
23+
#include "iceberg/update_requirements.h"
24+
25+
namespace iceberg {
26+
27+
// AssignUUID
28+
29+
void AssignUUID::ApplyTo(TableMetadataBuilder& builder) const {
30+
builder.AssignUUID(uuid_);
31+
}
32+
33+
void AssignUUID::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
34+
// AssignUUID doesn't generate any requirements
35+
}
36+
37+
// UpgradeFormatVersion
38+
39+
void UpgradeFormatVersion::ApplyTo(TableMetadataBuilder& builder) const {
40+
builder.UpgradeFormatVersion(format_version_);
41+
}
42+
43+
void UpgradeFormatVersion::GenerateRequirements(
44+
UpdateRequirementsContext& /*context*/) const {
45+
// UpgradeFormatVersion doesn't generate any requirements
46+
}
47+
48+
// AddSchema
49+
50+
void AddSchema::ApplyTo(TableMetadataBuilder& builder) const {
51+
builder.AddSchema(schema_);
52+
}
53+
54+
void AddSchema::GenerateRequirements(UpdateRequirementsContext& context) const {
55+
if (context.base() != nullptr) {
56+
context.AddRequirement(
57+
std::make_unique<AssertLastAssignedFieldId>(context.base()->last_column_id));
58+
}
59+
}
60+
61+
// SetCurrentSchema
62+
63+
void SetCurrentSchema::ApplyTo(TableMetadataBuilder& builder) const {
64+
builder.SetCurrentSchema(schema_id_);
65+
}
66+
67+
void SetCurrentSchema::GenerateRequirements(UpdateRequirementsContext& context) const {
68+
// Require current schema not changed
69+
if (context.base() != nullptr && !context.is_replace()) {
70+
if (context.base()->current_schema_id.has_value()) {
71+
context.AddRequirement(std::make_unique<AssertCurrentSchemaID>(
72+
context.base()->current_schema_id.value()));
73+
}
74+
}
75+
}
76+
77+
// AddPartitionSpec
78+
79+
void AddPartitionSpec::ApplyTo(TableMetadataBuilder& builder) const {
80+
builder.AddPartitionSpec(spec_);
81+
}
82+
83+
void AddPartitionSpec::GenerateRequirements(UpdateRequirementsContext& context) const {
84+
if (context.base() != nullptr) {
85+
context.AddRequirement(std::make_unique<AssertLastAssignedPartitionId>(
86+
context.base()->last_partition_id));
87+
}
88+
}
89+
90+
// SetDefaultPartitionSpec
91+
92+
void SetDefaultPartitionSpec::ApplyTo(TableMetadataBuilder& builder) const {
93+
builder.SetDefaultPartitionSpec(spec_id_);
94+
}
95+
96+
void SetDefaultPartitionSpec::GenerateRequirements(
97+
UpdateRequirementsContext& context) const {
98+
// Require default partition spec not changed
99+
if (context.base() != nullptr && !context.is_replace()) {
100+
context.AddRequirement(
101+
std::make_unique<AssertDefaultSpecID>(context.base()->default_spec_id));
102+
}
103+
}
104+
105+
// RemovePartitionSpecs
106+
107+
void RemovePartitionSpecs::ApplyTo(TableMetadataBuilder& builder) const {
108+
builder.RemovePartitionSpecs(spec_ids_);
109+
}
110+
111+
void RemovePartitionSpecs::GenerateRequirements(
112+
UpdateRequirementsContext& context) const {
113+
// Require default partition spec not changed
114+
if (context.base() != nullptr && !context.is_replace()) {
115+
context.AddRequirement(
116+
std::make_unique<AssertDefaultSpecID>(context.base()->default_spec_id));
117+
}
118+
119+
// Require that no branches have changed
120+
if (context.base() != nullptr && !context.is_replace()) {
121+
for (const auto& [name, ref] : context.base()->refs) {
122+
if (ref->type() == SnapshotRefType::kBranch && name != "main") {
123+
context.AddRequirement(
124+
std::make_unique<AssertRefSnapshotID>(name, ref->snapshot_id));
125+
}
126+
}
127+
}
128+
}
129+
130+
// RemoveSchemas
131+
132+
void RemoveSchemas::ApplyTo(TableMetadataBuilder& builder) const {
133+
builder.RemoveSchemas(schema_ids_);
134+
}
135+
136+
void RemoveSchemas::GenerateRequirements(UpdateRequirementsContext& context) const {
137+
// Require current schema not changed
138+
if (context.base() != nullptr && !context.is_replace()) {
139+
if (context.base()->current_schema_id.has_value()) {
140+
context.AddRequirement(std::make_unique<AssertCurrentSchemaID>(
141+
context.base()->current_schema_id.value()));
142+
}
143+
}
144+
145+
// Require that no branches have changed
146+
if (context.base() != nullptr && !context.is_replace()) {
147+
for (const auto& [name, ref] : context.base()->refs) {
148+
if (ref->type() == SnapshotRefType::kBranch && name != "main") {
149+
context.AddRequirement(
150+
std::make_unique<AssertRefSnapshotID>(name, ref->snapshot_id));
151+
}
152+
}
153+
}
154+
}
155+
156+
// AddSortOrder
157+
158+
void AddSortOrder::ApplyTo(TableMetadataBuilder& builder) const {
159+
builder.AddSortOrder(sort_order_);
160+
}
161+
162+
void AddSortOrder::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
163+
// AddSortOrder doesn't generate any requirements
164+
}
165+
166+
// SetDefaultSortOrder
167+
168+
void SetDefaultSortOrder::ApplyTo(TableMetadataBuilder& builder) const {
169+
builder.SetDefaultSortOrder(sort_order_id_);
170+
}
171+
172+
void SetDefaultSortOrder::GenerateRequirements(UpdateRequirementsContext& context) const {
173+
if (context.base() != nullptr && !context.is_replace()) {
174+
context.AddRequirement(std::make_unique<AssertDefaultSortOrderID>(
175+
context.base()->default_sort_order_id));
176+
}
177+
}
178+
179+
// AddSnapshot
180+
181+
void AddSnapshot::ApplyTo(TableMetadataBuilder& builder) const {
182+
builder.AddSnapshot(snapshot_);
183+
}
184+
185+
void AddSnapshot::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
186+
// AddSnapshot doesn't generate any requirements
187+
}
188+
189+
// RemoveSnapshots
190+
191+
void RemoveSnapshots::ApplyTo(TableMetadataBuilder& builder) const {
192+
builder.RemoveSnapshots(snapshot_ids_);
193+
}
194+
195+
void RemoveSnapshots::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
196+
// RemoveSnapshots doesn't generate any requirements
197+
}
198+
199+
// RemoveSnapshotRef
200+
201+
void RemoveSnapshotRef::ApplyTo(TableMetadataBuilder& builder) const {
202+
builder.RemoveRef(ref_name_);
203+
}
204+
205+
void RemoveSnapshotRef::GenerateRequirements(
206+
UpdateRequirementsContext& /*context*/) const {
207+
// RemoveSnapshotRef doesn't generate any requirements
208+
}
209+
210+
// SetSnapshotRef
211+
212+
void SetSnapshotRef::ApplyTo(TableMetadataBuilder& builder) const {
213+
// Create a SnapshotRef based on the type
214+
std::shared_ptr<SnapshotRef> ref;
215+
216+
if (type_ == SnapshotRefType::kBranch) {
217+
SnapshotRef::Branch branch;
218+
branch.min_snapshots_to_keep = min_snapshots_to_keep_;
219+
branch.max_snapshot_age_ms = max_snapshot_age_ms_;
220+
branch.max_ref_age_ms = max_ref_age_ms_;
221+
222+
ref = std::make_shared<SnapshotRef>();
223+
ref->snapshot_id = snapshot_id_;
224+
ref->retention = branch;
225+
} else {
226+
SnapshotRef::Tag tag;
227+
tag.max_ref_age_ms = max_ref_age_ms_;
228+
229+
ref = std::make_shared<SnapshotRef>();
230+
ref->snapshot_id = snapshot_id_;
231+
ref->retention = tag;
232+
}
233+
234+
builder.SetRef(ref_name_, ref);
235+
}
236+
237+
void SetSnapshotRef::GenerateRequirements(UpdateRequirementsContext& context) const {
238+
// Require that the ref is unchanged from the base
239+
if (context.base() != nullptr && !context.is_replace()) {
240+
// Find the reference in the base metadata
241+
auto it = context.base()->refs.find(ref_name_);
242+
std::optional<int64_t> base_snapshot_id;
243+
244+
if (it != context.base()->refs.end()) {
245+
base_snapshot_id = it->second->snapshot_id;
246+
}
247+
248+
// Require that the ref does not exist (nullopt) or is the same as the base snapshot
249+
context.AddRequirement(
250+
std::make_unique<AssertRefSnapshotID>(ref_name_, base_snapshot_id));
251+
}
252+
}
253+
254+
// SetProperties
255+
256+
void SetProperties::ApplyTo(TableMetadataBuilder& builder) const {
257+
builder.SetProperties(updated_);
258+
}
259+
260+
void SetProperties::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
261+
// SetProperties doesn't generate any requirements
262+
}
263+
264+
// RemoveProperties
265+
266+
void RemoveProperties::ApplyTo(TableMetadataBuilder& builder) const {
267+
builder.RemoveProperties(removed_);
268+
}
269+
270+
void RemoveProperties::GenerateRequirements(
271+
UpdateRequirementsContext& /*context*/) const {
272+
// RemoveProperties doesn't generate any requirements
273+
}
274+
275+
// SetLocation
276+
277+
void SetLocation::ApplyTo(TableMetadataBuilder& builder) const {
278+
builder.SetLocation(location_);
279+
}
280+
281+
void SetLocation::GenerateRequirements(UpdateRequirementsContext& /*context*/) const {
282+
// SetLocation doesn't generate any requirements
283+
}
284+
285+
} // namespace iceberg

0 commit comments

Comments
 (0)