feat(interface): implicit trait bound for DB cursors (#122)

* feat(interface): implicit trait bound for DB cursors

* test cursor

* walking fixed for RO

* impl for Walker for DupCursor
This commit is contained in:
rakita
2022-10-24 20:20:14 +02:00
committed by GitHub
parent a3185f9997
commit 4eca851429
6 changed files with 166 additions and 64 deletions

View File

@ -1,5 +1,7 @@
//! Cursor wrapper for libmdbx-sys. //! Cursor wrapper for libmdbx-sys.
use std::marker::PhantomData;
use crate::utils::*; use crate::utils::*;
use libmdbx::{self, TransactionKind, WriteFlags, RO, RW}; use libmdbx::{self, TransactionKind, WriteFlags, RO, RW};
use reth_interfaces::db::{ use reth_interfaces::db::{
@ -67,14 +69,20 @@ impl<'tx, K: TransactionKind, T: Table> DbCursorRO<'tx, T> for Cursor<'tx, K, T>
decode!(self.inner.get_current()) decode!(self.inner.get_current())
} }
fn walk(&'tx mut self, start_key: <T as Table>::Key) -> Result<Walker<'tx, T>, Error> { fn walk<'cursor>(
&'cursor mut self,
start_key: T::Key,
) -> Result<Walker<'cursor, 'tx, T, Self>, Error>
where
Self: Sized,
{
let start = self let start = self
.inner .inner
.set_range(start_key.encode().as_ref()) .set_range(start_key.encode().as_ref())
.map_err(|e| Error::Internal(e.into()))? .map_err(|e| Error::Internal(e.into()))?
.map(decoder::<T>); .map(decoder::<T>);
Ok(Walker::<'tx, T> { cursor: self, start }) Ok(Walker::<'cursor, 'tx, T, Self> { cursor: self, start, _tx_phantom: PhantomData {} })
} }
} }
@ -99,14 +107,18 @@ impl<'tx, K: TransactionKind, T: DupSort> DbDupCursorRO<'tx, T> for Cursor<'tx,
} }
/// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT table. /// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT table.
fn walk_dup(&'tx mut self, key: T::Key, subkey: T::SubKey) -> Result<DupWalker<'tx, T>, Error> { fn walk_dup<'cursor>(
&'cursor mut self,
key: T::Key,
subkey: T::SubKey,
) -> Result<DupWalker<'cursor, 'tx, T, Self>, Error> {
let start = self let start = self
.inner .inner
.get_both_range(key.encode().as_ref(), subkey.encode().as_ref()) .get_both_range(key.encode().as_ref(), subkey.encode().as_ref())
.map_err(|e| Error::Internal(e.into()))? .map_err(|e| Error::Internal(e.into()))?
.map(decode_one::<T>); .map(decode_one::<T>);
Ok(DupWalker::<'tx, T> { cursor: self, start }) Ok(DupWalker::<'cursor, 'tx, T, Self> { cursor: self, start, _tx_phantom: PhantomData {} })
} }
} }

View File

@ -137,7 +137,7 @@ mod tests {
use libmdbx::{NoWriteMap, WriteMap}; use libmdbx::{NoWriteMap, WriteMap};
use reth_interfaces::db::{ use reth_interfaces::db::{
tables::{Headers, PlainState}, tables::{Headers, PlainState},
Database, DbTx, DbTxMut, Database, DbCursorRO, DbTx, DbTxMut,
}; };
use reth_primitives::{Account, Address, Header, H256, U256}; use reth_primitives::{Account, Address, Header, H256, U256};
use std::str::FromStr; use std::str::FromStr;
@ -175,6 +175,31 @@ mod tests {
tx.commit().expect(ERROR_COMMIT); tx.commit().expect(ERROR_COMMIT);
} }
#[test]
fn db_cursor_walk() {
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
let value = Header::default();
let key = (1u64, H256::zero());
// PUT
let tx = env.tx_mut().expect(ERROR_INIT_TX);
tx.put::<Headers>(key.into(), value.clone()).expect(ERROR_PUT);
tx.commit().expect(ERROR_COMMIT);
// Cursor
let tx = env.tx().expect(ERROR_INIT_TX);
let mut cursor = tx.cursor::<Headers>().unwrap();
let first = cursor.first().unwrap();
assert!(first.is_some(), "First should be our put");
// Walk
let walk = cursor.walk(key.into()).unwrap();
let first = walk.into_iter().next().unwrap().unwrap();
assert_eq!(first.1, value, "First next should be put value");
}
#[test] #[test]
fn db_closure_put_get() { fn db_closure_put_get() {
let path = TempDir::new().expect(test_utils::ERROR_TEMPDIR).into_path(); let path = TempDir::new().expect(test_utils::ERROR_TEMPDIR).into_path();

View File

@ -2,7 +2,7 @@
use crate::{kv::cursor::Cursor, utils::decode_one}; use crate::{kv::cursor::Cursor, utils::decode_one};
use libmdbx::{EnvironmentKind, Transaction, TransactionKind, WriteFlags, RW}; use libmdbx::{EnvironmentKind, Transaction, TransactionKind, WriteFlags, RW};
use reth_interfaces::db::{DbTx, DbTxMut, DupSort, Encode, Error, Table}; use reth_interfaces::db::{DbTx, DbTxGAT, DbTxMut, DbTxMutGAT, DupSort, Encode, Error, Table};
use std::marker::PhantomData; use std::marker::PhantomData;
/// Wrapper for the libmdbx transaction. /// Wrapper for the libmdbx transaction.
@ -39,18 +39,24 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> Tx<'env, K, E> {
} }
} }
impl<'env, K: TransactionKind, E: EnvironmentKind> DbTx<'env> for Tx<'env, K, E> { impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxGAT<'a> for Tx<'_, K, E> {
/// Cursor GAT type Cursor<T: Table> = Cursor<'a, K, T>;
type Cursor<T: Table> = Cursor<'env, K, T>; type DupCursor<T: DupSort> = Cursor<'a, K, T>;
/// DupCursor GAT }
type DupCursor<T: DupSort> = Cursor<'env, K, T>;
/// Iterate over read only values in database. impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxMutGAT<'a> for Tx<'_, K, E> {
fn cursor<T: Table>(&self) -> Result<Self::Cursor<T>, Error> { type CursorMut<T: Table> = Cursor<'a, RW, T>;
type DupCursorMut<T: DupSort> = Cursor<'a, RW, T>;
}
impl<'tx, K: TransactionKind, E: EnvironmentKind> DbTx<'tx> for Tx<'tx, K, E> {
// Iterate over read only values in database.
fn cursor<T: Table>(&'tx self) -> Result<<Self as DbTxGAT<'tx>>::Cursor<T>, Error> {
self.new_cursor() self.new_cursor()
} }
/// Iterate over read only values in database. /// Iterate over read only values in database.
fn cursor_dup<T: DupSort>(&self) -> Result<Self::DupCursor<T>, Error> { fn cursor_dup<T: DupSort>(&'tx self) -> Result<<Self as DbTxGAT<'tx>>::DupCursor<T>, Error> {
self.new_cursor() self.new_cursor()
} }
@ -70,11 +76,7 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> DbTx<'env> for Tx<'env, K, E>
} }
} }
impl<'env, E: EnvironmentKind> DbTxMut<'env> for Tx<'env, RW, E> { impl<E: EnvironmentKind> DbTxMut<'_> for Tx<'_, RW, E> {
type CursorMut<T: Table> = Cursor<'env, RW, T>;
type DupCursorMut<T: DupSort> = Cursor<'env, RW, T>;
fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), Error> { fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), Error> {
self.inner self.inner
.put( .put(
@ -111,11 +113,13 @@ impl<'env, E: EnvironmentKind> DbTxMut<'env> for Tx<'env, RW, E> {
Ok(()) Ok(())
} }
fn cursor_mut<T: Table>(&self) -> Result<Self::CursorMut<T>, Error> { fn cursor_mut<T: Table>(&self) -> Result<<Self as DbTxMutGAT<'_>>::CursorMut<T>, Error> {
self.new_cursor() self.new_cursor()
} }
fn cursor_dup_mut<T: DupSort>(&self) -> Result<Self::DupCursorMut<T>, Error> { fn cursor_dup_mut<T: DupSort>(
&self,
) -> Result<<Self as DbTxMutGAT<'_>>::DupCursorMut<T>, Error> {
self.new_cursor() self.new_cursor()
} }
} }

View File

@ -2,8 +2,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use super::{ use super::{
Database, DatabaseGAT, DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, DbTx, DbTxMut, Database, DatabaseGAT, DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, DbTx, DbTxGAT,
DupSort, Table, DbTxMut, DbTxMutGAT, DupSort, DupWalker, Error, Table, Walker,
}; };
/// Mock database used for testing with inner BTreeMap structure /// Mock database used for testing with inner BTreeMap structure
@ -37,11 +37,17 @@ pub struct TxMock {
_table: BTreeMap<Vec<u8>, Vec<u8>>, _table: BTreeMap<Vec<u8>, Vec<u8>>,
} }
impl<'a> DbTxGAT<'a> for TxMock {
type Cursor<T: Table> = CursorMock;
type DupCursor<T: DupSort> = CursorMock;
}
impl<'a> DbTxMutGAT<'a> for TxMock {
type CursorMut<T: Table> = CursorMock;
type DupCursorMut<T: DupSort> = CursorMock;
}
impl<'a> DbTx<'a> for TxMock { impl<'a> DbTx<'a> for TxMock {
type Cursor<T: super::Table> = CursorMock;
type DupCursor<T: super::DupSort> = CursorMock;
fn get<T: super::Table>(&self, _key: T::Key) -> Result<Option<T::Value>, super::Error> { fn get<T: super::Table>(&self, _key: T::Key) -> Result<Option<T::Value>, super::Error> {
todo!() todo!()
} }
@ -50,20 +56,18 @@ impl<'a> DbTx<'a> for TxMock {
todo!() todo!()
} }
fn cursor<T: super::Table>(&self) -> Result<Self::Cursor<T>, super::Error> { fn cursor<T: super::Table>(&self) -> Result<<Self as DbTxGAT<'_>>::Cursor<T>, super::Error> {
todo!() todo!()
} }
fn cursor_dup<T: super::DupSort>(&self) -> Result<Self::DupCursor<T>, super::Error> { fn cursor_dup<T: super::DupSort>(
&self,
) -> Result<<Self as DbTxGAT<'_>>::DupCursor<T>, super::Error> {
todo!() todo!()
} }
} }
impl<'a> DbTxMut<'a> for TxMock { impl<'a> DbTxMut<'a> for TxMock {
type CursorMut<T: super::Table> = CursorMock;
type DupCursorMut<T: super::DupSort> = CursorMock;
fn put<T: super::Table>(&self, _key: T::Key, _value: T::Value) -> Result<(), super::Error> { fn put<T: super::Table>(&self, _key: T::Key, _value: T::Value) -> Result<(), super::Error> {
todo!() todo!()
} }
@ -76,11 +80,15 @@ impl<'a> DbTxMut<'a> for TxMock {
todo!() todo!()
} }
fn cursor_mut<T: super::Table>(&self) -> Result<Self::CursorMut<T>, super::Error> { fn cursor_mut<T: super::Table>(
&self,
) -> Result<<Self as DbTxMutGAT<'_>>::CursorMut<T>, super::Error> {
todo!() todo!()
} }
fn cursor_dup_mut<T: super::DupSort>(&self) -> Result<Self::DupCursorMut<T>, super::Error> { fn cursor_dup_mut<T: super::DupSort>(
&self,
) -> Result<<Self as DbTxMutGAT<'_>>::DupCursorMut<T>, super::Error> {
todo!() todo!()
} }
@ -123,7 +131,13 @@ impl<'tx, T: Table> DbCursorRO<'tx, T> for CursorMock {
todo!() todo!()
} }
fn walk(&'tx mut self, _start_key: T::Key) -> Result<super::Walker<'tx, T>, super::Error> { fn walk<'cursor>(
&'cursor mut self,
_start_key: T::Key,
) -> Result<Walker<'cursor, 'tx, T, Self>, Error>
where
Self: Sized,
{
todo!() todo!()
} }
} }
@ -141,11 +155,14 @@ impl<'tx, T: DupSort> DbDupCursorRO<'tx, T> for CursorMock {
todo!() todo!()
} }
fn walk_dup( fn walk_dup<'cursor>(
&'tx mut self, &'cursor mut self,
_key: <T>::Key, _key: <T>::Key,
_subkey: <T as DupSort>::SubKey, _subkey: <T as DupSort>::SubKey,
) -> Result<super::DupWalker<'tx, T>, super::Error> { ) -> Result<DupWalker<'cursor, 'tx, T, Self>, Error>
where
Self: Sized,
{
todo!() todo!()
} }
} }

View File

@ -6,6 +6,8 @@ pub mod models;
mod table; mod table;
pub mod tables; pub mod tables;
use std::marker::PhantomData;
pub use error::Error; pub use error::Error;
pub use table::*; pub use table::*;
@ -67,25 +69,22 @@ pub trait Database: for<'a> DatabaseGAT<'a> {
} }
} }
/// Read only transaction /// Implements the GAT method from:
pub trait DbTx<'a> { /// https://sabrinajewson.org/blog/the-better-alternative-to-lifetime-gats#the-better-gats.
///
/// Sealed trait which cannot be implemented by 3rd parties, exposed only for implementers
pub trait DbTxGAT<'a, __ImplicitBounds: Sealed = Bounds<&'a Self>>: Send + Sync {
/// Cursor GAT /// Cursor GAT
type Cursor<T: Table>: DbCursorRO<'a, T>; type Cursor<T: Table>: DbCursorRO<'a, T>;
/// DupCursor GAT /// DupCursor GAT
type DupCursor<T: DupSort>: DbDupCursorRO<'a, T> + DbCursorRO<'a, T>; type DupCursor<T: DupSort>: DbDupCursorRO<'a, T> + DbCursorRO<'a, T>;
/// Get value
fn get<T: Table>(&self, key: T::Key) -> Result<Option<T::Value>, Error>;
/// Commit for read only transaction will consume and free transaction and allows
/// freeing of memory pages
fn commit(self) -> Result<bool, Error>;
/// Iterate over read only values in table.
fn cursor<T: Table>(&self) -> Result<Self::Cursor<T>, Error>;
/// Iterate over read only values in dup sorted table.
fn cursor_dup<T: DupSort>(&self) -> Result<Self::DupCursor<T>, Error>;
} }
/// Read write transaction that allows writing to database /// Implements the GAT method from:
pub trait DbTxMut<'a> { /// https://sabrinajewson.org/blog/the-better-alternative-to-lifetime-gats#the-better-gats.
///
/// Sealed trait which cannot be implemented by 3rd parties, exposed only for implementers
pub trait DbTxMutGAT<'a, __ImplicitBounds: Sealed = Bounds<&'a Self>>: Send + Sync {
/// Cursor GAT /// Cursor GAT
type CursorMut<T: Table>: DbCursorRW<'a, T> + DbCursorRO<'a, T>; type CursorMut<T: Table>: DbCursorRW<'a, T> + DbCursorRO<'a, T>;
/// DupCursor GAT /// DupCursor GAT
@ -93,6 +92,23 @@ pub trait DbTxMut<'a> {
+ DbCursorRW<'a, T> + DbCursorRW<'a, T>
+ DbDupCursorRO<'a, T> + DbDupCursorRO<'a, T>
+ DbCursorRO<'a, T>; + DbCursorRO<'a, T>;
}
/// Read only transaction
pub trait DbTx<'tx>: for<'a> DbTxGAT<'a> {
/// Get value
fn get<T: Table>(&self, key: T::Key) -> Result<Option<T::Value>, Error>;
/// Commit for read only transaction will consume and free transaction and allows
/// freeing of memory pages
fn commit(self) -> Result<bool, Error>;
/// Iterate over read only values in table.
fn cursor<T: Table>(&'tx self) -> Result<<Self as DbTxGAT<'tx>>::Cursor<T>, Error>;
/// Iterate over read only values in dup sorted table.
fn cursor_dup<T: DupSort>(&'tx self) -> Result<<Self as DbTxGAT<'tx>>::DupCursor<T>, Error>;
}
/// Read write transaction that allows writing to database
pub trait DbTxMut<'tx>: for<'a> DbTxMutGAT<'a> {
/// Put value to database /// Put value to database
fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), Error>; fn put<T: Table>(&self, key: T::Key, value: T::Value) -> Result<(), Error>;
/// Delete value from database /// Delete value from database
@ -100,9 +116,11 @@ pub trait DbTxMut<'a> {
/// Clears database. /// Clears database.
fn clear<T: Table>(&self) -> Result<(), Error>; fn clear<T: Table>(&self) -> Result<(), Error>;
/// Cursor mut /// Cursor mut
fn cursor_mut<T: Table>(&self) -> Result<Self::CursorMut<T>, Error>; fn cursor_mut<T: Table>(&self) -> Result<<Self as DbTxMutGAT<'tx>>::CursorMut<T>, Error>;
/// DupCursor mut. /// DupCursor mut.
fn cursor_dup_mut<T: DupSort>(&self) -> Result<Self::DupCursorMut<T>, Error>; fn cursor_dup_mut<T: DupSort>(
&self,
) -> Result<<Self as DbTxMutGAT<'tx>>::DupCursorMut<T>, Error>;
} }
/// Alias type for a `(key, value)` result coming from a cursor. /// Alias type for a `(key, value)` result coming from a cursor.
@ -136,8 +154,18 @@ pub trait DbCursorRO<'tx, T: Table> {
/// Returns the current `(key, value)` pair of the cursor. /// Returns the current `(key, value)` pair of the cursor.
fn current(&mut self) -> PairResult<T>; fn current(&mut self) -> PairResult<T>;
/// Inner
fn inner(&'tx mut self) -> &'tx mut Self {
self
}
/// Returns an iterator starting at a key greater or equal than `start_key`. /// Returns an iterator starting at a key greater or equal than `start_key`.
fn walk(&'tx mut self, start_key: T::Key) -> Result<Walker<'tx, T>, Error>; fn walk<'cursor>(
&'cursor mut self,
start_key: T::Key,
) -> Result<Walker<'cursor, 'tx, T, Self>, Error>
where
Self: Sized;
} }
/// Read only curor over DupSort table. /// Read only curor over DupSort table.
@ -153,7 +181,13 @@ pub trait DbDupCursorRO<'tx, T: DupSort> {
/// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT /// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT
/// table. /// table.
fn walk_dup(&'tx mut self, key: T::Key, subkey: T::SubKey) -> Result<DupWalker<'tx, T>, Error>; fn walk_dup<'cursor>(
&'cursor mut self,
key: T::Key,
subkey: T::SubKey,
) -> Result<DupWalker<'cursor, 'tx, T, Self>, Error>
where
Self: Sized;
} }
/// Read write cursor over table. /// Read write cursor over table.
@ -178,14 +212,18 @@ pub trait DbDupCursorRW<'tx, T: DupSort> {
} }
/// Provides an iterator to `Cursor` when handling `Table`. /// Provides an iterator to `Cursor` when handling `Table`.
pub struct Walker<'cursor, T: Table> { pub struct Walker<'cursor, 'tx, T: Table, CURSOR: DbCursorRO<'tx, T> + Sized> {
/// Cursor to be used to walk through the table. /// Cursor to be used to walk through the table.
pub cursor: &'cursor mut dyn DbCursorRO<'cursor, T>, pub cursor: &'cursor mut CURSOR,
/// `(key, value)` where to start the walk. /// `(key, value)` where to start the walk.
pub start: IterPairResult<T>, pub start: IterPairResult<T>,
/// Phantom data for 'tx.
pub _tx_phantom: PhantomData<&'tx T>,
} }
impl<'cursor, T: Table> std::iter::Iterator for Walker<'cursor, T> { impl<'cursor, 'tx, T: Table, CURSOR: DbCursorRO<'tx, T> + Sized> std::iter::Iterator
for Walker<'cursor, 'tx, T, CURSOR>
{
type Item = Result<(T::Key, T::Value), Error>; type Item = Result<(T::Key, T::Value), Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let start = self.start.take(); let start = self.start.take();
@ -198,14 +236,18 @@ impl<'cursor, T: Table> std::iter::Iterator for Walker<'cursor, T> {
} }
/// Provides an iterator to `Cursor` when handling a `DupSort` table. /// Provides an iterator to `Cursor` when handling a `DupSort` table.
pub struct DupWalker<'cursor, T: DupSort> { pub struct DupWalker<'cursor, 'tx, T: DupSort, CURSOR: DbDupCursorRO<'tx, T> + Sized> {
/// Cursor to be used to walk through the table. /// Cursor to be used to walk through the table.
pub cursor: &'cursor mut dyn DbDupCursorRO<'cursor, T>, pub cursor: &'cursor mut CURSOR,
/// Value where to start the walk. /// Value where to start the walk.
pub start: Option<Result<T::Value, Error>>, pub start: Option<Result<T::Value, Error>>,
/// Phantom data for 'tx.
pub _tx_phantom: PhantomData<&'tx T>,
} }
impl<'cursor, T: DupSort> std::iter::Iterator for DupWalker<'cursor, T> { impl<'cursor, 'tx, T: DupSort, CURSOR: DbDupCursorRO<'tx, T> + Sized> std::iter::Iterator
for DupWalker<'cursor, 'tx, T, CURSOR>
{
type Item = Result<T::Value, Error>; type Item = Result<T::Value, Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let start = self.start.take(); let start = self.start.take();

View File

@ -21,7 +21,7 @@ pub type HeaderHash = H256;
/// element as BlockNumber, helps out with querying/sorting. /// element as BlockNumber, helps out with querying/sorting.
/// ///
/// Since it's used as a key, the `BlockNumber` is not compressed when encoding it. /// Since it's used as a key, the `BlockNumber` is not compressed when encoding it.
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub struct BlockNumHash((BlockNumber, BlockHash)); pub struct BlockNumHash((BlockNumber, BlockHash));
@ -58,7 +58,9 @@ impl Decode for BlockNumHash {
let value: bytes::Bytes = value.into(); let value: bytes::Bytes = value.into();
let num = u64::from_be_bytes( let num = u64::from_be_bytes(
value.as_ref().try_into().map_err(|_| Error::Decode(eyre!("Into bytes error.")))?, value.as_ref()[..8]
.try_into()
.map_err(|_| Error::Decode(eyre!("Into bytes error.")))?,
); );
let hash = H256::decode(value.slice(8..))?; let hash = H256::decode(value.slice(8..))?;