Merge pull request #60 from mozilla/format-pass

Formatting passes and enable linting on CI
without.crypto
Victor Porof 5 years ago committed by GitHub
commit 67a9c6b990
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .rustfmt.toml
  2. 11
      .travis.yml
  3. 32
      benches/cursor.rs
  4. 44
      benches/transaction.rs
  5. 8
      benches/utils.rs
  6. 3
      lmdb-sys/.rustfmt.toml
  7. 3
      lmdb-sys/src/lib.rs
  8. 30
      lmdb-sys/tests/lmdb.rs
  9. 384
      src/cursor.rs
  10. 22
      src/database.rs
  11. 139
      src/environment.rs
  12. 92
      src/error.rs
  13. 39
      src/lib.rs
  14. 278
      src/transaction.rs

@ -1 +1,4 @@
disable_all_formatting = true imports_layout = "Vertical"
max_width = 120
match_block_trailing_comma = true
use_small_heuristics = "Off"

@ -14,7 +14,18 @@ rust:
- beta - beta
- nightly - nightly
before_script:
# We install a known-to-have-rustfmt version of the nightly toolchain
# in order to run the nightly version of rustfmt, which supports rules
# that we depend upon. When updating, pick a suitable nightly version
# from https://rust-lang.github.io/rustup-components-history/
- rustup toolchain install nightly-2019-09-11
- rustup component add rustfmt --toolchain nightly-2019-09-11
- rustup component add clippy --toolchain nightly-2019-09-11
script: script:
- cargo +nightly-2019-09-11 fmt --all -- --check
- cargo +nightly-2019-09-11 clippy --all-features -- -D warnings -A clippy::match-ref-pats -A clippy::needless-lifetimes
- cargo build --verbose - cargo build --verbose
- export RUST_BACKTRACE=1 - export RUST_BACKTRACE=1
- cargo test --all --verbose - cargo test --all --verbose

@ -7,9 +7,17 @@ extern crate test;
mod utils; mod utils;
use ffi::*; use ffi::*;
use lmdb::{Cursor, Result, RoCursor, Transaction}; use lmdb::{
Cursor,
Result,
RoCursor,
Transaction,
};
use std::ptr; use std::ptr;
use test::{Bencher, black_box}; use test::{
black_box,
Bencher,
};
use utils::*; use utils::*;
/// Benchmark of iterator sequential read performance. /// Benchmark of iterator sequential read performance.
@ -27,20 +35,18 @@ fn bench_get_seq_iter(b: &mut Bencher) {
for (key, data) in cursor.iter().map(Result::unwrap) { for (key, data) in cursor.iter().map(Result::unwrap) {
i = i + key.len() + data.len(); i = i + key.len() + data.len();
count = count + 1; count += 1;
} }
for (key, data) in cursor.iter().filter_map(Result::ok) { for (key, data) in cursor.iter().filter_map(Result::ok) {
i = i + key.len() + data.len(); i = i + key.len() + data.len();
count = count + 1; count += 1;
} }
fn iterate<'a>(cursor: &mut RoCursor) -> Result<()> { fn iterate(cursor: &mut RoCursor) -> Result<()> {
let mut i = 0; let mut i = 0;
let mut count = 0u32;
for result in cursor.iter() { for result in cursor.iter() {
let (key, data) = result?; let (key, data) = result?;
i = i + key.len() + data.len(); i = i + key.len() + data.len();
count = count + 1;
} }
Ok(()) Ok(())
} }
@ -85,8 +91,14 @@ fn bench_get_seq_raw(b: &mut Bencher) {
let _txn = env.begin_ro_txn().unwrap(); let _txn = env.begin_ro_txn().unwrap();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key = MDB_val {
let mut data = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut cursor: *mut MDB_cursor = ptr::null_mut(); let mut cursor: *mut MDB_cursor = ptr::null_mut();
b.iter(|| unsafe { b.iter(|| unsafe {
@ -97,7 +109,7 @@ fn bench_get_seq_raw(b: &mut Bencher) {
while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 { while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 {
i += key.mv_size + data.mv_size; i += key.mv_size + data.mv_size;
count += 1; count += 1;
}; }
black_box(i); black_box(i);
assert_eq!(count, n); assert_eq!(count, n);

@ -10,10 +10,19 @@ mod utils;
use ffi::*; use ffi::*;
use libc::size_t; use libc::size_t;
use lmdb::{Transaction, WriteFlags}; use lmdb::{
use rand::{Rng, XorShiftRng}; Transaction,
WriteFlags,
};
use rand::{
Rng,
XorShiftRng,
};
use std::ptr; use std::ptr;
use test::{Bencher, black_box}; use test::{
black_box,
Bencher,
};
use utils::*; use utils::*;
#[bench] #[bench]
@ -23,13 +32,13 @@ fn bench_get_rand(b: &mut Bencher) {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_ro_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let mut keys: Vec<String> = (0..n).map(|n| get_key(n)).collect(); let mut keys: Vec<String> = (0..n).map(get_key).collect();
XorShiftRng::new_unseeded().shuffle(&mut keys[..]); XorShiftRng::new_unseeded().shuffle(&mut keys[..]);
b.iter(|| { b.iter(|| {
let mut i = 0usize; let mut i = 0usize;
for key in &keys { for key in &keys {
i = i + txn.get(db, key).unwrap().len(); i += txn.get(db, key).unwrap().len();
} }
black_box(i); black_box(i);
}); });
@ -42,14 +51,20 @@ fn bench_get_rand_raw(b: &mut Bencher) {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let _txn = env.begin_ro_txn().unwrap(); let _txn = env.begin_ro_txn().unwrap();
let mut keys: Vec<String> = (0..n).map(|n| get_key(n)).collect(); let mut keys: Vec<String> = (0..n).map(get_key).collect();
XorShiftRng::new_unseeded().shuffle(&mut keys[..]); XorShiftRng::new_unseeded().shuffle(&mut keys[..]);
let dbi = db.dbi(); let dbi = db.dbi();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key_val: MDB_val = MDB_val {
let mut data_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data_val: MDB_val = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
b.iter(|| unsafe { b.iter(|| unsafe {
let mut i: size_t = 0; let mut i: size_t = 0;
@ -59,7 +74,7 @@ fn bench_get_rand_raw(b: &mut Bencher) {
mdb_get(txn, dbi, &mut key_val, &mut data_val); mdb_get(txn, dbi, &mut key_val, &mut data_val);
i = i + key_val.mv_size; i += key_val.mv_size;
} }
black_box(i); black_box(i);
}); });
@ -95,8 +110,14 @@ fn bench_put_rand_raw(b: &mut Bencher) {
let dbi = db.dbi(); let dbi = db.dbi();
let env = _env.env(); let env = _env.env();
let mut key_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key_val: MDB_val = MDB_val {
let mut data_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data_val: MDB_val = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
b.iter(|| unsafe { b.iter(|| unsafe {
let mut txn: *mut MDB_txn = ptr::null_mut(); let mut txn: *mut MDB_txn = ptr::null_mut();
@ -104,7 +125,6 @@ fn bench_put_rand_raw(b: &mut Bencher) {
let mut i: ::libc::c_int = 0; let mut i: ::libc::c_int = 0;
for &(ref key, ref data) in items.iter() { for &(ref key, ref data) in items.iter() {
key_val.mv_size = key.len() as size_t; key_val.mv_size = key.len() as size_t;
key_val.mv_data = key.as_bytes().as_ptr() as *mut _; key_val.mv_data = key.as_bytes().as_ptr() as *mut _;
data_val.mv_size = data.len() as size_t; data_val.mv_size = data.len() as size_t;

@ -1,8 +1,12 @@
extern crate lmdb; extern crate lmdb;
extern crate tempdir; extern crate tempdir;
use lmdb::{Environment, Transaction, WriteFlags};
use self::tempdir::TempDir; use self::tempdir::TempDir;
use lmdb::{
Environment,
Transaction,
WriteFlags,
};
pub fn get_key(n: u32) -> String { pub fn get_key(n: u32) -> String {
format!("key{}", n) format!("key{}", n)
@ -12,7 +16,7 @@ pub fn get_data(n: u32) -> String {
format!("data{}", n) format!("data{}", n)
} }
pub fn setup_bench_db<'a>(num_rows: u32) -> (TempDir, Environment) { pub fn setup_bench_db(num_rows: u32) -> (TempDir, Environment) {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();

@ -0,0 +1,3 @@
ignore = [
"src/bindings.rs"
]

@ -1,5 +1,6 @@
#![allow(non_camel_case_types)]
#![deny(warnings)] #![deny(warnings)]
#![allow(non_camel_case_types)]
#![allow(clippy::all)]
#![doc(html_root_url = "https://docs.rs/lmdb-rkv-sys/0.9.0")] #![doc(html_root_url = "https://docs.rs/lmdb-rkv-sys/0.9.0")]
extern crate libc; extern crate libc;

@ -6,23 +6,23 @@ use std::process::Command;
#[test] #[test]
fn test_lmdb() { fn test_lmdb() {
let mut lmdb = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap()); let mut lmdb = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
lmdb.push("lmdb"); lmdb.push("lmdb");
lmdb.push("libraries"); lmdb.push("libraries");
lmdb.push("liblmdb"); lmdb.push("liblmdb");
let make_cmd = Command::new("make") let make_cmd = Command::new("make")
.current_dir(&lmdb) .current_dir(&lmdb)
.status() .status()
.expect("failed to execute process"); .expect("failed to execute process");
assert_eq!(make_cmd.success(), true); assert_eq!(make_cmd.success(), true);
let make_test_cmd = Command::new("make") let make_test_cmd = Command::new("make")
.arg("test") .arg("test")
.current_dir(&lmdb) .current_dir(&lmdb)
.status() .status()
.expect("failed to execute process"); .expect("failed to execute process");
assert_eq!(make_test_cmd.success(), true); assert_eq!(make_test_cmd.success(), true);
} }

@ -1,17 +1,31 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::{fmt, mem, ptr, result, slice}; use std::{
fmt,
use libc::{EINVAL, c_void, size_t, c_uint}; mem,
ptr,
result,
slice,
};
use libc::{
c_uint,
c_void,
size_t,
EINVAL,
};
use database::Database; use database::Database;
use error::{Error, Result, lmdb_result}; use error::{
lmdb_result,
Error,
Result,
};
use ffi; use ffi;
use flags::WriteFlags; use flags::WriteFlags;
use transaction::Transaction; use transaction::Transaction;
/// An LMDB cursor. /// An LMDB cursor.
pub trait Cursor<'txn> { pub trait Cursor<'txn> {
/// Returns a raw pointer to the underlying LMDB cursor. /// Returns a raw pointer to the underlying LMDB cursor.
/// ///
/// The caller **must** ensure that the pointer is not used after the /// The caller **must** ensure that the pointer is not used after the
@ -26,7 +40,11 @@ pub trait Cursor<'txn> {
let mut data_val = slice_to_val(data); let mut data_val = slice_to_val(data);
let key_ptr = key_val.mv_data; let key_ptr = key_val.mv_data;
lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &mut data_val, op))?; lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &mut data_val, op))?;
let key_out = if key_ptr != key_val.mv_data { Some(val_to_slice(key_val)) } else { None }; 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); let data_out = val_to_slice(data_val);
Ok((key_out, data_out)) Ok((key_out, data_out))
} }
@ -57,7 +75,10 @@ pub trait Cursor<'txn> {
/// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the
/// duplicate data items of each key will be returned before moving on to /// duplicate data items of each key will be returned before moving on to
/// the next key. /// the next key.
fn iter_from<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> { fn iter_from<K>(&mut self, key: K) -> Iter<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error), Err(error) => return Iter::Err(error),
@ -80,7 +101,10 @@ pub trait Cursor<'txn> {
/// Iterate over duplicate items in the database starting from the given /// Iterate over duplicate items in the database starting from the given
/// key. Each item will be returned as an iterator of its duplicates. /// key. Each item will be returned as an iterator of its duplicates.
fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn> where K: AsRef<[u8]> { fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return IterDup::Err(error), Err(error) => return IterDup::Err(error),
@ -89,7 +113,10 @@ pub trait Cursor<'txn> {
} }
/// Iterate over the duplicates of the item in the database with the given key. /// Iterate over the duplicates of the item in the database with the given key.
fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> { fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error), Err(error) => return Iter::Err(error),
@ -104,33 +131,37 @@ pub struct RoCursor<'txn> {
_marker: PhantomData<fn() -> &'txn ()>, _marker: PhantomData<fn() -> &'txn ()>,
} }
impl <'txn> Cursor<'txn> for RoCursor<'txn> { impl<'txn> Cursor<'txn> for RoCursor<'txn> {
fn cursor(&self) -> *mut ffi::MDB_cursor { fn cursor(&self) -> *mut ffi::MDB_cursor {
self.cursor self.cursor
} }
} }
impl <'txn> fmt::Debug for RoCursor<'txn> { impl<'txn> fmt::Debug for RoCursor<'txn> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("RoCursor").finish() f.debug_struct("RoCursor").finish()
} }
} }
impl <'txn> Drop for RoCursor<'txn> { impl<'txn> Drop for RoCursor<'txn> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::mdb_cursor_close(self.cursor) } unsafe { ffi::mdb_cursor_close(self.cursor) }
} }
} }
impl <'txn> RoCursor<'txn> { impl<'txn> RoCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. /// Creates a new read-only cursor in the given database and transaction.
/// Prefer using `Transaction::open_cursor`. /// Prefer using `Transaction::open_cursor`.
pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RoCursor<'txn>> where T: Transaction { pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RoCursor<'txn>>
where
T: Transaction,
{
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
unsafe { lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?; } unsafe {
lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
}
Ok(RoCursor { Ok(RoCursor {
cursor: cursor, cursor,
_marker: PhantomData, _marker: PhantomData,
}) })
} }
@ -142,50 +173,59 @@ pub struct RwCursor<'txn> {
_marker: PhantomData<fn() -> &'txn ()>, _marker: PhantomData<fn() -> &'txn ()>,
} }
impl <'txn> Cursor<'txn> for RwCursor<'txn> { impl<'txn> Cursor<'txn> for RwCursor<'txn> {
fn cursor(&self) -> *mut ffi::MDB_cursor { fn cursor(&self) -> *mut ffi::MDB_cursor {
self.cursor self.cursor
} }
} }
impl <'txn> fmt::Debug for RwCursor<'txn> { impl<'txn> fmt::Debug for RwCursor<'txn> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("RwCursor").finish() f.debug_struct("RwCursor").finish()
} }
} }
impl <'txn> Drop for RwCursor<'txn> { impl<'txn> Drop for RwCursor<'txn> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::mdb_cursor_close(self.cursor) } unsafe { ffi::mdb_cursor_close(self.cursor) }
} }
} }
impl <'txn> RwCursor<'txn> { impl<'txn> RwCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. /// Creates a new read-only cursor in the given database and transaction.
/// Prefer using `RwTransaction::open_rw_cursor`. /// Prefer using `RwTransaction::open_rw_cursor`.
pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>> where T: Transaction { pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>>
where
T: Transaction,
{
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
unsafe { lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?; } unsafe {
Ok(RwCursor { cursor: cursor, _marker: PhantomData }) 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 /// Puts a key/data pair into the database. The cursor will be positioned at
/// the new data item, or on failure usually near it. /// the new data item, or on failure usually near it.
pub fn put<K, D>(&mut self, key: &K, data: &D, flags: WriteFlags) -> Result<()> pub fn put<K, D>(&mut self, key: &K, data: &D, flags: WriteFlags) -> Result<()>
where K: AsRef<[u8]>, D: AsRef<[u8]> { where
K: AsRef<[u8]>,
D: AsRef<[u8]>,
{
let key = key.as_ref(); let key = key.as_ref();
let data = data.as_ref(); let data = data.as_ref();
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_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t, mv_data: key.as_ptr() as *mut c_void,
mv_data: data.as_ptr() as *mut c_void }; };
unsafe { let mut data_val: ffi::MDB_val = ffi::MDB_val {
lmdb_result(ffi::mdb_cursor_put(self.cursor(), mv_size: data.len() as size_t,
&mut key_val, mv_data: data.as_ptr() as *mut c_void,
&mut data_val, };
flags.bits())) unsafe { lmdb_result(ffi::mdb_cursor_put(self.cursor(), &mut key_val, &mut data_val, flags.bits())) }
}
} }
/// Deletes the current key/data pair. /// Deletes the current key/data pair.
@ -201,12 +241,14 @@ impl <'txn> RwCursor<'txn> {
unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val { unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val {
match slice { match slice {
Some(slice) => Some(slice) => ffi::MDB_val {
ffi::MDB_val { mv_size: slice.len() as size_t, mv_size: slice.len() as size_t,
mv_data: slice.as_ptr() as *mut c_void }, mv_data: slice.as_ptr() as *mut c_void,
None => },
ffi::MDB_val { mv_size: 0, None => ffi::MDB_val {
mv_data: ptr::null_mut() }, mv_size: 0,
mv_data: ptr::null_mut(),
},
} }
} }
@ -242,29 +284,43 @@ pub enum Iter<'txn> {
}, },
} }
impl <'txn> Iter<'txn> { impl<'txn> Iter<'txn> {
/// Creates a new iterator backed by the given cursor. /// 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> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
Iter::Ok { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData } Iter::Ok {
cursor,
op,
next_op,
_marker: PhantomData,
}
} }
} }
impl <'txn> fmt::Debug for Iter<'txn> { impl<'txn> fmt::Debug for Iter<'txn> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("Iter").finish() f.debug_struct("Iter").finish()
} }
} }
impl <'txn> Iterator for Iter<'txn> { impl<'txn> Iterator for Iter<'txn> {
type Item = Result<(&'txn [u8], &'txn [u8])>; type Item = Result<(&'txn [u8], &'txn [u8])>;
fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> { fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> {
match self { match self {
&mut Iter::Ok { cursor, ref mut op, next_op, _marker } => { &mut Iter::Ok {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; cursor,
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; ref mut op,
next_op,
_marker,
} => {
let mut key = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
let op = mem::replace(op, next_op); let op = mem::replace(op, next_op);
unsafe { unsafe {
match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) { match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) {
@ -309,33 +365,43 @@ pub enum IterDup<'txn> {
}, },
} }
impl <'txn> IterDup<'txn> { impl<'txn> IterDup<'txn> {
/// Creates a new iterator backed by the given cursor. /// Creates a new iterator backed by the given cursor.
fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
IterDup::Ok { cursor: cursor, op: op, _marker: PhantomData } IterDup::Ok {
cursor,
op,
_marker: PhantomData,
}
} }
} }
impl <'txn> fmt::Debug for IterDup<'txn> { impl<'txn> fmt::Debug for IterDup<'txn> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("IterDup").finish() f.debug_struct("IterDup").finish()
} }
} }
impl <'txn> Iterator for IterDup<'txn> { impl<'txn> Iterator for IterDup<'txn> {
type Item = Iter<'txn>; type Item = Iter<'txn>;
fn next(&mut self) -> Option<Iter<'txn>> { fn next(&mut self) -> Option<Iter<'txn>> {
match self { match self {
&mut IterDup::Ok { cursor, ref mut op, _marker } => { &mut IterDup::Ok {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; cursor,
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; ref mut op,
let op = mem::replace(op, ffi::MDB_NEXT_NODUP); _marker,
let err_code = unsafe { } => {
ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) let mut key = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
}; };
let mut data = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
let op = mem::replace(op, ffi::MDB_NEXT_NODUP);
let err_code = unsafe { ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) };
if err_code == ffi::MDB_SUCCESS { if err_code == ffi::MDB_SUCCESS {
Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)) Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
@ -352,10 +418,10 @@ impl <'txn> Iterator for IterDup<'txn> {
mod test { mod test {
use tempdir::TempDir; use tempdir::TempDir;
use super::*;
use environment::*; use environment::*;
use ffi::*; use ffi::*;
use flags::*; use flags::*;
use super::*;
#[test] #[test]
fn test_get() { fn test_get() {
@ -369,22 +435,14 @@ mod test {
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap()); assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_PREV).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_LAST).unwrap());
cursor.get(None, None, MDB_NEXT).unwrap()); assert_eq!((None, &b"val2"[..]), cursor.get(Some(b"key2"), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key3"[..]), None, MDB_SET_KEY).unwrap());
cursor.get(None, None, MDB_PREV).unwrap()); assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key2\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(None, None, MDB_LAST).unwrap());
assert_eq!((None, &b"val2"[..]),
cursor.get(Some(b"key2"), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(Some(&b"key3"[..]), None, MDB_SET_KEY).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(Some(&b"key2\0"[..]), None, MDB_SET_RANGE).unwrap());
} }
#[test] #[test]
@ -402,35 +460,24 @@ mod test {
txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((None, &b"val1"[..]), cursor.get(None, None, MDB_FIRST_DUP).unwrap());
assert_eq!((None, &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.get(None, None, MDB_FIRST_DUP).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(None, None, MDB_NEXT_NODUP).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val3"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]),
cursor.get(None, None, MDB_NEXT_NODUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]),
cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val3"[..]),
cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert!(cursor.get(None, None, MDB_NEXT_DUP).is_err()); assert!(cursor.get(None, None, MDB_NEXT_DUP).is_err());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_PREV_DUP).unwrap());
cursor.get(None, None, MDB_PREV_DUP).unwrap()); assert_eq!((None, &b"val3"[..]), cursor.get(None, None, MDB_LAST_DUP).unwrap());
assert_eq!((None, &b"val3"[..]), assert_eq!((Some(&b"key1"[..]), &b"val3"[..]), cursor.get(None, None, MDB_PREV_NODUP).unwrap());
cursor.get(None, None, MDB_LAST_DUP).unwrap()); assert_eq!((None, &b"val1"[..]), cursor.get(Some(&b"key1"[..]), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val3"[..]), assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key2"[..]), None, MDB_SET_KEY).unwrap());
cursor.get(None, None, MDB_PREV_NODUP).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key1\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((None, &b"val1"[..]), assert_eq!((None, &b"val3"[..]), cursor.get(Some(&b"key1"[..]), Some(&b"val3"[..]), MDB_GET_BOTH).unwrap());
cursor.get(Some(&b"key1"[..]), None, MDB_SET).unwrap()); assert_eq!(
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), (None, &b"val1"[..]),
cursor.get(Some(&b"key2"[..]), None, MDB_SET_KEY).unwrap()); cursor.get(Some(&b"key2"[..]), Some(&b"val"[..]), MDB_GET_BOTH_RANGE).unwrap()
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), );
cursor.get(Some(&b"key1\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((None, &b"val3"[..]),
cursor.get(Some(&b"key1"[..]), Some(&b"val3"[..]), MDB_GET_BOTH).unwrap());
assert_eq!((None, &b"val1"[..]),
cursor.get(Some(&b"key2"[..]), Some(&b"val"[..]), MDB_GET_BOTH_RANGE).unwrap());
} }
#[test] #[test]
@ -448,10 +495,8 @@ mod test {
txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((None, &b"val1val2val3"[..]), cursor.get(None, None, MDB_GET_MULTIPLE).unwrap());
assert_eq!((None, &b"val1val2val3"[..]),
cursor.get(None, None, MDB_GET_MULTIPLE).unwrap());
assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err()); assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err());
} }
@ -461,10 +506,8 @@ mod test {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let items: Vec<(&[u8], &[u8])> = vec!((b"key1", b"val1"), let items: Vec<(&[u8], &[u8])> =
(b"key2", b"val2"), vec![(b"key1", b"val1"), (b"key2", b"val2"), (b"key3", b"val3"), (b"key5", b"val5")];
(b"key3", b"val3"),
(b"key5", b"val5"));
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
@ -487,19 +530,27 @@ mod test {
assert_eq!(items, retr.unwrap()); assert_eq!(items, retr.unwrap());
cursor.get(Some(b"key2"), None, MDB_SET).unwrap(); cursor.get(Some(b"key2"), None, MDB_SET).unwrap();
assert_eq!(items.clone().into_iter().skip(2).collect::<Vec<_>>(), assert_eq!(
cursor.iter().collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(2).collect::<Vec<_>>(),
cursor.iter().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap()); assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap());
assert_eq!(items.clone().into_iter().skip(1).collect::<Vec<_>>(), assert_eq!(
cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(1).collect::<Vec<_>>(),
cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<_>>(), assert_eq!(
cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(3).collect::<Vec<_>>(),
cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(
cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap()); vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap()
);
} }
#[test] #[test]
@ -538,18 +589,20 @@ mod test {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let items: Vec<(&[u8], &[u8])> = vec!((b"a", b"1"), let items: Vec<(&[u8], &[u8])> = vec![
(b"a", b"2"), (b"a", b"1"),
(b"a", b"3"), (b"a", b"2"),
(b"b", b"1"), (b"a", b"3"),
(b"b", b"2"), (b"b", b"1"),
(b"b", b"3"), (b"b", b"2"),
(b"c", b"1"), (b"b", b"3"),
(b"c", b"2"), (b"c", b"1"),
(b"c", b"3"), (b"c", b"2"),
(b"e", b"1"), (b"c", b"3"),
(b"e", b"2"), (b"e", b"1"),
(b"e", b"3")); (b"e", b"2"),
(b"e", b"3"),
];
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
@ -561,29 +614,40 @@ mod test {
let txn = env.begin_ro_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_ro_cursor(db).unwrap(); let mut cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); assert_eq!(items, cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap());
cursor.get(Some(b"b"), None, MDB_SET).unwrap(); cursor.get(Some(b"b"), None, MDB_SET).unwrap();
assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(
cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap()
assert_eq!(items, );
cursor.iter_dup_start().flat_map(|x| x).collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
assert_eq!(items, cursor.iter_dup_start().flatten().collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"b").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); assert_eq!(
items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(), cursor.iter_dup_from(b"b").flatten().collect::<Result<Vec<_>>>().unwrap()
cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); );
assert_eq!(items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(
cursor.iter_dup_from(b"d").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"ab").flatten().collect::<Result<Vec<_>>>().unwrap()
assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(), );
cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
assert_eq!(
assert_eq!(items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(), items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap()); cursor.iter_dup_from(b"d").flatten().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"f").flatten().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(0, cursor.iter_dup_of(b"foo").count()); assert_eq!(0, cursor.iter_dup_of(b"foo").count());
} }
@ -601,11 +665,9 @@ mod test {
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap(); cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();
cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap(); cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap();
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.del(WriteFlags::empty()).unwrap(); cursor.del(WriteFlags::empty()).unwrap();
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_LAST).unwrap());
cursor.get(None, None, MDB_LAST).unwrap());
} }
} }

@ -4,7 +4,10 @@ use std::ptr;
use ffi; use ffi;
use error::{Result, lmdb_result}; use error::{
lmdb_result,
Result,
};
/// A handle to an individual database in an environment. /// A handle to an individual database in an environment.
/// ///
@ -15,20 +18,22 @@ pub struct Database {
} }
impl Database { impl Database {
/// Opens a new database handle in the given transaction. /// Opens a new database handle in the given transaction.
/// ///
/// Prefer using `Environment::open_db`, `Environment::create_db`, `TransactionExt::open_db`, /// Prefer using `Environment::open_db`, `Environment::create_db`, `TransactionExt::open_db`,
/// or `RwTransaction::create_db`. /// or `RwTransaction::create_db`.
pub(crate) unsafe fn new(txn: *mut ffi::MDB_txn, pub(crate) unsafe fn new(txn: *mut ffi::MDB_txn, name: Option<&str>, flags: c_uint) -> Result<Database> {
name: Option<&str>,
flags: c_uint)
-> Result<Database> {
let c_name = name.map(|n| CString::new(n).unwrap()); let c_name = name.map(|n| CString::new(n).unwrap());
let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() }; let name_ptr = if let Some(ref c_name) = c_name {
c_name.as_ptr()
} else {
ptr::null()
};
let mut dbi: ffi::MDB_dbi = 0; let mut dbi: ffi::MDB_dbi = 0;
lmdb_result(ffi::mdb_dbi_open(txn, name_ptr, flags, &mut dbi))?; lmdb_result(ffi::mdb_dbi_open(txn, name_ptr, flags, &mut dbi))?;
Ok(Database { dbi: dbi }) Ok(Database {
dbi,
})
} }
pub(crate) fn freelist_db() -> Database { pub(crate) fn freelist_db() -> Database {
@ -41,6 +46,7 @@ impl Database {
/// ///
/// The caller **must** ensure that the handle is not used after the lifetime of the /// The caller **must** ensure that the handle is not used after the lifetime of the
/// environment, or after the database has been closed. /// environment, or after the database has been closed.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn dbi(&self) -> ffi::MDB_dbi { pub fn dbi(&self) -> ffi::MDB_dbi {
self.dbi self.dbi
} }

@ -1,22 +1,44 @@
use libc::{c_uint, size_t}; use libc::{
use std::{fmt, ptr, result, mem}; c_uint,
size_t,
};
use std::ffi::CString; use std::ffi::CString;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(windows)] #[cfg(windows)]
use std::ffi::OsStr; use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::Path; use std::path::Path;
use std::sync::Mutex; use std::sync::Mutex;
use std::{
fmt,
mem,
ptr,
result,
};
use ffi; use ffi;
use byteorder::{ByteOrder, NativeEndian}; use byteorder::{
ByteOrder,
NativeEndian,
};
use cursor::Cursor; use cursor::Cursor;
use error::{Error, Result, lmdb_result};
use database::Database; use database::Database;
use transaction::{RoTransaction, RwTransaction, Transaction}; use error::{
use flags::{DatabaseFlags, EnvironmentFlags}; lmdb_result,
Error,
Result,
};
use flags::{
DatabaseFlags,
EnvironmentFlags,
};
use transaction::{
RoTransaction,
RwTransaction,
Transaction,
};
#[cfg(windows)] #[cfg(windows)]
/// Adding a 'missing' trait from windows OsStrExt /// Adding a 'missing' trait from windows OsStrExt
@ -39,14 +61,14 @@ pub struct Environment {
} }
impl Environment { impl Environment {
/// Creates a new builder for specifying options for opening an LMDB environment. /// Creates a new builder for specifying options for opening an LMDB environment.
#[allow(clippy::new_ret_no_self)]
pub fn new() -> EnvironmentBuilder { pub fn new() -> EnvironmentBuilder {
EnvironmentBuilder { EnvironmentBuilder {
flags: EnvironmentFlags::empty(), flags: EnvironmentFlags::empty(),
max_readers: None, max_readers: None,
max_dbs: None, max_dbs: None,
map_size: None map_size: None,
} }
} }
@ -95,10 +117,7 @@ impl Environment {
/// ///
/// This function will fail with `Error::BadRslot` if called by a thread with an open /// This function will fail with `Error::BadRslot` if called by a thread with an open
/// transaction. /// transaction.
pub fn create_db<'env>(&'env self, pub fn create_db<'env>(&'env self, name: Option<&str>, flags: DatabaseFlags) -> Result<Database> {
name: Option<&str>,
flags: DatabaseFlags)
-> Result<Database> {
let mutex = self.dbi_open_mutex.lock(); let mutex = self.dbi_open_mutex.lock();
let txn = self.begin_rw_txn()?; let txn = self.begin_rw_txn()?;
let db = unsafe { txn.create_db(name, flags)? }; let db = unsafe { txn.create_db(name, flags)? };
@ -110,7 +129,7 @@ impl Environment {
/// Retrieves the set of flags which the database is opened with. /// Retrieves the set of flags which the database is opened with.
/// ///
/// The database must belong to to this environment. /// The database must belong to to this environment.
pub fn get_db_flags<'env>(&'env self, db: Database) -> Result<DatabaseFlags> { pub fn get_db_flags(&self, db: Database) -> Result<DatabaseFlags> {
let txn = self.begin_ro_txn()?; let txn = self.begin_ro_txn()?;
let mut flags: c_uint = 0; let mut flags: c_uint = 0;
unsafe { unsafe {
@ -137,7 +156,14 @@ impl Environment {
/// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`.
pub fn sync(&self, force: bool) -> Result<()> { pub fn sync(&self, force: bool) -> Result<()> {
unsafe { unsafe {
lmdb_result(ffi::mdb_env_sync(self.env(), if force { 1 } else { 0 })) lmdb_result(ffi::mdb_env_sync(
self.env(),
if force {
1
} else {
0
},
))
} }
} }
@ -239,9 +265,7 @@ impl Environment {
/// with size 0 to update the environment. Otherwise, new transaction creation /// with size 0 to update the environment. Otherwise, new transaction creation
/// will fail with `Error::MapResized`. /// will fail with `Error::MapResized`.
pub fn set_map_size(&self, size: size_t) -> Result<()> { pub fn set_map_size(&self, size: size_t) -> Result<()> {
unsafe { unsafe { lmdb_result(ffi::mdb_env_set_mapsize(self.env(), size)) }
lmdb_result(ffi::mdb_env_set_mapsize(self.env(), size))
}
} }
} }
@ -253,9 +277,7 @@ pub struct Stat(ffi::MDB_stat);
impl Stat { impl Stat {
/// Create a new Stat with zero'd inner struct `ffi::MDB_stat`. /// Create a new Stat with zero'd inner struct `ffi::MDB_stat`.
pub(crate) fn new() -> Stat { pub(crate) fn new() -> Stat {
unsafe { unsafe { Stat(mem::zeroed()) }
Stat(mem::zeroed())
}
} }
/// Returns a mut pointer to `ffi::MDB_stat`. /// Returns a mut pointer to `ffi::MDB_stat`.
@ -368,7 +390,6 @@ pub struct EnvironmentBuilder {
} }
impl EnvironmentBuilder { impl EnvironmentBuilder {
/// Open an environment. /// Open an environment.
/// ///
/// On UNIX, the database files will be opened with 644 permissions. /// On UNIX, the database files will be opened with 644 permissions.
@ -390,26 +411,27 @@ impl EnvironmentBuilder {
unsafe { unsafe {
lmdb_try!(ffi::mdb_env_create(&mut env)); lmdb_try!(ffi::mdb_env_create(&mut env));
if let Some(max_readers) = self.max_readers { if let Some(max_readers) = self.max_readers {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
if let Some(max_dbs) = self.max_dbs { if let Some(max_dbs) = self.max_dbs {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
if let Some(map_size) = self.map_size { if let Some(map_size) = self.map_size {
lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
let path = match CString::new(path.as_os_str().as_bytes()) { let path = match CString::new(path.as_os_str().as_bytes()) {
Ok(path) => path, Ok(path) => path,
Err(..) => return Err(::Error::Invalid), Err(..) => return Err(::Error::Invalid),
}; };
lmdb_try_with_cleanup!(ffi::mdb_env_open(env, path.as_ptr(), self.flags.bits(), mode), lmdb_try_with_cleanup!(
ffi::mdb_env_close(env)); ffi::mdb_env_open(env, path.as_ptr(), self.flags.bits(), mode),
ffi::mdb_env_close(env)
);
} }
Ok(Environment { env: env, dbi_open_mutex: Mutex::new(()) }) Ok(Environment {
env,
dbi_open_mutex: Mutex::new(()),
})
} }
/// Sets the provided options in the environment. /// Sets the provided options in the environment.
@ -465,8 +487,11 @@ mod test {
extern crate byteorder; extern crate byteorder;
use self::byteorder::{
ByteOrder,
LittleEndian,
};
use tempdir::TempDir; use tempdir::TempDir;
use self::byteorder::{ByteOrder, LittleEndian};
use flags::*; use flags::*;
@ -477,34 +502,30 @@ mod test {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
// opening non-existent env with read-only should fail // opening non-existent env with read-only should fail
assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY) assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_err());
.open(dir.path())
.is_err());
// opening non-existent env should succeed // opening non-existent env should succeed
assert!(Environment::new().open(dir.path()).is_ok()); assert!(Environment::new().open(dir.path()).is_ok());
// opening env with read-only should succeed // opening env with read-only should succeed
assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY) assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_ok());
.open(dir.path())
.is_ok());
} }
#[test] #[test]
fn test_begin_txn() { fn test_begin_txn() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
{ // writable environment {
// writable environment
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
assert!(env.begin_rw_txn().is_ok()); assert!(env.begin_rw_txn().is_ok());
assert!(env.begin_ro_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
} }
{ // read-only environment {
let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY) // read-only environment
.open(dir.path()) let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap();
.unwrap();
assert!(env.begin_rw_txn().is_err()); assert!(env.begin_rw_txn().is_err());
assert!(env.begin_ro_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
@ -514,9 +535,7 @@ mod test {
#[test] #[test]
fn test_open_db() { fn test_open_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(1) let env = Environment::new().set_max_dbs(1).open(dir.path()).unwrap();
.open(dir.path())
.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());
@ -525,9 +544,7 @@ mod test {
#[test] #[test]
fn test_create_db() { fn test_create_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(11) let env = Environment::new().set_max_dbs(11).open(dir.path()).unwrap();
.open(dir.path())
.unwrap();
assert!(env.open_db(Some("testdb")).is_err()); assert!(env.open_db(Some("testdb")).is_err());
assert!(env.create_db(Some("testdb"), DatabaseFlags::empty()).is_ok()); assert!(env.create_db(Some("testdb"), DatabaseFlags::empty()).is_ok());
assert!(env.open_db(Some("testdb")).is_ok()) assert!(env.open_db(Some("testdb")).is_ok())
@ -536,12 +553,12 @@ mod test {
#[test] #[test]
fn test_close_database() { fn test_close_database() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let mut env = Environment::new().set_max_dbs(10) let mut env = Environment::new().set_max_dbs(10).open(dir.path()).unwrap();
.open(dir.path())
.unwrap();
let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap(); let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap();
unsafe { env.close_db(db); } unsafe {
env.close_db(db);
}
assert!(env.open_db(Some("db")).is_ok()); assert!(env.open_db(Some("db")).is_ok());
} }
@ -551,10 +568,9 @@ mod test {
{ {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
assert!(env.sync(true).is_ok()); assert!(env.sync(true).is_ok());
} { }
let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY) {
.open(dir.path()) let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap();
.unwrap();
assert!(env.sync(true).is_err()); assert!(env.sync(true).is_err());
} }
} }
@ -598,10 +614,7 @@ mod test {
fn test_info() { fn test_info() {
let map_size = 1024 * 1024; let map_size = 1024 * 1024;
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new() let env = Environment::new().set_map_size(map_size).open(dir.path()).unwrap();
.set_map_size(map_size)
.open(dir.path())
.unwrap();
let info = env.info().unwrap(); let info = env.info().unwrap();
assert_eq!(info.map_size(), map_size); assert_eq!(info.map_size(), map_size);

@ -2,7 +2,11 @@ use libc::c_int;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::{fmt, result, str}; use std::{
fmt,
result,
str,
};
use ffi; use ffi;
@ -54,57 +58,57 @@ pub enum Error {
} }
impl Error { impl Error {
/// Converts a raw error code to an `Error`. /// Converts a raw error code to an `Error`.
pub fn from_err_code(err_code: c_int) -> Error { pub fn from_err_code(err_code: c_int) -> Error {
match err_code { match err_code {
ffi::MDB_KEYEXIST => Error::KeyExist, ffi::MDB_KEYEXIST => Error::KeyExist,
ffi::MDB_NOTFOUND => Error::NotFound, ffi::MDB_NOTFOUND => Error::NotFound,
ffi::MDB_PAGE_NOTFOUND => Error::PageNotFound, ffi::MDB_PAGE_NOTFOUND => Error::PageNotFound,
ffi::MDB_CORRUPTED => Error::Corrupted, ffi::MDB_CORRUPTED => Error::Corrupted,
ffi::MDB_PANIC => Error::Panic, ffi::MDB_PANIC => Error::Panic,
ffi::MDB_VERSION_MISMATCH => Error::VersionMismatch, ffi::MDB_VERSION_MISMATCH => Error::VersionMismatch,
ffi::MDB_INVALID => Error::Invalid, ffi::MDB_INVALID => Error::Invalid,
ffi::MDB_MAP_FULL => Error::MapFull, ffi::MDB_MAP_FULL => Error::MapFull,
ffi::MDB_DBS_FULL => Error::DbsFull, ffi::MDB_DBS_FULL => Error::DbsFull,
ffi::MDB_READERS_FULL => Error::ReadersFull, ffi::MDB_READERS_FULL => Error::ReadersFull,
ffi::MDB_TLS_FULL => Error::TlsFull, ffi::MDB_TLS_FULL => Error::TlsFull,
ffi::MDB_TXN_FULL => Error::TxnFull, ffi::MDB_TXN_FULL => Error::TxnFull,
ffi::MDB_CURSOR_FULL => Error::CursorFull, ffi::MDB_CURSOR_FULL => Error::CursorFull,
ffi::MDB_PAGE_FULL => Error::PageFull, ffi::MDB_PAGE_FULL => Error::PageFull,
ffi::MDB_MAP_RESIZED => Error::MapResized, ffi::MDB_MAP_RESIZED => Error::MapResized,
ffi::MDB_INCOMPATIBLE => Error::Incompatible, ffi::MDB_INCOMPATIBLE => Error::Incompatible,
ffi::MDB_BAD_RSLOT => Error::BadRslot, ffi::MDB_BAD_RSLOT => Error::BadRslot,
ffi::MDB_BAD_TXN => Error::BadTxn, ffi::MDB_BAD_TXN => Error::BadTxn,
ffi::MDB_BAD_VALSIZE => Error::BadValSize, ffi::MDB_BAD_VALSIZE => Error::BadValSize,
ffi::MDB_BAD_DBI => Error::BadDbi, ffi::MDB_BAD_DBI => Error::BadDbi,
other => Error::Other(other), other => Error::Other(other),
} }
} }
/// Converts an `Error` to the raw error code. /// Converts an `Error` to the raw error code.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn to_err_code(&self) -> c_int { pub fn to_err_code(&self) -> c_int {
match *self { match *self {
Error::KeyExist => ffi::MDB_KEYEXIST, Error::KeyExist => ffi::MDB_KEYEXIST,
Error::NotFound => ffi::MDB_NOTFOUND, Error::NotFound => ffi::MDB_NOTFOUND,
Error::PageNotFound => ffi::MDB_PAGE_NOTFOUND, Error::PageNotFound => ffi::MDB_PAGE_NOTFOUND,
Error::Corrupted => ffi::MDB_CORRUPTED, Error::Corrupted => ffi::MDB_CORRUPTED,
Error::Panic => ffi::MDB_PANIC, Error::Panic => ffi::MDB_PANIC,
Error::VersionMismatch => ffi::MDB_VERSION_MISMATCH, Error::VersionMismatch => ffi::MDB_VERSION_MISMATCH,
Error::Invalid => ffi::MDB_INVALID, Error::Invalid => ffi::MDB_INVALID,
Error::MapFull => ffi::MDB_MAP_FULL, Error::MapFull => ffi::MDB_MAP_FULL,
Error::DbsFull => ffi::MDB_DBS_FULL, Error::DbsFull => ffi::MDB_DBS_FULL,
Error::ReadersFull => ffi::MDB_READERS_FULL, Error::ReadersFull => ffi::MDB_READERS_FULL,
Error::TlsFull => ffi::MDB_TLS_FULL, Error::TlsFull => ffi::MDB_TLS_FULL,
Error::TxnFull => ffi::MDB_TXN_FULL, Error::TxnFull => ffi::MDB_TXN_FULL,
Error::CursorFull => ffi::MDB_CURSOR_FULL, Error::CursorFull => ffi::MDB_CURSOR_FULL,
Error::PageFull => ffi::MDB_PAGE_FULL, Error::PageFull => ffi::MDB_PAGE_FULL,
Error::MapResized => ffi::MDB_MAP_RESIZED, Error::MapResized => ffi::MDB_MAP_RESIZED,
Error::Incompatible => ffi::MDB_INCOMPATIBLE, Error::Incompatible => ffi::MDB_INCOMPATIBLE,
Error::BadRslot => ffi::MDB_BAD_RSLOT, Error::BadRslot => ffi::MDB_BAD_RSLOT,
Error::BadTxn => ffi::MDB_BAD_TXN, Error::BadTxn => ffi::MDB_BAD_TXN,
Error::BadValSize => ffi::MDB_BAD_VALSIZE, Error::BadValSize => ffi::MDB_BAD_VALSIZE,
Error::BadDbi => ffi::MDB_BAD_DBI, Error::BadDbi => ffi::MDB_BAD_DBI,
Error::Other(err_code) => err_code, Error::Other(err_code) => err_code,
} }
} }
@ -146,9 +150,7 @@ mod test {
#[test] #[test]
fn test_description() { fn test_description() {
assert_eq!("Permission denied", assert_eq!("Permission denied", Error::from_err_code(13).description());
Error::from_err_code(13).description()); assert_eq!("MDB_NOTFOUND: No matching key/data pair found", Error::NotFound.description());
assert_eq!("MDB_NOTFOUND: No matching key/data pair found",
Error::NotFound.description());
} }
} }

@ -4,19 +4,21 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.12.0")] #![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.12.0")]
extern crate byteorder;
extern crate libc; extern crate libc;
extern crate lmdb_rkv_sys as ffi; extern crate lmdb_rkv_sys as ffi;
extern crate byteorder;
#[cfg(test)] extern crate tempdir; #[cfg(test)]
#[macro_use] extern crate bitflags; extern crate tempdir;
#[macro_use]
extern crate bitflags;
pub use cursor::{ pub use cursor::{
Cursor, Cursor,
RoCursor,
RwCursor,
Iter, Iter,
IterDup, IterDup,
RoCursor,
RwCursor,
}; };
pub use database::Database; pub use database::Database;
pub use environment::{ pub use environment::{
@ -25,7 +27,10 @@ pub use environment::{
Info, Info,
Stat, Stat,
}; };
pub use error::{Error, Result}; pub use error::{
Error,
Result,
};
pub use flags::*; pub use flags::*;
pub use transaction::{ pub use transaction::{
InactiveTransaction, InactiveTransaction,
@ -35,37 +40,40 @@ pub use transaction::{
}; };
macro_rules! lmdb_try { macro_rules! lmdb_try {
($expr:expr) => ({ ($expr:expr) => {{
match $expr { match $expr {
::ffi::MDB_SUCCESS => (), ::ffi::MDB_SUCCESS => (),
err_code => return Err(::Error::from_err_code(err_code)), err_code => return Err(::Error::from_err_code(err_code)),
} }
}) }};
} }
macro_rules! lmdb_try_with_cleanup { macro_rules! lmdb_try_with_cleanup {
($expr:expr, $cleanup:expr) => ({ ($expr:expr, $cleanup:expr) => {{
match $expr { match $expr {
::ffi::MDB_SUCCESS => (), ::ffi::MDB_SUCCESS => (),
err_code => { err_code => {
let _ = $cleanup; let _ = $cleanup;
return Err(::Error::from_err_code(err_code)) return Err(::Error::from_err_code(err_code));
}, },
} }
}) }};
} }
mod flags;
mod cursor; mod cursor;
mod database; mod database;
mod environment; mod environment;
mod error; mod error;
mod flags;
mod transaction; mod transaction;
#[cfg(test)] #[cfg(test)]
mod test_utils { mod test_utils {
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{
ByteOrder,
LittleEndian,
};
use tempdir::TempDir; use tempdir::TempDir;
use super::*; use super::*;
@ -91,10 +99,7 @@ mod test_utils {
let mut value = [0u8; 8]; let mut value = [0u8; 8];
LittleEndian::write_u64(&mut value, height); LittleEndian::write_u64(&mut value, height);
let mut tx = env.begin_rw_txn().expect("begin_rw_txn"); let mut tx = env.begin_rw_txn().expect("begin_rw_txn");
tx.put(index, tx.put(index, &HEIGHT_KEY, &value, WriteFlags::empty()).expect("tx.put");
&HEIGHT_KEY,
&value,
WriteFlags::empty()).expect("tx.put");
tx.commit().expect("tx.commit") tx.commit().expect("tx.commit")
} }
} }

@ -1,20 +1,43 @@
use libc::{c_uint, c_void, size_t}; use libc::{
use std::{fmt, mem, ptr, result, slice}; c_uint,
use std::marker::PhantomData ; c_void,
size_t,
};
use std::marker::PhantomData;
use std::{
fmt,
mem,
ptr,
result,
slice,
};
use ffi; use ffi;
use cursor::{RoCursor, RwCursor}; use cursor::{
use environment::{Environment, Stat}; RoCursor,
RwCursor,
};
use database::Database; use database::Database;
use error::{Error, Result, lmdb_result}; use environment::{
use flags::{DatabaseFlags, EnvironmentFlags, WriteFlags}; Environment,
Stat,
};
use error::{
lmdb_result,
Error,
Result,
};
use flags::{
DatabaseFlags,
EnvironmentFlags,
WriteFlags,
};
/// An LMDB transaction. /// An LMDB transaction.
/// ///
/// All database operations require a transaction. /// All database operations require a transaction.
pub trait Transaction : Sized { pub trait Transaction: Sized {
/// Returns a raw pointer to the underlying LMDB transaction. /// Returns a raw pointer to the underlying LMDB transaction.
/// ///
/// The caller **must** ensure that the pointer is not used after the /// The caller **must** ensure that the pointer is not used after the
@ -68,22 +91,22 @@ pub trait Transaction : Sized {
/// returned. Retrieval of other items requires the use of /// returned. Retrieval of other items requires the use of
/// `Transaction::cursor_get`. If the item is not in the database, then /// `Transaction::cursor_get`. If the item is not in the database, then
/// `Error::NotFound` will be returned. /// `Error::NotFound` will be returned.
fn get<'txn, K>(&'txn self, fn get<'txn, K>(&'txn self, database: Database, key: &K) -> Result<&'txn [u8]>
database: Database, where
key: &K) K: AsRef<[u8]>,
-> Result<&'txn [u8]> {
where K: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
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_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: 0, mv_data: key.as_ptr() as *mut c_void,
mv_data: ptr::null_mut() }; };
let mut data_val: ffi::MDB_val = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
unsafe { unsafe {
match ffi::mdb_get(self.txn(), database.dbi(), &mut key_val, &mut data_val) { match ffi::mdb_get(self.txn(), database.dbi(), &mut key_val, &mut data_val) {
ffi::MDB_SUCCESS => { ffi::MDB_SUCCESS => Ok(slice::from_raw_parts(data_val.mv_data as *const u8, data_val.mv_size as usize)),
Ok(slice::from_raw_parts(data_val.mv_data as *const u8,
data_val.mv_size as usize))
},
err_code => Err(Error::from_err_code(err_code)), err_code => Err(Error::from_err_code(err_code)),
} }
} }
@ -119,27 +142,29 @@ pub struct RoTransaction<'env> {
_marker: PhantomData<&'env ()>, _marker: PhantomData<&'env ()>,
} }
impl <'env> fmt::Debug for RoTransaction<'env> { impl<'env> fmt::Debug for RoTransaction<'env> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("RoTransaction").finish() f.debug_struct("RoTransaction").finish()
} }
} }
impl <'env> Drop for RoTransaction<'env> { impl<'env> Drop for RoTransaction<'env> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::mdb_txn_abort(self.txn) } unsafe { ffi::mdb_txn_abort(self.txn) }
} }
} }
impl <'env> RoTransaction<'env> { impl<'env> RoTransaction<'env> {
/// Creates a new read-only transaction in the given environment. Prefer /// Creates a new read-only transaction in the given environment. Prefer
/// using `Environment::begin_ro_txn`. /// using `Environment::begin_ro_txn`.
pub(crate) fn new(env: &'env Environment) -> Result<RoTransaction<'env>> { pub(crate) fn new(env: &'env Environment) -> Result<RoTransaction<'env>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe { unsafe {
lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), ffi::MDB_RDONLY, &mut txn))?; lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), ffi::MDB_RDONLY, &mut txn))?;
Ok(RoTransaction { txn: txn, _marker: PhantomData }) Ok(RoTransaction {
txn,
_marker: PhantomData,
})
} }
} }
@ -161,11 +186,14 @@ impl <'env> RoTransaction<'env> {
mem::forget(self); mem::forget(self);
ffi::mdb_txn_reset(txn) ffi::mdb_txn_reset(txn)
}; };
InactiveTransaction { txn: txn, _marker: PhantomData } InactiveTransaction {
txn,
_marker: PhantomData,
}
} }
} }
impl <'env> Transaction for RoTransaction<'env> { impl<'env> Transaction for RoTransaction<'env> {
fn txn(&self) -> *mut ffi::MDB_txn { fn txn(&self) -> *mut ffi::MDB_txn {
self.txn self.txn
} }
@ -177,20 +205,19 @@ pub struct InactiveTransaction<'env> {
_marker: PhantomData<&'env ()>, _marker: PhantomData<&'env ()>,
} }
impl <'env> fmt::Debug for InactiveTransaction<'env> { impl<'env> fmt::Debug for InactiveTransaction<'env> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("InactiveTransaction").finish() f.debug_struct("InactiveTransaction").finish()
} }
} }
impl <'env> Drop for InactiveTransaction<'env> { impl<'env> Drop for InactiveTransaction<'env> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::mdb_txn_abort(self.txn) } unsafe { ffi::mdb_txn_abort(self.txn) }
} }
} }
impl <'env> InactiveTransaction<'env> { impl<'env> InactiveTransaction<'env> {
/// Renews the inactive transaction, returning an active read-only /// Renews the inactive transaction, returning an active read-only
/// transaction. /// transaction.
/// ///
@ -202,7 +229,10 @@ impl <'env> InactiveTransaction<'env> {
mem::forget(self); mem::forget(self);
lmdb_result(ffi::mdb_txn_renew(txn))? lmdb_result(ffi::mdb_txn_renew(txn))?
}; };
Ok(RoTransaction { txn: txn, _marker: PhantomData }) Ok(RoTransaction {
txn,
_marker: PhantomData,
})
} }
} }
@ -212,30 +242,29 @@ pub struct RwTransaction<'env> {
_marker: PhantomData<&'env ()>, _marker: PhantomData<&'env ()>,
} }
impl <'env> fmt::Debug for RwTransaction<'env> { impl<'env> fmt::Debug for RwTransaction<'env> {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
f.debug_struct("RwTransaction").finish() f.debug_struct("RwTransaction").finish()
} }
} }
impl <'env> Drop for RwTransaction<'env> { impl<'env> Drop for RwTransaction<'env> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::mdb_txn_abort(self.txn) } unsafe { ffi::mdb_txn_abort(self.txn) }
} }
} }
impl <'env> RwTransaction<'env> { impl<'env> RwTransaction<'env> {
/// Creates a new read-write transaction in the given environment. Prefer /// Creates a new read-write transaction in the given environment. Prefer
/// using `Environment::begin_ro_txn`. /// using `Environment::begin_ro_txn`.
pub(crate) fn new(env: &'env Environment) -> Result<RwTransaction<'env>> { pub(crate) fn new(env: &'env Environment) -> Result<RwTransaction<'env>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe { unsafe {
lmdb_result(ffi::mdb_txn_begin(env.env(), lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), EnvironmentFlags::empty().bits(), &mut txn))?;
ptr::null_mut(), Ok(RwTransaction {
EnvironmentFlags::empty().bits(), txn,
&mut txn))?; _marker: PhantomData,
Ok(RwTransaction { txn: txn, _marker: PhantomData }) })
} }
} }
@ -271,51 +300,55 @@ impl <'env> RwTransaction<'env> {
/// behavior is to enter the new key/data pair, replacing any previously /// behavior is to enter the new key/data pair, replacing any previously
/// existing key if duplicates are disallowed, or adding a duplicate data /// existing key if duplicates are disallowed, or adding a duplicate data
/// item if duplicates are allowed (`DatabaseFlags::DUP_SORT`). /// item if duplicates are allowed (`DatabaseFlags::DUP_SORT`).
pub fn put<K, D>(&mut self, pub fn put<K, D>(&mut self, database: Database, key: &K, data: &D, flags: WriteFlags) -> Result<()>
database: Database, where
key: &K, K: AsRef<[u8]>,
data: &D, D: AsRef<[u8]>,
flags: WriteFlags) {
-> Result<()>
where K: AsRef<[u8]>, D: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
let data = data.as_ref(); let data = data.as_ref();
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_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t, mv_data: key.as_ptr() as *mut c_void,
mv_data: data.as_ptr() as *mut c_void }; };
unsafe { let mut data_val: ffi::MDB_val = ffi::MDB_val {
lmdb_result(ffi::mdb_put(self.txn(), mv_size: data.len() as size_t,
database.dbi(), mv_data: data.as_ptr() as *mut c_void,
&mut key_val, };
&mut data_val, unsafe { lmdb_result(ffi::mdb_put(self.txn(), database.dbi(), &mut key_val, &mut data_val, flags.bits())) }
flags.bits()))
}
} }
/// Returns a buffer which can be used to write a value into the item at the /// Returns a buffer which can be used to write a value into the item at the
/// given key and with the given length. The buffer must be completely /// given key and with the given length. The buffer must be completely
/// filled by the caller. /// filled by the caller.
pub fn reserve<'txn, K>(&'txn mut self, pub fn reserve<'txn, K>(
database: Database, &'txn mut self,
key: &K, database: Database,
len: size_t, key: &K,
flags: WriteFlags) len: size_t,
-> Result<&'txn mut [u8]> flags: WriteFlags,
where K: AsRef<[u8]> { ) -> Result<&'txn mut [u8]>
where
K: AsRef<[u8]>,
{
let key = key.as_ref(); let key = key.as_ref();
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_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: len, mv_data: key.as_ptr() as *mut c_void,
mv_data: ptr::null_mut::<c_void>() }; };
let mut data_val: ffi::MDB_val = ffi::MDB_val {
mv_size: len,
mv_data: ptr::null_mut::<c_void>(),
};
unsafe { unsafe {
lmdb_result(ffi::mdb_put(self.txn(), lmdb_result(ffi::mdb_put(
database.dbi(), self.txn(),
&mut key_val, database.dbi(),
&mut data_val, &mut key_val,
flags.bits() | ffi::MDB_RESERVE))?; &mut data_val,
Ok(slice::from_raw_parts_mut(data_val.mv_data as *mut u8, flags.bits() | ffi::MDB_RESERVE,
data_val.mv_size as usize)) ))?;
Ok(slice::from_raw_parts_mut(data_val.mv_data as *mut u8, data_val.mv_size as usize))
} }
} }
@ -329,34 +362,24 @@ impl <'env> RwTransaction<'env> {
/// `Some` only the matching data item will be deleted. This function will /// `Some` only the matching data item will be deleted. This function will
/// return `Error::NotFound` if the specified key/data pair is not in the /// return `Error::NotFound` if the specified key/data pair is not in the
/// database. /// database.
pub fn del<K>(&mut self, pub fn del<K>(&mut self, database: Database, key: &K, data: Option<&[u8]>) -> Result<()>
database: Database, where
key: &K, K: AsRef<[u8]>,
data: Option<&[u8]>) {
-> Result<()>
where K: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
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_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let data_val: Option<ffi::MDB_val> = mv_data: key.as_ptr() as *mut c_void,
data.map(|data| ffi::MDB_val { mv_size: data.len() as size_t, };
mv_data: data.as_ptr() as *mut c_void }); let data_val: Option<ffi::MDB_val> = data.map(|data| ffi::MDB_val {
mv_size: data.len() as size_t,
mv_data: data.as_ptr() as *mut c_void,
});
if let Some(mut d) = data_val { if let Some(mut d) = data_val {
unsafe { unsafe { lmdb_result(ffi::mdb_del(self.txn(), database.dbi(), &mut key_val, &mut d)) }
lmdb_result(ffi::mdb_del(self.txn(),
database.dbi(),
&mut key_val,
&mut d))
}
} else { } else {
unsafe { unsafe { lmdb_result(ffi::mdb_del(self.txn(), database.dbi(), &mut key_val, ptr::null_mut())) }
lmdb_result(ffi::mdb_del(self.txn(),
database.dbi(),
&mut key_val,
ptr::null_mut()))
}
} }
} }
@ -382,11 +405,14 @@ impl <'env> RwTransaction<'env> {
let env: *mut ffi::MDB_env = ffi::mdb_txn_env(self.txn()); let env: *mut ffi::MDB_env = ffi::mdb_txn_env(self.txn());
ffi::mdb_txn_begin(env, self.txn(), 0, &mut nested); ffi::mdb_txn_begin(env, self.txn(), 0, &mut nested);
} }
Ok(RwTransaction { txn: nested, _marker: PhantomData }) Ok(RwTransaction {
txn: nested,
_marker: PhantomData,
})
} }
} }
impl <'env> Transaction for RwTransaction<'env> { impl<'env> Transaction for RwTransaction<'env> {
fn txn(&self) -> *mut ffi::MDB_txn { fn txn(&self) -> *mut ffi::MDB_txn {
self.txn self.txn
} }
@ -396,16 +422,21 @@ impl <'env> Transaction for RwTransaction<'env> {
mod test { mod test {
use std::io::Write; use std::io::Write;
use std::sync::{Arc, Barrier}; use std::sync::{
use std::thread::{self, JoinHandle}; Arc,
Barrier,
};
use std::thread::{
self,
JoinHandle,
};
use tempdir::TempDir; use tempdir::TempDir;
use environment::*;
use error::*;
use flags::*;
use super::*; use super::*;
use cursor::Cursor; use cursor::Cursor;
use error::*;
use flags::*;
#[test] #[test]
fn test_put_get_del() { fn test_put_get_del() {
@ -451,9 +482,8 @@ mod test {
{ {
let mut cur = txn.open_ro_cursor(db).unwrap(); let mut cur = txn.open_ro_cursor(db).unwrap();
let iter = cur.iter_dup_of(b"key1"); let iter = cur.iter_dup_of(b"key1");
let vals = iter.map(|x| x.unwrap()).map(|(_,x)| x).collect::<Vec<_>>(); let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>();
assert_eq!(vals, vec![b"val1", b"val2", b"val3"]); assert_eq!(vals, vec![b"val1", b"val2", b"val3"]);
} }
txn.commit().unwrap(); txn.commit().unwrap();
@ -466,7 +496,7 @@ mod test {
{ {
let mut cur = txn.open_ro_cursor(db).unwrap(); let mut cur = txn.open_ro_cursor(db).unwrap();
let iter = cur.iter_dup_of(b"key1"); let iter = cur.iter_dup_of(b"key1");
let vals = iter.map(|x| x.unwrap()).map(|(_,x)| x).collect::<Vec<_>>(); let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>();
assert_eq!(vals, vec![b"val1", b"val3"]); assert_eq!(vals, vec![b"val1", b"val3"]);
let iter = cur.iter_dup_of(b"key2"); let iter = cur.iter_dup_of(b"key2");
@ -475,7 +505,6 @@ mod test {
txn.commit().unwrap(); txn.commit().unwrap();
} }
#[test] #[test]
fn test_reserve() { fn test_reserve() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
@ -557,12 +586,10 @@ mod test {
assert_eq!(txn.get(db, b"key"), Err(Error::NotFound)); assert_eq!(txn.get(db, b"key"), Err(Error::NotFound));
} }
#[test] #[test]
fn test_drop_db() { fn test_drop_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(2) let env = Environment::new().set_max_dbs(2).open(dir.path()).unwrap();
.open(dir.path()).unwrap();
let db = env.create_db(Some("test"), DatabaseFlags::empty()).unwrap(); let db = env.create_db(Some("test"), DatabaseFlags::empty()).unwrap();
{ {
@ -572,7 +599,9 @@ mod test {
} }
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
unsafe { txn.drop_db(db).unwrap(); } unsafe {
txn.drop_db(db).unwrap();
}
txn.commit().unwrap(); txn.commit().unwrap();
} }
@ -595,7 +624,7 @@ mod test {
let reader_env = env.clone(); let reader_env = env.clone();
let reader_barrier = barrier.clone(); let reader_barrier = barrier.clone();
threads.push(thread::spawn(move|| { threads.push(thread::spawn(move || {
let db = reader_env.open_db(None).unwrap(); let db = reader_env.open_db(None).unwrap();
{ {
let txn = reader_env.begin_ro_txn().unwrap(); let txn = reader_env.begin_ro_txn().unwrap();
@ -635,14 +664,10 @@ mod test {
for i in 0..n { for i in 0..n {
let writer_env = env.clone(); let writer_env = env.clone();
threads.push(thread::spawn(move|| { threads.push(thread::spawn(move || {
let db = writer_env.open_db(None).unwrap(); let db = writer_env.open_db(None).unwrap();
let mut txn = writer_env.begin_rw_txn().unwrap(); let mut txn = writer_env.begin_rw_txn().unwrap();
txn.put(db, txn.put(db, &format!("{}{}", key, i), &format!("{}{}", val, i), WriteFlags::empty()).unwrap();
&format!("{}{}", key, i),
&format!("{}{}", val, i),
WriteFlags::empty())
.unwrap();
txn.commit().is_ok() txn.commit().is_ok()
})); }));
} }
@ -652,8 +677,7 @@ mod test {
let txn = env.begin_ro_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
for i in 0..n { for i in 0..n {
assert_eq!(format!("{}{}", val, i).as_bytes(), assert_eq!(format!("{}{}", val, i).as_bytes(), txn.get(db, &format!("{}{}", key, i)).unwrap());
txn.get(db, &format!("{}{}", key, i)).unwrap());
} }
} }

Loading…
Cancel
Save