make ReadTransaction::get return option; cleanup tests

without.crypto
Dan Burkert 10 years ago
parent 6b00d64e5b
commit 40f499eb86
  1. 109
      src/cursor.rs
  2. 50
      src/environment.rs
  3. 1
      src/lib.rs
  4. 90
      src/transaction.rs

@ -40,7 +40,7 @@ pub trait ReadCursor<'txn> : Cursor<'txn> {
} }
/// Open a new read-only cursor on the given database. /// Open a new read-only cursor on the given database.
fn iter<'t>(&'t mut self) -> Items<'t> { fn iter(&mut self) -> Items<'txn> {
Items::new(self) Items::new(self)
} }
} }
@ -221,12 +221,10 @@ impl <'txn> Iterator<(&'txn [u8], &'txn [u8])> for Items<'txn> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use libc::{c_void, size_t, c_uint};
use std::{io, ptr}; use std::{io, ptr};
use test::{Bencher, black_box}; use test::{Bencher, black_box};
use environment::*; use environment::*;
use error::{LmdbResult, lmdb_result};
use ffi::*; use ffi::*;
use super::*; use super::*;
use transaction::*; use transaction::*;
@ -237,23 +235,21 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let items = vec!((b"key1", b"val1"),
(b"key2", b"val2"),
(b"key3", b"val3"));
{ {
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); for &(key, data) in items.iter() {
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, key, data, WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap(); }
txn.commit().unwrap(); txn.commit().unwrap();
} }
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
let mut cursor = txn.open_read_cursor(db).unwrap(); let mut cursor = txn.open_read_cursor(db).unwrap();
let iter = cursor.iter(); assert_eq!(items, cursor.iter().collect::<Vec<(&[u8], &[u8])>>());
let items: Vec<(&[u8], &[u8])> = iter.collect();
assert_eq!(vec!((b"key1", b"val1"),
(b"key2", b"val2"),
(b"key3", b"val3")),
items);
} }
#[test] #[test]
@ -354,77 +350,6 @@ mod test {
assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err()); assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err());
} }
/// Checks assumptions about which get operations return keys.
#[test]
fn test_get_keys() {
let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
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 *mut c_void },
None =>
MDB_val { mv_size: 0,
mv_data: ptr::null_mut() },
}
}
/// Returns true if the cursor get sets the key.
fn sets_key(cursor: &Cursor,
key: Option<&[u8]>,
data: Option<&[u8]>,
op: c_uint)
-> LmdbResult<bool> {
unsafe {
let mut key_val = slice_to_val(key);
let mut data_val = slice_to_val(data);
let key_ptr = key_val.mv_data;
try!(lmdb_result(mdb_cursor_get(cursor.cursor(),
&mut key_val,
&mut data_val,
op)));
Ok(key_ptr != key_val.mv_data)
}
}
let db = env.create_db(None, MDB_DUPSORT | MDB_DUPFIXED).unwrap();
let mut txn = env.begin_write_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val4", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val5", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val7", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val8", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val9", WriteFlags::empty()).unwrap();
let cursor = txn.open_read_cursor(db).unwrap();
assert!(sets_key(&cursor, None, None, MDB_FIRST).unwrap());
assert!(!sets_key(&cursor, None, None, MDB_FIRST_DUP).unwrap());
assert!(!sets_key(&cursor, Some(b"key2"), Some(b"val5"), MDB_GET_BOTH).unwrap());
assert!(!sets_key(&cursor, Some(b"key2"), Some(b"val"), MDB_GET_BOTH_RANGE).unwrap());
assert!(sets_key(&cursor, None, None, MDB_GET_CURRENT).unwrap());
assert!(!sets_key(&cursor, None, None, MDB_GET_MULTIPLE).unwrap());
assert!(sets_key(&cursor, None, None, MDB_LAST).unwrap());
assert!(!sets_key(&cursor, None, None, MDB_LAST_DUP).unwrap());
sets_key(&cursor, None, None, MDB_FIRST).unwrap();
assert!(sets_key(&cursor, None, None, MDB_NEXT).unwrap());
assert!(sets_key(&cursor, None, None, MDB_NEXT_DUP).unwrap());
sets_key(&cursor, None, None, MDB_FIRST).unwrap();
assert!(sets_key(&cursor, None, None, MDB_NEXT_MULTIPLE).unwrap());
assert!(sets_key(&cursor, None, None, MDB_NEXT_NODUP).unwrap());
assert!(sets_key(&cursor, None, None, MDB_PREV).unwrap());
sets_key(&cursor, None, None, MDB_LAST).unwrap();
assert!(sets_key(&cursor, None, None, MDB_PREV_DUP).unwrap());
assert!(sets_key(&cursor, None, None, MDB_PREV_NODUP).unwrap());
assert!(!sets_key(&cursor, Some(b"key2"), None, MDB_SET).unwrap());
assert!(sets_key(&cursor, Some(b"key2"), None, MDB_SET_KEY).unwrap());
assert!(sets_key(&cursor, Some(b"key2"), None, MDB_SET_RANGE).unwrap());
}
#[test] #[test]
fn test_put_del() { fn test_put_del() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
@ -446,7 +371,7 @@ mod test {
cursor.get(None, None, MDB_LAST).unwrap()); cursor.get(None, None, MDB_LAST).unwrap());
} }
fn get_bench_ctx<'a>(num_rows: u32) -> (io::TempDir, Environment) { fn setup_bench_db<'a>(num_rows: u32) -> (io::TempDir, Environment) {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
@ -454,7 +379,11 @@ mod test {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
for i in range(0, num_rows) { for i in range(0, num_rows) {
txn.put(db, format!("key{}", i).as_bytes(), format!("val{}", i).as_bytes(), WriteFlags::empty()).unwrap(); txn.put(db,
format!("key{}", i).as_bytes(),
format!("val{}", i).as_bytes(),
WriteFlags::empty())
.unwrap();
} }
txn.commit().unwrap(); txn.commit().unwrap();
} }
@ -465,7 +394,7 @@ mod test {
#[bench] #[bench]
fn bench_seq_iter(b: &mut Bencher) { fn bench_seq_iter(b: &mut Bencher) {
let n = 100; let n = 100;
let (_dir, env) = get_bench_ctx(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
@ -473,10 +402,12 @@ mod test {
let mut cursor = txn.open_read_cursor(db).unwrap(); let mut cursor = txn.open_read_cursor(db).unwrap();
let mut i = 0; let mut i = 0;
let mut count = 0u32; let mut count = 0u32;
for (key, data) in cursor.iter() { for (key, data) in cursor.iter() {
i = i + key.len() + data.len(); i = i + key.len() + data.len();
count = count + 1; count = count + 1;
} }
black_box(i); black_box(i);
assert_eq!(count, n); assert_eq!(count, n);
}); });
@ -486,7 +417,7 @@ mod test {
#[bench] #[bench]
fn bench_seq_cursor(b: &mut Bencher) { fn bench_seq_cursor(b: &mut Bencher) {
let n = 100; let n = 100;
let (_dir, env) = get_bench_ctx(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
@ -509,7 +440,7 @@ mod test {
#[bench] #[bench]
fn bench_seq_raw(b: &mut Bencher) { fn bench_seq_raw(b: &mut Bencher) {
let n = 100; let n = 100;
let (_dir, env) = get_bench_ctx(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let dbi: MDB_dbi = db.dbi(); let dbi: MDB_dbi = db.dbi();

@ -110,17 +110,25 @@ impl Environment {
/// Close a database handle. Normally unnecessary. /// Close a database handle. Normally unnecessary.
/// ///
/// This call is not mutex protected. Handles should only be closed by a single thread, and only
/// if no other threads are going to reference the database handle or one of its cursors any
/// further. Do not close a handle if an existing transaction has modified its database. Doing
/// so can cause misbehavior from database corruption to errors like `MDB_BAD_VALSIZE` (since the
/// DB name is gone).
///
/// Closing a database handle is not necessary, but lets `Transaction::open_database` reuse the /// Closing a database handle is not necessary, but lets `Transaction::open_database` reuse the
/// handle value. Usually it's better to set a bigger `EnvironmentBuilder::set_max_dbs`, unless /// handle value. Usually it's better to set a bigger `EnvironmentBuilder::set_max_dbs`, unless
/// that value would be large. /// that value would be large.
pub unsafe fn close_db(&self, db: Database) { pub fn close_db(&mut self, name: Option<&str>) -> LmdbResult<()> {
mdb_dbi_close(self.env, db.dbi()) let db = try!(self.open_db(name));
unsafe { mdb_dbi_close(self.env, db.dbi()) };
Ok(())
}
pub fn clear_db(&mut self, name: Option<&str>) -> LmdbResult<()> {
let db = try!(self.open_db(name));
let txn = try!(self.begin_write_txn());
unsafe { lmdb_result(mdb_drop(txn.txn(), db.dbi(), 0)) }
}
pub fn drop_db(&mut self, name: Option<&str>) -> LmdbResult<()> {
let db = try!(self.open_db(name));
let txn = try!(self.begin_write_txn());
unsafe { lmdb_result(mdb_drop(txn.txn(), db.dbi(), 1)) }
} }
} }
@ -236,10 +244,10 @@ mod test {
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.is_err()); .is_err());
// opening non-existent env should not fail // opening non-existent env should succeed
assert!(Environment::new().open(dir.path(), io::USER_RWX).is_ok()); assert!(Environment::new().open(dir.path(), io::USER_RWX).is_ok());
// opening env with read-only should not fail // opening env with read-only should succeed
assert!(Environment::new().set_flags(MDB_RDONLY) assert!(Environment::new().set_flags(MDB_RDONLY)
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.is_ok()); .is_ok());
@ -248,25 +256,20 @@ mod test {
#[test] #[test]
fn test_begin_txn() { fn test_begin_txn() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
{ // writable environment
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
{
// Mutable env, mutable txn
assert!(env.begin_write_txn().is_ok()); assert!(env.begin_write_txn().is_ok());
} {
// Mutable env, read-only txn
assert!(env.begin_read_txn().is_ok()); assert!(env.begin_read_txn().is_ok());
} { }
// Read-only env, mutable txn
{ // read-only environment
let env = Environment::new().set_flags(MDB_RDONLY) let env = Environment::new().set_flags(MDB_RDONLY)
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.unwrap(); .unwrap();
assert!(env.begin_write_txn().is_err()); assert!(env.begin_write_txn().is_err());
} {
// Read-only env, read-only txn
let env = Environment::new().set_flags(MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.unwrap();
assert!(env.begin_read_txn().is_ok()); assert!(env.begin_read_txn().is_ok());
} }
} }
@ -274,9 +277,10 @@ mod test {
#[test] #[test]
fn test_open_db() { fn test_open_db() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(10) let env = Environment::new().set_max_dbs(1)
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.unwrap(); .unwrap();
assert!(env.open_db(None).is_ok()); assert!(env.open_db(None).is_ok());
assert!(env.open_db(Some("testdb")).is_err()); assert!(env.open_db(Some("testdb")).is_err());
} }
@ -284,7 +288,7 @@ mod test {
#[test] #[test]
fn test_create_db() { fn test_create_db() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(10) let env = Environment::new().set_max_dbs(11)
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.unwrap(); .unwrap();
assert!(env.open_db(Some("testdb")).is_err()); assert!(env.open_db(Some("testdb")).is_err());

@ -13,7 +13,6 @@ extern crate "lmdb-sys" as ffi;
extern crate test; extern crate test;
extern crate collections; extern crate collections;
pub use cursor::Cursor; pub use cursor::Cursor;
pub use database::Database; pub use database::Database;
pub use environment::{Environment, EnvironmentBuilder}; pub use environment::{Environment, EnvironmentBuilder};

@ -6,7 +6,7 @@ use std::io::BufWriter;
use cursor::{RoCursor, RwCursor}; use cursor::{RoCursor, RwCursor};
use environment::Environment; use environment::Environment;
use database::Database; use database::Database;
use error::{LmdbResult, lmdb_result}; use error::{LmdbError, LmdbResult, lmdb_result};
use ffi; use ffi;
use ffi::MDB_txn; use ffi::MDB_txn;
use ffi::{DatabaseFlags, EnvironmentFlags, WriteFlags, MDB_RDONLY, MDB_RESERVE}; use ffi::{DatabaseFlags, EnvironmentFlags, WriteFlags, MDB_RDONLY, MDB_RESERVE};
@ -31,7 +31,7 @@ pub trait TransactionExt<'env> : Transaction<'env> {
/// Any pending operations will be saved. /// Any pending operations will be saved.
fn commit(self) -> LmdbResult<()> { fn commit(self) -> LmdbResult<()> {
unsafe { unsafe {
let result = lmdb_result(::ffi::mdb_txn_commit(self.txn())); let result = lmdb_result(ffi::mdb_txn_commit(self.txn()));
mem::forget(self); mem::forget(self);
result result
} }
@ -57,22 +57,26 @@ pub trait ReadTransaction<'env> : Transaction<'env> {
/// This function retrieves the data associated with the given key in the database. If the /// This function retrieves the data associated with the given key in the database. If the
/// database supports duplicate keys (`MDB_DUPSORT`) then the first data item for the key will /// database supports duplicate keys (`MDB_DUPSORT`) then the first data item for the key will
/// be returned. Retrieval of other items requires the use of `Transaction::cursor_get`. /// be returned. Retrieval of other items requires the use of `Transaction::cursor_get`.
fn get<'txn>(&'txn self, database: Database, key: &[u8]) -> LmdbResult<&'txn [u8]> { fn get<'txn>(&'txn self, database: Database, key: &[u8]) -> LmdbResult<Option<&'txn [u8]>> {
let mut key_val: ::ffi::MDB_val = ::ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t,
mv_data: key.as_ptr() as *mut c_void }; mv_data: key.as_ptr() as *mut c_void };
let mut data_val: ::ffi::MDB_val = ::ffi::MDB_val { mv_size: 0, let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: 0,
mv_data: ptr::null_mut() }; mv_data: ptr::null_mut() };
unsafe { unsafe {
try!(lmdb_result(::ffi::mdb_get(self.txn(), let err_code = ffi::mdb_get(self.txn(), database.dbi(), &mut key_val, &mut data_val);
database.dbi(), if err_code == 0 {
&mut key_val,
&mut data_val)));
let slice: &'txn [u8] = let slice: &'txn [u8] =
mem::transmute(raw::Slice { mem::transmute(raw::Slice {
data: data_val.mv_data as *const u8, data: data_val.mv_data as *const u8,
len: data_val.mv_size as uint len: data_val.mv_size as uint
}); });
Ok(slice) Ok(Some(slice))
} else if err_code == ffi::MDB_NOTFOUND {
Ok(None)
} else {
Err(LmdbError::from_err_code(err_code))
}
} }
} }
@ -282,8 +286,8 @@ mod test {
use std::sync::{Arc, Barrier, Future}; use std::sync::{Arc, Barrier, Future};
use environment::*; use environment::*;
use super::*;
use ffi::*; use ffi::*;
use super::*;
#[test] #[test]
@ -299,13 +303,13 @@ mod test {
txn.commit().unwrap(); txn.commit().unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
assert_eq!(b"val1", txn.get(db, b"key1").unwrap()); assert_eq!(b"val1", txn.get(db, b"key1").unwrap().unwrap());
assert_eq!(b"val2", txn.get(db, b"key2").unwrap()); assert_eq!(b"val2", txn.get(db, b"key2").unwrap().unwrap());
assert_eq!(b"val3", txn.get(db, b"key3").unwrap()); assert_eq!(b"val3", txn.get(db, b"key3").unwrap().unwrap());
assert!(txn.get(db, b"key").is_err()); assert!(txn.get(db, b"key").unwrap().is_none());
txn.del(db, b"key1", None).unwrap(); txn.del(db, b"key1", None).unwrap();
assert!(txn.get(db, b"key1").is_err()); assert!(txn.get(db, b"key1").unwrap().is_none());
} }
#[test] #[test]
@ -322,50 +326,42 @@ mod test {
txn.commit().unwrap(); txn.commit().unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
assert_eq!(b"val1", txn.get(db, b"key1").unwrap()); assert_eq!(b"val1", txn.get(db, b"key1").unwrap().unwrap());
assert!(txn.get(db, b"key").is_err()); assert!(txn.get(db, b"key").unwrap().is_none());
txn.del(db, b"key1", None).unwrap(); txn.del(db, b"key1", None).unwrap();
assert!(txn.get(db, b"key1").is_err()); assert!(txn.get(db, b"key1").unwrap().is_none());
} }
#[test] #[test]
fn test_close_database() { fn test_close_database() {
let dir = io::TempDir::new("test").unwrap(); let dir = io::TempDir::new("test").unwrap();
let env = Arc::new(Environment::new() let mut env = Environment::new().set_max_dbs(10)
.set_max_dbs(10)
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.unwrap()); .unwrap();
let db1 = {
let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap();
let txn = env.begin_write_txn().unwrap();
txn.commit().unwrap();
db
};
let db2 = { env.create_db(Some("db"), DatabaseFlags::empty()).unwrap();
let db = env.open_db(Some("db")).unwrap(); env.close_db(Some("db")).unwrap();
let txn = env.begin_read_txn().unwrap(); assert!(env.open_db(Some("db")).is_ok());
txn.commit().unwrap(); }
db
};
// Check that database handles are reused properly #[test]
assert!(db1.dbi() == db2.dbi()); fn test_clear_db() {
let dir = io::TempDir::new("test").unwrap();
let mut env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
{ {
let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_write_txn().unwrap();
txn.put(db1, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key", b"val", WriteFlags::empty()).unwrap();
assert!(txn.commit().is_ok()); txn.commit().unwrap();
} }
unsafe { env.close_db(db1) }; env.clear_db(None).unwrap();
{ let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let txn = env.begin_read_txn().unwrap();
assert!(txn.put(db1, b"key2", b"val2", WriteFlags::empty()).is_err()); txn.get(db, b"key").is_err();
}
} }
#[test] #[test]
@ -388,14 +384,14 @@ mod test {
let db = reader_env.open_db(None).unwrap(); let db = reader_env.open_db(None).unwrap();
{ {
let txn = reader_env.begin_read_txn().unwrap(); let txn = reader_env.begin_read_txn().unwrap();
assert!(txn.get(db, key).is_err()); assert!(txn.get(db, key).unwrap().is_none());
txn.abort(); txn.abort();
} }
reader_barrier.wait(); reader_barrier.wait();
reader_barrier.wait(); reader_barrier.wait();
{ {
let txn = reader_env.begin_read_txn().unwrap(); let txn = reader_env.begin_read_txn().unwrap();
txn.get(db, key).unwrap() == val txn.get(db, key).unwrap().unwrap() == val
} }
})); }));
} }
@ -443,7 +439,7 @@ mod test {
for i in range(0, n) { for i in range(0, n) {
assert_eq!( assert_eq!(
format!("{}{}", val, i).as_bytes(), format!("{}{}", val, i).as_bytes(),
txn.get(db, format!("{}{}", key, i).as_bytes()).unwrap()); txn.get(db, format!("{}{}", key, i).as_bytes()).unwrap().unwrap());
} }
} }
} }

Loading…
Cancel
Save