2424#include < string>
2525#include < unordered_map>
2626
27- #include " iceberg/result.h"
2827#include " iceberg/snapshot.h"
2928#include " iceberg/table_metadata.h"
3029#include " iceberg/transaction.h"
31- #include " iceberg/util/error_collector.h"
3230#include " iceberg/util/macros.h"
3331#include " iceberg/util/snapshot_util_internal.h"
3432
@@ -43,12 +41,8 @@ Result<std::shared_ptr<UpdateSnapshotReference>> UpdateSnapshotReference::Make(
4341}
4442
4543UpdateSnapshotReference::UpdateSnapshotReference (std::shared_ptr<Transaction> transaction)
46- : PendingUpdate(std::move(transaction)) {
47- // Initialize updated_refs_ with current refs from base metadata
48- for (const auto & [name, ref] : base ().refs ) {
49- updated_refs_[name] = ref;
50- }
51- }
44+ : PendingUpdate(std::move(transaction)),
45+ updated_refs_ (base().refs.begin(), base().refs.end()) {}
5246
5347UpdateSnapshotReference::~UpdateSnapshotReference () = default ;
5448
@@ -113,7 +107,9 @@ UpdateSnapshotReference& UpdateSnapshotReference::ReplaceBranch(const std::strin
113107 ICEBERG_BUILDER_CHECK (it != updated_refs_.end (), " Branch does not exist: {}" , name);
114108 ICEBERG_BUILDER_CHECK (it->second ->type () == SnapshotRefType::kBranch ,
115109 " Ref '{}' is a tag not a branch" , name);
116- it->second ->snapshot_id = snapshot_id;
110+ // Clone the ref before modifying to avoid affecting base metadata
111+ auto cloned = it->second ->Clone (snapshot_id);
112+ it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
117113 return *this ;
118114}
119115
@@ -161,7 +157,9 @@ UpdateSnapshotReference& UpdateSnapshotReference::ReplaceBranchInternal(
161157 " Cannot fast-forward: {} is not an ancestor of {}" , from, to);
162158 }
163159
164- from_it->second ->snapshot_id = to_it->second ->snapshot_id ;
160+ // Clone the ref before modifying to avoid affecting base metadata
161+ auto cloned = from_it->second ->Clone (to_it->second ->snapshot_id );
162+ from_it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
165163 return *this ;
166164}
167165
@@ -172,7 +170,9 @@ UpdateSnapshotReference& UpdateSnapshotReference::ReplaceTag(const std::string&
172170 ICEBERG_BUILDER_CHECK (it != updated_refs_.end (), " Tag does not exist: {}" , name);
173171 ICEBERG_BUILDER_CHECK (it->second ->type () == SnapshotRefType::kTag ,
174172 " Ref '{}' is a branch not a tag" , name);
175- it->second ->snapshot_id = snapshot_id;
173+ // Clone the ref before modifying to avoid affecting base metadata
174+ auto cloned = it->second ->Clone (snapshot_id);
175+ it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
176176 return *this ;
177177}
178178
@@ -183,11 +183,14 @@ UpdateSnapshotReference& UpdateSnapshotReference::SetMinSnapshotsToKeep(
183183 ICEBERG_BUILDER_CHECK (it != updated_refs_.end (), " Branch does not exist: {}" , name);
184184 ICEBERG_BUILDER_CHECK (it->second ->type () == SnapshotRefType::kBranch ,
185185 " Ref '{}' is a tag not a branch" , name);
186- std::get<SnapshotRef::Branch>(it->second ->retention ).min_snapshots_to_keep =
186+ // Clone the ref before modifying to avoid affecting base metadata
187+ auto cloned = it->second ->Clone ();
188+ std::get<SnapshotRef::Branch>(cloned->retention ).min_snapshots_to_keep =
187189 min_snapshots_to_keep;
188- ICEBERG_BUILDER_CHECK (it-> second ->Validate (),
190+ ICEBERG_BUILDER_CHECK (cloned ->Validate (),
189191 " Invalid min_snapshots_to_keep {} for branch '{}'" ,
190192 min_snapshots_to_keep, name);
193+ it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
191194 return *this ;
192195}
193196
@@ -198,11 +201,14 @@ UpdateSnapshotReference& UpdateSnapshotReference::SetMaxSnapshotAgeMs(
198201 ICEBERG_BUILDER_CHECK (it != updated_refs_.end (), " Branch does not exist: {}" , name);
199202 ICEBERG_BUILDER_CHECK (it->second ->type () == SnapshotRefType::kBranch ,
200203 " Ref '{}' is a tag not a branch" , name);
201- std::get<SnapshotRef::Branch>(it->second ->retention ).max_snapshot_age_ms =
204+ // Clone the ref before modifying to avoid affecting base metadata
205+ auto cloned = it->second ->Clone ();
206+ std::get<SnapshotRef::Branch>(cloned->retention ).max_snapshot_age_ms =
202207 max_snapshot_age_ms;
203- ICEBERG_BUILDER_CHECK (it-> second ->Validate (),
208+ ICEBERG_BUILDER_CHECK (cloned ->Validate (),
204209 " Invalid max_snapshot_age_ms {} for branch '{}'" ,
205210 max_snapshot_age_ms, name);
211+ it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
206212 return *this ;
207213}
208214
@@ -211,20 +217,41 @@ UpdateSnapshotReference& UpdateSnapshotReference::SetMaxRefAgeMs(const std::stri
211217 ICEBERG_BUILDER_CHECK (!name.empty (), " Reference name cannot be empty" );
212218 auto it = updated_refs_.find (name);
213219 ICEBERG_BUILDER_CHECK (it != updated_refs_.end (), " Ref does not exist: {}" , name);
214- if (it->second ->type () == SnapshotRefType::kBranch ) {
215- std::get<SnapshotRef::Branch>(it->second ->retention ).max_ref_age_ms = max_ref_age_ms;
220+ // Clone the ref before modifying to avoid affecting base metadata
221+ auto cloned = it->second ->Clone ();
222+ if (cloned->type () == SnapshotRefType::kBranch ) {
223+ std::get<SnapshotRef::Branch>(cloned->retention ).max_ref_age_ms = max_ref_age_ms;
216224 } else {
217- std::get<SnapshotRef::Tag>(it-> second ->retention ).max_ref_age_ms = max_ref_age_ms;
225+ std::get<SnapshotRef::Tag>(cloned ->retention ).max_ref_age_ms = max_ref_age_ms;
218226 }
219- ICEBERG_BUILDER_CHECK (it-> second ->Validate (), " Invalid max_ref_age_ms {} for ref '{}'" ,
227+ ICEBERG_BUILDER_CHECK (cloned ->Validate (), " Invalid max_ref_age_ms {} for ref '{}'" ,
220228 max_ref_age_ms, name);
229+ it->second = std::shared_ptr<SnapshotRef>(cloned.release ());
221230 return *this ;
222231}
223232
224- Result<std::unordered_map<std::string, std::shared_ptr<SnapshotRef>>>
225- UpdateSnapshotReference::Apply () {
233+ Result<UpdateSnapshotReference::ApplyResult> UpdateSnapshotReference::Apply () {
226234 ICEBERG_RETURN_UNEXPECTED (CheckErrors ());
227- return updated_refs_;
235+
236+ ApplyResult result;
237+ const auto & current_refs = base ().refs ;
238+
239+ // Identify references which have been removed
240+ for (const auto & [name, ref] : current_refs) {
241+ if (updated_refs_.find (name) == updated_refs_.end ()) {
242+ result.to_remove .push_back (name);
243+ }
244+ }
245+
246+ // Identify references which have been created or updated
247+ for (const auto & [name, ref] : updated_refs_) {
248+ auto current_it = current_refs.find (name);
249+ if (current_it == current_refs.end () || *current_it->second != *ref) {
250+ result.to_set .emplace_back (name, ref);
251+ }
252+ }
253+
254+ return result;
228255}
229256
230257} // namespace iceberg
0 commit comments