Cursor put/del support

without.crypto
Dan Burkert 10 years ago
parent bed03ebff6
commit b21d5e92a4
  1. 100
      src/cursor.rs
  2. 20
      src/flags.rs
  3. 14
      src/transaction.rs

@ -6,32 +6,24 @@ use database::Database;
use error::{LmdbResult, lmdb_result}; use error::{LmdbResult, lmdb_result};
use ffi; use ffi;
use ffi::{MDB_cursor, mdb_cursor_open, MDB_cursor_op, MDB_val}; use ffi::{MDB_cursor, mdb_cursor_open, MDB_cursor_op, MDB_val};
use flags::WriteFlags;
use transaction::Transaction; use transaction::Transaction;
/// A cursor for navigating within a database. /// A cursor for navigating within a database.
pub struct Cursor<'txn> { pub struct Cursor<'txn> {
cursor: *mut MDB_cursor, 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 { #[unsafe_destructor]
match slice { impl <'txn> Drop for Cursor<'txn> {
Some(slice) => fn drop(&mut self) {
MDB_val { mv_size: slice.len() as size_t, unsafe { ffi::mdb_cursor_close(self.cursor) }
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
})
}
impl <'txn> Cursor<'txn> { impl <'txn> Cursor<'txn> {
/// Creates a new cursor into the given database in the given transaction. Prefer using /// 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))); } unsafe { try!(lmdb_result(mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); }
Ok(Cursor { Ok(Cursor {
cursor: 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)) 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)] #[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_KEY).unwrap());
assert!(sets_key(&cursor, Some(b"key2"), None, MDB_cursor_op::MDB_SET_RANGE).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());
}
} }

@ -158,6 +158,13 @@ bitflags! {
#[deriving(Show)] #[deriving(Show)]
flags WriteFlags: c_uint { flags WriteFlags: c_uint {
#[doc="Enter the new key/data pair only if the key"]
#[doc="does not already appear in the database. The function will return"]
#[doc="`KeyExist` if the key already appears in the database, even if"]
#[doc="the database supports duplicates (`MDB_DUPSORT`). The `data`"]
#[doc="parameter will be set to point to the existing item."]
const MDB_NOOVERWRITE = 0x10,
#[doc="Enter the new key/data pair only if it does not"] #[doc="Enter the new key/data pair only if it does not"]
#[doc="already appear in the database. This flag may only be specified"] #[doc="already appear in the database. This flag may only be specified"]
#[doc="if the database was opened with `MDB_DUPSORT`. The function will"] #[doc="if the database was opened with `MDB_DUPSORT`. The function will"]
@ -165,12 +172,13 @@ bitflags! {
#[doc="database."] #[doc="database."]
const MDB_NODUPDATA = 0x20, const MDB_NODUPDATA = 0x20,
#[doc="Enter the new key/data pair only if the key"] #[doc="For `Cursor::put`. Replace the item at the current cursor position."]
#[doc="does not already appear in the database. The function will return"] #[doc="The key parameter must match the current position. If using"]
#[doc="`KeyExist` if the key already appears in the database, even if"] #[doc="sorted duplicates (`MDB_DUPSORT`) the data item must still sort into the"]
#[doc="the database supports duplicates (`MDB_DUPSORT`). The `data`"] #[doc="same position. This is intended to be used when the new data is the same"]
#[doc="parameter will be set to point to the existing item."] #[doc="size as the old. Otherwise it will simply perform a delete of the old"]
const MDB_NOOVERWRITE = 0x10, #[doc="record followed by an insert."]
const MDB_CURRENT = 0x40,
#[doc="Reserve space for data of the given size, but"] #[doc="Reserve space for data of the given size, but"]
#[doc="don't copy the given data. Instead, return a pointer to the"] #[doc="don't copy the given data. Instead, return a pointer to the"]

@ -15,7 +15,9 @@ use flags::{DatabaseFlags, EnvironmentFlags, WriteFlags};
/// All database operations require a transaction. /// All database operations require a transaction.
pub struct Transaction<'env> { pub struct Transaction<'env> {
txn: *mut MDB_txn, txn: *mut MDB_txn,
_marker: marker::ContravariantLifetime<'env>, _no_sync: marker::NoSync,
_no_send: marker::NoSend,
_contravariant: marker::ContravariantLifetime<'env>,
} }
#[unsafe_destructor] #[unsafe_destructor]
@ -38,7 +40,9 @@ impl <'env> Transaction<'env> {
&mut txn))); &mut txn)));
Ok(Transaction { Ok(Transaction {
txn: txn, txn: txn,
_marker: marker::ContravariantLifetime::<'env>, _no_sync: marker::NoSync,
_no_send: marker::NoSend,
_contravariant: marker::ContravariantLifetime::<'env>,
}) })
} }
} }
@ -173,7 +177,11 @@ impl <'env> Transaction<'env> {
} }
} }
pub fn open_cursor<'txn>(&'txn self, db: Database) -> LmdbResult<Cursor<'txn>> { /// Open a new cursor over the database.
///
/// Takes a mutable reference to the transaction since cursors can mutate the transaction, which
/// will invalidates values read during the transaction.
pub fn open_cursor<'txn>(&'txn mut self, db: Database) -> LmdbResult<Cursor<'txn>> {
Cursor::new(self, db) Cursor::new(self, db)
} }
} }

Loading…
Cancel
Save