From 644efb3f71586f1e5d7de1e8ca061d191bf3bf13 Mon Sep 17 00:00:00 2001 From: Evgeny Fomin Date: Thu, 7 Nov 2024 15:54:47 +0100 Subject: [PATCH] wip --- grovedb/src/element/insert.rs | 86 ++++++++++++++++++++++++----------- grovedb/src/element/mod.rs | 7 +++ grovedb/src/merk_cache.rs | 17 +++---- 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/grovedb/src/element/insert.rs b/grovedb/src/element/insert.rs index 5c381e52..030d1510 100644 --- a/grovedb/src/element/insert.rs +++ b/grovedb/src/element/insert.rs @@ -23,7 +23,7 @@ impl Element { /// If transaction is passed, the operation will be committed on the /// transaction commit. pub fn insert<'db, K: AsRef<[u8]>, S: StorageContext<'db>>( - &self, + self, merk: &mut Merk, key: K, options: Option, @@ -31,21 +31,40 @@ impl Element { ) -> CostResult<(), Error> { check_grovedb_v0_with_cost!("insert", grove_version.grovedb_versions.element.insert); - let serialized = cost_return_on_error_default!(self.serialize(grove_version)); + let mut original_cost = Default::default(); + + let to_insert = if let Some(mut prev) = cost_return_on_error!( + &mut original_cost, + Self::get_optional_from_storage(&merk.storage, key.as_ref(), grove_version) + ) { + cost_return_on_error_no_add!( + original_cost, + self.promote_to_referenced_variant(&mut prev) + ) + } else { + self + }; + + let serialized = cost_return_on_error_default!(to_insert.serialize(grove_version)); - if !merk.is_sum_tree && self.is_sum_item() { + if !merk.is_sum_tree && to_insert.is_sum_item() { return Err(Error::InvalidInput("cannot add sum item to non sum tree")) - .wrap_with_cost(Default::default()); + .wrap_with_cost(original_cost); } let merk_feature_type = - cost_return_on_error_default!(self.get_feature_type(merk.is_sum_tree)); - let batch_operations = if matches!(self, Element::SumItem(..)) { - let value_cost = - cost_return_on_error_default!(self.get_specialized_cost(grove_version)); + cost_return_on_error_default!(to_insert.get_feature_type(merk.is_sum_tree)); + let batch_operations = if matches!( + to_insert, + Element::SumItem(..) | Element::SumItemWithBackwardsReferences(..) + ) { + let value_cost = cost_return_on_error_no_add!( + original_cost, + to_insert.get_specialized_cost(grove_version) + ); let cost = value_cost - + self.get_flags().as_ref().map_or(0, |flags| { + + to_insert.get_flags().as_ref().map_or(0, |flags| { let flags_len = flags.len() as u32; flags_len + flags_len.required_space() as u32 }); @@ -70,6 +89,7 @@ impl Element { grove_version, ) .map_err(|e| Error::CorruptedData(e.to_string())) + .add_cost(original_cost) } #[cfg(feature = "full")] @@ -122,7 +142,7 @@ impl Element { /// If transaction is passed, the operation will be committed on the /// transaction commit. pub fn insert_if_not_exists<'db, S: StorageContext<'db>>( - &self, + self, merk: &mut Merk, key: &[u8], options: Option, @@ -228,10 +248,8 @@ impl Element { /// be loaded by this moment If transaction is not passed, the batch /// will be written immediately. If transaction is passed, the operation /// will be committed on the transaction commit. - /// The bool represents if we indeed inserted. - /// If the value changed we return the old element. - // TODO: a combo of `bool` and `Option::Some` in case `bool` equals - // to true could be covered just by `Option`. + /// The bool represents whether a propagation of references is needed. + /// If the value changed, it returns the old element under `Some`. pub fn insert_if_changed_value<'db, S: StorageContext<'db>>( self, merk: &mut Merk, @@ -257,18 +275,21 @@ impl Element { } else { self }; - let needs_insert = match &previous_element { - None => true, - Some(previous_element) => !previous_element.eq_no_backreferences(&to_insert), - }; - if !needs_insert { - Ok((false, None)).wrap_with_cost(cost) - } else { + + let changed = previous_element + .as_ref() + .map(|p| !p.eq_no_backreferences(&to_insert)) + .unwrap_or_default(); + + if changed { + let has_references = to_insert.has_backward_references(); cost_return_on_error!( &mut cost, to_insert.insert(merk, key, options, grove_version) ); - Ok((true, previous_element)).wrap_with_cost(cost) + Ok((has_references, previous_element)).wrap_with_cost(cost) + } else { + Ok((false, None)).wrap_with_cost(cost) } } @@ -329,7 +350,7 @@ impl Element { /// If transaction is passed, the operation will be committed on the /// transaction commit. pub fn insert_reference<'db, K: AsRef<[u8]>, S: StorageContext<'db>>( - &self, + self, merk: &mut Merk, key: K, referenced_value: Hash, @@ -341,15 +362,26 @@ impl Element { grove_version.grovedb_versions.element.insert_reference ); - let serialized = match self.serialize(grove_version) { + let mut cost = Default::default(); + + let to_insert = if let Some(mut prev) = cost_return_on_error!( + &mut cost, + Self::get_optional_from_storage(&merk.storage, key.as_ref(), grove_version) + ) { + cost_return_on_error_no_add!(cost, self.promote_to_referenced_variant(&mut prev)) + } else { + self + }; + + let serialized = match to_insert.serialize(grove_version) { Ok(s) => s, - Err(e) => return Err(e).wrap_with_cost(Default::default()), + Err(e) => return Err(e).wrap_with_cost(cost), }; - let mut cost = OperationCost::default(); let merk_feature_type = cost_return_on_error!( &mut cost, - self.get_feature_type(merk.is_sum_tree) + to_insert + .get_feature_type(merk.is_sum_tree) .wrap_with_cost(OperationCost::default()) ); diff --git a/grovedb/src/element/mod.rs b/grovedb/src/element/mod.rs index 25ec964f..4c65da51 100644 --- a/grovedb/src/element/mod.rs +++ b/grovedb/src/element/mod.rs @@ -325,6 +325,13 @@ impl Element { } } + /// Returns true if there are references to this `Element`. + pub(crate) fn has_backward_references(&self) -> bool { + matches!(self, Element::BidirectionalReference(_, refs, ..) + | Element::ItemWithBackwardsReferences(_, refs, ..) + | Element::SumItemWithBackwardsReferences(_, refs, ..) if !refs.is_empty()) + } + /// Checks elements for equality ignoring backreferences part. pub(crate) fn eq_no_backreferences(&self, other: &Self) -> bool { use Element::*; diff --git a/grovedb/src/merk_cache.rs b/grovedb/src/merk_cache.rs index 6353a430..70be0ea0 100644 --- a/grovedb/src/merk_cache.rs +++ b/grovedb/src/merk_cache.rs @@ -282,16 +282,17 @@ impl<'db, 'c, 'b, B: AsRef<[u8]>> MerkHandle<'db, 'c, 'b, B> { options: Option, ) -> CostResult<(), Error> { let mut costs = Default::default(); - if let (_, Some(mut old_element)) = cost_return_on_error!( + + // In case the item that was changed has been referenced, we indicate that + // references should be propagated after + if cost_return_on_error!( &mut costs, element.insert_if_changed_value(self.merk, key, options, self.version) - ) { - // In case the item that was changed has been referenced, we indicate that - // references should be propagated after - if old_element.take_backward_references().is_some() { - self.updated_reference_handle - .mark_updated_reference(key.to_vec()); - } + ) + .0 + { + self.updated_reference_handle + .mark_updated_reference(key.to_vec()); } *self.to_propagate = true;