This is an automated email from the ASF dual-hosted git repository.

liurenjie1024 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new ed12ac41 feat(catalog): Add TableCommit::apply to help update metadata 
(#1464)
ed12ac41 is described below

commit ed12ac41ab9f321be8796ee7ea1716472001f4df
Author: Shawn Chang <[email protected]>
AuthorDate: Tue Jun 24 17:11:08 2025 -0700

    feat(catalog): Add TableCommit::apply to help update metadata (#1464)
    
    ## Which issue does this PR close?
    
    
    
    - Closes https://github.com/apache/iceberg-rust/issues/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
---
 crates/iceberg/src/catalog/mod.rs | 92 ++++++++++++++++++++++++++++++++++++++-
 crates/iceberg/src/error.rs       |  2 +-
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/crates/iceberg/src/catalog/mod.rs 
b/crates/iceberg/src/catalog/mod.rs
index 5cfbae20..ebb9a66c 100644
--- a/crates/iceberg/src/catalog/mod.rs
+++ b/crates/iceberg/src/catalog/mod.rs
@@ -22,6 +22,7 @@ use std::fmt::{Debug, Display};
 use std::future::Future;
 use std::mem::take;
 use std::ops::Deref;
+use std::sync::Arc;
 
 use _serde::deserialize_snapshot;
 use async_trait::async_trait;
@@ -313,6 +314,27 @@ impl TableCommit {
     pub fn take_updates(&mut self) -> Vec<TableUpdate> {
         take(&mut self.updates)
     }
+
+    /// Applies this [`TableCommit`] to the given [`Table`] as part of a 
catalog update.
+    /// Typically used by [`Catalog::update_table`] to validate requirements 
and apply metadata updates.
+    ///
+    /// Returns a new [`Table`] with updated metadata,
+    /// or an error if validation or application fails.
+    pub fn apply(self, table: Table) -> Result<Table> {
+        // check requirements
+        for requirement in self.requirements {
+            requirement.check(Some(table.metadata()))?;
+        }
+
+        // apply updates to metadata builder
+        let mut metadata_builder = table.metadata().clone().into_builder(None);
+
+        for update in self.updates {
+            metadata_builder = update.apply(metadata_builder)?;
+        }
+
+        Ok(table.with_metadata(Arc::new(metadata_builder.build()?.metadata)))
+    }
 }
 
 /// TableRequirement represents a requirement for a table in the catalog.
@@ -884,12 +906,15 @@ mod _serde_set_statistics {
 mod tests {
     use std::collections::HashMap;
     use std::fmt::Debug;
+    use std::fs::File;
+    use std::io::BufReader;
 
     use serde::Serialize;
     use serde::de::DeserializeOwned;
     use uuid::uuid;
 
     use super::ViewUpdate;
+    use crate::io::FileIOBuilder;
     use crate::spec::{
         BlobMetadata, FormatVersion, MAIN_BRANCH, NestedField, NullOrder, 
Operation,
         PartitionStatisticsFile, PrimitiveType, Schema, Snapshot, 
SnapshotReference,
@@ -898,7 +923,10 @@ mod tests {
         UnboundPartitionSpec, ViewFormatVersion, ViewRepresentation, 
ViewRepresentations,
         ViewVersion,
     };
-    use crate::{NamespaceIdent, TableCreation, TableIdent, TableRequirement, 
TableUpdate};
+    use crate::table::Table;
+    use crate::{
+        NamespaceIdent, TableCommit, TableCreation, TableIdent, 
TableRequirement, TableUpdate,
+    };
 
     #[test]
     fn test_parent_namespace() {
@@ -2111,4 +2139,66 @@ mod tests {
             },
         );
     }
+
+    #[test]
+    fn test_table_commit() {
+        let table = {
+            let file = File::open(format!(
+                "{}/testdata/table_metadata/{}",
+                env!("CARGO_MANIFEST_DIR"),
+                "TableMetadataV2Valid.json"
+            ))
+            .unwrap();
+            let reader = BufReader::new(file);
+            let resp = serde_json::from_reader::<_, 
TableMetadata>(reader).unwrap();
+
+            Table::builder()
+                .metadata(resp)
+                
.metadata_location("s3://bucket/test/location/metadata/v2.json".to_string())
+                .identifier(TableIdent::from_strs(["ns1", "test1"]).unwrap())
+                .file_io(FileIOBuilder::new("memory").build().unwrap())
+                .build()
+                .unwrap()
+        };
+
+        let updates = vec![
+            TableUpdate::SetLocation {
+                location: 
"s3://bucket/test/new_location/metadata/v2.json".to_string(),
+            },
+            TableUpdate::SetProperties {
+                updates: vec![
+                    ("prop1".to_string(), "v1".to_string()),
+                    ("prop2".to_string(), "v2".to_string()),
+                ]
+                .into_iter()
+                .collect(),
+            },
+        ];
+
+        let requirements = vec![TableRequirement::UuidMatch {
+            uuid: table.metadata().table_uuid,
+        }];
+
+        let table_commit = TableCommit::builder()
+            .ident(table.identifier().to_owned())
+            .updates(updates)
+            .requirements(requirements)
+            .build();
+
+        let updated_table = table_commit.apply(table).unwrap();
+
+        assert_eq!(
+            updated_table.metadata().properties.get("prop1").unwrap(),
+            "v1"
+        );
+        assert_eq!(
+            updated_table.metadata().properties.get("prop2").unwrap(),
+            "v2"
+        );
+
+        assert_eq!(
+            updated_table.metadata().location,
+            "s3://bucket/test/new_location/metadata/v2.json".to_string()
+        )
+    }
 }
diff --git a/crates/iceberg/src/error.rs b/crates/iceberg/src/error.rs
index 37529ee6..07fd2641 100644
--- a/crates/iceberg/src/error.rs
+++ b/crates/iceberg/src/error.rs
@@ -39,7 +39,7 @@ pub enum ErrorKind {
     /// Iceberg data is invalid.
     ///
     /// This error is returned when we try to read a table from iceberg but
-    /// failed to parse it's metadata or data file correctly.
+    /// failed to parse its metadata or data file correctly.
     ///
     /// The table could be invalid or corrupted.
     DataInvalid,

Reply via email to