Skip to content

Commit c64f0b4

Browse files
committed
Simplify empty database iter handling
This commit changes the API of Cursor::iter_dup_of, and is thus a breaking change.
1 parent c616e3d commit c64f0b4

File tree

1 file changed

+42
-57
lines changed

1 file changed

+42
-57
lines changed

src/cursor.rs

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use libc::{c_void, size_t, c_uint};
2-
use std::{fmt, ptr, result, slice};
31
use std::marker::PhantomData;
2+
use std::{fmt, mem, ptr, result, slice};
3+
4+
use libc::{EINVAL, c_void, size_t, c_uint};
45

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

2021
/// Retrieves a key/data pair from the cursor. Depending on the cursor op,
2122
/// the current key may be returned.
22-
fn get(&self,
23-
key: Option<&[u8]>,
24-
data: Option<&[u8]>,
25-
op: c_uint)
26-
-> Result<(Option<&'txn [u8]>, &'txn [u8])> {
23+
fn get(&self, key: Option<&[u8]>, data: Option<&[u8]>, op: c_uint) -> Result<(Option<&'txn [u8]>, &'txn [u8])> {
2724
unsafe {
2825
let mut key_val = slice_to_val(key);
2926
let mut data_val = slice_to_val(data);
@@ -52,14 +49,7 @@ pub trait Cursor<'txn> {
5249
/// duplicate data items of each key will be returned before moving on to
5350
/// the next key.
5451
fn iter_start(&mut self) -> Iter<'txn> {
55-
// When the db is empty, this returns NotFound, which means the iterator should not even
56-
// try to proceed or it too will error with code 22
57-
let complete = self.get(None, None, ffi::MDB_FIRST)
58-
.map(|_| false)
59-
.unwrap_or(true);
60-
let mut iter = Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT);
61-
iter.complete = complete;
62-
iter
52+
Iter::new(self.cursor(), ffi::MDB_FIRST, ffi::MDB_NEXT)
6353
}
6454

6555
/// Iterate over database items starting from the given key.
@@ -69,10 +59,9 @@ pub trait Cursor<'txn> {
6959
/// the next key.
7060
fn iter_from<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> {
7161
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
72-
Err(Error::NotFound) => Ok(()),
73-
Err(error) => Err(error),
74-
Ok(_) => Ok(()),
75-
}.unwrap();
62+
Ok(_) | Err(Error::NotFound) => (),
63+
Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
64+
};
7665
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
7766
}
7867

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

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

104-
/// Iterate over the duplicates of the item in the database with the given
105-
/// key.
106-
fn iter_dup_of<K>(&mut self, key: &K) -> Result<Iter<'txn>> where K:
107-
AsRef<[u8]> {
108-
self.get(Some(key.as_ref()), None, ffi::MDB_SET)?;
109-
Ok(Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
91+
/// Iterate over the duplicates of the item in the database with the given key.
92+
fn iter_dup_of<K>(&mut self, key: &K) -> Iter<'txn> where K: AsRef<[u8]> {
93+
match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
94+
Ok(_) | Err(Error::NotFound) => (),
95+
Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
96+
};
97+
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)
11098
}
11199
}
112100

@@ -231,15 +219,14 @@ pub struct Iter<'txn> {
231219
cursor: *mut ffi::MDB_cursor,
232220
op: c_uint,
233221
next_op: c_uint,
234-
complete: bool,
235222
_marker: PhantomData<fn(&'txn ())>,
236223
}
237224

238225
impl <'txn> Iter<'txn> {
239226

240227
/// Creates a new iterator backed by the given cursor.
241228
fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
242-
Iter { cursor: cursor, op: op, next_op: next_op, complete: false, _marker: PhantomData }
229+
Iter { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData }
243230
}
244231
}
245232

@@ -254,26 +241,16 @@ impl <'txn> Iterator for Iter<'txn> {
254241
type Item = (&'txn [u8], &'txn [u8]);
255242

256243
fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> {
257-
if self.complete {
258-
return None
259-
}
260244
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
261245
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
262-
246+
let op = mem::replace(&mut self.op, self.next_op);
263247
unsafe {
264-
let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op);
265-
// Set the operation for the next get
266-
self.op = self.next_op;
267-
if err_code == ffi::MDB_SUCCESS {
268-
Some((val_to_slice(key), val_to_slice(data)))
269-
} else {
270-
// The documentation for mdb_cursor_get specifies that it may fail with MDB_NOTFOUND
271-
// and MDB_EINVAL (and we shouldn't be passing in invalid parameters).
272-
// TODO: validate that these are the only failures possible.
273-
debug_assert!(err_code == ffi::MDB_NOTFOUND,
274-
"Unexpected LMDB error {:?}.", Error::from_err_code(err_code));
275-
self.complete = true;
276-
None
248+
match ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op) {
249+
ffi::MDB_SUCCESS => Some((val_to_slice(key), val_to_slice(data))),
250+
// EINVAL can occur when the cursor was previously seeked to a non-existent value,
251+
// e.g. iter_from with a key greater than all values in the database.
252+
ffi::MDB_NOTFOUND | EINVAL => None,
253+
error => panic!("mdb_cursor_get returned an unexpected error: {}", error),
277254
}
278255
}
279256
}
@@ -310,12 +287,12 @@ impl <'txn> Iterator for IterDup<'txn> {
310287
fn next(&mut self) -> Option<Iter<'txn>> {
311288
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
312289
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
290+
let op = mem::replace(&mut self.op, ffi::MDB_NEXT_NODUP);
313291
let err_code = unsafe {
314-
ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op)
292+
ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op)
315293
};
316294

317295
if err_code == ffi::MDB_SUCCESS {
318-
self.op = ffi::MDB_NEXT;
319296
Some(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
320297
} else {
321298
None
@@ -475,25 +452,33 @@ mod test {
475452
}
476453

477454
#[test]
478-
fn test_iter_start_empty() {
455+
fn test_iter_empty_database() {
479456
let dir = TempDir::new("test").unwrap();
480457
let env = Environment::new().open(dir.path()).unwrap();
481458
let db = env.open_db(None).unwrap();
482459
let txn = env.begin_ro_txn().unwrap();
483460
let mut cursor = txn.open_ro_cursor(db).unwrap();
484461

462+
assert_eq!(0, cursor.iter().count());
485463
assert_eq!(0, cursor.iter_start().count());
464+
assert_eq!(0, cursor.iter_from(b"foo").count());
486465
}
487466

488467
#[test]
489-
fn test_iter_empty() {
468+
fn test_iter_empty_dup_database() {
490469
let dir = TempDir::new("test").unwrap();
491470
let env = Environment::new().open(dir.path()).unwrap();
492-
let db = env.open_db(None).unwrap();
471+
let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
493472
let txn = env.begin_ro_txn().unwrap();
494473
let mut cursor = txn.open_ro_cursor(db).unwrap();
495474

496475
assert_eq!(0, cursor.iter().count());
476+
assert_eq!(0, cursor.iter_start().count());
477+
assert_eq!(0, cursor.iter_from(b"foo").count());
478+
assert_eq!(0, cursor.iter_dup().count());
479+
assert_eq!(0, cursor.iter_dup_start().count());
480+
assert_eq!(0, cursor.iter_dup_from(b"foo").count());
481+
assert_eq!(0, cursor.iter_dup_of(b"foo").count());
497482
}
498483

499484
#[test]
@@ -547,9 +532,9 @@ mod test {
547532
cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Vec<_>>());
548533

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

552-
assert!(cursor.iter_dup_of(b"foo").is_err());
537+
assert_eq!(0, cursor.iter_dup_of(b"foo").count());
553538
}
554539

555540
#[test]

0 commit comments

Comments
 (0)