use std::marker::PhantomData; use std::{
fmt,
mem,
ptr,
result,
slice,
};
use libc::{
c_uint,
c_void,
size_t,
EINVAL,
};
use database::Database; use error::{
lmdb_result,
Error,
Result,
}; use ffi; use flags::WriteFlags; use transaction::Transaction;
/// An LMDB cursor. pubtrait Cursor<'txn> { /// Returns a raw pointer to the underlying LMDB cursor. /// /// The caller **must** ensure that the pointer is not used after the /// lifetime of the cursor. fn cursor(&self) -> *mut ffi::MDB_cursor;
/// Retrieves a key/data pair from the cursor. Depending on the cursor op, /// the current key may be returned. fn get(&self, key: Option<&[u8]>, data: Option<&[u8]>, op: c_uint) -> Result<(Option<&'txn [u8]>, &'txn [u8])> { unsafe { letmut key_val = slice_to_val(key); letmut data_val = slice_to_val(data); let key_ptr = key_val.mv_data;
lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &<span style='color:red'>mut data_val, op))?; let key_out = if key_ptr != key_val.mv_data {
Some(val_to_slice(key_val))
} else {
None
}; let data_out = val_to_slice(data_val);
Ok((key_out, data_out))
}
}
/// Iterate over database items. The iterator will begin with item next /// after the cursor, and continue until the end of the database. For new /// cursors, the iterator will begin with the first item in the database. /// /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// duplicate data items of each key will be returned before moving on to /// the next key. fn iter(&mutself) -> Iter<'txn> {
Iter::new(self.cursor(), ffi::MDB_NEXT, ffi::MDB_NEXT)
}
/// Iterate over database items starting from the beginning of the database. /// /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// duplicate data items of each key will be returned before moving on to /// the next key. fn iter_start(&mutself) -> Iter<'txn> {
Iter::new(self.cursor(), ffi::MDB_FIRST, ffi::MDB_NEXT)
}
/// Iterate over database items starting from the given key. /// /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// duplicate data items of each key will be returned before moving on to /// the next key. fn iter_from<K>(&mutself, key: K) -> Iter<'txn> where
K: AsRef<[u8]>,
{ matchself.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error),
};
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
}
/// Iterate over duplicate database items. The iterator will begin with the /// item next after the cursor, and continue until the end of the database. /// Each item will be returned as an iterator of its duplicates. fn iter_dup(&mutself) -> IterDup<'txn> {
IterDup::new(self.cursor(), ffi::MDB_NEXT)
}
/// Iterate over duplicate database items starting from the beginning of the /// database. Each item will be returned as an iterator of its duplicates. fn iter_dup_start(&mutself) -> IterDup<'txn> {
IterDup::new(self.cursor(), ffi::MDB_FIRST)
}
/// Iterate over duplicate items in the database starting from the given /// key. Each item will be returned as an iterator of its duplicates. fn iter_dup_from<K>(&mutself, key: K) -> IterDup<'txn> where
K: AsRef<[u8]>,
{ matchself.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (),
Err(error) => return IterDup::Err(error),
};
IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
}
/// Iterate over the duplicates of the item in the database with the given key. fn iter_dup_of<K>(&mutself, key: K) -> Iter<'txn> where
K: AsRef<[u8]>,
{ matchself.get(Some(key.as_ref()), None, ffi::MDB_SET) {
Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error),
};
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)
}
}
/// A read-only cursor for navigating the items within a database. pubstruct RoCursor<'txn> {
cursor: *mut ffi::MDB_cursor,
_marker: PhantomData<fn() -> &'txn ()>,
}
impl<'txn> Drop for RwCursor<'txn> { fn drop(&mutself) { unsafe { ffi::mdb_cursor_close(self.cursor) }
}
}
impl<'txn> RwCursor<'txn> { /// Creates a new read-only cursor in the given database and transaction. /// Prefer using `RwTransaction::open_rw_cursor`. pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>> where
T: Transaction,
{ letmut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); unsafe {
lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
}
Ok(RwCursor {
cursor,
_marker: PhantomData,
})
}
/// Puts a key/data pair into the database. The cursor will be positioned at /// the new data item, or on failure usually near it. pubfn put<K, D>(&mutself, key: &K, data: &D, flags: WriteFlags) -> Result<()> where
K: AsRef<[u8]>,
D: AsRef<[u8]>,
{ let key = key.as_ref(); let data = data.as_ref(); letmut key_val: ffi::MDB_val = ffi::MDB_val {
mv_size: key.len() as size_t,
mv_data: key.as_ptr() as *mut c_void,
}; letmut data_val: ffi::MDB_val = ffi::MDB_val {
mv_size: data.len() as size_t,
mv_data: data.as_ptr() as *mut c_void,
}; unsafe { lmdb_result(ffi::mdb_cursor_put(self.cursor(), &mut key_val, &mut data_val, flags.bits())) }
}
/// Deletes the current key/data pair. /// /// ### Flags /// /// `WriteFlags::NO_DUP_DATA` may be used to delete all data items for the /// current key, if the database was opened with `DatabaseFlags::DUP_SORT`. pubfn del(&mutself, flags: WriteFlags) -> Result<()> { unsafe { lmdb_result(ffi::mdb_cursor_del(self.cursor(), flags.bits())) }
}
}
unsafefn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] {
slice::from_raw_parts(val.mv_data as *const u8, val.mv_size as usize)
}
/// An iterator over the key/value pairs in an LMDB database. pubenum Iter<'txn> { /// An iterator that returns an error on every call to Iter.next(). /// Cursor.iter*() creates an Iter of this type when LMDB returns an error /// on retrieval of a cursor. Using this variant instead of returning /// an error makes Cursor.iter()* methods infallible, so consumers only /// need to check the result of Iter.next().
Err(Error),
/// An iterator that returns an Item on calls to Iter.next(). /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant /// might still return an error, if retrieval of the key/value pair /// fails for some reason.
Ok { /// The LMDB cursor with which to iterate.
cursor: *mut ffi::MDB_cursor,
/// The first operation to perform when the consumer calls Iter.next().
op: c_uint,
/// The next and subsequent operations to perform.
next_op: c_uint,
/// A marker to ensure the iterator doesn't outlive the transaction.
_marker: PhantomData<fn(&'txn ())>,
},
}
impl<'txn> Iter<'txn> { /// Creates a new iterator backed by the given cursor. fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
Iter::Ok {
cursor,
op,
next_op,
_marker: PhantomData,
}
}
}
impl<'txn> Iterator for Iter<'txn> { type Item = Result<(&'txn [u8], &'txn [u8])>;
fn next(&mutself) -> Option<Result<(&'txn [u8], &'txn [u8])>> { matchself {
&mut Iter::Ok {
cursor, refmut op,
next_op,
_marker,
} => { letmut key = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
}; letmut data = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
}; let op = mem::replace(op, next_op); unsafe { match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) {
ffi::MDB_SUCCESS => Some(Ok((val_to_slice(key), val_to_slice(data)))), // EINVAL can occur when the cursor was previously seeked to a non-existent value, // e.g. iter_from with a key greater than all values in the database.
ffi::MDB_NOTFOUND | EINVAL => None,
error => Some(Err(Error::from_err_code(error))),
}
}
},
&mut Iter::Err(err) => Some(Err(err)),
}
}
}
/// An iterator over the keys and duplicate values in an LMDB database. /// /// The yielded items of the iterator are themselves iterators over the duplicate values for a /// specific key. pubenum IterDup<'txn> { /// An iterator that returns an error on every call to Iter.next(). /// Cursor.iter*() creates an Iter of this type when LMDB returns an error /// on retrieval of a cursor. Using this variant instead of returning /// an error makes Cursor.iter()* methods infallible, so consumers only /// need to check the result of Iter.next().
Err(Error),
/// An iterator that returns an Item on calls to Iter.next(). /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant /// might still return an error, if retrieval of the key/value pair /// fails for some reason.
Ok { /// The LMDB cursor with which to iterate.
cursor: *mut ffi::MDB_cursor,
/// The first operation to perform when the consumer calls Iter.next().
op: c_uint,
/// A marker to ensure the iterator doesn't outlive the transaction.
_marker: PhantomData<fn(&'txn ())>,
},
}
impl<'txn> IterDup<'txn> { /// Creates a new iterator backed by the given cursor. fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
IterDup::Ok {
cursor,
op,
_marker: PhantomData,
}
}
}
usesuper::*; use environment::*; use ffi::*; use flags::*;
#[test] fn test_get() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.open_db(None).unwrap();
#[test] fn test_get_dup() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
#[test] fn test_get_dupfixed() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
#[test] fn test_iter() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.open_db(None).unwrap();
{ letmut txn = env.begin_rw_txn().unwrap(); for &(ref key, ref data) in &items {
txn.put(db, key, data, WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let txn = env.begin_ro_txn().unwrap(); letmut cursor = txn.open_ro_cursor(db).unwrap();
// Because Result implements FromIterator, we can collect the iterator // of items of type Result<_, E> into a Result<Vec<_, E>> by specifying // the collection type via the turbofish syntax.
assert_eq!(items, cursor.iter().collect::<Result<Vec<_>>>().unwrap());
// Alternately, we can collect it into an appropriately typed variable. let retr: Result<Vec<_>> = cursor.iter_start().collect();
assert_eq!(items, retr.unwrap());
#[test] fn test_iter_empty_database() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.open_db(None).unwrap(); let txn = env.begin_ro_txn().unwrap(); letmut cursor = txn.open_ro_cursor(db).unwrap();
#[test] fn test_iter_empty_dup_database() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap(); let txn = env.begin_ro_txn().unwrap(); letmut cursor = txn.open_ro_cursor(db).unwrap();
#[test] fn test_iter_dup() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
#[test] fn test_put_del() { let dir = TempDir::new("test").unwrap(); let env = Environment::new().open(dir.path()).unwrap(); let db = env.open_db(None).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.