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 72f519f commit cd47589
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 45 deletions.
5 changes: 3 additions & 2 deletions costs/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ impl<T> CostsExt for T {}
/// 1. Early termination on error;
/// 2. Because of 1, `Result` is removed from the equation;
/// 3. `CostContext` is removed too because it is added to external cost
/// accumulator; 4. Early termination uses external cost accumulator so previous
/// costs won't be lost.
/// accumulator;

Check warning on line 182 in costs/src/context.rs

View workflow job for this annotation

GitHub Actions / clippy

doc list item without indentation

warning: doc list item without indentation --> costs/src/context.rs:182:5 | 182 | /// accumulator; | ^ | = help: if this is supposed to be its own paragraph, add a blank line = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation = note: `#[warn(clippy::doc_lazy_continuation)]` on by default help: indent this line | 182 | /// accumulator; | +++
/// 4. Early termination uses external cost accumulator so previous costs won't
/// be lost.
#[macro_export]
macro_rules! cost_return_on_error {
( &mut $cost:ident, $($body:tt)+ ) => {
Expand Down
50 changes: 46 additions & 4 deletions grovedb/src/element/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,38 @@ impl Element {
}
}

#[cfg(feature = "full")]
/// Promote `Element` to referenced variant in case the old one was already
/// referenced.
fn promote_to_referenced_variant(self, old_element: &mut Element) -> Result<Element, Error> {
if let Some(refs) = old_element.take_backward_references() {
// Since variants with backward references are publicly available, we still have
// to address them, meaning filling in the actual information about references
// from the database by discarding user input.

match self {
Element::Item(value, flags)
| Element::ItemWithBackwardsReferences(value, _, flags) => {
Ok(Element::ItemWithBackwardsReferences(value, refs, flags))
}
Element::Reference(ref_path, max_hops, flags)
| Element::BidirectionalReference(ref_path, _, max_hops, flags) => Ok(
Element::BidirectionalReference(ref_path, refs, max_hops, flags),
),
Element::SumItem(sum, flags)
| Element::SumItemWithBackwardsReferences(sum, _, flags) => {
Ok(Element::SumItemWithBackwardsReferences(sum, refs, flags))
}

Element::Tree(..) | Element::SumTree(..) => Err(Error::NotSupported(
"cannot insert subtree in place of a referenced item".to_owned(),
)),
}
} else {
Ok(self)
}
}

#[cfg(feature = "full")]
/// Insert an element in Merk under a key if the value is different from
/// what already exists; path should be resolved and proper Merk should
Expand All @@ -198,8 +230,10 @@ impl Element {
/// 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`.
pub fn insert_if_changed_value<'db, S: StorageContext<'db>>(
&self,
self,
merk: &mut Merk<S>,
key: &[u8],
options: Option<MerkOptions>,
Expand All @@ -214,18 +248,26 @@ impl Element {
);

let mut cost = OperationCost::default();
let previous_element = cost_return_on_error!(
let mut previous_element = cost_return_on_error!(
&mut cost,
Self::get_optional_from_storage(&merk.storage, key, grove_version)
);
let to_insert = if let Some(prev) = previous_element.as_mut() {
cost_return_on_error_no_add!(cost, self.promote_to_referenced_variant(prev))
} else {
self
};
let needs_insert = match &previous_element {
None => true,
Some(previous_element) => previous_element != self,
Some(previous_element) => !previous_element.eq_no_backreferences(&to_insert),
};
if !needs_insert {
Ok((false, None)).wrap_with_cost(cost)
} else {
cost_return_on_error!(&mut cost, self.insert(merk, key, options, grove_version));
cost_return_on_error!(
&mut cost,
to_insert.insert(merk, key, options, grove_version)
);
Ok((true, previous_element)).wrap_with_cost(cost)
}
}
Expand Down
52 changes: 52 additions & 0 deletions grovedb/src/element/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub use query::QueryOptions;
mod serialize;
#[cfg(any(feature = "full", feature = "verify"))]
use std::fmt;
use std::mem;

use bincode::{Decode, Encode};
#[cfg(any(feature = "full", feature = "verify"))]
Expand Down Expand Up @@ -306,6 +307,57 @@ impl Element {
);
crate::value_hash(&bytes).map(Result::Ok)
}

/// Returns backward references if the `Element` in question participates in
/// bidirectional referencing machinery.
pub(crate) fn take_backward_references(
&mut self,
) -> Option<Vec<(ReferencePathType, CascadeOnUpdate)>> {
match self {
Element::BidirectionalReference(_, refs, ..)
| Element::ItemWithBackwardsReferences(_, refs, ..)
| Element::SumItemWithBackwardsReferences(_, refs, ..)
if !refs.is_empty() =>
{
Some(mem::take(refs))
}
_ => None,
}
}

/// Checks elements for equality ignoring backreferences part.
pub(crate) fn eq_no_backreferences(&self, other: &Self) -> bool {
use Element::*;

match (self, other) {
(
Item(value_left, flags_left)
| ItemWithBackwardsReferences(value_left, _, flags_left),
Item(value_right, flags_right)
| ItemWithBackwardsReferences(value_right, _, flags_right),
) => value_left == value_right && flags_left == flags_right,
(
SumItem(sum_left, flags_left)
| SumItemWithBackwardsReferences(sum_left, _, flags_left),
SumItem(sum_right, flags_right)
| SumItemWithBackwardsReferences(sum_right, _, flags_right),
) => sum_left == sum_right && flags_left == flags_right,
(
Reference(ref_path_left, max_hops_left, flags_left)
| BidirectionalReference(ref_path_left, _, max_hops_left, flags_left),
Reference(ref_path_right, max_hops_right, flags_right)
| BidirectionalReference(ref_path_right, _, max_hops_right, flags_right),
) => {
ref_path_left == ref_path_right
&& max_hops_left == max_hops_right
&& flags_left == flags_right
}
(left @ Tree(..), right @ Tree(..)) => left == right,
(left @ SumTree(..), right @ SumTree(..)) => left == right,

_ => false,
}
}
}

#[cfg(any(feature = "full", feature = "visualize"))]
Expand Down
Loading

0 comments on commit cd47589

Please sign in to comment.