Skip to content
This repository was archived by the owner on Sep 13, 2023. It is now read-only.

merge danburkert/master #10

Merged
merged 11 commits into from
Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 9 additions & 13 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,22 @@ environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
- TARGET: i686-pc-windows-msvc
# Temporarily disable MinGW builds due to https://github.com/rust-lang/rust/issues/47048.
# Once that issue is fixed, presumably by https://github.com/rust-lang/rust/pull/51989,
# we should reenable them.
# - TARGET: x86_64-pc-windows-gnu
# MSYS_BITS: 64
# - TARGET: i686-pc-windows-gnu
# MSYS_BITS: 32

install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- set PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- if defined MSYS_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS_BITS%\bin
- rustc -V
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V

build_script:
- git submodule -q update --init
- cargo build --target %TARGET% --all -v

test_script:
- SET RUST_BACKTRACE=1
- cargo test --target %TARGET% --all -v
- cargo test --release --target %TARGET% --all -v

cache:
- C:\Users\appveyor\.cargo\registry
- target
2 changes: 1 addition & 1 deletion lmdb-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ build = "build.rs"
libc = "0.2"

[build-dependencies]
pkg-config = "0.3"
pkg-config = "0.3.2"
cc = "1"
17 changes: 11 additions & 6 deletions lmdb-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ fn main() {
lmdb.push("liblmdb");

if !pkg_config::find_library("liblmdb").is_ok() {
cc::Build::new()
.file(lmdb.join("mdb.c"))
.file(lmdb.join("midl.c"))
// https://github.com/LMDB/lmdb/blob/LMDB_0.9.21/libraries/liblmdb/Makefile#L25
.opt_level(2)
.compile("liblmdb.a")
let target = env::var("TARGET").expect("No TARGET found");
let mut build = cc::Build::new();
if target.contains("android") {
build.define("ANDROID", "1");
}
build
.file(lmdb.join("mdb.c"))
.file(lmdb.join("midl.c"))
// https://github.com/LMDB/lmdb/blob/LMDB_0.9.21/libraries/liblmdb/Makefile#L25
.opt_level(2)
.compile("liblmdb.a")
}
}
102 changes: 60 additions & 42 deletions src/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use libc::{c_void, size_t, c_uint};
use std::{fmt, ptr, result, slice};
use std::marker::PhantomData;
use std::{fmt, mem, ptr, result, slice};

use libc::{EINVAL, c_void, size_t, c_uint};

use database::Database;
use error::{Error, Result, lmdb_result};
Expand All @@ -19,11 +20,7 @@ pub trait Cursor<'txn> {

/// Retrieves a key/data pair from the cursor. Depending on the cursor op,
/// the current key may be returned.
fn get(&self,
key: Option<&[u8]>,
data: Option<&[u8]>,
op: c_uint)
-> Result<(Option<&'txn [u8]>, &'txn [u8])> {
fn get(&self, key: Option<&[u8]>, data: Option<&[u8]>, op: c_uint) -> Result<(Option<&'txn [u8]>, &'txn [u8])> {
unsafe {
let mut key_val = slice_to_val(key);
let mut data_val = slice_to_val(data);
Expand Down Expand Up @@ -52,8 +49,7 @@ pub trait Cursor<'txn> {
/// duplicate data items of each key will be returned before moving on to
/// the next key.
fn iter_start(&mut self) -> Iter<'txn> {
self.get(None, None, ffi::MDB_FIRST).unwrap();
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
Iter::new(self.cursor(), ffi::MDB_FIRST, ffi::MDB_NEXT)
}

/// Iterate over database items starting from the given key.
Expand All @@ -63,10 +59,9 @@ pub trait Cursor<'txn> {
/// the next key.
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) {
Err(Error::NotFound) => Ok(()),
Err(error) => Err(error),
Ok(_) => Ok(()),
}.unwrap();
Ok(_) | Err(Error::NotFound) => (),
Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
};
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
}

Expand All @@ -80,27 +75,26 @@ pub trait Cursor<'txn> {
/// Iterate over duplicate database items starting from the beginning of the
/// database. Each item will be returned as an iterator of its duplicates.
fn iter_dup_start(&mut self) -> IterDup<'txn> {
self.get(None, None, ffi::MDB_FIRST).unwrap();
IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
IterDup::new(self.cursor(), ffi::MDB_FIRST)
}

/// Iterate over duplicate items in the database starting from the given
/// key. Each item will be returned as an iterator of its duplicates.
fn iter_dup_from<K>(&mut self, key: &K) -> IterDup<'txn> where K: AsRef<[u8]> {
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Err(Error::NotFound) => Ok(()),
Err(error) => Err(error),
Ok(_) => Ok(()),
}.unwrap();
Ok(_) | Err(Error::NotFound) => (),
Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
};
IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
}

/// Iterate over the duplicates of the item in the database with the given
/// key.
fn iter_dup_of<K>(&mut self, key: &K) -> Result<Iter<'txn>> where K:
AsRef<[u8]> {
self.get(Some(key.as_ref()), None, ffi::MDB_SET)?;
Ok(Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
/// 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]> {
match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
Ok(_) | Err(Error::NotFound) => (),
Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
};
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)
}
}

Expand Down Expand Up @@ -249,20 +243,14 @@ impl <'txn> Iterator for Iter<'txn> {
fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> {
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(&mut self.op, self.next_op);
unsafe {
let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op);
// Set the operation for the next get
self.op = self.next_op;
if err_code == ffi::MDB_SUCCESS {
Some((val_to_slice(key), val_to_slice(data)))
} else {
// The documentation for mdb_cursor_get specifies that it may fail with MDB_NOTFOUND
// and MDB_EINVAL (and we shouldn't be passing in invalid parameters).
// TODO: validate that these are the only failures possible.
debug_assert!(err_code == ffi::MDB_NOTFOUND,
"Unexpected LMDB error {:?}.", Error::from_err_code(err_code));
None
match ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op) {
ffi::MDB_SUCCESS => Some((val_to_slice(key), val_to_slice(data))),
// EINVAL can occur when the cursor was previously seeked to a non-existent value,
// e.g. iter_from with a key greater than all values in the database.
ffi::MDB_NOTFOUND | EINVAL => None,
error => panic!("mdb_cursor_get returned an unexpected error: {}", error),
}
}
}
Expand Down Expand Up @@ -299,12 +287,12 @@ impl <'txn> Iterator for IterDup<'txn> {
fn next(&mut self) -> Option<Iter<'txn>> {
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(&mut self.op, ffi::MDB_NEXT_NODUP);
let err_code = unsafe {
ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op)
ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op)
};

if err_code == ffi::MDB_SUCCESS {
self.op = ffi::MDB_NEXT;
Some(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
} else {
None
Expand Down Expand Up @@ -463,6 +451,36 @@ mod test {
cursor.iter_from(b"key6").collect::<Vec<_>>());
}

#[test]
fn test_iter_empty_database() {
let dir = TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path()).unwrap();
let db = env.open_db(None).unwrap();
let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_ro_cursor(db).unwrap();

assert_eq!(0, cursor.iter().count());
assert_eq!(0, cursor.iter_start().count());
assert_eq!(0, cursor.iter_from(b"foo").count());
}

#[test]
fn test_iter_empty_dup_database() {
let dir = TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path()).unwrap();
let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_ro_cursor(db).unwrap();

assert_eq!(0, cursor.iter().count());
assert_eq!(0, cursor.iter_start().count());
assert_eq!(0, cursor.iter_from(b"foo").count());
assert_eq!(0, cursor.iter_dup().count());
assert_eq!(0, cursor.iter_dup_start().count());
assert_eq!(0, cursor.iter_dup_from(b"foo").count());
assert_eq!(0, cursor.iter_dup_of(b"foo").count());
}

#[test]
fn test_iter_dup() {
let dir = TempDir::new("test").unwrap();
Expand Down Expand Up @@ -514,9 +532,9 @@ mod test {
cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Vec<_>>());

assert_eq!(items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_of(b"b").unwrap().collect::<Vec<_>>());
cursor.iter_dup_of(b"b").collect::<Vec<_>>());

assert!(cursor.iter_dup_of(b"foo").is_err());
assert_eq!(0, cursor.iter_dup_of(b"foo").count());
}

#[test]
Expand Down
6 changes: 4 additions & 2 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ impl EnvironmentBuilder {
///
/// On UNIX, the database files will be opened with 644 permissions.
///
/// The path may not contain the null character.
/// The path may not contain the null character, Windows UNC (Uniform Naming Convention)
/// paths are not supported either.
pub fn open(&self, path: &Path) -> Result<Environment> {
self.open_with_permissions(path, 0o644)
}
Expand All @@ -251,7 +252,8 @@ impl EnvironmentBuilder {
///
/// On Windows, the permissions will be ignored.
///
/// The path may not contain the null character.
/// The path may not contain the null character, Windows UNC (Uniform Naming Convention)
/// paths are not supported either.
pub fn open_with_permissions(&self, path: &Path, mode: ffi::mode_t) -> Result<Environment> {
let mut env: *mut ffi::MDB_env = ptr::null_mut();
unsafe {
Expand Down