in crates/iceberg/src/spec/table_metadata_builder.rs [979:1019]
fn update_snapshot_log(&mut self) -> Result<()> {
let intermediate_snapshots = self.get_intermediate_snapshots();
let has_removed_snapshots = self
.changes
.iter()
.any(|update| matches!(update, TableUpdate::RemoveSnapshots { .. }));
if intermediate_snapshots.is_empty() && !has_removed_snapshots {
return Ok(());
}
let mut new_snapshot_log = Vec::new();
for log_entry in &self.metadata.snapshot_log {
let snapshot_id = log_entry.snapshot_id;
if self.metadata.snapshots.contains_key(&snapshot_id) {
if !intermediate_snapshots.contains(&snapshot_id) {
new_snapshot_log.push(log_entry.clone());
}
} else if has_removed_snapshots {
// any invalid entry causes the history before it to be removed. otherwise, there could be
// history gaps that cause time-travel queries to produce incorrect results. for example,
// if history is [(t1, s1), (t2, s2), (t3, s3)] and s2 is removed, the history cannot be
// [(t1, s1), (t3, s3)] because it appears that s3 was current during the time between t2
// and t3 when in fact s2 was the current snapshot.
new_snapshot_log.clear();
}
}
if let Some(current_snapshot_id) = self.metadata.current_snapshot_id {
let last_id = new_snapshot_log.last().map(|entry| entry.snapshot_id);
if last_id != Some(current_snapshot_id) {
return Err(Error::new(
ErrorKind::DataInvalid,
"Cannot set invalid snapshot log: latest entry is not the current snapshot",
));
}
};
self.metadata.snapshot_log = new_snapshot_log;
Ok(())
}