Skip to content

Commit

Permalink
upd
Browse files Browse the repository at this point in the history
  • Loading branch information
fominok committed Dec 20, 2024
1 parent 2dccf6a commit c3fd3c8
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 38 deletions.
155 changes: 142 additions & 13 deletions grovedb/src/bidirectional_references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub struct BidirectionalReference {
pub flags: Option<ElementFlags>,
}

/// Insert bidirectional reference at specified location performing required
/// checks and updates
pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u8]>>(

Check warning on line 41 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

this lifetime isn't used in the function definition

warning: this lifetime isn't used in the function definition --> grovedb/src/bidirectional_references.rs:41:66 | 41 | pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u8]>>( | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes = note: `#[warn(clippy::extra_unused_lifetimes)]` on by default

Check warning on line 41 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

the following explicit lifetimes could be elided: 'db

warning: the following explicit lifetimes could be elided: 'db --> grovedb/src/bidirectional_references.rs:41:57 | 41 | pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u8]>>( | ^^^ 42 | merk_cache: &mut MerkCache<'db, 'b, B>, | ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 41 ~ pub(crate) fn process_bidirectional_reference_insertion<'b, 'k, B: AsRef<[u8]>>( 42 ~ merk_cache: &mut MerkCache<'_, 'b, B>, |

Check warning on line 41 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

function `process_bidirectional_reference_insertion` is never used

warning: function `process_bidirectional_reference_insertion` is never used --> grovedb/src/bidirectional_references.rs:41:15 | 41 | pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u8]>>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
merk_cache: &mut MerkCache<'db, 'b, B>,
path: SubtreePath<'b, B>,
Expand Down Expand Up @@ -162,7 +164,7 @@ pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u
..
} = cost_return_on_error!(
&mut cost,
util::follow_reference(
util::follow_reference_once(
merk_cache,
path.derive_owned(),
key,
Expand Down Expand Up @@ -206,13 +208,137 @@ pub(crate) fn process_bidirectional_reference_insertion<'db, 'b, 'k, B: AsRef<[u
Ok(()).wrap_with_cost(cost)
}

pub(crate) fn process_update_element_with_backward_references<'db, 'b, 'k, B: AsRef<[u8]>>(
merk_cache: &mut MerkCache<'db, 'b, B>,
/// Post-processing of possible backward references relationships after
/// insertion of anything but bidirectional reference
pub(crate) fn process_update_element_with_backward_references<'db, 'b, 'c, B: AsRef<[u8]>>(

Check warning on line 213 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

function `process_update_element_with_backward_references` is never used

warning: function `process_update_element_with_backward_references` is never used --> grovedb/src/bidirectional_references.rs:213:15 | 213 | pub(crate) fn process_update_element_with_backward_references<'db, 'b, 'c, B: AsRef<[u8]>>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
merk_cache: &'c mut MerkCache<'db, 'b, B>,
merk: MerkHandle<'db, 'c>,
path: SubtreePath<'b, B>,
key: &[u8],
Delta { new, old }: Delta,
delta: Delta,
) -> CostResult<(), Error> {
let mut cost = Default::default();

// On no changes no propagations shall happen:
if !delta.has_changed() {
return Ok(()).wrap_with_cost(cost);
}

// If there was no overwrite we short-circuit as well:
let Some(old) = delta.old else {
return Ok(()).wrap_with_cost(cost);
};

match (old, delta.new) {
(
Element::ItemWithBackwardsReferences(..) | Element::SumItemWithBackwardsReferences(..),
Element::ItemWithBackwardsReferences(..) | Element::SumItemWithBackwardsReferences(..),
) => {
// Update with another backward references-compatible element variant, that
// means value hash propagation across backward references' chains:
cost_return_on_error!(
&mut cost,
propagate_backward_references(
merk_cache,
merk,
path.derive_owned(),
key.to_vec(),
cost_return_on_error!(&mut cost, delta.new.value_hash(&merk_cache.version))

Check warning on line 246 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

warning: this expression creates a reference which is immediately dereferenced by the compiler --> grovedb/src/bidirectional_references.rs:246:75 | 246 | cost_return_on_error!(&mut cost, delta.new.value_hash(&merk_cache.version)) | ^^^^^^^^^^^^^^^^^^^ help: change this to: `merk_cache.version` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
)
);
}
(
Element::ItemWithBackwardsReferences(..) | Element::SumItemWithBackwardsReferences(..),
_,
) => {
// Update with non backward references-compatible element, equals to cascade
// deletion of references' chains:
cost_return_on_error!(
&mut cost,
delete_backward_references_recursively(
merk_cache,
merk,
path.derive_owned(),
key.to_vec()
)
);
}
_ => {
// All other overwrites don't require special attention
}
}

Ok(()).wrap_with_cost(cost)
}

/// Recursively deletes all backward references' chains of a key if all of them
/// allow cascade deletion.
fn delete_backward_references_recursively<'db, 'b, 'c, B: AsRef<[u8]>>(

Check warning on line 276 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

function `delete_backward_references_recursively` is never used

warning: function `delete_backward_references_recursively` is never used --> grovedb/src/bidirectional_references.rs:276:4 | 276 | fn delete_backward_references_recursively<'db, 'b, 'c, B: AsRef<[u8]>>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
merk_cache: &'c MerkCache<'db, 'b, B>,
merk: MerkHandle<'db, 'c>,
path: SubtreePathBuilder<'b, B>,
key: Vec<u8>,
) -> CostResult<(), Error> {
todo!()
let mut cost = Default::default();
let mut queue = VecDeque::new();

queue.push_back((merk, path, key));

// Just like with propagation we follow all references chains...
while let Some((mut current_merk, current_path, current_key)) = queue.pop_front() {
let backward_references = cost_return_on_error!(
&mut cost,
get_backward_references(&mut current_merk, &current_key)
);
for (idx, backward_ref) in backward_references.into_iter() {
if !backward_ref.cascade_on_update {
return Err(Error::BidirectionalReferenceRule(
"deletion of backward references through deletion of an element requires \
`cascade_on_update` setting"
.to_owned(),
))
.wrap_with_cost(cost);
}

let ResolvedReference {
target_merk: origin_bidi_merk,
target_path: origin_bidi_path,
target_key: origin_bidi_key,
..
} = cost_return_on_error!(
&mut cost,
util::follow_reference_once(
merk_cache,
current_path.clone(),
&current_key,
backward_ref.inverted_reference
)
);

// ... except removing backward references from meta...
cost_return_on_error!(
&mut cost,
remove_backward_reference(&mut current_merk, &current_key, idx)
);

queue.push_back((origin_bidi_merk, origin_bidi_path, origin_bidi_key));
}

// ... and the element altogether
cost_return_on_error!(
&mut cost,
current_merk.for_merk(|m| Element::delete(
m,
current_key,
None,
false,
false,
&merk_cache.version

Check warning on line 336 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

warning: this expression creates a reference which is immediately dereferenced by the compiler --> grovedb/src/bidirectional_references.rs:336:17 | 336 | &merk_cache.version | ^^^^^^^^^^^^^^^^^^^ help: change this to: `merk_cache.version` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
))
);
}

Ok(()).wrap_with_cost(cost)
}

/// Recursively updates all backward references' chains of a key.
Expand All @@ -233,7 +359,7 @@ fn propagate_backward_references<'db, 'b, 'c, B: AsRef<[u8]>>(
&mut cost,
get_backward_references(&mut current_merk, &current_key)
);
for backward_ref in backward_references.into_iter() {
for (_, backward_ref) in backward_references.into_iter() {
let ResolvedReference {
target_merk: mut origin_bidi_merk,
target_path: origin_bidi_path,
Expand Down Expand Up @@ -383,7 +509,7 @@ fn add_backward_reference<'db, 'c>(
fn get_backward_references<'db, 'c>(

Check warning on line 509 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

the following explicit lifetimes could be elided: 'db, 'c

warning: the following explicit lifetimes could be elided: 'db, 'c --> grovedb/src/bidirectional_references.rs:509:28 | 509 | fn get_backward_references<'db, 'c>( | ^^^ ^^ 510 | merk: &mut MerkHandle<'db, 'c>, | ^^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 509 ~ fn get_backward_references( 510 ~ merk: &mut MerkHandle<'_, '_>, |

Check warning on line 509 in grovedb/src/bidirectional_references.rs

View workflow job for this annotation

GitHub Actions / clippy

function `get_backward_references` is never used

warning: function `get_backward_references` is never used --> grovedb/src/bidirectional_references.rs:509:4 | 509 | fn get_backward_references<'db, 'c>( | ^^^^^^^^^^^^^^^^^^^^^^^
merk: &mut MerkHandle<'db, 'c>,
key: &[u8],
) -> CostResult<Vec<BackwardReference>, Error> {
) -> CostResult<Vec<(SlotIdx, BackwardReference)>, Error> {
let mut cost = Default::default();

let (prefix, bits) =
Expand Down Expand Up @@ -412,9 +538,9 @@ fn get_backward_references<'db, 'c>(
})
);

backward_references.push(cost_return_on_error_no_add!(
cost,
BackwardReference::deserialize(&bytes)
backward_references.push((
idx,
cost_return_on_error_no_add!(cost, BackwardReference::deserialize(&bytes)),
));
}

Expand Down Expand Up @@ -522,9 +648,12 @@ mod tests {
.unwrap()
.into_iter()
.map(
|BackwardReference {
inverted_reference, ..
}| inverted_reference
|(
_,
BackwardReference {
inverted_reference, ..
},
)| inverted_reference
)
.collect::<Vec<_>>(),
vec![
Expand Down
47 changes: 22 additions & 25 deletions grovedb/src/element/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ use crate::{Element, Element::SumItem, Error, Hash};

pub struct Delta<'e> {
pub new: &'e Element,
pub old: Element,
pub old: Option<Element>,
}

impl Delta<'_> {
pub(crate) fn has_changed(&self) -> bool {
&self.old != self.new
self.old.as_ref().map(|o| o != self.new).unwrap_or(true)
}
}

Expand Down Expand Up @@ -200,19 +200,14 @@ impl Element {

#[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
/// 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.
/// what already exists, returning delta.
pub fn insert_if_changed_value<'db, S: StorageContext<'db>>(
&self,
merk: &mut Merk<S>,
key: &[u8],
options: Option<MerkOptions>,
grove_version: &GroveVersion,
) -> CostResult<(bool, Option<Element>), Error> {
) -> CostResult<Delta, Error> {
check_grovedb_v0_with_cost!(
"insert_if_changed_value",
grove_version
Expand All @@ -226,16 +221,16 @@ impl Element {
&mut cost,
Self::get_optional_from_storage(&merk.storage, key, grove_version)
);
let needs_insert = match &previous_element {
None => true,
Some(previous_element) => previous_element != self,
let delta = Delta {
new: self,
old: previous_element,
};
if !needs_insert {
Ok((false, None)).wrap_with_cost(cost)
} else {

if delta.has_changed() {
cost_return_on_error!(&mut cost, self.insert(merk, key, options, grove_version));
Ok((true, previous_element)).wrap_with_cost(cost)
}

Ok(delta).wrap_with_cost(cost)
}

#[cfg(feature = "full")]
Expand Down Expand Up @@ -515,15 +510,15 @@ mod tests {

merk.commit(grove_version);

let (inserted, previous) = Element::new_item(b"value".to_vec())
let element = Element::new_item(b"value".to_vec());
let delta = element
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

merk.commit(grove_version);

assert!(!inserted);
assert_eq!(previous, None);
assert!(!delta.has_changed());
assert_eq!(
Element::get(&merk, b"another-key", true, grove_version)
.unwrap()
Expand Down Expand Up @@ -557,13 +552,14 @@ mod tests {

let batch = StorageBatch::new();
let mut merk = empty_path_merk(&*storage, &batch, &tx, grove_version);
let (inserted, previous) = Element::new_item(b"value2".to_vec())
let element = Element::new_item(b"value2".to_vec());
let delta = element
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

assert!(inserted);
assert_eq!(previous, Some(Element::new_item(b"value".to_vec())),);
assert!(delta.has_changed());
assert_eq!(delta.old, Some(Element::new_item(b"value".to_vec())),);

storage
.commit_multi_context_batch(batch, Some(&tx))
Expand All @@ -587,13 +583,14 @@ mod tests {
.insert(&mut merk, b"mykey", None, grove_version)
.unwrap()
.expect("expected successful insertion");
let (inserted, previous) = Element::new_item(b"value2".to_vec())
let element = Element::new_item(b"value2".to_vec());
let delta = element
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

assert!(inserted);
assert_eq!(previous, None);
assert!(delta.has_changed());
assert_eq!(delta.old, None);

assert_eq!(
Element::get(&merk, b"another-key", true, grove_version)
Expand Down

0 comments on commit c3fd3c8

Please sign in to comment.