|
|
|
@ -6,32 +6,24 @@ use database::Database; |
|
|
|
|
use error::{LmdbResult, lmdb_result}; |
|
|
|
|
use ffi; |
|
|
|
|
use ffi::{MDB_cursor, mdb_cursor_open, MDB_cursor_op, MDB_val}; |
|
|
|
|
use flags::WriteFlags; |
|
|
|
|
use transaction::Transaction; |
|
|
|
|
|
|
|
|
|
/// A cursor for navigating within a database.
|
|
|
|
|
pub struct Cursor<'txn> { |
|
|
|
|
cursor: *mut MDB_cursor, |
|
|
|
|
_marker: marker::ContravariantLifetime<'txn>, |
|
|
|
|
_no_sync: marker::NoSync, |
|
|
|
|
_no_send: marker::NoSend, |
|
|
|
|
_contravariant: marker::ContravariantLifetime<'txn>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe fn slice_to_val(slice: Option<&[u8]>) -> MDB_val { |
|
|
|
|
match slice { |
|
|
|
|
Some(slice) => |
|
|
|
|
MDB_val { mv_size: slice.len() as size_t, |
|
|
|
|
mv_data: slice.as_ptr() as *const c_void }, |
|
|
|
|
None => |
|
|
|
|
MDB_val { mv_size: 0, |
|
|
|
|
mv_data: ptr::null() }, |
|
|
|
|
#[unsafe_destructor] |
|
|
|
|
impl <'txn> Drop for Cursor<'txn> { |
|
|
|
|
fn drop(&mut self) { |
|
|
|
|
unsafe { ffi::mdb_cursor_close(self.cursor) } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe fn val_to_slice<'a>(val: MDB_val) -> &'a [u8] { |
|
|
|
|
mem::transmute(raw::Slice { |
|
|
|
|
data: val.mv_data as *const u8, |
|
|
|
|
len: val.mv_size as uint |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl <'txn> Cursor<'txn> { |
|
|
|
|
|
|
|
|
|
/// Creates a new cursor into the given database in the given transaction. Prefer using
|
|
|
|
@ -42,7 +34,9 @@ impl <'txn> Cursor<'txn> { |
|
|
|
|
unsafe { try!(lmdb_result(mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); } |
|
|
|
|
Ok(Cursor { |
|
|
|
|
cursor: cursor, |
|
|
|
|
_marker: marker::ContravariantLifetime::<'txn>, |
|
|
|
|
_no_sync: marker::NoSync, |
|
|
|
|
_no_send: marker::NoSend, |
|
|
|
|
_contravariant: marker::ContravariantLifetime::<'txn>, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -70,6 +64,57 @@ impl <'txn> Cursor<'txn> { |
|
|
|
|
Ok((key_out, data_out)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Puts a key/data pair into the database. The cursor will be positioned at the new data item,
|
|
|
|
|
/// or on failure usually near it.
|
|
|
|
|
pub fn put(&self, |
|
|
|
|
key: &[u8], |
|
|
|
|
data: &[u8], |
|
|
|
|
flags: WriteFlags) |
|
|
|
|
-> LmdbResult<()> { |
|
|
|
|
|
|
|
|
|
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, |
|
|
|
|
mv_data: key.as_ptr() as *const c_void }; |
|
|
|
|
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t, |
|
|
|
|
mv_data: data.as_ptr() as *const 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
|
|
|
|
|
///
|
|
|
|
|
/// `MDB_NODUPDATA` may be used to delete all data items for the current key, if the database
|
|
|
|
|
/// was opened with `MDB_DUPSORT`.
|
|
|
|
|
pub fn del(&self, flags: WriteFlags) -> LmdbResult<()> { |
|
|
|
|
unsafe { |
|
|
|
|
lmdb_result(ffi::mdb_cursor_del(self.cursor(), flags.bits())) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe fn slice_to_val(slice: Option<&[u8]>) -> MDB_val { |
|
|
|
|
match slice { |
|
|
|
|
Some(slice) => |
|
|
|
|
MDB_val { mv_size: slice.len() as size_t, |
|
|
|
|
mv_data: slice.as_ptr() as *const c_void }, |
|
|
|
|
None => |
|
|
|
|
MDB_val { mv_size: 0, |
|
|
|
|
mv_data: ptr::null() }, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe fn val_to_slice<'a>(val: MDB_val) -> &'a [u8] { |
|
|
|
|
mem::transmute(raw::Slice { |
|
|
|
|
data: val.mv_data as *const u8, |
|
|
|
|
len: val.mv_size as uint |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
@ -253,4 +298,25 @@ mod test { |
|
|
|
|
assert!(sets_key(&cursor, Some(b"key2"), None, MDB_cursor_op::MDB_SET_KEY).unwrap()); |
|
|
|
|
assert!(sets_key(&cursor, Some(b"key2"), None, MDB_cursor_op::MDB_SET_RANGE).unwrap()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_put_del() { |
|
|
|
|
let dir = io::TempDir::new("test").unwrap(); |
|
|
|
|
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); |
|
|
|
|
|
|
|
|
|
let mut txn = env.begin_txn(EnvironmentFlags::empty()).unwrap(); |
|
|
|
|
let db = txn.open_db(None, DatabaseFlags::empty()).unwrap(); |
|
|
|
|
let cursor = txn.open_cursor(db).unwrap(); |
|
|
|
|
|
|
|
|
|
cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap(); |
|
|
|
|
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap(); |
|
|
|
|
cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap(); |
|
|
|
|
|
|
|
|
|
assert_eq!((Some(b"key3"), b"val3"), |
|
|
|
|
cursor.get(None, None, MDB_cursor_op::MDB_GET_CURRENT).unwrap()); |
|
|
|
|
|
|
|
|
|
cursor.del(WriteFlags::empty()).unwrap(); |
|
|
|
|
assert_eq!((Some(b"key2"), b"val2"), |
|
|
|
|
cursor.get(None, None, MDB_cursor_op::MDB_LAST).unwrap()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|