iceberg/transaction/
update_properties.rs1use std::collections::{HashMap, HashSet};
19use std::sync::Arc;
20
21use async_trait::async_trait;
22
23use crate::table::Table;
24use crate::transaction::action::{ActionCommit, TransactionAction};
25use crate::{Error, ErrorKind, Result, TableUpdate};
26
27pub struct UpdatePropertiesAction {
34 updates: HashMap<String, String>,
35 removals: HashSet<String>,
36}
37
38impl UpdatePropertiesAction {
39 pub(crate) fn new() -> Self {
41 UpdatePropertiesAction {
42 updates: HashMap::default(),
43 removals: HashSet::default(),
44 }
45 }
46
47 pub fn set(mut self, key: String, value: String) -> Self {
58 self.updates.insert(key, value);
59 self
60 }
61
62 pub fn remove(mut self, key: String) -> Self {
72 self.removals.insert(key);
73 self
74 }
75}
76
77#[async_trait]
78impl TransactionAction for UpdatePropertiesAction {
79 async fn commit(self: Arc<Self>, _table: &Table) -> Result<ActionCommit> {
80 if let Some(overlapping_key) = self.removals.iter().find(|k| self.updates.contains_key(*k))
81 {
82 return Err(Error::new(
83 ErrorKind::PreconditionFailed,
84 format!("Key {overlapping_key} is present in both removal set and update set"),
85 ));
86 }
87
88 let updates: Vec<TableUpdate> = vec![
89 TableUpdate::SetProperties {
90 updates: self.updates.clone(),
91 },
92 TableUpdate::RemoveProperties {
93 removals: self.removals.clone().into_iter().collect::<Vec<String>>(),
94 },
95 ];
96
97 Ok(ActionCommit::new(updates, vec![]))
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use std::collections::{HashMap, HashSet};
104
105 use as_any::Downcast;
106
107 use crate::transaction::Transaction;
108 use crate::transaction::action::ApplyTransactionAction;
109 use crate::transaction::tests::make_v2_table;
110 use crate::transaction::update_properties::UpdatePropertiesAction;
111
112 #[test]
113 fn test_update_table_property() {
114 let table = make_v2_table();
115 let tx = Transaction::new(&table);
116 let tx = tx
117 .update_table_properties()
118 .set("a".to_string(), "b".to_string())
119 .remove("b".to_string())
120 .apply(tx)
121 .unwrap();
122
123 assert_eq!(tx.actions.len(), 1);
124
125 let action = (*tx.actions[0])
126 .downcast_ref::<UpdatePropertiesAction>()
127 .unwrap();
128 assert_eq!(
129 action.updates,
130 HashMap::from([("a".to_string(), "b".to_string())])
131 );
132
133 assert_eq!(action.removals, HashSet::from(["b".to_string()]));
134 }
135}