diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 99fad00fe..6339fa6b4 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -38,6 +38,8 @@ pub enum Error { InvalidProof(&'static str), #[error("invalid path: {0}")] InvalidPath(&'static str), + #[error("missing parameter: {0}")] + MissingParameter(&'static str), // Irrecoverable errors #[error("storage error: {0}")] StorageError(#[from] PrefixedRocksDbStorageError), diff --git a/grovedb/src/operations/get.rs b/grovedb/src/operations/get.rs index 54066d5dc..2fe0b1460 100644 --- a/grovedb/src/operations/get.rs +++ b/grovedb/src/operations/get.rs @@ -112,7 +112,7 @@ impl GroveDb { None => &self.subtrees, Some(_) => &self.temp_subtrees, }; - get_path_query_on_trees(self, path_query, subtrees) + self.get_path_query_on_trees(path_query, subtrees) } fn get_path_query_on_trees( @@ -124,173 +124,6 @@ impl GroveDb { let merk = subtrees .get(&Self::compress_subtree_key(path, None)) .ok_or(Error::InvalidPath("no subtree found under that path"))?; - let sized_query = &path_query.query; - let mut result = Vec::new(); - let mut iter = merk.raw_iter(); - - let mut limit = if sized_query.limit.is_some() { - sized_query.limit.unwrap() - } else { - u16::MAX - }; - let original_offset = if sized_query.offset.is_some() { - sized_query.offset.unwrap() - } else { - 0 as u16 - }; - let mut offset = original_offset; - - for item in sized_query.query.iter() { - match item { - QueryItem::Key(key) => { - if limit > 0 { - if offset == 0 { - result.push(Element::get(merk, key)?); - limit -= 1; - } else { - offset -= 1; - } - } - } - QueryItem::Range(Range { start, end }) => { - iter.seek(if sized_query.left_to_right { - start - } else { - end - }); - while limit > 0 - && iter.valid() - && iter.key().is_some() - && iter.key() - != Some(if sized_query.left_to_right { - end - } else { - start - }) - { - let element = - raw_decode(iter.value().expect("if key exists then value should too"))?; - match element { - Element::Tree(_) => { - // if the query had a subquery then we should get elements from it - if let Some(subquery_key) = path_query.subquery_key { - // this means that for each element we should get the element at - // the subquery_key - let mut path_vec = path.to_vec(); - path_vec.push(iter.key().expect("key should exist")); - - if path_query.subquery.is_some() { - path_vec.push(subquery_key); - - let inner_merk = self - .subtrees - .get(&Self::compress_subtree_key( - path_vec.as_slice(), - None, - )) - .ok_or(Error::InvalidPath( - "no subtree found under that path", - ))?; - let inner_limit = if sized_query.limit.is_some() { - Some(limit) - } else { - None - }; - let inner_offset = if sized_query.offset.is_some() { - Some(offset) - } else { - None - }; - let inner_query = SizedQuery::new( - path_query.subquery.clone().unwrap(), - inner_limit, - inner_offset, - sized_query.left_to_right, - ); - let (mut sub_elements, skipped) = - Element::get_sized_query(inner_merk, &inner_query)?; - limit -= sub_elements.len() as u16; - offset -= skipped; - result.append(&mut sub_elements); - } else { - let inner_merk = self - .subtrees - .get(&Self::compress_subtree_key( - path_vec.as_slice(), - None, - )) - .ok_or(Error::InvalidPath( - "no subtree found under that path", - ))?; - if offset == 0 { - result.push(Element::get(inner_merk, subquery_key)?); - limit -= 1; - } else { - offset -= 1; - } - } - } - } - _ => { - if offset == 0 { - result.push(element); - limit -= 1; - } else { - offset -= 1; - } - } - } - if sized_query.left_to_right { - iter.next(); - } else { - iter.prev(); - } - } - } - QueryItem::RangeInclusive(r) => { - let start = r.start(); - let end = r.end(); - iter.seek(if sized_query.left_to_right { - start - } else { - end - }); - let mut work = true; - while iter.valid() && iter.key().is_some() && work { - if iter.key() - == Some(if sized_query.left_to_right { - end - } else { - start - }) - { - work = false; - } - if offset == 0 { - let element = raw_decode( - iter.value().expect("if key exists then value should too"), - )?; - result.push(element); - limit -= 1; - } else { - offset -= 1; - } - if sized_query.left_to_right { - iter.next(); - } else { - iter.prev(); - } - } - } - QueryItem::RangeFull(_) => {} - QueryItem::RangeFrom(_) => {} - QueryItem::RangeTo(_) => {} - QueryItem::RangeToInclusive(_) => {} - } - if limit == 0 { - break; - } - } - Ok((result, original_offset - offset)) + Element::get_path_query(merk, path_query) } } diff --git a/grovedb/src/operations/proof.rs b/grovedb/src/operations/proof.rs index 7ebb1ce79..896247659 100644 --- a/grovedb/src/operations/proof.rs +++ b/grovedb/src/operations/proof.rs @@ -65,7 +65,7 @@ impl GroveDb { // First we must get elements if reduced_proof_query.subquery_key.is_some() { - self.get_path_queries(&[&reduced_proof_query]); + self.get_path_queries(&[&reduced_proof_query], None); let mut path_vec = path.to_vec(); diff --git a/grovedb/src/subtree.rs b/grovedb/src/subtree.rs index 980739ce2..054937f65 100644 --- a/grovedb/src/subtree.rs +++ b/grovedb/src/subtree.rs @@ -1,6 +1,7 @@ //! Module for subtrees handling. //! Subtrees handling is isolated so basically this module is about adapting //! Merk API to GroveDB needs. +use std::collections::HashMap; use std::ops::{Range, RangeFrom, RangeTo, RangeToInclusive}; use merk::{ @@ -17,7 +18,7 @@ use storage::{ RawIterator, Storage, Store, }; -use crate::{Error, Merk, SizedQuery}; +use crate::{Error, GroveDb, Merk, PathQuery, SizedQuery}; /// Variants of GroveDB stored entities #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -72,12 +73,111 @@ impl Element { Ok(elements) } + fn basic_push(_subtrees: Option<&HashMap, Merk>>, _key: Option<&[u8]>, element: Element, _path: Option<&[&[u8]]>, results: &mut Vec, mut limit: u16, mut offset: u16) -> Result<(), Error> { + if offset == 0 { + results.push(element); + limit -= 1; + } else { + offset -= 1; + } + Ok(()) + } + + fn path_query_push(subtrees_option: Option<&HashMap, Merk>>, key: Option<&[u8]>, element: Element, path: Option<&[&[u8]]>, results: &mut Vec, mut limit: u16, mut offset: u16) -> Result<(), Error> { + match element { + Element::Tree(_) => { + // if the query had a subquery then we should get elements from it + if let Some(subquery_key) = path_query.subquery_key { + let subtrees = subtrees_option.ok_or(Error::MissingParameter( + "subtrees must be provided when using a subquery key", + ))?; + // this means that for each element we should get the element at + // the subquery_key + let mut path_vec = path.to_vec(); + path_vec.push(key); + + if path_query.subquery.is_some() { + path_vec.push(subquery_key); + + let inner_merk = subtrees + .get(&Self::compress_subtree_key( + path_vec.as_slice(), + None, + )) + .ok_or(Error::InvalidPath( + "no subtree found under that path", + ))?; + let inner_limit = if sized_query.limit.is_some() { + Some(limit) + } else { + None + }; + let inner_offset = if sized_query.offset.is_some() { + Some(offset) + } else { + None + }; + let inner_query = SizedQuery::new( + path_query.subquery.clone().unwrap(), + inner_limit, + inner_offset, + sized_query.left_to_right, + ); + let (mut sub_elements, skipped) = + Element::get_sized_query(inner_merk, &inner_query)?; + limit -= sub_elements.len() as u16; + offset -= skipped; + results.append(&mut sub_elements); + } else { + let inner_merk = subtrees + .get(&Self::compress_subtree_key( + path_vec.as_slice(), + None, + )) + .ok_or(Error::InvalidPath( + "no subtree found under that path", + ))?; + if offset == 0 { + results.push(Element::get(inner_merk, subquery_key)?); + limit -= 1; + } else { + offset -= 1; + } + } + } + } + _ => { + if offset == 0 { + results.push(element); + limit -= 1; + } else { + offset -= 1; + } + } + } + Ok(()) + } + // Returns a vector of elements, and the number of skipped elements pub fn get_sized_query( merk: &Merk, sized_query: &SizedQuery, ) -> Result<(Vec, u16), Error> { - let mut result = Vec::new(); + Element::get_sized_query_apply_function(merk, sized_query, None, Element::basic_push) + } + + pub fn get_sized_query_apply_function( + merk: &Merk, + sized_query: &SizedQuery, + subtrees: Option<&HashMap, Merk>>, + add_element_function: fn(subtrees: Option<&HashMap, Merk>>, + key: Option<&[u8]>, + element: Element, + &mut Vec, + mut limit: u16, + mut offset: u16) + ) -> Result<(Vec, u16), Error> { + let mut results = Vec::new(); let mut iter = merk.raw_iter(); let mut limit = if sized_query.limit.is_some() { @@ -93,6 +193,63 @@ impl Element { let mut offset = original_offset; for item in sized_query.query.iter() { + if !item.is_range() { + // this is a query on a key + if let QueryItem::Key(key) = item { + add_element_function(subtrees, Some(key.as_slice()), Element::get(merk, key)?, &mut results, limit, offset); + } + } else { + // this is a query on a range + item.seek_for_iter(&mut iter, sized_query.left_to_right); + let mut work = true; + + loop { + let (valid, next_valid) = + item.iter_is_valid_for_type(&iter, limit, work, sized_query.left_to_right); + if !valid { + break; + } + work = next_valid; + let element = + raw_decode(iter.value().expect("if key exists then value should too"))?; + let key = iter.key().expect("key should exist")?; + add_element_function(subtrees, key, element, &mut results, limit, offset); + if sized_query.left_to_right { + iter.next(); + } else { + iter.prev(); + } + } + } + if limit == 0 { + break; + } + } + Ok((result, original_offset - offset)) + } + + // Returns a vector of elements, and the number of skipped elements + pub fn get_path_query( + merk: &Merk, + path_query: &PathQuery, + ) -> Result<(Vec, u16), Error> { + let path = path_query.path; + let mut result = Vec::new(); + let mut iter = merk.raw_iter(); + + let mut limit = if path_query.query.limit.is_some() { + sized_query.limit.unwrap() + } else { + u16::MAX + }; + let original_offset = if path_query.query.offset.is_some() { + sized_query.offset.unwrap() + } else { + 0 as u16 + }; + let mut offset = original_offset; + + for item in path_query.query.query.iter() { if !item.is_range() { // this is a query on a key if let QueryItem::Key(key) = item { @@ -120,6 +277,7 @@ impl Element { work = next_valid; let element = raw_decode(iter.value().expect("if key exists then value should too"))?; + if offset == 0 { result.push(element); limit -= 1; diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 2fbb4c983..ee8feef90 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -1104,7 +1104,6 @@ fn populate_tree_for_range_subquery(db: &mut TempGroveDb) { } #[test] -#[ignore] fn test_get_range_query_with_non_unique_subquery() { let mut db = make_grovedb(); @@ -1160,7 +1159,6 @@ fn test_get_range_query_with_non_unique_subquery() { } #[test] -#[ignore] fn test_get_range_inclusive_full_query_with_non_unique_subquery() { let mut db = make_grovedb();