lmdb_zero/tx.rs
1// Copyright 2016 FullContact, Inc
2// Copyright 2017 Jason Lingle
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use std::cell::Cell;
11use std::mem;
12use std::ops::{Deref, DerefMut};
13use std::ptr;
14use libc::c_uint;
15
16use ffi;
17use ffi2;
18use supercow::{Supercow, NonSyncSupercow};
19
20use env::{self, Environment, Stat};
21use dbi::{db, Database};
22use error::{Error, Result};
23use mdb_vals::*;
24use traits::*;
25use cursor::{self, Cursor, StaleCursor};
26
27/// Flags used when calling the various `put` functions.
28pub mod put {
29 use ffi;
30 use libc;
31
32 bitflags! {
33 /// Flags used when calling the various `put` functions.
34 ///
35 /// Note that `RESERVE` and `MULTIPLE` are not exposed in these flags
36 /// because their memory ownership and/or parameter semantics are
37 /// different. `CURRENT` is expressed separately on the cursor
38 /// functions.
39 pub struct Flags : libc::c_uint {
40 /// Enter the new key/data pair only if it does not already appear
41 /// in the database. This flag may only be specified if the
42 /// database was opened with `DUPSORT`. The function will return
43 /// `KEYEXIST` if the key/data pair already appears in the
44 /// database.
45 ///
46 /// ## Example
47 ///
48 /// ```
49 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
50 /// # fn main() {
51 /// # let env = create_env();
52 /// let db = lmdb::Database::open(
53 /// &env, Some("reversed"),
54 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
55 /// .unwrap();
56 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
57 /// {
58 /// let mut access = txn.access();
59 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
60 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
61 /// // Duplicate, but that's OK by default
62 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
63 /// // `NODUPDATA` blocks adding an identical item
64 /// assert!(access.put(&db, "Fruit", "Apple", lmdb::put::NODUPDATA).is_err());
65 /// // But doesn't affect pairs not already present
66 /// access.put(&db, "Fruit", "Durian", lmdb::put::NODUPDATA).unwrap();
67 /// }
68 /// txn.commit().unwrap();
69 /// # }
70 /// ```
71 ///
72 /// When used on a cursor, the cursor is positioned at the
73 /// conflicting key/value pair if this results in a `KEYEXIST`
74 /// error.
75 ///
76 /// ```
77 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
78 /// # fn main() {
79 /// # let env = create_env();
80 /// let db = lmdb::Database::open(
81 /// &env, Some("reversed"),
82 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
83 /// .unwrap();
84 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
85 /// {
86 /// let mut access = txn.access();
87 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
88 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
89 /// access.put(&db, "Fruit", "Durian", lmdb::put::Flags::empty()).unwrap();
90 ///
91 /// let mut cursor = txn.cursor(&db).unwrap();
92 /// assert_eq!(Err(lmdb::Error::Code(lmdb::error::KEYEXIST)),
93 /// cursor.put(&mut access, "Fruit", "Durian",
94 /// lmdb::put::NODUPDATA));
95 /// assert_eq!(("Fruit", "Durian"), cursor.get_current(&access).unwrap());
96 /// }
97 /// txn.commit().unwrap();
98 /// # }
99 /// ```
100 const NODUPDATA = ffi::MDB_NODUPDATA;
101 /// Enter the new key/data pair only if the key does not already
102 /// appear in the database. The function will return `KEYEXIST` if
103 /// the key already appears in the database, even if the database
104 /// supports duplicates (`DUPSORT`).
105 ///
106 /// ## Examples
107 ///
108 /// ### In a 1:1 database
109 ///
110 /// ```
111 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
112 /// # fn main() {
113 /// # let env = create_env();
114 /// let db = lmdb::Database::open(
115 /// &env, None, &lmdb::DatabaseOptions::defaults())
116 /// .unwrap();
117 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
118 /// {
119 /// let mut access = txn.access();
120 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
121 /// // By default, collisions overwrite the old value
122 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
123 /// assert_eq!("Orange", access.get::<str,str>(&db, "Fruit").unwrap());
124 /// // But `NOOVERWRITE` prevents that
125 /// assert!(access.put(&db, "Fruit", "Durian", lmdb::put::NOOVERWRITE).is_err());
126 /// assert_eq!("Orange", access.get::<str,str>(&db, "Fruit").unwrap());
127 /// }
128 /// txn.commit().unwrap();
129 /// # }
130 /// ```
131 ///
132 /// ### In a `DUPSORT` database
133 ///
134 /// ```
135 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
136 /// # fn main() {
137 /// # let env = create_env();
138 /// let db = lmdb::Database::open(
139 /// &env, Some("reversed"),
140 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
141 /// .unwrap();
142 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
143 /// {
144 /// let mut access = txn.access();
145 /// // Ordinarily, we can add multiple items per key
146 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
147 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
148 /// let mut cursor = txn.cursor(&db).unwrap();
149 /// cursor.seek_k::<str,str>(&access, "Fruit").unwrap();
150 /// assert_eq!(2, cursor.count().unwrap());
151 ///
152 /// // But this can be prevented with `NOOVERWRITE`
153 /// access.put(&db, "Veggie", "Carrot", lmdb::put::NOOVERWRITE).unwrap();
154 /// assert!(access.put(&db, "Veggie", "Squash", lmdb::put::NOOVERWRITE).is_err());
155 /// cursor.seek_k::<str,str>(&access, "Veggie").unwrap();
156 /// assert_eq!(1, cursor.count().unwrap());
157 /// }
158 /// txn.commit().unwrap();
159 /// # }
160 /// ```
161 // TODO: "The data parameter will be set to point to the existing
162 // item." We should provide functionality to support that.
163 const NOOVERWRITE = ffi::MDB_NOOVERWRITE;
164 /// Append the given key/data pair to the end of the database. This
165 /// option allows fast bulk loading when keys are already known to
166 /// be in the correct order. Loading unsorted keys with this flag
167 /// will cause a `KEYEXIST` error.
168 ///
169 /// ## Example
170 ///
171 /// ```
172 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
173 /// # fn main() {
174 /// # let env = create_env();
175 /// let db = lmdb::Database::open(
176 /// &env, None, &lmdb::DatabaseOptions::defaults())
177 /// .unwrap();
178 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
179 /// {
180 /// let mut access = txn.access();
181 /// // Load values in ascending order
182 /// access.put(&db, "France", "Paris", lmdb::put::APPEND).unwrap();
183 /// access.put(&db, "Germany", "Berlin", lmdb::put::APPEND).unwrap();
184 /// access.put(&db, "Latvia", "Rīga", lmdb::put::APPEND).unwrap();
185 /// // Error if you violate ordering
186 /// assert!(access.put(&db, "Armenia", "Yerevan", lmdb::put::APPEND)
187 /// .is_err());
188 /// }
189 /// txn.commit().unwrap();
190 /// # }
191 /// ```
192 const APPEND = ffi::MDB_APPEND;
193 /// As with `APPEND` above, but for sorted dup data.
194 const APPENDDUP = ffi::MDB_APPENDDUP;
195 }
196 }
197}
198
199/// Flags used when deleting items.
200pub mod del {
201 use ffi;
202 use libc;
203
204 bitflags! {
205 /// Flags used when deleting items via cursors.
206 pub struct Flags : libc::c_uint {
207 /// Delete all of the data items for the current key instead of
208 /// just the current item. This flag may only be specified if the
209 /// database was opened with `DUPSORT`.
210 ///
211 /// ## Example
212 ///
213 /// ```
214 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
215 /// # fn main() {
216 /// # let env = create_env();
217 /// let db = lmdb::Database::open(
218 /// &env, Some("reversed"),
219 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
220 /// .unwrap();
221 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
222 /// {
223 /// let mut access = txn.access();
224 /// let f = lmdb::put::Flags::empty();
225 /// access.put(&db, "Fruit", "Apple", f).unwrap();
226 /// access.put(&db, "Fruit", "Orange", f).unwrap();
227 /// access.put(&db, "Fruit", "Durian", f).unwrap();
228 ///
229 /// let mut cursor = txn.cursor(&db).unwrap();
230 /// cursor.seek_kv("Fruit", "Durian").unwrap();
231 /// // By default, only the current item is deleted.
232 /// cursor.del(&mut access, lmdb::del::Flags::empty()).unwrap();
233 /// cursor.seek_k::<str,str>(&access, "Fruit").unwrap();
234 /// assert_eq!(2, cursor.count().unwrap());
235 /// // But with `NODUPDATA`, they will all go away
236 /// cursor.del(&mut access, lmdb::del::NODUPDATA).unwrap();
237 /// assert!(cursor.seek_k::<str,str>(&access, "Fruit").is_err());
238 /// }
239 /// txn.commit().unwrap();
240 /// # }
241 /// ```
242 const NODUPDATA = ffi::MDB_NODUPDATA;
243 }
244 }
245}
246
247// This is internal, but used by other parts of the library
248#[derive(Debug)]
249pub struct TxHandle(pub *mut ffi::MDB_txn);
250
251impl Drop for TxHandle {
252 fn drop(&mut self) {
253 if !self.0.is_null() {
254 unsafe {
255 ffi::mdb_txn_abort(self.0);
256 }
257 self.0 = ptr::null_mut();
258 }
259 }
260}
261
262impl TxHandle {
263 pub unsafe fn commit(&mut self) -> Result<()> {
264 let txn_p = mem::replace(&mut self.0, ptr::null_mut());
265 lmdb_call!(ffi::mdb_txn_commit(txn_p));
266 Ok(())
267 }
268}
269
270/// Base functionality for an LMDB transaction.
271///
272/// The type is "const" in a similar usage to the modifier in C: One cannot use
273/// it to make any modifications, but also cannot rely on it actually being
274/// read-only. `ConstTransaction`s are used to write code that can operate in
275/// either kind of transaction.
276///
277/// Unlike most other LMDB wrappers, transactions here are (indirectly) the
278/// things in control of accessing data behind cursors. This is in order to
279/// correctly express memory semantics: Moving a cursor does not invalidate
280/// memory obtained from the cursor; however, any mutation through the same
281/// transaction does. We therefore model accesses to data in the environment as
282/// borrows of the transaction and the database themselves (possibly mutable on
283/// the latter), which allows the borrow checker to ensure that all references
284/// are dropped before doing a structural modification.
285///
286/// Note that due to limitations in the Rust borrow checker, one actually needs
287/// to use the `*Accessor` structs to access data. Any transaction will yield
288/// at most one accessor, which is implemented with a runtime check that should
289/// in the vast majority of cases get optimised out.
290///
291/// Mutability of a transaction reference does not indicate mutability of the
292/// underlying database, but rather exclusivity for enforcement of child
293/// transaction semantics.
294///
295/// ## Ownership
296///
297/// Transactions support all three ownership modes (but owned mode is not
298/// useful). See `ReadTransaction` and `WriteTransaction` for details.
299///
300/// ## Lifetime
301///
302/// A `ConstTransaction` must be strictly outlived by its `Environment`.
303///
304/// `'env` is covariant: given two lifetimes `'x` and `'y` where `'x: 'y`, a
305/// `&ConstTransaction<'x>` will implicitly coerce to `&ConstTransaction<'y>`.
306///
307/// ```rust,norun
308/// # #![allow(dead_code)]
309/// # extern crate lmdb_zero as lmdb;
310/// # fn main() { }
311/// #
312/// fn convariance<'x, 'y>(db: &lmdb::ConstTransaction<'x>)
313/// where 'x: 'y {
314/// let _db2: &lmdb::ConstTransaction<'y> = db;
315/// }
316/// ```
317///
318/// Because of this property, if you need to hold onto an
319/// `&lmdb::ConstTransaction` and must explicitly name both lifetimes,
320/// it is usually best to use the same lifetime for both the reference and the
321/// parameter, eg `&'x lmdb::ConstTransaction<'x>`.
322#[derive(Debug)]
323pub struct ConstTransaction<'env> {
324 env: NonSyncSupercow<'env, Environment>,
325 tx: TxHandle,
326 has_yielded_accessor: Cell<bool>,
327}
328
329/// A read-only LMDB transaction.
330///
331/// In addition to all operations valid on `ConstTransaction`, a
332/// `ReadTransaction` can additionally operate on cursors with a lifetime
333/// scoped to the environment instead of the transaction.
334///
335/// ## Ownership
336///
337/// `ReadTransaction`s can be created with all three ownership modes (but owned
338/// mode is not useful).
339///
340/// ### Example — Shared mode
341///
342/// ```
343/// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
344/// use std::sync::Arc;
345///
346/// # fn main() {
347/// let env = Arc::new(create_env());
348/// let db = Arc::new(lmdb::Database::open(
349/// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap());
350///
351/// // Type and lifetime annotated explicitly for clarity
352/// let txn: lmdb::ReadTransaction<'static> = lmdb::ReadTransaction::new(
353/// env.clone()).unwrap();
354///
355/// // Do stuff with `txn`...
356/// # drop(txn); drop(db);
357/// # }
358/// ```
359///
360/// ## Lifetime
361///
362/// All notes for `ConstTransaction` apply.
363#[derive(Debug)]
364// This MUST be a newtype struct and MUST NOT `impl Drop`
365pub struct ReadTransaction<'env>(ConstTransaction<'env>);
366/// A read-write LMDB transaction.
367///
368/// In addition to all operations valid on `ConstTransaction`, it is also
369/// possible to perform writes to the underlying databases.
370///
371///
372/// ## Ownership
373///
374/// `WriteTransaction`s can be created with all three ownership modes (but
375/// owned mode is not useful).
376///
377/// ### Example — Shared mode
378///
379/// ```
380/// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
381/// use std::sync::Arc;
382///
383/// # fn main() {
384/// let env = Arc::new(create_env());
385/// let db = Arc::new(lmdb::Database::open(
386/// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap());
387///
388/// // Type and lifetime annotated explicitly for clarity
389/// let txn: lmdb::WriteTransaction<'static> = lmdb::WriteTransaction::new(
390/// env.clone()).unwrap();
391///
392/// // Do stuff with `txn`...
393///
394/// txn.commit().unwrap();
395/// # }
396/// ```
397///
398/// ## Lifetime
399///
400/// All notes for `ConstTransaction` apply.
401#[derive(Debug)]
402// This MUST be a newtype struct and MUST NOT `impl Drop`
403pub struct WriteTransaction<'env>(ConstTransaction<'env>);
404
405/// A read-only LMDB transaction that has been reset.
406///
407/// It can be renewed by calling `ResetTransaction::renew()`.
408///
409/// ## Lifetime
410///
411/// All notes for `ReadTransaction` apply.
412#[derive(Debug)]
413pub struct ResetTransaction<'env>(ReadTransaction<'env>);
414
415/// A read-only data accessor obtained from a `ConstTransaction`.
416///
417/// There is no corresponding `ReadAccessor`, since there are no additional
418/// operations one can do with a known-read-only accessor.
419///
420/// ## Lifetime
421///
422/// A `ConstAccessor` must be strictly outlived by its parent transaction. The
423/// parent transaction cannot be destroyed (committed, etc) until the borrow
424/// from the accessor ends. This in many cases requires adding an extra scope
425/// (with bare `{ }` braces) in which to obtain the accessor, as can be seen in
426/// many of the examples.
427///
428/// The lifitem of a reference to a `ConstAccessor` dictates the lifetime of
429/// the data accessed via the accessor.
430///
431/// The `'txn` lifetime parameter is covariant. That is, given two lifetimes
432/// `'x` and `'y` where `'x: 'y`, a `&ConstAccessor<'x>` can be implicitly
433/// coerced into a `&ConstAccessor<'y>`.
434///
435/// ```rust,norun
436/// # #![allow(dead_code)]
437/// # extern crate lmdb_zero as lmdb;
438/// # fn main() { }
439/// #
440/// fn convariance<'x, 'y>(db: &lmdb::ConstAccessor<'x>)
441/// where 'x: 'y {
442/// let _db2: &lmdb::ConstAccessor<'y> = db;
443/// }
444/// ```
445///
446/// Because of this property, if you need to hold onto an
447/// `&lmdb::ConstAccessor` and must explicitly name both lifetimes, it
448/// is usually best to use the same lifetime for both the reference and the
449/// parameter, eg `&'x lmdb::ConstAccessor<'x>`.
450#[derive(Debug)]
451pub struct ConstAccessor<'txn>(&'txn ConstTransaction<'txn>);
452
453/// ConstAccessor implements Drop trait so that if it gets
454/// dropped, a new accessor can be safely obtained
455impl<'txn> Drop for ConstAccessor<'txn> {
456 fn drop(&mut self) {
457 self.0.has_yielded_accessor.set(false)
458 }
459}
460
461/// A read-write data accessor obtained from a `WriteTransaction`.
462///
463/// All operations that can be performed on `ConstAccessor` can also be
464/// performed on `WriteAccessor`.
465///
466/// ## Lifetime
467///
468/// Nominally, `WriteAccessor` would behave the same as `ConstAccessor`.
469///
470/// However, there is never any useful reason to explicitly reference a
471/// `&WriteAccessor` (ie, a shared reference). Instead, one talks about a
472/// `&mut WriteAccessor`. The unfortunate consequence here is that the `'txn`
473/// lifetime ends up being _invariant_; that is, the following code will not
474/// compile:
475///
476/// ```rust,ignore
477/// # #![allow(dead_code)]
478/// # extern crate lmdb_zero as lmdb;
479/// # fn main() { }
480/// #
481/// fn convariance<'x, 'y>(db: &mut lmdb::WriteAccessor<'x>)
482/// where 'x: 'y {
483/// let _db2: &mut lmdb::WriteAccessor<'y> = db; // ERROR!
484/// }
485/// ```
486///
487/// The compiler's error messages here tend to be unhelpful. In certain cases,
488/// it will suggest changing the function declaration above to something like
489/// `&'x mut lmdb::WriteAccessor<'x>`. Applying such a fix when it is suggested
490/// _will appear to work_. But what happens is that you end up propagating
491/// `&'txn mut lmdb::WriteAccessor<'txn>` the whole way up your call stack.
492/// Since `'txn` is invariant, it is inferred to be exactly equal to the
493/// lifetime of the transaction, and now you've declared that the borrow from
494/// the transaction exists for the entire lifetime of the transaction. This
495/// means that you cannot actually commit the transaction.
496///
497/// Instead, make sure you always have separate type parameters on the `&mut`
498/// and the `WriteAccessor` itself. This can usually be accomplished by letting
499/// lifetime elision run its course. If you must name both, generally go with
500/// `&'access mut WriteAccessor<'txn>`. The `'access` lifetime is the lifetime
501/// of any data you obtain via the accessor.
502#[derive(Debug)]
503pub struct WriteAccessor<'txn>(ConstAccessor<'txn>);
504
505impl<'env> ConstTransaction<'env> {
506 fn new<'outer: 'env, E>(env: E,
507 parent: Option<&'env mut ConstTransaction<'outer>>,
508 flags: c_uint) -> Result<Self>
509 where E : Into<NonSyncSupercow<'env, Environment>> {
510 let env : NonSyncSupercow<'env, Environment> = env.into();
511
512 let mut rawtx: *mut ffi::MDB_txn = ptr::null_mut();
513 unsafe {
514 lmdb_call!(ffi::mdb_txn_begin(
515 env::env_ptr(&env), parent.map_or(ptr::null_mut(), |p| p.tx.0),
516 flags, &mut rawtx));
517 }
518
519 Ok(ConstTransaction {
520 env: env,
521 tx: TxHandle(rawtx),
522 has_yielded_accessor: Cell::new(false),
523 })
524 }
525
526 /// Returns an accessor used to manipulate data in this transaction.
527 ///
528 /// ## Ownership
529 ///
530 /// Unlike most other lmdb-zero APIs, accessors do not support shared
531 /// ownership modes (e.g., where the accessor would hold on to a
532 /// `Rc<ConstTransaction>`). If you need dynamically-managed lifetime,
533 /// instead simply drop the accessor and get a new one the next time one is
534 /// needed.
535 ///
536 /// ## Panics
537 ///
538 /// Panics if this function has already been called on this transaction and
539 /// the returned value has not yet been dropped.
540 ///
541 /// ## Example
542 ///
543 /// ```rust,should_panic
544 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
545 /// # #[allow(unused_vars)]
546 /// # fn main() {
547 /// # let env = create_env();
548 /// let txn = lmdb::ReadTransaction::new(&env).unwrap();
549 /// // Get access the first time
550 /// let access = txn.access();
551 ///
552 /// // You can't get the accessor again in the same scope, since this
553 /// // would create two references to the same logical memory and allow
554 /// // creating aliased mutable references and so forth.
555 /// let access2 = txn.access(); // PANIC!
556 /// # }
557 /// ```
558 #[inline]
559 pub fn access(&self) -> ConstAccessor {
560 assert!(!self.has_yielded_accessor.get(),
561 "Transaction accessor already returned");
562 self.has_yielded_accessor.set(true);
563 ConstAccessor(self)
564 }
565
566 /// Creates a new cursor scoped to this transaction, bound to the given
567 /// database.
568 ///
569 /// This method is functionally equivalent to the method on `CreateCursor`
570 /// and exists for convenience and backwards-compatibility.
571 ///
572 /// If you have an, e.g., `Rc<ReadTransaction>` and want to get a
573 /// `Cursor<'static,'db>`, make sure you have the `CreateCursor` trait
574 /// imported so that the needed alternate implementations of this method
575 /// are available.
576 #[inline]
577 pub fn cursor<'txn, 'db, DB>(&'txn self, db: DB)
578 -> Result<Cursor<'txn,'db>>
579 where DB : Into<Supercow<'db, Database<'db>>> {
580 Cursor::construct(Supercow::borrowed(self), db.into())
581 }
582
583 /// Returns the internal id of this transaction.
584 pub fn id(&self) -> usize {
585 unsafe {
586 ffi2::mdb_txn_id(self.tx.0)
587 }
588 }
589
590 /// Retrieves statistics for a database.
591 pub fn db_stat(&self, db: &Database) -> Result<Stat> {
592 try!(db.assert_same_env(&self.env));
593
594 unsafe {
595 let mut raw: ffi::MDB_stat = mem::zeroed();
596 lmdb_call!(ffi::mdb_stat(self.tx.0, db.as_raw(), &mut raw));
597 Ok(raw.into())
598 }
599 }
600
601 /// Retrieve the DB flags for a database handle.
602 pub fn db_flags(&self, db: &Database) -> Result<db::Flags> {
603 try!(db.assert_same_env(&self.env));
604
605 let mut raw: c_uint = 0;
606 unsafe {
607 lmdb_call!(ffi::mdb_dbi_flags(self.tx.0, db.as_raw(), &mut raw));
608 }
609 Ok(db::Flags::from_bits_truncate(raw))
610 }
611
612 #[inline]
613 fn assert_sensible_cursor(&self, cursor: &Cursor)
614 -> Result<()> {
615 if self as *const ConstTransaction !=
616 cursor::txn_ref(cursor) as *const ConstTransaction
617 {
618 Err(Error::Mismatch)
619 } else {
620 Ok(())
621 }
622 }
623}
624
625// Internally used by other parts of the crate
626#[inline]
627pub fn assert_sensible_cursor(access: &ConstAccessor, cursor: &Cursor)
628 -> Result<()> {
629 access.0.assert_sensible_cursor(cursor)
630}
631#[inline]
632pub fn assert_same_env(txn: &ConstTransaction, db: &Database)
633 -> Result<()> {
634 db.assert_same_env(&txn.env)
635}
636#[inline]
637pub fn assert_in_env(txn: &ConstTransaction, env: &Environment)
638 -> Result<()> {
639 if env as *const Environment != &*txn.env as *const Environment {
640 Err(Error::Mismatch)
641 } else {
642 Ok(())
643 }
644}
645#[inline]
646pub fn txptr(txn: &ConstTransaction) -> *mut ffi::MDB_txn {
647 txn.tx.0
648}
649
650impl<'env> Deref for ReadTransaction<'env> {
651 type Target = ConstTransaction<'env>;
652
653 fn deref(&self) -> &ConstTransaction<'env> {
654 &self.0
655 }
656}
657
658impl<'env> DerefMut for ReadTransaction<'env> {
659 fn deref_mut(&mut self) -> &mut ConstTransaction<'env> {
660 &mut self.0
661 }
662}
663
664impl<'env> ReadTransaction<'env> {
665 /// Opens a new, read-only transaction within the given environment.
666 ///
667 /// ## Note
668 ///
669 /// A transaction and its cursors must only be used by a single thread
670 /// (enforced by the rust compiler), and a thread may only have a single
671 /// transaction at a time. If `NOTLS` is in use, this does not apply to
672 /// read-only transactions. Attempting to open a read-only transaction
673 /// while the current thread holds a read-write transaction will deadlock.
674 pub fn new<E>(env: E) -> Result<Self>
675 where E : Into<NonSyncSupercow<'env, Environment>> {
676 Ok(ReadTransaction(try!(ConstTransaction::new(
677 env, None, ffi::MDB_RDONLY))))
678 }
679
680 /// Dissociates the given cursor from this transaction and its database,
681 /// returning a `StaleCursor` which can be reused later.
682 ///
683 /// This only fails if `cursor` does not belong to this transaction.
684 ///
685 /// ## Example
686 ///
687 /// ```
688 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
689 /// # fn main() {
690 /// # let env = create_env();
691 /// # let db = lmdb::Database::open(
692 /// # &env, None, &lmdb::DatabaseOptions::defaults())
693 /// # .unwrap();
694 /// let mut saved_cursor;
695 /// {
696 /// let txn = lmdb::ReadTransaction::new(&env).unwrap();
697 /// let cursor = txn.cursor(&db).unwrap();
698 /// // Do some stuff with `txn` and `cursor`
699 ///
700 /// // We don't want to realloc `cursor` next time, so save it away
701 /// saved_cursor = txn.dissoc_cursor(cursor).unwrap();
702 /// } // Read transaction goes away, but our saved cursor remains
703 ///
704 /// {
705 /// let txn = lmdb::ReadTransaction::new(&env).unwrap();
706 /// // Rebind the old cursor. It continues operating on `db`.
707 /// let cursor = txn.assoc_cursor(saved_cursor).unwrap();
708 /// // Do stuff with txn, cursor
709 ///
710 /// // We can save the cursor away again
711 /// saved_cursor = txn.dissoc_cursor(cursor).unwrap();
712 /// }
713 /// # }
714 /// ```
715 ///
716 /// ## Example — Shared ownership mode
717 ///
718 /// Cursors can also be dissociated and reassociated with transactions with
719 /// shared ownership mode. This can also include changing the ownership
720 /// mode. To be able to use shared ownership mode, make sure that the
721 /// `AssocCursor` trait is imported or else you will simply borrow the
722 /// inner transaction instead of taking a copy of the `Rc`, etc.
723 ///
724 /// ```
725 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
726 /// use std::sync::Arc;
727 ///
728 /// use lmdb::traits::{AssocCursor, CreateCursor};
729 ///
730 /// # fn main() {
731 /// // N.B. Unnecessary type and lifetime annotations included for clarity
732 /// let env: Arc<lmdb::Environment> = Arc::new(create_env());
733 /// let db: Arc<lmdb::Database<'static>> = Arc::new(lmdb::Database::open(
734 /// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap());
735 ///
736 /// let mut saved_cursor: lmdb::StaleCursor<'static>;
737 /// {
738 /// // `Arc` is unnecessary in this trivial example, but let's pretend
739 /// // there was good use for this.
740 /// let txn: Arc<lmdb::ReadTransaction> = Arc::new(
741 /// lmdb::ReadTransaction::new(env.clone()).unwrap());
742 /// let cursor: lmdb::Cursor<'static, 'static> =
743 /// txn.cursor(db.clone()).unwrap();
744 ///
745 /// // Do some stuff with `txn` and `cursor`
746 ///
747 /// // We don't want to realloc `cursor` next time, so save it away
748 /// saved_cursor = txn.dissoc_cursor(cursor).unwrap();
749 /// }
750 ///
751 /// {
752 /// let txn: Arc<lmdb::ReadTransaction<'static>> =
753 /// Arc::new(lmdb::ReadTransaction::new(env.clone()).unwrap());
754 /// // Rebind the old cursor. It continues operating on `db`.
755 /// let cursor: lmdb::Cursor<'static, 'static> =
756 /// txn.assoc_cursor(saved_cursor).unwrap();
757 /// // Do stuff with txn, cursor
758 ///
759 /// // We can save the cursor away again
760 /// saved_cursor = txn.dissoc_cursor(cursor).unwrap();
761 /// }
762 /// # }
763 /// ```
764 pub fn dissoc_cursor<'txn,'db>(&self, cursor: Cursor<'txn,'db>)
765 -> Result<StaleCursor<'db>>
766 where 'env: 'db {
767 try!(self.assert_sensible_cursor(&cursor));
768 let env = Supercow::clone_non_owned(&self.env)
769 .expect("Cannot use owned `Environment` with `dissoc_cursor`");
770 Ok(cursor::to_stale(cursor, env))
771 }
772
773 /// Associates a saved read-only with this transaction.
774 ///
775 /// The cursor will be rebound to this transaction, but will continue using
776 /// the same database that it was previously.
777 ///
778 /// This method is functionally equivalent to the method on `AssocCursor`
779 /// and exists for convenience and backwards-compatibility.
780 ///
781 /// If you have an, e.g., `Rc<ReadTransaction>` and want to get a
782 /// `Cursor<'static,'db>`, make sure you have the `AssocCursor` trait
783 /// imported so that the needed alternate implementations of this method
784 /// are available.
785 pub fn assoc_cursor<'txn,'db>(&'txn self, cursor: StaleCursor<'db>)
786 -> Result<Cursor<'txn,'db>> {
787 let self_as_const: &'txn ConstTransaction = &*self;
788 Cursor::from_stale(cursor,
789 NonSyncSupercow::borrowed(&*self_as_const))
790 }
791
792 /// Resets this transaction, releasing most of its resources but allowing
793 /// it to be quickly renewed if desired.
794 ///
795 /// ## Example
796 ///
797 /// ```
798 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
799 /// # fn main() {
800 /// # let env = create_env();
801 /// let mut saved_txn;
802 /// {
803 /// let txn = lmdb::ReadTransaction::new(&env).unwrap();
804 /// {
805 /// let access = txn.access();
806 /// // Do stuff with `txn`, `access`
807 /// }
808 /// // Save our transaction so we don't have to reallocate it next time,
809 /// // but we also don't keep locks around and will later move to the
810 /// // latest version of the environment.
811 /// saved_txn = txn.reset();
812 /// }
813 ///
814 /// {
815 /// // Instead of creating a brand new transaction, renew the one we
816 /// // saved.
817 /// let txn = saved_txn.renew().unwrap();
818 /// {
819 /// let access = txn.access();
820 /// // Do stuff with `txn`, `access`
821 /// }
822 ///
823 /// // We can save the transaction away again
824 /// saved_txn = txn.reset();
825 /// }
826 /// # }
827 /// ```
828 pub fn reset(self) -> ResetTransaction<'env> {
829 unsafe { ffi::mdb_txn_reset(self.0.tx.0); }
830 ResetTransaction(self)
831 }
832}
833
834impl<'env> ResetTransaction<'env> {
835 /// Renews this read-only transaction, making it available for more
836 /// reading.
837 pub fn renew(self) -> Result<ReadTransaction<'env>> {
838 unsafe { lmdb_call!(ffi::mdb_txn_renew((self.0).0.tx.0)); }
839 Ok(self.0)
840 }
841}
842
843impl<'env> Deref for WriteTransaction<'env> {
844 type Target = ConstTransaction<'env>;
845
846
847 fn deref(&self) -> &ConstTransaction<'env> {
848 &self.0
849 }
850}
851
852impl<'env> DerefMut for WriteTransaction<'env> {
853 fn deref_mut(&mut self) -> &mut ConstTransaction<'env> {
854 &mut self.0
855 }
856}
857
858impl<'env> WriteTransaction<'env> {
859 /// Creates a new, read-write transaction in the given environment.
860 ///
861 /// ## Note
862 ///
863 /// A transaction and its cursors must only be used by a single thread
864 /// (enforced by the rust compiler), and a thread may only have a single
865 /// read-write transaction at a time (even if `NOTLS` is in use --- trying
866 /// to start two top-level read-write transactions on the same thread will
867 /// deadlock).
868 pub fn new<E>(env: E) -> Result<Self>
869 where E : Into<NonSyncSupercow<'env, Environment>> {
870 Ok(WriteTransaction(try!(ConstTransaction::new(env, None, 0))))
871 }
872
873 /// Opens a new, read-write transaction as a child transaction of the given
874 /// parent. While the new transaction exists, no operations may be
875 /// performed on the parent or any of its cursors. (These bindings are
876 /// actually stricter, and do not permit cursors or other references into
877 /// the parent to coexist with the child transaction.)
878 ///
879 /// After this call, whether or not it succeeds, it is possible to call
880 /// `access()` on the original transaction again one more time, since the
881 /// Rust borrow rules guarantee the old accessor was destroyed by the
882 /// caller already.
883 ///
884 /// ## Note
885 ///
886 /// A transaction and its cursors must only be used by a single thread
887 /// (enforced by the rust compiler).
888 ///
889 /// ## Example
890 ///
891 /// ```
892 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
893 /// # fn main() {
894 /// # let env = create_env();
895 /// let db = lmdb::Database::open(
896 /// &env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
897 /// let mut txn = lmdb::WriteTransaction::new(&env).unwrap();
898 /// let f = lmdb::put::Flags::empty();
899 /// {
900 /// let mut access = txn.access();
901 /// access.put(&db, "Germany", "Berlin", f).unwrap();
902 /// access.put(&db, "Latvia", "Rīga", f).unwrap();
903 /// access.put(&db, "France", "Paris", f).unwrap();
904 /// }
905 ///
906 /// {
907 /// // Open a child transaction and do some more reading and writing.
908 /// let subtx = txn.child_tx().unwrap();
909 /// let mut access = subtx.access();
910 /// assert_eq!("Berlin", access.get::<str,str>(&db, "Germany").unwrap());
911 /// access.put(&db, "Germany", "Frankfurt", f).unwrap();
912 /// assert_eq!("Frankfurt", access.get::<str,str>(&db, "Germany").unwrap());
913 /// // Don't commit --- let the child transaction abort (roll back)
914 /// }
915 ///
916 /// {
917 /// let mut access = txn.access();
918 /// // Now we can do some more reading and writing on the original
919 /// // transaction.
920 /// // The effect of the aborted child transaction are not visible.
921 /// access.put(&db, "United Kingdom", "London", f).unwrap();
922 /// assert_eq!("Berlin", access.get::<str,str>(&db, "Germany").unwrap());
923 /// }
924 ///
925 /// {
926 /// // Another child.
927 /// let subtx = txn.child_tx().unwrap();
928 /// {
929 /// let mut access = subtx.access();
930 /// access.put(&db, "Spain", "Madrid", f).unwrap();
931 /// }
932 /// // Commit this one this time.
933 /// subtx.commit().unwrap();
934 /// }
935 ///
936 /// {
937 /// // Now the changes from the child are visible to this transaction,
938 /// // but still not outside it.
939 /// let mut access = txn.access();
940 /// assert_eq!("Madrid", access.get::<str,str>(&db, "Spain").unwrap());
941 /// }
942 ///
943 /// txn.commit().unwrap();
944 /// # }
945 /// ```
946 pub fn child_tx<'a>(&'a mut self) -> Result<WriteTransaction<'a>>
947 where 'env: 'a {
948 let env = Supercow::share(&mut self.0.env);
949 Ok(WriteTransaction(try!(ConstTransaction::new(
950 env, Some(&mut*self), 0))))
951 }
952
953 /// Commits this write transaction.
954 pub fn commit(mut self) -> Result<()> {
955 unsafe {
956 self.0.tx.commit()
957 }
958 }
959
960 /// Returns a read/write accessor on this transaction.
961 ///
962 /// ## Panics
963 ///
964 /// Panics if an accessor has already been obtained from this transaction
965 /// and not yet dropped.
966 #[inline]
967 pub fn access(&self) -> WriteAccessor {
968 WriteAccessor(self.0.access())
969 }
970}
971
972impl<'txn> ConstAccessor<'txn> {
973 /// Get items from a database.
974 ///
975 /// This function retrieves key/data pairs from the database. A reference
976 /// to the data associated with the given key is returned. If the database
977 /// supports duplicate keys (`DUPSORT`) then the first data item for the
978 /// key will be returned. Retrieval of other items requires the use of
979 /// cursoring.
980 ///
981 /// The returned memory is valid until the next mutation through the
982 /// transaction or the end of the transaction (both are enforced through
983 /// the borrow checker).
984 ///
985 /// ## Errors
986 ///
987 /// This call may return errors for reasons other than the key not being
988 /// found. The easiest way to handle "not found" is generally to use the
989 /// `to_opt` method on `traits::LmdbResultExt` to promote the value into a
990 /// `Result<Option<V>>`. Most important of these other errors is the
991 /// possibility of the key being found, but the value not being convertible
992 /// to a `&V`.
993 #[inline]
994 pub fn get<K : AsLmdbBytes + ?Sized, V : FromLmdbBytes + ?Sized>(
995 &self, db: &Database, key: &K) -> Result<&V>
996 {
997 try!(db.assert_same_env(self.env()));
998
999 let mut mv_key = as_val(key);
1000 let mut out_val = EMPTY_VAL;
1001 unsafe {
1002 lmdb_call!(ffi::mdb_get(
1003 self.txptr(), db.as_raw(), &mut mv_key, &mut out_val));
1004 }
1005
1006 from_val(self, &out_val)
1007 }
1008
1009 fn txptr(&self) -> *mut ffi::MDB_txn {
1010 self.0.tx.0
1011 }
1012
1013 fn env(&self) -> &Environment {
1014 &*self.0.env
1015 }
1016}
1017
1018impl<'txn> Deref for WriteAccessor<'txn> {
1019 type Target = ConstAccessor<'txn>;
1020
1021 fn deref(&self) -> &ConstAccessor<'txn> {
1022 &self.0
1023 }
1024}
1025
1026impl<'txn> WriteAccessor<'txn> {
1027 /// Store items into a database.
1028 ///
1029 /// This function stores key/data pairs in the database. The default
1030 /// behavior is to enter the new key/data pair, replacing any previously
1031 /// existing key if duplicates are disallowed, or adding a duplicate data
1032 /// item if duplicates are allowed (`DUPSORT`).
1033 #[inline]
1034 pub fn put<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>(
1035 &mut self, db: &Database, key: &K, value: &V,
1036 flags: put::Flags) -> Result<()>
1037 {
1038 try!(db.assert_same_env(self.env()));
1039
1040 let mut mv_key = as_val(key);
1041 let mut mv_val = as_val(value);
1042 unsafe {
1043 lmdb_call!(ffi::mdb_put(
1044 self.txptr(), db.as_raw(), &mut mv_key, &mut mv_val,
1045 flags.bits()));
1046 }
1047 Ok(())
1048 }
1049
1050 /// Store items into a database.
1051 ///
1052 /// This function stores key/data pairs in the database. The default
1053 /// behavior is to enter the new key/data pair, replacing any previously
1054 /// existing key if duplicates are disallowed, or adding a duplicate data
1055 /// item if duplicates are allowed (`DUPSORT`).
1056 ///
1057 /// Unlike `put()`, this does not take a value. Instead, it reserves space
1058 /// for the value (equal to the size of `V`) and then returns a mutable
1059 /// reference to it. Be aware that the `FromReservedLmdbBytes` conversion
1060 /// will be invoked on whatever memory happens to be at the destination
1061 /// location.
1062 ///
1063 /// ## Example
1064 ///
1065 /// ```
1066 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
1067 /// #[repr(C, packed)]
1068 /// #[derive(Clone,Copy,Debug,PartialEq,Eq)]
1069 /// struct MyStruct {
1070 /// x: i32,
1071 /// y: i32,
1072 /// }
1073 /// unsafe impl lmdb::traits::LmdbRaw for MyStruct { }
1074 ///
1075 /// # fn main() {
1076 /// # let env = create_env();
1077 /// # let db = lmdb::Database::open(
1078 /// # &env, None, &lmdb::DatabaseOptions::defaults())
1079 /// # .unwrap();
1080 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
1081 /// {
1082 /// let mut access = txn.access();
1083 /// {
1084 /// let dst: &mut MyStruct = access.put_reserve(
1085 /// &db, "foo", lmdb::put::Flags::empty()).unwrap();
1086 /// // Writing to `dst` actually writes directly into the database.
1087 /// dst.x = 42;
1088 /// dst.y = 56;
1089 /// // Drop `dst` so we can use `access` again
1090 /// }
1091 /// assert_eq!(&MyStruct { x: 42, y: 56 },
1092 /// access.get(&db, "foo").unwrap());
1093 /// }
1094 /// txn.commit().unwrap();
1095 /// # }
1096 /// ```
1097 #[inline]
1098 pub fn put_reserve<K : AsLmdbBytes + ?Sized,
1099 V : FromReservedLmdbBytes + Sized>(
1100 &mut self, db: &Database, key: &K, flags: put::Flags) -> Result<&mut V>
1101 {
1102 unsafe {
1103 self.put_reserve_unsized(db, key, mem::size_of::<V>(), flags)
1104 }
1105 }
1106
1107 /// Store items into a database.
1108 ///
1109 /// This function stores key/data pairs in the database. The default
1110 /// behavior is to enter the new key/data pair, replacing any previously
1111 /// existing key if duplicates are disallowed, or adding a duplicate data
1112 /// item if duplicates are allowed (`DUPSORT`).
1113 ///
1114 /// Unlike `put()`, this does not take a value. Instead, it reserves space
1115 /// for the value (equal to an array of `count` objects of size `V`) and
1116 /// then returns a mutable reference to it. Be aware that the content of
1117 /// the returned slice is simply whatever happens to be in the destination
1118 /// memory at the time of this call.
1119 ///
1120 /// ## Example
1121 ///
1122 /// ```
1123 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
1124 /// # fn main() {
1125 /// # let env = create_env();
1126 /// # let db = lmdb::Database::open(
1127 /// # &env, None, &lmdb::DatabaseOptions::defaults())
1128 /// # .unwrap();
1129 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
1130 /// {
1131 /// let mut access = txn.access();
1132 /// {
1133 /// let bytes: &mut [u8] = access.put_reserve_array(
1134 /// &db, "foo", 4, lmdb::put::Flags::empty()).unwrap();
1135 /// // More realistically, one could zero-copy data from a file/socket
1136 /// // into `bytes`, for example.
1137 /// bytes[0] = b'b'; bytes[1] = b'y';
1138 /// bytes[2] = b't'; bytes[3] = b'e';
1139 /// }
1140 /// assert_eq!("byte", access.get::<str,str>(&db, "foo").unwrap());
1141 /// }
1142 /// txn.commit().unwrap();
1143 /// # }
1144 /// ```
1145 #[inline]
1146 pub fn put_reserve_array<K : AsLmdbBytes + ?Sized, V : LmdbRaw>(
1147 &mut self, db: &Database, key: &K, count: usize, flags: put::Flags)
1148 -> Result<&mut [V]>
1149 {
1150 unsafe {
1151 self.put_reserve_unsized(
1152 db, key, mem::size_of::<V>() * count, flags)
1153 }
1154 }
1155
1156 /// Store items into a database.
1157 ///
1158 /// This function stores key/data pairs in the database. The default
1159 /// behavior is to enter the new key/data pair, replacing any previously
1160 /// existing key if duplicates are disallowed, or adding a duplicate data
1161 /// item if duplicates are allowed (`DUPSORT`).
1162 ///
1163 /// Unlike `put()`, this does not take a value. Instead, it reserves space
1164 /// equal to `size` bytes for the value and then returns a mutable
1165 /// reference to it. Be aware that the `FromReservedLmdbBytes` conversion
1166 /// will be invoked on whatever memory happens to be at the destination
1167 /// location.
1168 ///
1169 /// ## Unsafety
1170 ///
1171 /// The caller must ensure that `size` is a valid size for `V`.
1172 #[inline]
1173 pub unsafe fn put_reserve_unsized<K : AsLmdbBytes + ?Sized,
1174 V : FromReservedLmdbBytes + ?Sized>(
1175 &mut self, db: &Database, key: &K, size: usize, flags: put::Flags)
1176 -> Result<&mut V>
1177 {
1178 try!(db.assert_same_env(self.env()));
1179
1180 let mut mv_key = as_val(key);
1181 let mut out_val = EMPTY_VAL;
1182 out_val.mv_size = size;
1183 lmdb_call!(ffi::mdb_put(
1184 self.txptr(), db.as_raw(), &mut mv_key, &mut out_val,
1185 flags.bits() | ffi::MDB_RESERVE));
1186
1187 Ok(from_reserved(self, &out_val))
1188 }
1189
1190 /// Delete items from a database by key.
1191 ///
1192 /// This function removes key/data pairs from the database. All values
1193 /// whose key matches `key` are deleted, including in the case of
1194 /// `DUPSORT`. This function will return `NOTFOUND` if the specified
1195 /// key is not in the database.
1196 ///
1197 /// ## Example
1198 ///
1199 /// ```
1200 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
1201 /// # fn main() {
1202 /// # let env = create_env();
1203 /// let db = lmdb::Database::open(
1204 /// &env, Some("example"),
1205 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
1206 /// .unwrap();
1207 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
1208 /// {
1209 /// let mut access = txn.access();
1210 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
1211 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
1212 /// assert_eq!("Apple", access.get::<str,str>(&db, "Fruit").unwrap());
1213 /// access.del_key(&db, "Fruit").unwrap();
1214 /// assert!(access.get::<str,str>(&db, "Fruit").is_err());
1215 /// }
1216 /// txn.commit().unwrap();
1217 /// # }
1218 /// ```
1219 #[inline]
1220 pub fn del_key<K : AsLmdbBytes + ?Sized>(
1221 &mut self, db: &Database, key: &K) -> Result<()>
1222 {
1223 try!(db.assert_same_env(self.env()));
1224
1225 let mut mv_key = as_val(key);
1226 unsafe {
1227 lmdb_call!(ffi::mdb_del(
1228 self.txptr(), db.as_raw(), &mut mv_key, ptr::null_mut()));
1229 }
1230
1231 Ok(())
1232 }
1233
1234 /// Delete items from a database by key and value.
1235 ///
1236 /// This function removes key/data pairs from the database. If the database
1237 /// does not support sorted duplicate data items (`DUPSORT`) the `val`
1238 /// parameter is ignored and this call behaves like `del()`. Otherwise, if
1239 /// the data item matching both `key` and `val` will be deleted. This
1240 /// function will return `NOTFOUND` if the specified key/data pair is not
1241 /// in the database.
1242 ///
1243 /// ## Example
1244 ///
1245 /// ```
1246 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
1247 /// # fn main() {
1248 /// # let env = create_env();
1249 /// let db = lmdb::Database::open(
1250 /// &env, Some("example"),
1251 /// &lmdb::DatabaseOptions::create_multimap_unsized::<str,str>())
1252 /// .unwrap();
1253 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
1254 /// {
1255 /// let mut access = txn.access();
1256 /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap();
1257 /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap();
1258 /// assert_eq!("Apple", access.get::<str,str>(&db, "Fruit").unwrap());
1259 /// access.del_item(&db, "Fruit", "Apple").unwrap();
1260 /// assert_eq!("Orange", access.get::<str,str>(&db, "Fruit").unwrap());
1261 /// }
1262 /// txn.commit().unwrap();
1263 /// # }
1264 /// ```
1265 #[inline]
1266 pub fn del_item<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>(
1267 &mut self, db: &Database, key: &K, val: &V) -> Result<()>
1268 {
1269 try!(db.assert_same_env(self.env()));
1270
1271 let mut mv_key = as_val(key);
1272 let mut mv_val = as_val(val);
1273 unsafe {
1274 lmdb_call!(ffi::mdb_del(
1275 self.txptr(), db.as_raw(), &mut mv_key, &mut mv_val));
1276 }
1277
1278 Ok(())
1279 }
1280
1281 /// Completely clears the content of the given database.
1282 ///
1283 /// ## Example
1284 ///
1285 /// ```
1286 /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
1287 /// # fn main() {
1288 /// # let env = create_env();
1289 /// # let db = lmdb::Database::open(
1290 /// # &env, None, &lmdb::DatabaseOptions::defaults())
1291 /// # .unwrap();
1292 /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
1293 /// {
1294 /// let mut access = txn.access();
1295 /// let f = lmdb::put::Flags::empty();
1296 /// access.put(&db, "Germany", "Berlin", f).unwrap();
1297 /// access.put(&db, "France", "Paris", f).unwrap();
1298 /// access.put(&db, "Latvia", "Rīga", f).unwrap();
1299 /// assert_eq!(3, txn.db_stat(&db).unwrap().entries);
1300 ///
1301 /// access.clear_db(&db).unwrap();
1302 /// assert_eq!(0, txn.db_stat(&db).unwrap().entries);
1303 /// }
1304 /// txn.commit().unwrap();
1305 /// # }
1306 /// ```
1307 pub fn clear_db(&mut self, db: &Database) -> Result<()> {
1308 try!(db.assert_same_env(self.env()));
1309 unsafe {
1310 lmdb_call!(ffi::mdb_drop(self.txptr(), db.as_raw(), 0));
1311 }
1312 Ok(())
1313 }
1314}