Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fominok committed Nov 7, 2024
1 parent cd47589 commit 644efb3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 35 deletions.
86 changes: 59 additions & 27 deletions grovedb/src/element/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,48 @@ 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<S>,
key: K,
options: Option<MerkOptions>,
grove_version: &GroveVersion,
) -> 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
});
Expand All @@ -70,6 +89,7 @@ impl Element {
grove_version,
)
.map_err(|e| Error::CorruptedData(e.to_string()))
.add_cost(original_cost)
}

#[cfg(feature = "full")]
Expand Down Expand Up @@ -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<S>,
key: &[u8],
options: Option<MerkOptions>,
Expand Down Expand Up @@ -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<S>,
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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<S>,
key: K,
referenced_value: Hash,
Expand All @@ -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())
);

Expand Down
7 changes: 7 additions & 0 deletions grovedb/src/element/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
17 changes: 9 additions & 8 deletions grovedb/src/merk_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,17 @@ impl<'db, 'c, 'b, B: AsRef<[u8]>> MerkHandle<'db, 'c, 'b, B> {
options: Option<MerkOptions>,
) -> 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;

Expand Down

0 comments on commit 644efb3

Please sign in to comment.