adding stats to transaction, preparing for DUPSORT cursors, attempt for RW cursors abandonned

without.crypto
Niko 2 years ago
parent 39f7fc6a95
commit f1223115fc
  1. 2
      src/backend/impl_lmdb.rs
  2. 29
      src/backend/impl_lmdb/cursor.rs
  3. 60
      src/backend/impl_lmdb/iter.rs
  4. 57
      src/backend/impl_lmdb/transaction.rs
  5. 2
      src/backend/impl_safe.rs
  6. 27
      src/backend/impl_safe/cursor.rs
  7. 14
      src/backend/impl_safe/iter.rs
  8. 41
      src/backend/impl_safe/transaction.rs
  9. 38
      src/backend/traits.rs
  10. 15
      src/readwrite.rs
  11. 117
      src/store/integermulti.rs
  12. 46
      src/store/multi.rs
  13. 12
      src/store/single.rs

@ -29,6 +29,6 @@ pub use environment::{EnvironmentBuilderImpl, EnvironmentImpl};
pub use error::ErrorImpl;
pub use flags::{DatabaseFlagsImpl, EnvironmentFlagsImpl, WriteFlagsImpl};
pub use info::InfoImpl;
pub use iter::IterImpl;
pub use iter::{IterDupImpl, IterImpl};
pub use stat::StatImpl;
pub use transaction::{RoTransactionImpl, RwTransactionImpl};

@ -10,8 +10,8 @@
use lmdb::Cursor;
use super::IterImpl;
use crate::backend::traits::BackendRoCursor;
use super::{IterDupImpl, IterImpl};
use crate::backend::traits::{BackendRoCursor, BackendRwCursor};
#[derive(Debug)]
pub struct RoCursorImpl<'c>(pub(crate) lmdb::RoCursor<'c>);
@ -41,16 +41,20 @@ impl<'c> BackendRoCursor<'c> for RoCursorImpl<'c> {
{
IterImpl::new(self.0, |cursor| cursor.iter_dup_of(key))
}
fn into_iter_prev(self) -> Self::Iter {
IterImpl::new(self.0, lmdb::RoCursor::iter_prev)
}
}
#[derive(Debug)]
pub struct RwCursorImpl<'c>(pub(crate) lmdb::RwCursor<'c>);
pub struct RwCursorImpl<'c>(pub(crate) lmdb::RoCursor<'c>);
impl<'c> BackendRoCursor<'c> for RwCursorImpl<'c> {
type Iter = IterImpl<'c, lmdb::RwCursor<'c>>;
type Iter = IterImpl<'c, lmdb::RoCursor<'c>>;
fn into_iter(self) -> Self::Iter {
IterImpl::new(self.0, lmdb::RwCursor::iter)
IterImpl::new(self.0, lmdb::RoCursor::iter)
}
fn into_iter_from<K>(self, key: K) -> Self::Iter
@ -66,4 +70,19 @@ impl<'c> BackendRoCursor<'c> for RwCursorImpl<'c> {
{
IterImpl::new(self.0, |cursor| cursor.iter_dup_of(key))
}
fn into_iter_prev(self) -> Self::Iter {
IterImpl::new(self.0, lmdb::RoCursor::iter_prev)
}
}
impl<'c> BackendRwCursor<'c> for RwCursorImpl<'c> {
type Iter = IterDupImpl<'c, lmdb::RoCursor<'c>>;
fn into_iter_prev_dup_from<K>(self, key: K) -> Self::Iter
where
K: AsRef<[u8]> + 'c,
{
IterDupImpl::new(self.0, |cursor| cursor.iter_prev_dup_from(key))
}
}

@ -9,7 +9,8 @@
// specific language governing permissions and limitations under the License.
use super::ErrorImpl;
use crate::backend::traits::BackendIter;
use crate::backend::traits::{BackendDupIter, BackendIter, BackendRoCursor};
use lmdb::Cursor;
pub struct IterImpl<'i, C> {
// LMDB semantics dictate that a cursor must be valid for the entire lifetime
@ -39,3 +40,60 @@ impl<'i, C> BackendIter<'i> for IterImpl<'i, C> {
self.iter.next().map(|e| e.map_err(ErrorImpl::LmdbError))
}
}
pub struct ProxyIterImpl<'i> {
// Here we do not keep the cursor, because we are in a sub-iterator (the iterator of values on a duplicate key)
// and the lmdb cursor is kept by the higher level iterator.
// becareful to keep the higher level iterator (the iterator of keys) alive as long as you are iterating on this iterator.
#[allow(dead_code)]
iter: lmdb::Iter<'i>,
}
impl<'i> ProxyIterImpl<'i> {
pub(crate) fn new_from_lmdb_iter(iter: lmdb::Iter<'i>) -> ProxyIterImpl<'i> {
ProxyIterImpl { iter }
}
}
impl<'i> BackendIter<'i> for ProxyIterImpl<'i> {
type Error = ErrorImpl;
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<(&'i [u8], &'i [u8]), Self::Error>> {
self.iter.next().map(|e| e.map_err(ErrorImpl::LmdbError))
}
}
pub struct IterDupImpl<'i, C> {
// LMDB semantics dictate that a cursor must be valid for the entire lifetime
// of an iterator. In other words, cursors must not be dropped while an
// iterator built from it is alive. Unfortunately, the LMDB crate API does
// not express this through the type system, so we must enforce it somehow.
#[allow(dead_code)]
cursor: C,
iter: lmdb::IterPrevDup<'i>,
}
impl<'i, C> IterDupImpl<'i, C> {
pub(crate) fn new(
mut cursor: C,
to_iter: impl FnOnce(&mut C) -> lmdb::IterPrevDup<'i>,
) -> IterDupImpl<'i, C> {
let iter = to_iter(&mut cursor);
IterDupImpl { cursor, iter }
}
}
impl<'i, C> BackendDupIter<'i> for IterDupImpl<'i, C> {
type Error = ErrorImpl;
type Iter = ProxyIterImpl<'i>;
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<Self::Iter, Self::Error>> {
let next = self.iter.next();
match next {
None => None,
Some(n) => Some(Ok(ProxyIterImpl::new_from_lmdb_iter(n))),
}
}
}

@ -10,10 +10,10 @@
use lmdb::Transaction;
use super::{DatabaseImpl, ErrorImpl, RoCursorImpl, StatImpl, WriteFlagsImpl};
use super::{DatabaseImpl, ErrorImpl, RoCursorImpl, RwCursorImpl, StatImpl, WriteFlagsImpl};
use crate::backend::traits::{
BackendRoCursorTransaction, BackendRoTransaction, BackendRwCursorTransaction,
BackendRwTransaction,
BackendRwCursorType, BackendRwDupPrevCursorTransaction, BackendRwTransaction,
};
#[derive(Debug)]
@ -33,7 +33,6 @@ impl<'t> BackendRoTransaction for RoTransactionImpl<'t> {
}
fn stat(&self, db: &Self::Database) -> Result<Self::Stat, Self::Error> {
//Err(ErrorImpl::LmdbError(lmdb::Error::Invalid))
self.0
.stat(db.0)
.map(StatImpl)
@ -43,6 +42,26 @@ impl<'t> BackendRoTransaction for RoTransactionImpl<'t> {
impl<'t> BackendRoCursorTransaction<'t> for RoTransactionImpl<'t> {
type RoCursor = RoCursorImpl<'t>;
type RwCursor = RwCursorImpl<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
self.0
.open_ro_cursor(db.0)
.map(RoCursorImpl)
.map_err(ErrorImpl::LmdbError)
}
fn open_ro_dup_cursor(&'t self, db: &Self::Database) -> Result<Self::RwCursor, Self::Error> {
self.0
.open_ro_cursor(db.0)
.map(RwCursorImpl)
.map_err(ErrorImpl::LmdbError)
}
}
impl<'t> BackendRwCursorTransaction<'t> for RwTransactionImpl<'t> {
type RoCursor = RoCursorImpl<'t>;
type RwCursor = RwCursorImpl<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
self.0
@ -50,6 +69,13 @@ impl<'t> BackendRoCursorTransaction<'t> for RoTransactionImpl<'t> {
.map(RoCursorImpl)
.map_err(ErrorImpl::LmdbError)
}
fn open_ro_dup_cursor(&'t self, db: &Self::Database) -> Result<Self::RwCursor, Self::Error> {
self.0
.open_ro_cursor(db.0)
.map(RwCursorImpl)
.map_err(ErrorImpl::LmdbError)
}
}
#[derive(Debug)]
@ -113,13 +139,22 @@ impl<'t> BackendRwTransaction for RwTransactionImpl<'t> {
}
}
impl<'t> BackendRwCursorTransaction<'t> for RwTransactionImpl<'t> {
type RoCursor = RoCursorImpl<'t>;
pub enum BackendRwCursorFamily {}
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
self.0
.open_ro_cursor(db.0)
.map(RoCursorImpl)
.map_err(ErrorImpl::LmdbError)
}
impl<'t> BackendRwCursorType<'t> for BackendRwCursorFamily {
type Type = RwCursorImpl<'t>;
}
impl<'t> BackendRwDupPrevCursorTransaction for RwTransactionImpl<'t> {
type RwCursor = BackendRwCursorFamily;
// fn open_rw_dup_prev_cursor<'a>(
// &mut self,
// db: &Self::Database,
// ) -> Result<RwCursorImpl<'a>, Self::Error> {
// self.0
// .open_rw_cursor(db.0)
// .map(RwCursorImpl)
// .map_err(ErrorImpl::LmdbError)
// }
}

@ -25,6 +25,6 @@ pub use environment::{EnvironmentBuilderImpl, EnvironmentImpl};
pub use error::ErrorImpl;
pub use flags::{DatabaseFlagsImpl, EnvironmentFlagsImpl, WriteFlagsImpl};
pub use info::InfoImpl;
pub use iter::IterImpl;
pub use iter::{ IterImpl, IterDupImpl };
pub use stat::StatImpl;
pub use transaction::{RoTransactionImpl, RwTransactionImpl};

@ -8,8 +8,8 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use super::{snapshot::Snapshot, IterImpl};
use crate::backend::traits::BackendRoCursor;
use super::{snapshot::Snapshot, IterDupImpl, IterImpl};
use crate::backend::traits::{BackendRoCursor, BackendRwCursor};
#[derive(Debug)]
pub struct RoCursorImpl<'c>(pub(crate) &'c Snapshot);
@ -39,6 +39,10 @@ impl<'c> BackendRoCursor<'c> for RoCursorImpl<'c> {
self.0.iter().filter(move |&(k, _)| k == key.as_ref()),
))
}
fn into_iter_prev(self) -> Self::Iter {
unimplemented!()
}
}
#[cfg(feature = "db-dup-sort")]
@ -70,6 +74,10 @@ impl<'c> BackendRoCursor<'c> for RoCursorImpl<'c> {
let flattened = filtered.flat_map(|(key, values)| values.map(move |value| (key, value)));
IterImpl(Box::new(flattened))
}
fn into_iter_prev(self) -> Self::Iter {
unimplemented!()
}
}
#[derive(Debug)]
@ -95,4 +103,19 @@ impl<'c> BackendRoCursor<'c> for RwCursorImpl<'c> {
{
unimplemented!()
}
fn into_iter_prev(self) -> Self::Iter {
unimplemented!()
}
}
impl<'c> BackendRwCursor<'c> for RwCursorImpl<'c> {
type Iter = IterDupImpl<'c>;
fn into_iter_prev_dup_from<K>(self, _key: K) -> Self::Iter
where
K: AsRef<[u8]> + 'c,
{
unimplemented!()
}
}

@ -9,7 +9,7 @@
// specific language governing permissions and limitations under the License.
use super::ErrorImpl;
use crate::backend::traits::BackendIter;
use crate::backend::traits::{BackendDupIter, BackendIter};
// FIXME: Use generics instead.
pub struct IterImpl<'i>(pub(crate) Box<dyn Iterator<Item = (&'i [u8], &'i [u8])> + 'i>);
@ -22,3 +22,15 @@ impl<'i> BackendIter<'i> for IterImpl<'i> {
self.0.next().map(Ok)
}
}
pub struct IterDupImpl<'i>(pub(crate) Box<dyn Iterator<Item = IterImpl<'i>> + 'i>);
impl<'i> BackendDupIter<'i> for IterDupImpl<'i> {
type Error = ErrorImpl;
type Iter = IterImpl<'i>;
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<Self::Iter, Self::Error>> {
None
}
}

@ -12,12 +12,12 @@
use std::{collections::HashMap, sync::Arc};
use super::{
snapshot::Snapshot, DatabaseImpl, EnvironmentImpl, ErrorImpl, RoCursorImpl, StatImpl,
WriteFlagsImpl,
snapshot::Snapshot, DatabaseImpl, EnvironmentImpl, ErrorImpl, RoCursorImpl, RwCursorImpl,
StatImpl, WriteFlagsImpl,
};
use crate::backend::traits::{
BackendRoCursorTransaction, BackendRoTransaction, BackendRwCursorTransaction,
BackendRwTransaction,
BackendRwCursorType, BackendRwDupPrevCursorTransaction, BackendRwTransaction,
};
#[derive(Debug)]
@ -60,18 +60,23 @@ impl<'t> BackendRoTransaction for RoTransactionImpl<'t> {
// noop
}
fn stat(&self, db: &Self::Database) -> Result<Self::Stat, Self::Error> {
Err(ErrorImpl::DbNotFoundError)
fn stat(&self, _db: &Self::Database) -> Result<Self::Stat, Self::Error> {
unimplemented!()
}
}
impl<'t> BackendRoCursorTransaction<'t> for RoTransactionImpl<'t> {
type RoCursor = RoCursorImpl<'t>;
type RwCursor = RwCursorImpl<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
Ok(RoCursorImpl(snapshot))
}
fn open_ro_dup_cursor(&'t self, _db: &Self::Database) -> Result<Self::RwCursor, Self::Error> {
unimplemented!()
}
}
#[derive(Debug)]
@ -111,8 +116,8 @@ impl<'t> BackendRwTransaction for RwTransactionImpl<'t> {
snapshot.get(key).ok_or(ErrorImpl::KeyValuePairNotFound)
}
fn stat(&self, db: &Self::Database) -> Result<Self::Stat, Self::Error> {
Err(ErrorImpl::DbNotFoundError)
fn stat(&self, _db: &Self::Database) -> Result<Self::Stat, Self::Error> {
unimplemented!()
}
#[cfg(not(feature = "db-dup-sort"))]
@ -211,9 +216,31 @@ impl<'t> BackendRwTransaction for RwTransactionImpl<'t> {
impl<'t> BackendRwCursorTransaction<'t> for RwTransactionImpl<'t> {
type RoCursor = RoCursorImpl<'t>;
type RwCursor = RwCursorImpl<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
Ok(RoCursorImpl(snapshot))
}
fn open_ro_dup_cursor(&'t self, _db: &Self::Database) -> Result<Self::RwCursor, Self::Error> {
unimplemented!()
}
}
pub enum BackendRwCursorFamily {}
impl<'t> BackendRwCursorType<'t> for BackendRwCursorFamily {
type Type = RwCursorImpl<'t>;
}
impl BackendRwDupPrevCursorTransaction for RwTransactionImpl<'_> {
type RwCursor = BackendRwCursorFamily;
// fn open_rw_dup_prev_cursor<'c>(
// &mut self,
// db: &Self::Database,
// ) -> Result<RwCursorImpl<'c>, Self::Error> {
// Err(ErrorImpl::DbNotFoundError)
// }
}

@ -181,14 +181,32 @@ pub trait BackendRwTransaction: Debug {
pub trait BackendRoCursorTransaction<'t>: BackendRoTransaction {
type RoCursor: BackendRoCursor<'t>;
type RwCursor: BackendRwCursor<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error>;
fn open_ro_dup_cursor(&'t self, db: &Self::Database) -> Result<Self::RwCursor, Self::Error>;
}
pub trait BackendRwCursorTransaction<'t>: BackendRwTransaction {
type RoCursor: BackendRoCursor<'t>;
type RwCursor: BackendRwCursor<'t>;
fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error>;
fn open_ro_dup_cursor(&'t self, db: &Self::Database) -> Result<Self::RwCursor, Self::Error>;
}
pub trait BackendRwCursorType<'t> {
type Type;
}
pub trait BackendRwDupPrevCursorTransaction: BackendRwTransaction {
type RwCursor: for<'t> BackendRwCursorType<'t>;
// fn open_rw_dup_prev_cursor<'t>(
// &mut self,
// db: &Self::Database,
// ) -> Result<<Self::RwCursor as BackendRwCursorType<'t>>::Type, Self::Error>;
}
pub trait BackendRoCursor<'c>: Debug {
@ -203,6 +221,16 @@ pub trait BackendRoCursor<'c>: Debug {
fn into_iter_dup_of<K>(self, key: K) -> Self::Iter
where
K: AsRef<[u8]> + 'c;
fn into_iter_prev(self) -> Self::Iter;
}
pub trait BackendRwCursor<'c>: Debug {
type Iter: BackendDupIter<'c>;
fn into_iter_prev_dup_from<K>(self, key: K) -> Self::Iter
where
K: AsRef<[u8]> + 'c;
}
pub trait BackendIter<'i> {
@ -211,3 +239,11 @@ pub trait BackendIter<'i> {
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<(&'i [u8], &'i [u8]), Self::Error>>;
}
pub trait BackendDupIter<'i> {
type Error: BackendError;
type Iter: BackendIter<'i>;
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<Self::Iter, Self::Error>>;
}

@ -11,7 +11,7 @@
use crate::{
backend::{
BackendDatabase, BackendRoCursor, BackendRoCursorTransaction, BackendRoTransaction,
BackendRwCursorTransaction, BackendRwTransaction, BackendStat,
BackendRwCursor, BackendRwCursorTransaction, BackendRwTransaction, BackendStat,
},
error::StoreError,
helpers::read_transform,
@ -24,6 +24,7 @@ pub struct Writer<T>(T);
pub trait Readable<'r> {
type Database: BackendDatabase;
type RoCursor: BackendRoCursor<'r>;
type RwCursor: BackendRwCursor<'r>;
type Stat: BackendStat;
fn get<K>(&'r self, db: &Self::Database, k: &K) -> Result<Option<Value<'r>>, StoreError>
@ -32,6 +33,8 @@ pub trait Readable<'r> {
fn open_ro_cursor(&'r self, db: &Self::Database) -> Result<Self::RoCursor, StoreError>;
fn open_ro_dup_cursor(&'r self, db: &Self::Database) -> Result<Self::RwCursor, StoreError>;
fn stat(&'r self, db: &Self::Database) -> Result<Self::Stat, StoreError>;
}
@ -41,6 +44,7 @@ where
{
type Database = T::Database;
type RoCursor = T::RoCursor;
type RwCursor = T::RwCursor;
type Stat = T::Stat;
fn get<K>(&'r self, db: &T::Database, k: &K) -> Result<Option<Value<'r>>, StoreError>
@ -58,6 +62,10 @@ where
self.0.open_ro_cursor(db).map_err(|e| e.into())
}
fn open_ro_dup_cursor(&'r self, db: &T::Database) -> Result<T::RwCursor, StoreError> {
self.0.open_ro_dup_cursor(db).map_err(|e| e.into())
}
fn stat(&'r self, db: &Self::Database) -> Result<Self::Stat, StoreError> {
self.0.stat(db).map_err(|e| e.into())
}
@ -84,6 +92,7 @@ where
{
type Database = T::Database;
type RoCursor = T::RoCursor;
type RwCursor = T::RwCursor;
type Stat = T::Stat;
fn get<K>(&'r self, db: &T::Database, k: &K) -> Result<Option<Value<'r>>, StoreError>
@ -101,6 +110,10 @@ where
self.0.open_ro_cursor(db).map_err(|e| e.into())
}
fn open_ro_dup_cursor(&'r self, db: &T::Database) -> Result<T::RwCursor, StoreError> {
self.0.open_ro_dup_cursor(db).map_err(|e| e.into())
}
fn stat(&'r self, db: &Self::Database) -> Result<Self::Stat, StoreError> {
self.0.stat(db).map_err(|e| e.into())
}

@ -11,12 +11,15 @@
use std::marker::PhantomData;
use crate::{
backend::{BackendDatabase, BackendIter, BackendRoCursor, BackendRwTransaction},
backend::{
BackendDatabase, BackendDupIter, BackendIter, BackendRoCursor, BackendRwCursor,
BackendRwTransaction,
},
error::StoreError,
readwrite::{Readable, Writer},
store::{
keys::{Key, PrimitiveInt},
multi::{Iter, MultiStore},
multi::{DIter, Iter, MultiStore},
},
value::Value,
};
@ -41,6 +44,20 @@ where
}
}
pub fn iter_prev_dup_from<'r, I, C, R>(
&self,
reader: &'r R,
k: K,
) -> Result<DIter<'r, I>, StoreError>
where
R: Readable<'r, Database = D, RwCursor = C>,
I: BackendDupIter<'r>,
C: BackendRwCursor<'r, Iter = I>,
K: 'r,
{
self.inner.iter_prev_dup_from(reader, Key::new(&k)?)
}
pub fn get<'r, R, I, C>(&self, reader: &'r R, k: K) -> Result<Iter<'r, I>, StoreError>
where
R: Readable<'r, Database = D, RoCursor = C>,
@ -103,8 +120,8 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::BackendDatabaseFlags;
use crate::*;
use std::fs;
use tempfile::Builder;
@ -262,6 +279,100 @@ mod tests {
}
}
#[cfg(feature = "lmdb")]
#[test]
pub fn test_dup_iter_integer() {
let path_str = "test-env";
let root = Builder::new().prefix(path_str).tempdir().unwrap();
// we set an encryption key with all zeros... for test purpose only ;)
let key: [u8; 32] = [0; 32];
{
fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let mut manager = Manager::<backend::LmdbEnvironment>::singleton()
.write()
.unwrap();
let shared_rkv = manager
.get_or_create(root.path(), |path| {
// Rkv::new::<Lmdb>(path) // use this instead to disable encryption
Rkv::with_encryption_key_and_mapsize::<backend::Lmdb>(
path,
key,
2 * 1024 * 1024 * 1024,
)
})
.unwrap();
let env = shared_rkv.read().unwrap();
println!("LMDB Version: {}", env.version());
let mut opts = StoreOptions::<backend::LmdbDatabaseFlags>::create();
opts.flags.set(DatabaseFlags::DUP_FIXED, true);
let expiry_store = env.open_multi_integer("expiry", opts).unwrap();
let mut writer = env.write().expect("writer");
expiry_store
.put(&mut writer, 1, &Value::Blob(&[104u8, 101u8, 108u8, 108u8]))
.expect("write");
expiry_store
.put(&mut writer, 1, &Value::Blob(&[104u8, 101u8, 108u8, 108u8]))
.expect("write");
expiry_store
.put(&mut writer, 1, &Value::Blob(&[104u8, 101u8, 108u8, 107u8]))
.expect("write");
expiry_store
.put(&mut writer, 2, &Value::Blob(&[2u8, 2u8, 2u8, 2u8]))
.expect("write");
expiry_store
.put(&mut writer, 2, &Value::Blob(&[2u8, 2u8, 2u8, 3u8]))
.expect("write");
expiry_store
.put(&mut writer, 4, &Value::Blob(&[4u8, 2u8, 2u8, 4u8]))
.expect("write");
expiry_store
.put(&mut writer, 4, &Value::Blob(&[4u8, 2u8, 2u8, 3u8]))
.expect("write");
let mut iter = expiry_store.get(&writer, 1).expect("firstkey");
assert_eq!(
iter.next().expect("firstvalue").expect("ok").1,
Value::Blob(&[104u8, 101u8, 108u8, 107u8])
);
assert_eq!(
iter.next().expect("secondvalue").expect("ok").1,
Value::Blob(&[104u8, 101u8, 108u8, 108u8])
);
assert!(iter.next().is_none());
let mut iter = expiry_store.iter_prev_dup_from(&writer, 4).unwrap();
let mut sub_iter = iter.next().unwrap().unwrap();
assert_eq!(sub_iter.next().expect("first").expect("ok").1, [2, 2, 2, 3]);
assert_eq!(
sub_iter.next().expect("second").expect("ok").1,
[2, 2, 2, 2]
);
assert!(sub_iter.next().is_none());
let mut sub_iter2 = iter.next().unwrap().unwrap();
assert_eq!(
sub_iter2.next().expect("first").expect("ok").1,
[104, 101, 108, 108]
);
assert_eq!(
sub_iter2.next().expect("second").expect("ok").1,
[104, 101, 108, 107]
);
assert!(sub_iter2.next().is_none());
//writer.commit().unwrap();
}
}
#[test]
fn test_del() {
let root = Builder::new()

@ -11,7 +11,10 @@
use std::marker::PhantomData;
use crate::{
backend::{BackendDatabase, BackendFlags, BackendIter, BackendRoCursor, BackendRwTransaction},
backend::{
BackendDatabase, BackendDupIter, BackendFlags, BackendIter, BackendRoCursor,
BackendRwCursor, BackendRwTransaction,
},
error::StoreError,
helpers::read_transform,
readwrite::{Readable, Writer},
@ -30,6 +33,11 @@ pub struct Iter<'i, I> {
phantom: PhantomData<&'i ()>,
}
pub struct DIter<'i, I> {
iter: I,
phantom: PhantomData<&'i ()>,
}
impl<D> MultiStore<D>
where
D: BackendDatabase,
@ -38,6 +46,27 @@ where
MultiStore { db }
}
/// Provides a cursor to all of the keys below the given key
/// the values are iterators of the duplicate key's values.
pub fn iter_prev_dup_from<'r, K, I, C, R>(
&self,
reader: &'r R,
k: K,
) -> Result<DIter<'r, I>, StoreError>
where
R: Readable<'r, Database = D, RwCursor = C>,
I: BackendDupIter<'r>,
C: BackendRwCursor<'r, Iter = I>,
K: AsRef<[u8]> + 'r,
{
let cursor = reader.open_ro_dup_cursor(&self.db)?;
let iter = cursor.into_iter_prev_dup_from(k);
Ok(DIter {
iter,
phantom: PhantomData,
})
}
/// Provides a cursor to all of the values for the duplicate entries that match this
/// key
pub fn get<'r, R, I, C, K>(&self, reader: &'r R, k: K) -> Result<Iter<'r, I>, StoreError>
@ -131,3 +160,18 @@ where
}
}
}
impl<'i, I> Iterator for DIter<'i, I>
where
I: BackendDupIter<'i>,
{
type Item = Result<I::Iter, StoreError>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
None => None,
Some(Ok(val)) => Some(Ok(val)),
Some(Err(err)) => Some(Err(err.into())),
}
}
}

@ -11,10 +11,7 @@
use std::marker::PhantomData;
use crate::{
backend::{
BackendDatabase, BackendFlags, BackendIter, BackendRoCursor, BackendRwTransaction,
BackendStat,
},
backend::{BackendDatabase, BackendFlags, BackendIter, BackendRoCursor, BackendRwTransaction},
error::StoreError,
helpers::read_transform,
readwrite::{Readable, Writer},
@ -56,13 +53,6 @@ where
reader.stat(&self.db)
}
// pub fn stat<'r, T>(&self, writer: &mut Writer<T>) -> Result<::Stat, StoreError>
// where
// T: BackendRwTransaction<Database = D>,
// {
// writer.stat(&self.db)
// }
// TODO: flags
pub fn put<T, K>(&self, writer: &mut Writer<T>, k: K, v: &Value) -> EmptyResult
where

Loading…
Cancel
Save