1use std::marker::PhantomData;
2use std::{
3 fmt,
4 mem,
5 ptr,
6 result,
7 slice,
8};
9
10use libc::{
11 c_uint,
12 c_void,
13 size_t,
14 EINVAL,
15};
16
17use database::Database;
18use error::{
19 lmdb_result,
20 Error,
21 Result,
22};
23use ffi;
24use flags::WriteFlags;
25use transaction::Transaction;
26
27pub trait Cursor<'txn> {
29 fn cursor(&self) -> *mut ffi::MDB_cursor;
34
35 fn get(&self, key: Option<&[u8]>, data: Option<&[u8]>, op: c_uint) -> Result<(Option<&'txn [u8]>, &'txn [u8])> {
38 unsafe {
39 let mut key_val = slice_to_val(key);
40 let mut data_val = slice_to_val(data);
41 let key_ptr = key_val.mv_data;
42 lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &mut data_val, op))?;
43 let key_out = if key_ptr != key_val.mv_data {
44 Some(val_to_slice(key_val))
45 } else {
46 None
47 };
48 let data_out = val_to_slice(data_val);
49 Ok((key_out, data_out))
50 }
51 }
52
53 fn iter(&mut self) -> Iter<'txn> {
61 Iter::new(self.cursor(), ffi::MDB_NEXT, ffi::MDB_NEXT)
62 }
63
64 fn iter_start(&mut self) -> Iter<'txn> {
70 Iter::new(self.cursor(), ffi::MDB_FIRST, ffi::MDB_NEXT)
71 }
72
73 fn iter_from<K>(&mut self, key: K) -> Iter<'txn>
79 where
80 K: AsRef<[u8]>,
81 {
82 match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
83 Ok(_) | Err(Error::NotFound) => (),
84 Err(error) => return Iter::Err(error),
85 };
86 Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
87 }
88
89 fn iter_dup(&mut self) -> IterDup<'txn> {
93 IterDup::new(self.cursor(), ffi::MDB_NEXT)
94 }
95
96 fn iter_dup_start(&mut self) -> IterDup<'txn> {
99 IterDup::new(self.cursor(), ffi::MDB_FIRST)
100 }
101
102 fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn>
105 where
106 K: AsRef<[u8]>,
107 {
108 match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
109 Ok(_) | Err(Error::NotFound) => (),
110 Err(error) => return IterDup::Err(error),
111 };
112 IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
113 }
114
115 fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn>
117 where
118 K: AsRef<[u8]>,
119 {
120 match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
121 Ok(_) | Err(Error::NotFound) => (),
122 Err(error) => return Iter::Err(error),
123 };
124 Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)
125 }
126}
127
128pub struct RoCursor<'txn> {
130 cursor: *mut ffi::MDB_cursor,
131 _marker: PhantomData<fn() -> &'txn ()>,
132}
133
134impl<'txn> Cursor<'txn> for RoCursor<'txn> {
135 fn cursor(&self) -> *mut ffi::MDB_cursor {
136 self.cursor
137 }
138}
139
140impl<'txn> fmt::Debug for RoCursor<'txn> {
141 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
142 f.debug_struct("RoCursor").finish()
143 }
144}
145
146impl<'txn> Drop for RoCursor<'txn> {
147 fn drop(&mut self) {
148 unsafe { ffi::mdb_cursor_close(self.cursor) }
149 }
150}
151
152impl<'txn> RoCursor<'txn> {
153 pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RoCursor<'txn>>
156 where
157 T: Transaction,
158 {
159 let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
160 unsafe {
161 lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
162 }
163 Ok(RoCursor {
164 cursor,
165 _marker: PhantomData,
166 })
167 }
168}
169
170pub struct RwCursor<'txn> {
172 cursor: *mut ffi::MDB_cursor,
173 _marker: PhantomData<fn() -> &'txn ()>,
174}
175
176impl<'txn> Cursor<'txn> for RwCursor<'txn> {
177 fn cursor(&self) -> *mut ffi::MDB_cursor {
178 self.cursor
179 }
180}
181
182impl<'txn> fmt::Debug for RwCursor<'txn> {
183 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
184 f.debug_struct("RwCursor").finish()
185 }
186}
187
188impl<'txn> Drop for RwCursor<'txn> {
189 fn drop(&mut self) {
190 unsafe { ffi::mdb_cursor_close(self.cursor) }
191 }
192}
193
194impl<'txn> RwCursor<'txn> {
195 pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>>
198 where
199 T: Transaction,
200 {
201 let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
202 unsafe {
203 lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
204 }
205 Ok(RwCursor {
206 cursor,
207 _marker: PhantomData,
208 })
209 }
210
211 pub fn put<K, D>(&mut self, key: &K, data: &D, flags: WriteFlags) -> Result<()>
214 where
215 K: AsRef<[u8]>,
216 D: AsRef<[u8]>,
217 {
218 let key = key.as_ref();
219 let data = data.as_ref();
220 let mut key_val: ffi::MDB_val = ffi::MDB_val {
221 mv_size: key.len() as size_t,
222 mv_data: key.as_ptr() as *mut c_void,
223 };
224 let mut data_val: ffi::MDB_val = ffi::MDB_val {
225 mv_size: data.len() as size_t,
226 mv_data: data.as_ptr() as *mut c_void,
227 };
228 unsafe { lmdb_result(ffi::mdb_cursor_put(self.cursor(), &mut key_val, &mut data_val, flags.bits())) }
229 }
230
231 pub fn del(&mut self, flags: WriteFlags) -> Result<()> {
238 unsafe { lmdb_result(ffi::mdb_cursor_del(self.cursor(), flags.bits())) }
239 }
240}
241
242unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val {
243 match slice {
244 Some(slice) => ffi::MDB_val {
245 mv_size: slice.len() as size_t,
246 mv_data: slice.as_ptr() as *mut c_void,
247 },
248 None => ffi::MDB_val {
249 mv_size: 0,
250 mv_data: ptr::null_mut(),
251 },
252 }
253}
254
255unsafe fn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] {
256 slice::from_raw_parts(val.mv_data as *const u8, val.mv_size as usize)
257}
258
259pub enum Iter<'txn> {
261 Err(Error),
267
268 Ok {
273 cursor: *mut ffi::MDB_cursor,
275
276 op: c_uint,
278
279 next_op: c_uint,
281
282 _marker: PhantomData<fn(&'txn ())>,
284 },
285}
286
287impl<'txn> Iter<'txn> {
288 fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
290 Iter::Ok {
291 cursor,
292 op,
293 next_op,
294 _marker: PhantomData,
295 }
296 }
297}
298
299impl<'txn> fmt::Debug for Iter<'txn> {
300 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
301 f.debug_struct("Iter").finish()
302 }
303}
304
305impl<'txn> Iterator for Iter<'txn> {
306 type Item = Result<(&'txn [u8], &'txn [u8])>;
307
308 fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> {
309 match self {
310 &mut Iter::Ok {
311 cursor,
312 ref mut op,
313 next_op,
314 _marker,
315 } => {
316 let mut key = ffi::MDB_val {
317 mv_size: 0,
318 mv_data: ptr::null_mut(),
319 };
320 let mut data = ffi::MDB_val {
321 mv_size: 0,
322 mv_data: ptr::null_mut(),
323 };
324 let op = mem::replace(op, next_op);
325 unsafe {
326 match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) {
327 ffi::MDB_SUCCESS => Some(Ok((val_to_slice(key), val_to_slice(data)))),
328 ffi::MDB_NOTFOUND | EINVAL => None,
331 error => Some(Err(Error::from_err_code(error))),
332 }
333 }
334 },
335 &mut Iter::Err(err) => Some(Err(err)),
336 }
337 }
338}
339
340pub enum IterDup<'txn> {
345 Err(Error),
351
352 Ok {
357 cursor: *mut ffi::MDB_cursor,
359
360 op: c_uint,
362
363 _marker: PhantomData<fn(&'txn ())>,
365 },
366}
367
368impl<'txn> IterDup<'txn> {
369 fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
371 IterDup::Ok {
372 cursor,
373 op,
374 _marker: PhantomData,
375 }
376 }
377}
378
379impl<'txn> fmt::Debug for IterDup<'txn> {
380 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
381 f.debug_struct("IterDup").finish()
382 }
383}
384
385impl<'txn> Iterator for IterDup<'txn> {
386 type Item = Iter<'txn>;
387
388 fn next(&mut self) -> Option<Iter<'txn>> {
389 match self {
390 &mut IterDup::Ok {
391 cursor,
392 ref mut op,
393 _marker,
394 } => {
395 let mut key = ffi::MDB_val {
396 mv_size: 0,
397 mv_data: ptr::null_mut(),
398 };
399 let mut data = ffi::MDB_val {
400 mv_size: 0,
401 mv_data: ptr::null_mut(),
402 };
403 let op = mem::replace(op, ffi::MDB_NEXT_NODUP);
404 let err_code = unsafe { ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) };
405
406 if err_code == ffi::MDB_SUCCESS {
407 Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
408 } else {
409 None
410 }
411 },
412 &mut IterDup::Err(err) => Some(Iter::Err(err)),
413 }
414 }
415}
416
417#[cfg(test)]
418mod test {
419 use tempdir::TempDir;
420
421 use super::*;
422 use environment::*;
423 use ffi::*;
424 use flags::*;
425
426 #[test]
427 fn test_get() {
428 let dir = TempDir::new("test").unwrap();
429 let env = Environment::new().open(dir.path()).unwrap();
430 let db = env.open_db(None).unwrap();
431
432 let mut txn = env.begin_rw_txn().unwrap();
433 txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
434 txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
435 txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
436
437 let cursor = txn.open_ro_cursor(db).unwrap();
438 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
439 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
440 assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT).unwrap());
441 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_PREV).unwrap());
442 assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_LAST).unwrap());
443 assert_eq!((None, &b"val2"[..]), cursor.get(Some(b"key2"), None, MDB_SET).unwrap());
444 assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key3"[..]), None, MDB_SET_KEY).unwrap());
445 assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key2\0"[..]), None, MDB_SET_RANGE).unwrap());
446 }
447
448 #[test]
449 fn test_get_dup() {
450 let dir = TempDir::new("test").unwrap();
451 let env = Environment::new().open(dir.path()).unwrap();
452 let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
453
454 let mut txn = env.begin_rw_txn().unwrap();
455 txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
456 txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
457 txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
458 txn.put(db, b"key2", b"val1", WriteFlags::empty()).unwrap();
459 txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
460 txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
461
462 let cursor = txn.open_ro_cursor(db).unwrap();
463 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
464 assert_eq!((None, &b"val1"[..]), cursor.get(None, None, MDB_FIRST_DUP).unwrap());
465 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
466 assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(None, None, MDB_NEXT_NODUP).unwrap());
467 assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
468 assert_eq!((Some(&b"key2"[..]), &b"val3"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
469 assert!(cursor.get(None, None, MDB_NEXT_DUP).is_err());
470 assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_PREV_DUP).unwrap());
471 assert_eq!((None, &b"val3"[..]), cursor.get(None, None, MDB_LAST_DUP).unwrap());
472 assert_eq!((Some(&b"key1"[..]), &b"val3"[..]), cursor.get(None, None, MDB_PREV_NODUP).unwrap());
473 assert_eq!((None, &b"val1"[..]), cursor.get(Some(&b"key1"[..]), None, MDB_SET).unwrap());
474 assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key2"[..]), None, MDB_SET_KEY).unwrap());
475 assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key1\0"[..]), None, MDB_SET_RANGE).unwrap());
476 assert_eq!((None, &b"val3"[..]), cursor.get(Some(&b"key1"[..]), Some(&b"val3"[..]), MDB_GET_BOTH).unwrap());
477 assert_eq!(
478 (None, &b"val1"[..]),
479 cursor.get(Some(&b"key2"[..]), Some(&b"val"[..]), MDB_GET_BOTH_RANGE).unwrap()
480 );
481 }
482
483 #[test]
484 fn test_get_dupfixed() {
485 let dir = TempDir::new("test").unwrap();
486 let env = Environment::new().open(dir.path()).unwrap();
487 let db = env.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
488
489 let mut txn = env.begin_rw_txn().unwrap();
490 txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
491 txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
492 txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
493 txn.put(db, b"key2", b"val4", WriteFlags::empty()).unwrap();
494 txn.put(db, b"key2", b"val5", WriteFlags::empty()).unwrap();
495 txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
496
497 let cursor = txn.open_ro_cursor(db).unwrap();
498 assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
499 assert_eq!((None, &b"val1val2val3"[..]), cursor.get(None, None, MDB_GET_MULTIPLE).unwrap());
500 assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err());
501 }
502
503 #[test]
504 fn test_iter() {
505 let dir = TempDir::new("test").unwrap();
506 let env = Environment::new().open(dir.path()).unwrap();
507 let db = env.open_db(None).unwrap();
508
509 let items: Vec<(&[u8], &[u8])> =
510 vec![(b"key1", b"val1"), (b"key2", b"val2"), (b"key3", b"val3"), (b"key5", b"val5")];
511
512 {
513 let mut txn = env.begin_rw_txn().unwrap();
514 for &(ref key, ref data) in &items {
515 txn.put(db, key, data, WriteFlags::empty()).unwrap();
516 }
517 txn.commit().unwrap();
518 }
519
520 let txn = env.begin_ro_txn().unwrap();
521 let mut cursor = txn.open_ro_cursor(db).unwrap();
522
523 assert_eq!(items, cursor.iter().collect::<Result<Vec<_>>>().unwrap());
527
528 let retr: Result<Vec<_>> = cursor.iter_start().collect();
530 assert_eq!(items, retr.unwrap());
531
532 cursor.get(Some(b"key2"), None, MDB_SET).unwrap();
533 assert_eq!(
534 items.clone().into_iter().skip(2).collect::<Vec<_>>(),
535 cursor.iter().collect::<Result<Vec<_>>>().unwrap()
536 );
537
538 assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap());
539
540 assert_eq!(
541 items.clone().into_iter().skip(1).collect::<Vec<_>>(),
542 cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap()
543 );
544
545 assert_eq!(
546 items.clone().into_iter().skip(3).collect::<Vec<_>>(),
547 cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap()
548 );
549
550 assert_eq!(
551 vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
552 cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap()
553 );
554 }
555
556 #[test]
557 fn test_iter_empty_database() {
558 let dir = TempDir::new("test").unwrap();
559 let env = Environment::new().open(dir.path()).unwrap();
560 let db = env.open_db(None).unwrap();
561 let txn = env.begin_ro_txn().unwrap();
562 let mut cursor = txn.open_ro_cursor(db).unwrap();
563
564 assert_eq!(0, cursor.iter().count());
565 assert_eq!(0, cursor.iter_start().count());
566 assert_eq!(0, cursor.iter_from(b"foo").count());
567 }
568
569 #[test]
570 fn test_iter_empty_dup_database() {
571 let dir = TempDir::new("test").unwrap();
572 let env = Environment::new().open(dir.path()).unwrap();
573 let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
574 let txn = env.begin_ro_txn().unwrap();
575 let mut cursor = txn.open_ro_cursor(db).unwrap();
576
577 assert_eq!(0, cursor.iter().count());
578 assert_eq!(0, cursor.iter_start().count());
579 assert_eq!(0, cursor.iter_from(b"foo").count());
580 assert_eq!(0, cursor.iter_dup().count());
581 assert_eq!(0, cursor.iter_dup_start().count());
582 assert_eq!(0, cursor.iter_dup_from(b"foo").count());
583 assert_eq!(0, cursor.iter_dup_of(b"foo").count());
584 }
585
586 #[test]
587 fn test_iter_dup() {
588 let dir = TempDir::new("test").unwrap();
589 let env = Environment::new().open(dir.path()).unwrap();
590 let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
591
592 let items: Vec<(&[u8], &[u8])> = vec![
593 (b"a", b"1"),
594 (b"a", b"2"),
595 (b"a", b"3"),
596 (b"b", b"1"),
597 (b"b", b"2"),
598 (b"b", b"3"),
599 (b"c", b"1"),
600 (b"c", b"2"),
601 (b"c", b"3"),
602 (b"e", b"1"),
603 (b"e", b"2"),
604 (b"e", b"3"),
605 ];
606
607 {
608 let mut txn = env.begin_rw_txn().unwrap();
609 for &(ref key, ref data) in &items {
610 txn.put(db, key, data, WriteFlags::empty()).unwrap();
611 }
612 txn.commit().unwrap();
613 }
614
615 let txn = env.begin_ro_txn().unwrap();
616 let mut cursor = txn.open_ro_cursor(db).unwrap();
617 assert_eq!(items, cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap());
618
619 cursor.get(Some(b"b"), None, MDB_SET).unwrap();
620 assert_eq!(
621 items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
622 cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap()
623 );
624
625 assert_eq!(items, cursor.iter_dup_start().flatten().collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
626
627 assert_eq!(
628 items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
629 cursor.iter_dup_from(b"b").flatten().collect::<Result<Vec<_>>>().unwrap()
630 );
631
632 assert_eq!(
633 items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
634 cursor.iter_dup_from(b"ab").flatten().collect::<Result<Vec<_>>>().unwrap()
635 );
636
637 assert_eq!(
638 items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(),
639 cursor.iter_dup_from(b"d").flatten().collect::<Result<Vec<_>>>().unwrap()
640 );
641
642 assert_eq!(
643 vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
644 cursor.iter_dup_from(b"f").flatten().collect::<Result<Vec<_>>>().unwrap()
645 );
646
647 assert_eq!(
648 items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(),
649 cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap()
650 );
651
652 assert_eq!(0, cursor.iter_dup_of(b"foo").count());
653 }
654
655 #[test]
656 fn test_put_del() {
657 let dir = TempDir::new("test").unwrap();
658 let env = Environment::new().open(dir.path()).unwrap();
659 let db = env.open_db(None).unwrap();
660
661 let mut txn = env.begin_rw_txn().unwrap();
662 let mut cursor = txn.open_rw_cursor(db).unwrap();
663
664 cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap();
665 cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();
666 cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap();
667
668 assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
669
670 cursor.del(WriteFlags::empty()).unwrap();
671 assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_LAST).unwrap());
672 }
673}