Skip to content

Commit ed12ac4

Browse files
authored
feat(catalog): Add TableCommit::apply to help update metadata (#1464)
## Which issue does this PR close? - Closes #1386 ## What changes are included in this PR? - Added `TableCommit::apply` to apply updates and requirements to a table and return a table with updated metadata - minor typo fix ## Are these changes tested? added unit test
1 parent c85d980 commit ed12ac4

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

crates/iceberg/src/catalog/mod.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::fmt::{Debug, Display};
2222
use std::future::Future;
2323
use std::mem::take;
2424
use std::ops::Deref;
25+
use std::sync::Arc;
2526

2627
use _serde::deserialize_snapshot;
2728
use async_trait::async_trait;
@@ -313,6 +314,27 @@ impl TableCommit {
313314
pub fn take_updates(&mut self) -> Vec<TableUpdate> {
314315
take(&mut self.updates)
315316
}
317+
318+
/// Applies this [`TableCommit`] to the given [`Table`] as part of a catalog update.
319+
/// Typically used by [`Catalog::update_table`] to validate requirements and apply metadata updates.
320+
///
321+
/// Returns a new [`Table`] with updated metadata,
322+
/// or an error if validation or application fails.
323+
pub fn apply(self, table: Table) -> Result<Table> {
324+
// check requirements
325+
for requirement in self.requirements {
326+
requirement.check(Some(table.metadata()))?;
327+
}
328+
329+
// apply updates to metadata builder
330+
let mut metadata_builder = table.metadata().clone().into_builder(None);
331+
332+
for update in self.updates {
333+
metadata_builder = update.apply(metadata_builder)?;
334+
}
335+
336+
Ok(table.with_metadata(Arc::new(metadata_builder.build()?.metadata)))
337+
}
316338
}
317339

318340
/// TableRequirement represents a requirement for a table in the catalog.
@@ -884,12 +906,15 @@ mod _serde_set_statistics {
884906
mod tests {
885907
use std::collections::HashMap;
886908
use std::fmt::Debug;
909+
use std::fs::File;
910+
use std::io::BufReader;
887911

888912
use serde::Serialize;
889913
use serde::de::DeserializeOwned;
890914
use uuid::uuid;
891915

892916
use super::ViewUpdate;
917+
use crate::io::FileIOBuilder;
893918
use crate::spec::{
894919
BlobMetadata, FormatVersion, MAIN_BRANCH, NestedField, NullOrder, Operation,
895920
PartitionStatisticsFile, PrimitiveType, Schema, Snapshot, SnapshotReference,
@@ -898,7 +923,10 @@ mod tests {
898923
UnboundPartitionSpec, ViewFormatVersion, ViewRepresentation, ViewRepresentations,
899924
ViewVersion,
900925
};
901-
use crate::{NamespaceIdent, TableCreation, TableIdent, TableRequirement, TableUpdate};
926+
use crate::table::Table;
927+
use crate::{
928+
NamespaceIdent, TableCommit, TableCreation, TableIdent, TableRequirement, TableUpdate,
929+
};
902930

903931
#[test]
904932
fn test_parent_namespace() {
@@ -2111,4 +2139,66 @@ mod tests {
21112139
},
21122140
);
21132141
}
2142+
2143+
#[test]
2144+
fn test_table_commit() {
2145+
let table = {
2146+
let file = File::open(format!(
2147+
"{}/testdata/table_metadata/{}",
2148+
env!("CARGO_MANIFEST_DIR"),
2149+
"TableMetadataV2Valid.json"
2150+
))
2151+
.unwrap();
2152+
let reader = BufReader::new(file);
2153+
let resp = serde_json::from_reader::<_, TableMetadata>(reader).unwrap();
2154+
2155+
Table::builder()
2156+
.metadata(resp)
2157+
.metadata_location("s3://bucket/test/location/metadata/v2.json".to_string())
2158+
.identifier(TableIdent::from_strs(["ns1", "test1"]).unwrap())
2159+
.file_io(FileIOBuilder::new("memory").build().unwrap())
2160+
.build()
2161+
.unwrap()
2162+
};
2163+
2164+
let updates = vec![
2165+
TableUpdate::SetLocation {
2166+
location: "s3://bucket/test/new_location/metadata/v2.json".to_string(),
2167+
},
2168+
TableUpdate::SetProperties {
2169+
updates: vec![
2170+
("prop1".to_string(), "v1".to_string()),
2171+
("prop2".to_string(), "v2".to_string()),
2172+
]
2173+
.into_iter()
2174+
.collect(),
2175+
},
2176+
];
2177+
2178+
let requirements = vec![TableRequirement::UuidMatch {
2179+
uuid: table.metadata().table_uuid,
2180+
}];
2181+
2182+
let table_commit = TableCommit::builder()
2183+
.ident(table.identifier().to_owned())
2184+
.updates(updates)
2185+
.requirements(requirements)
2186+
.build();
2187+
2188+
let updated_table = table_commit.apply(table).unwrap();
2189+
2190+
assert_eq!(
2191+
updated_table.metadata().properties.get("prop1").unwrap(),
2192+
"v1"
2193+
);
2194+
assert_eq!(
2195+
updated_table.metadata().properties.get("prop2").unwrap(),
2196+
"v2"
2197+
);
2198+
2199+
assert_eq!(
2200+
updated_table.metadata().location,
2201+
"s3://bucket/test/new_location/metadata/v2.json".to_string()
2202+
)
2203+
}
21142204
}

crates/iceberg/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub enum ErrorKind {
3939
/// Iceberg data is invalid.
4040
///
4141
/// This error is returned when we try to read a table from iceberg but
42-
/// failed to parse it's metadata or data file correctly.
42+
/// failed to parse its metadata or data file correctly.
4343
///
4444
/// The table could be invalid or corrupted.
4545
DataInvalid,

0 commit comments

Comments
 (0)