lmdb_zero/
dbi.rs

1// Copyright 2016 FullContact, Inc
2// Copyright 2017, 2018 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::cmp::{Ord, Ordering};
11use std::ffi::CString;
12use std::mem;
13use std::ptr;
14use libc::c_int;
15
16use ffi;
17use supercow::Supercow;
18
19use env::{self, Environment};
20use error::{Error, Result};
21use mdb_vals::*;
22use traits::*;
23use tx::TxHandle;
24
25/// Flags used when opening databases.
26pub mod db {
27    use ffi;
28    use libc;
29
30    bitflags! {
31        /// Flags used when opening databases.
32        pub struct Flags : libc::c_uint {
33            /// Keys are strings to be compared in reverse order, from the end
34            /// of the strings to the beginning. By default, Keys are treated
35            /// as strings and compared from beginning to end.
36            ///
37            /// NOTE: This is *not* reverse sort, but rather right-to-left
38            /// comparison.
39            ///
40            /// ## Example
41            ///
42            /// ```
43            /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
44            /// # fn main() {
45            /// # let env = create_env();
46            /// let db = lmdb::Database::open(
47            ///   &env, Some("reversed"), &lmdb::DatabaseOptions::new(
48            ///     lmdb::db::REVERSEKEY | lmdb::db::CREATE)).unwrap();
49            /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
50            /// {
51            ///   let mut access = txn.access();
52            ///   let f = lmdb::put::Flags::empty();
53            ///   access.put(&db, "Germany", "Berlin", f).unwrap();
54            ///   access.put(&db, "Latvia", "Rīga", f).unwrap();
55            ///   access.put(&db, "France", "Paris", f).unwrap();
56            ///
57            ///   let mut cursor = txn.cursor(&db).unwrap();
58            ///   // The keys are compared as if we had input "aivtaL", "ecnarF",
59            ///   // and "ynamreG", so "Latvia" comes first and "Germany" comes
60            ///   // last.
61            ///   assert_eq!(("Latvia", "Rīga"), cursor.first(&access).unwrap());
62            ///   assert_eq!(("Germany", "Berlin"), cursor.last(&access).unwrap());
63            /// }
64            /// txn.commit().unwrap();
65            /// # }
66            /// ```
67            const REVERSEKEY = ffi::MDB_REVERSEKEY;
68            /// Duplicate keys may be used in the database. (Or, from another
69            /// perspective, keys may have multiple data items, stored in
70            /// sorted order.) By default keys must be unique and may have only
71            /// a single data item.
72            ///
73            /// ## Example
74            /// ```
75            /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
76            /// # fn main() {
77            /// # let env = create_env();
78            /// let db = lmdb::Database::open(
79            ///   &env, Some("example"), &lmdb::DatabaseOptions::new(
80            ///     lmdb::db::DUPSORT | lmdb::db::CREATE)).unwrap();
81            /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
82            /// {
83            ///   let mut access = txn.access();
84            ///   let f = lmdb::put::Flags::empty();
85            ///   access.put(&db, "Fruit", "Orange", f).unwrap();
86            ///   access.put(&db, "Fruit", "Apple", f).unwrap();
87            ///   access.put(&db, "Veggie", "Carrot", f).unwrap();
88            ///
89            ///   let mut cursor = txn.cursor(&db).unwrap();
90            ///   assert_eq!(("Fruit", "Apple"),
91            ///              cursor.seek_k_both(&access, "Fruit").unwrap());
92            ///   assert_eq!(("Fruit", "Orange"), cursor.next(&access).unwrap());
93            ///   assert_eq!(("Veggie", "Carrot"), cursor.next(&access).unwrap());
94            /// }
95            /// txn.commit().unwrap();
96            /// # }
97            /// ```
98            const DUPSORT = ffi::MDB_DUPSORT;
99            /// Keys are binary integers in native byte order, either
100            /// `libc::c_uint` or `libc::size_t`, and will be sorted as such.
101            /// The keys must all be of the same size.
102            ///
103            /// ## Example
104            ///
105            /// ```
106            /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
107            /// # fn main() {
108            /// # let env = create_env();
109            /// use lmdb::unaligned as u;
110            ///
111            /// let db = lmdb::Database::open(
112            ///   &env, Some("reversed"), &lmdb::DatabaseOptions::new(
113            ///     lmdb::db::INTEGERKEY | lmdb::db::CREATE)).unwrap();
114            /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
115            /// {
116            ///   let mut access = txn.access();
117            ///   let f = lmdb::put::Flags::empty();
118            ///   // Write the keys in native byte order.
119            ///   // Note that on little-endian systems this means a
120            ///   // byte-by-byte comparison would not order the keys the way
121            ///   // one might expect.
122            ///   access.put(&db, &42u32, "Fourty-two", f).unwrap();
123            ///   access.put(&db, &65536u32, "65'536", f).unwrap();
124            ///   access.put(&db, &0u32, "Zero", f).unwrap();
125            ///
126            ///   let mut cursor = txn.cursor(&db).unwrap();
127            ///   // But because we used `INTEGERKEY`, they are in fact sorted
128            ///   // ascending by integer value.
129            ///   assert_eq!((u(&0u32), "Zero"), cursor.first(&access).unwrap());
130            ///   assert_eq!((u(&42u32), "Fourty-two"), cursor.next(&access).unwrap());
131            ///   assert_eq!((u(&65536u32), "65'536"), cursor.next(&access).unwrap());
132            /// }
133            /// txn.commit().unwrap();
134            /// # }
135            /// ```
136            const INTEGERKEY = ffi::MDB_INTEGERKEY;
137            /// This flag may only be used in combination with `DUPSORT`. This
138            /// option tells the library that the data items for this database
139            /// are all the same size, which allows further optimizations in
140            /// storage and retrieval. When all data items are the same size,
141            /// the `get_multiple` and `next_multiple` cursor operations may be
142            /// used to retrieve multiple items at once.
143            ///
144            /// ## Notes
145            ///
146            /// There are no runtime checks that values are actually the same
147            /// size; failing to uphold this constraint may result in
148            /// unpredictable behaviour.
149            ///
150            /// ## Example
151            ///
152            /// ```
153            /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
154            /// # fn main() {
155            /// # let env = create_env();
156            /// use lmdb::Unaligned as U;
157            /// use lmdb::LmdbResultExt;
158            ///
159            /// let db = lmdb::Database::open(
160            ///   &env, Some("reversed"), &lmdb::DatabaseOptions::new(
161            ///     lmdb::db::DUPSORT | lmdb::db::DUPFIXED |
162            ///     lmdb::db::INTEGERDUP | lmdb::db::CREATE))
163            ///   .unwrap();
164            /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
165            /// {
166            ///   let mut access = txn.access();
167            ///   let f = lmdb::put::Flags::empty();
168            ///   // Map strings to their constituent chars
169            ///   for s in &["foo", "bar", "xyzzy"] {
170            ///     for c in s.chars() {
171            ///       access.put(&db, *s, &c, f).unwrap();
172            ///     }
173            ///   }
174            ///
175            ///   // Read in all the chars of "xyzzy" in sorted order via
176            ///   // cursoring.
177            ///   // Note that we have to read `u32`s because `char`s are not
178            ///   // directly convertable from byte arrays.
179            ///   let mut xyzzy: Vec<u32> = Vec::new();
180            ///   let mut cursor = txn.cursor(&db).unwrap();
181            ///   cursor.seek_k::<str,U<u32>>(&access, "xyzzy").unwrap();
182            ///   loop {
183            ///     let chars = if xyzzy.is_empty() {
184            ///       // First page.
185            ///       // Note that in this example we probably get everything
186            ///       // on the first page, but with more values we'd need to
187            ///       // use `next_multiple` to get the rest.
188            ///       cursor.get_multiple::<[U<u32>]>(&access).unwrap()
189            ///     } else {
190            ///       // .to_opt() to turn NOTFOUND into None
191            ///       match cursor.next_multiple::<[U<u32>]>(&access).to_opt() {
192            ///         // Ok if there's still more for the current key
193            ///         Ok(Some(c)) => c,
194            ///         // NOTFOUND error (=> None) at end of key
195            ///         Ok(None) => break,
196            ///         // Handle other errors
197            ///         Err(e) => panic!("LMDB error: {}", e),
198            ///       }
199            ///     };
200            ///     for ch in chars {
201            ///       xyzzy.push(ch.get());
202            ///     }
203            ///   }
204            ///   // Now we've read in all the values in sorted order.
205            ///   assert_eq!(&['x' as u32, 'y' as u32, 'z' as u32],
206            ///              &xyzzy[..]);
207            /// }
208            /// txn.commit().unwrap();
209            /// # }
210            /// ```
211            const DUPFIXED = ffi::MDB_DUPFIXED;
212            /// This option specifies that duplicate data items are binary
213            /// integers, similar to `INTEGERKEY` keys.
214            const INTEGERDUP = ffi::MDB_INTEGERDUP;
215            /// This option specifies that duplicate data items should be
216            /// compared as strings in reverse order.
217            ///
218            /// NOTE: As with `REVERSEKEY`, this indicates a right-to-left
219            /// comparison, *not* an order reversal.
220            ///
221            /// ## Example
222            ///
223            /// ```
224            /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
225            /// # fn main() {
226            /// # let env = create_env();
227            /// let db = lmdb::Database::open(
228            ///   &env, Some("reversed"), &lmdb::DatabaseOptions::new(
229            ///     lmdb::db::DUPSORT | lmdb::db::REVERSEDUP |
230            ///     lmdb::db::CREATE)).unwrap();
231            /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
232            /// {
233            ///   let mut access = txn.access();
234            ///   let f = lmdb::put::Flags::empty();
235            ///   access.put(&db, "Colorado", "Denver", f).unwrap();
236            ///   access.put(&db, "Colorado", "Golden", f).unwrap();
237            ///   access.put(&db, "Colorado", "Lakewood", f).unwrap();
238            ///
239            ///   let mut cursor = txn.cursor(&db).unwrap();
240            ///   // doowekaL, nedloG, revneD
241            ///   assert_eq!(("Colorado", "Lakewood"), cursor.first(&access).unwrap());
242            ///   assert_eq!(("Colorado", "Golden"), cursor.next(&access).unwrap());
243            ///   assert_eq!(("Colorado", "Denver"), cursor.next(&access).unwrap());
244            /// }
245            /// txn.commit().unwrap();
246            /// # }
247            /// ```
248            const REVERSEDUP = ffi::MDB_REVERSEDUP;
249            /// Create the named database if it doesn't exist. This option is
250            /// not allowed in a read-only environment.
251            const CREATE = ffi::MDB_CREATE;
252        }
253    }
254}
255
256#[derive(Debug)]
257struct DbHandle<'a> {
258    env: Supercow<'a, Environment>,
259    dbi: ffi::MDB_dbi,
260    close_on_drop: bool,
261}
262
263impl<'a> Drop for DbHandle<'a> {
264    fn drop(&mut self) {
265        if self.close_on_drop {
266            env::dbi_close(&self.env, self.dbi);
267        }
268    }
269}
270
271/// A handle on an LMDB database within an environment.
272///
273/// Note that in many respects the RAII aspect of this struct is more a matter
274/// of convenience than correctness. In particular, if holding a read
275/// transaction open, it is possible to obtain a handle to a database created
276/// after that transaction started, but this handle will not point to anything
277/// within that transaction.
278///
279/// The library does, however, guarantee that there will be at most one
280/// `Database` object with the same dbi and environment per process.
281///
282/// ## Lifetime
283///
284/// A `Database` in borrowed mode must be strictly outlived by its
285/// `Environment`.
286///
287/// `'a` is covariant: given two lifetimes `'x` and `'y` where `'x: 'y`, a
288/// `&Database<'x>` will implicitly coerce to `&Database<'y>`.
289///
290/// ```rust,norun
291/// # #![allow(dead_code)]
292/// # extern crate lmdb_zero as lmdb;
293/// # fn main() { }
294/// #
295/// fn convariance<'x, 'y>(db: &lmdb::Database<'x>)
296/// where 'x: 'y {
297///   let _db2: &lmdb::Database<'y> = db;
298/// }
299/// ```
300///
301/// Because of this property, if you need to hold onto an `&lmdb::Database` and
302/// must explicitly name both lifetimes, it is usually best to use the same
303/// lifetime for both the reference and the parameter, eg `&'x lmdb::Database<'x>`.
304///
305/// ## Ownership Modes
306///
307/// All three ownership modes are fully supported. Most examples use borrowed
308/// mode, which is used by simply passing an `&'env Environment` to `open`.
309///
310/// ### Owned Mode
311///
312/// Owned mode is useful when your application only uses one `Database`; this
313/// alleviates the need to track both the `Environment` and the `Database`.
314///
315/// ```
316/// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
317/// fn setup() -> lmdb::Database<'static> {
318///   // N.B. Unneeded type and lifetime annotations included for clarity.
319///   let env: lmdb::Environment = create_env();
320///   // Move `env` into the new `Database` because we only want to use the
321///   // default database. Since it owns the `Environment`, its lifetime
322///   // parameter is simply `'static`.
323///   let db: lmdb::Database<'static> = lmdb::Database::open(
324///     env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
325///   // And since it owns the `Environment`, we can even return it without
326///   // worrying about `env`.
327///   db
328/// }
329///
330/// # fn main() {
331/// let db = setup();
332/// // Do stuff with `db`...
333///
334/// // When `db` is dropped, so is the inner `Environment`.
335/// # }
336/// ```
337///
338/// ### Shared Mode
339///
340/// Shared mode allows to have the `Database` hold on to the `Environment` via
341/// an `Arc` instead of a bare reference. This has all the benefits of owned
342/// mode and none of the drawbacks, but makes it harder to determine when
343/// exactly the `Environment` gets dropped since this only happens after all
344/// referents are (dynamically) dropped.
345///
346/// Without resorting to `unsafe`, shared mode is also the only way to define a
347/// structure which holds both the `Environment` itself and its child
348/// `Database` values.
349///
350/// ```
351/// # #![allow(dead_code)]
352/// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
353/// use std::sync::Arc;
354///
355/// struct ApplicationContext {
356///   env: Arc<lmdb::Environment>,
357///   // You could of course also put these under `Arc`s as well, for example
358///   // if using shared mode with transactions and/or cursors.
359///   dict: lmdb::Database<'static>,
360///   freq: lmdb::Database<'static>,
361/// }
362///
363/// impl ApplicationContext {
364///   fn into_env(self) -> Arc<lmdb::Environment> { self.env }
365/// }
366///
367/// # fn main() {
368/// let env = Arc::new(create_env());
369/// let dict = lmdb::Database::open(
370///   env.clone(), Some("dict"),
371///   &lmdb::DatabaseOptions::create_map::<str>()).unwrap();
372/// let freq = lmdb::Database::open(
373///   env.clone(), Some("freq"),
374///   &lmdb::DatabaseOptions::create_map::<str>()).unwrap();
375///
376/// let context = ApplicationContext {
377///   env: env,
378///   dict: dict,
379///   freq: freq,
380/// };
381///
382/// // Pass `context` around the application freely...
383///
384/// // We could just let `ApplicationContext` drop, but if we want to be
385/// // absolutely sure we know when the `Environment` drops (by panicking if
386/// // it doesn't do so when we want), we can disassemble the struct and check
387/// // manually.
388/// let env = context.into_env(); // Databases get dropped
389/// Arc::try_unwrap(env).unwrap(); // Regain ownership of `Environment`,
390///                                // then drop it.
391/// # }
392/// ```
393#[derive(Debug)]
394pub struct Database<'a> {
395    db: DbHandle<'a>,
396}
397
398/// Describes the options used for creating or opening a database.
399#[derive(Clone,Debug)]
400pub struct DatabaseOptions {
401    /// The integer flags to pass to LMDB
402    pub flags: db::Flags,
403    key_cmp: Option<ffi::MDB_cmp_func>,
404    val_cmp: Option<ffi::MDB_cmp_func>,
405}
406
407impl DatabaseOptions {
408    /// Creates a new `DatabaseOptions` with the given flags, using natural key
409    /// and value ordering.
410    pub fn new(flags: db::Flags) -> DatabaseOptions {
411        DatabaseOptions {
412            flags: flags,
413            key_cmp: None,
414            val_cmp: None,
415        }
416    }
417
418    /// Synonym for `DatabaseOptions::new(db::Flags::empty())`.
419    ///
420    /// Note that this does not even have `db::CREATE` set. This is most useful
421    /// when opening the anonymous default database.
422    pub fn defaults() -> DatabaseOptions {
423        DatabaseOptions::new(db::Flags::empty())
424    }
425
426    /// Sorts keys in the database by interpreting them as `K` and using their
427    /// comparison order. Keys which fail to convert are considered equal.
428    ///
429    /// The comparison function is called whenever it is necessary to compare a
430    /// key specified by the application with a key currently stored in the
431    /// database. If no comparison function is specified, and no special key
432    /// flags were specified in the options, the keys are compared lexically,
433    /// with shorter keys collating before longer keys.
434    ///
435    /// ## Warning
436    ///
437    /// This function must be called before any data access functions are used,
438    /// otherwise data corruption may occur. The same comparison function must
439    /// be used by every program accessing the database, every time the
440    /// database is used.
441    ///
442    /// ## Example
443    ///
444    /// ```
445    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
446    /// #[repr(C, packed)]
447    /// #[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord)]
448    /// struct MyStruct {
449    ///   x: i32,
450    ///   y: i32,
451    /// }
452    /// unsafe impl lmdb::traits::LmdbRaw for MyStruct { }
453    /// unsafe impl lmdb::traits::LmdbOrdKey for MyStruct { }
454    ///
455    /// fn my(x: i32, y: i32) -> MyStruct {
456    ///   MyStruct { x: x, y: y }
457    /// }
458    ///
459    /// # fn main() {
460    /// # let env = create_env();
461    /// let mut opts = lmdb::DatabaseOptions::new(lmdb::db::CREATE);
462    /// opts.sort_keys_as::<MyStruct>();
463    /// let db = lmdb::Database::open(&env, Some("example"), &opts).unwrap();
464    /// let txn = lmdb::WriteTransaction::new(&env).unwrap();
465    /// {
466    ///   let mut access = txn.access();
467    ///   let f = lmdb::put::Flags::empty();
468    ///   access.put(&db, &my(0, 0), "origin", f).unwrap();
469    ///   access.put(&db, &my(-1, 0), "x=-1", f).unwrap();
470    ///   access.put(&db, &my(1, 0), "x=+1", f).unwrap();
471    ///   access.put(&db, &my(0, -1), "y=-1", f).unwrap();
472    ///
473    ///   let mut cursor = txn.cursor(&db).unwrap();
474    ///   // The keys are sorted by the Rust-derived comparison. The default
475    ///   // byte-string comparison would have resulted in (0,0), (0,-1),
476    ///   // (1,0), (-1,0).
477    ///   assert_eq!((&my(-1, 0), "x=-1"), cursor.first(&access).unwrap());
478    ///   assert_eq!((&my(0, -1), "y=-1"), cursor.next(&access).unwrap());
479    ///   assert_eq!((&my(0, 0), "origin"), cursor.next(&access).unwrap());
480    ///   assert_eq!((&my(1, 0), "x=+1"), cursor.next(&access).unwrap());
481    /// }
482    /// txn.commit().unwrap();
483    /// # }
484    pub fn sort_keys_as<K : LmdbOrdKey + ?Sized>(&mut self) {
485        self.key_cmp = Some(DatabaseOptions::entry_cmp_as::<K>);
486    }
487
488    /// Sorts duplicate values in the database by interpreting them as `V` and
489    /// using their comparison order. Values which fail to convert are
490    /// considered equal.
491    ///
492    /// This comparison function is called whenever it is necessary to compare
493    /// a data item specified by the application with a data item currently
494    /// stored in the database. This function only takes effect if the database
495    /// iss opened with the `DUPSORT` flag. If no comparison function is
496    /// specified, and no special key flags were specified in the flags of
497    /// these options, the data items are compared lexically, with shorter
498    /// items collating before longer items.
499    ///
500    /// ## Warning
501    ///
502    /// This function must be called before any data access functions are used,
503    /// otherwise data corruption may occur. The same comparison function must
504    /// be used by every program accessing the database, every time the
505    /// database is used.
506    pub fn sort_values_as<V : LmdbOrdKey + ?Sized>(&mut self) {
507        self.val_cmp = Some(DatabaseOptions::entry_cmp_as::<V>);
508    }
509
510    /// Concisely creates a `DatabaseOptions` to configure a database to have a
511    /// 1:1 mapping using the given key type.
512    ///
513    /// The flags always have `db::CREATE` set. If `K` is understood by LMDB as
514    /// an integer, `db::INTEGERKEY` is set. Otherwise, unless `K` sorts
515    /// properly via byte-string comparison, `sort_keys_as` is called to
516    /// configure the database to use `K`'s `Ord` implementation.
517    pub fn create_map<K : LmdbOrdKey + ?Sized>() -> Self {
518        let mut this = DatabaseOptions::new(db::CREATE);
519        if K::ordered_as_integer() {
520            this.flags |= db::INTEGERKEY;
521        } else if !K::ordered_by_bytes() {
522            this.sort_keys_as::<K>();
523        }
524        this
525    }
526
527    /// Concisely creates a `DatabaseOptions` to configure a database to have a
528    /// 1:M mapping using the given key and unsized value types.
529    ///
530    /// The flags are configured as described with `create_map` with
531    /// `db::DUPSORT` added. If `V` is understood by LMDB as an integer,
532    /// `INTEGERDUP` is set. Otherwise, if `V` is not byte-string comparable,
533    /// `sort_values_as` is used to order values by `V`'s `Ord`
534    /// implementation.
535    pub fn create_multimap_unsized<K : LmdbOrdKey + ?Sized,
536                                   V : LmdbOrdKey + ?Sized>
537        () -> Self
538    {
539        let mut this = DatabaseOptions::create_map::<K>();
540        this.flags |= db::DUPSORT;
541        if V::ordered_as_integer() {
542            this.flags |= db::INTEGERDUP;
543        } else if !V::ordered_by_bytes() {
544            this.sort_values_as::<V>();
545        }
546        this
547    }
548
549    /// Concisely creates a `DatabaseOptions` to configure a database to have a
550    /// 1:M mapping using the given key and fixed-size value types.
551    ///
552    /// This is the same as `create_multimap_unsized`, except that `DUPFIXED`
553    /// is additionally set unconditionally.
554    pub fn create_multimap<K : LmdbOrdKey + ?Sized,
555                           V : LmdbOrdKey + Sized>
556        () -> Self
557    {
558        let mut this = DatabaseOptions::create_multimap_unsized::<K, V>();
559        this.flags |= db::DUPFIXED;
560        this
561    }
562
563    extern fn entry_cmp_as<V : LmdbOrdKey + ?Sized>(
564        ap: *const ffi::MDB_val, bp: *const ffi::MDB_val) -> c_int
565    {
566        match unsafe {
567            V::from_lmdb_bytes(mdb_val_as_bytes(&ap, &*ap)).cmp(
568                &V::from_lmdb_bytes(mdb_val_as_bytes(&bp, &*bp)))
569        } {
570            Ordering::Less => -1,
571            Ordering::Equal => 0,
572            Ordering::Greater => 1,
573        }
574    }
575}
576
577impl<'a> Database<'a> {
578    /// Open a database in the environment.
579    ///
580    /// A database handle denotes the name and parameters of a database,
581    /// independently of whether such a database exists. The database handle is
582    /// implicitly closed when the `Database` object is dropped.
583    ///
584    /// To use named databases (with `name != None`),
585    /// `EnvBuilder::set_maxdbs()` must have been called to reserve space for
586    /// the extra databases. Database names are keys in the unnamed database,
587    /// and may be read but not written.
588    ///
589    /// Transaction-local databases are not supported because the resulting
590    /// ownership semantics are not expressible in rust. This call implicitly
591    /// creates a write transaction and uses it to create the database, then
592    /// commits it on success.
593    ///
594    /// One may not open the same database handle multiple times. Attempting to
595    /// do so will result in the `Error::Reopened` error.
596    ///
597    /// Beware that if the underlying `MDB_env` is being shared (for example,
598    /// with native code via `Environment::borrow_raw`), using this method to
599    /// open a `Database` can result in premature closing of the database
600    /// handle since the `Database` is presumed to own the database handle, but
601    /// LMDB will return a shared handle if the database is already open
602    /// elsewhere. The [`disown()`](#method.disown) method can be used to
603    /// ensure the `Database` does not assume ownership of the database
604    /// handle.
605    ///
606    /// ## Examples
607    ///
608    /// ### Open the default database with default options
609    ///
610    /// ```
611    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
612    /// # #[allow(unused_vars)]
613    /// # fn main() {
614    /// # let env = create_env();
615    /// {
616    ///   let db = lmdb::Database::open(
617    ///     &env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
618    ///   // Do stuff with `db`
619    /// } // The `db` handle is released
620    /// # }
621    /// ```
622    ///
623    /// ### Open a named database, creating it if it doesn't exist
624    ///
625    /// ```
626    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
627    /// # #[allow(unused_vars)]
628    /// # fn main() {
629    /// # let env = create_env();
630    /// // NOT SHOWN: Call `EnvBuilder::set_maxdbs()` with a value greater than
631    /// // one so that there is space for the named database(s).
632    /// {
633    ///   let db = lmdb::Database::open(
634    ///     &env, Some("example-db"), &lmdb::DatabaseOptions::new(
635    ///       lmdb::db::CREATE)).unwrap();
636    ///   // Do stuff with `db`
637    /// } // The `db` handle is released
638    /// # }
639    /// ```
640    ///
641    /// ### Trying to open the same database more than once
642    /// ```
643    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
644    /// # #[allow(unused_vars)]
645    /// # fn main() {
646    /// # let env = create_env();
647    /// {
648    ///   let db = lmdb::Database::open(
649    ///     &env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
650    ///   // Can't open the same database twice
651    ///   assert!(lmdb::Database::open(
652    ///     &env, None, &lmdb::DatabaseOptions::defaults()).is_err());
653    /// }
654    /// # }
655    /// ```
656    ///
657    /// ### Open a database on a read-only environment
658    ///
659    /// Databases can be opened in read-only environments as long as they
660    /// already exist.
661    ///
662    /// ```
663    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
664    /// # fn main() {
665    /// # let tdir = tempdir::TempDir::new_in(".", "lmdbzero").unwrap();
666    /// # {
667    /// #   let mut builder = lmdb::EnvBuilder::new().unwrap();
668    /// #   builder.set_maxdbs(2).unwrap();
669    /// #   let env = unsafe { builder.open(
670    /// #     tdir.path().to_str().unwrap(),
671    /// #     lmdb::open::Flags::empty(), 0o600).unwrap() };
672    /// #   let db = lmdb::Database::open(
673    /// #     &env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
674    /// # }
675    /// # let env = {
676    /// #   let mut builder = lmdb::EnvBuilder::new().unwrap();
677    /// #   builder.set_maxdbs(2).unwrap();
678    /// #   unsafe { builder.open(
679    /// #     tdir.path().to_str().unwrap(),
680    /// #     lmdb::open::RDONLY, 0o400).unwrap() }
681    /// # };
682    /// {
683    ///   // Succeeds -- The DB already exists
684    ///   let db = lmdb::Database::open(
685    ///     &env, None,
686    ///     &lmdb::DatabaseOptions::new(lmdb::db::Flags::empty())).unwrap();
687    ///   // Fails -- Can't create a new one in read-only mode
688    ///   assert!(lmdb::Database::open(
689    ///     &env, Some("name"),
690    ///     &lmdb::DatabaseOptions::new(lmdb::db::CREATE)).is_err());
691    /// }
692    /// # }
693    /// ```
694    pub fn open<E>(env: E, name: Option<&str>,
695                   options: &DatabaseOptions)
696                   -> Result<Database<'a>>
697    where E : Into<Supercow<'a, Environment>> {
698        let env: Supercow<'a, Environment> = env.into();
699
700        let mut raw: ffi::MDB_dbi = 0;
701        let name_cstr = match name {
702            None => None,
703            Some(s) => Some(try!(CString::new(s))),
704        };
705        let raw = unsafe {
706            use env;
707            // Locking the hash set here is also used to serialise calls to
708            // `mdb_dbi_open()`, which are not permitted to be concurrent.
709            let mut locked_dbis = env::env_open_dbis(&env).lock()
710                .expect("open_dbis lock poisoned");
711
712            let mut raw_tx: *mut ffi::MDB_txn = ptr::null_mut();
713            let mut txn_flags = 0;
714            if env.flags().unwrap().contains(env::open::RDONLY) {
715                txn_flags = ffi::MDB_RDONLY;
716            }
717            lmdb_call!(ffi::mdb_txn_begin(
718                env::env_ptr(&env), ptr::null_mut(), txn_flags, &mut raw_tx));
719            let mut wrapped_tx = TxHandle(raw_tx); // For auto-closing etc
720            lmdb_call!(ffi::mdb_dbi_open(
721                raw_tx, name_cstr.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
722                options.flags.bits(), &mut raw));
723
724            if !locked_dbis.insert(raw) {
725                return Err(Error::Reopened)
726            }
727
728            if let Some(fun) = options.key_cmp {
729                lmdb_call!(ffi::mdb_set_compare(raw_tx, raw, fun));
730            }
731            if let Some(fun) = options.val_cmp {
732                lmdb_call!(ffi::mdb_set_dupsort(raw_tx, raw, fun));
733            }
734
735            try!(wrapped_tx.commit());
736            raw
737        };
738
739        Ok(Database {
740            db: DbHandle {
741                env: env,
742                dbi: raw,
743                close_on_drop: true,
744            }
745        })
746    }
747
748    /// Wrap a raw `MDB_dbi` associated with this environment.
749    ///
750    /// This call assumes ownership of the `MDB_dbi`, and the resulting
751    /// `Database` will close it on drop. If this is not desired, see
752    /// [`borrow_raw()`](#method.borrow_raw) instead.
753    ///
754    /// ## Unsafety
755    ///
756    /// `raw` must reference a database currently open in `env`.
757    ///
758    /// The caller must ensure that nothing closes the handle until
759    /// `into_raw()` is called and that nothing uses it after the `Database` is
760    /// dropped normally.
761    ///
762    /// ## Panics
763    ///
764    /// Panics if `raw` is a handle already owned by `env`.
765    pub unsafe fn from_raw<E>(env: E, raw: ffi::MDB_dbi) -> Self
766    where E : Into<Supercow<'a, Environment>> {
767        let env: Supercow<'a, Environment> = env.into();
768
769        use env;
770        {
771            let mut locked_dbis = env::env_open_dbis(&env).lock()
772                .expect("open_dbis lock poisoned");
773
774            if !locked_dbis.insert(raw) {
775                panic!("DBI {} already opened in this environment", raw);
776            }
777        }
778
779        Database {
780            db: DbHandle {
781                env: env,
782                dbi: raw,
783                close_on_drop: true,
784            }
785        }
786    }
787
788    /// Wrap a raw `MDB_dbi` associated with this environment without taking
789    /// ownership.
790    ///
791    /// This call does not assume ownership of `raw`, and as a result neither
792    /// checks whether any other `Database`s exist with the same handle, nor
793    /// records this particular handle in `env`'s list of owned databases.
794    ///
795    /// ## Unsafety
796    ///
797    /// `raw` must reference a database currently open in `env`.
798    ///
799    /// The caller must ensure that nothing closes the handle until the
800    /// resulting `Database` is dropped.
801    pub unsafe fn borrow_raw<E>(env: E, raw: ffi::MDB_dbi) -> Self
802    where E : Into<Supercow<'a, Environment>> {
803        Database {
804            db: DbHandle {
805                env: env.into(),
806                dbi: raw,
807                close_on_drop: false,
808            }
809        }
810    }
811
812    /// Deletes this database.
813    ///
814    /// This call implicitly creates a new write transaction to perform the
815    /// operation, so that the lifetime of the database handle does not depend
816    /// on the outcome. The database handle is closed implicitly by this
817    /// operation.
818    ///
819    /// Note that the _other_ `mdb_drop` operation which simply clears the
820    /// database is exposed through `WriteAccessor` and is transactional.
821    ///
822    /// ## Example
823    ///
824    /// ```
825    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
826    /// # #[allow(unused_vars)]
827    /// # fn main() {
828    /// # let env = create_env();
829    /// // NOT SHOWN: Call `EnvBuilder::set_maxdbs()` with a value greater than
830    /// // one so that there is space for the named database(s).
831    /// {
832    ///   let db = lmdb::Database::open(
833    ///     &env, Some("example-db"), &lmdb::DatabaseOptions::new(
834    ///       lmdb::db::CREATE)).unwrap();
835    ///   // Do stuff with `db`
836    ///
837    ///   // Delete the database itself. This also consumes `db`.
838    ///   db.delete().unwrap();
839    ///
840    ///   // We can now recreate the database if we so desire.
841    ///   // Note that you should not use delete+open to clear a database; use
842    ///   // `WriteAccessor::clear_db()` to do that.
843    ///   let db = lmdb::Database::open(
844    ///     &env, Some("example-db"), &lmdb::DatabaseOptions::new(
845    ///       lmdb::db::CREATE)).unwrap();
846    /// }
847    /// # }
848    /// ```
849    pub fn delete(self) -> Result<()> {
850        try!(env::dbi_delete(&self.db.env, self.db.dbi));
851        mem::forget(self.db);
852        Ok(())
853    }
854
855    /// Returns a reference to the `Environment` to which this `Database`
856    /// belongs.
857    ///
858    /// This can be used to elide needing to pass both an `&Environment` and an
859    /// `&Database` around, but is also useful for the use-case wherein the
860    /// `Database` owns the `Environment`.
861    ///
862    /// Because this may borrow an `Environment` owned by this `Database`, the
863    /// lifetime of the returned reference is dependent on self rather than
864    /// being `'env`. (In fact, `'env` is usually `'static` if the
865    /// `Environment` is owned by the `Database`, so returning `&'env Environment`
866    /// is impossible anyway.)
867    ///
868    /// ## Example
869    ///
870    /// ```
871    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
872    /// # #[allow(unused_vars)]
873    /// # fn main() {
874    /// let env: lmdb::Environment = create_env();
875    /// // We only want one `Database`, so don't bother keeping both variables
876    /// // around and instead let the `Database` own the `Environment`.
877    /// let db = lmdb::Database::open(
878    ///   env, None, &lmdb::DatabaseOptions::defaults()).unwrap();
879    ///
880    /// // `env` has been consumed, but we can still do useful things by
881    /// // getting a reference to the inner value.
882    /// let txn = lmdb::ReadTransaction::new(db.env()).unwrap();
883    ///
884    /// // Do stuff with `txn`, etc.
885    /// # }
886    /// ```
887    #[inline]
888    pub fn env(&self) -> &Environment {
889        &*self.db.env
890    }
891
892    /// Checks that `other_env` is the same as the environment on this
893    /// `Database`.
894    ///
895    /// If it matches, returns `Ok(())`; otherwise, returns `Err`.
896    pub fn assert_same_env(&self, other_env: &Environment)
897                           -> Result<()> {
898        if &*self.db.env as *const Environment !=
899            other_env as *const Environment
900        {
901            Err(Error::Mismatch)
902        } else {
903            Ok(())
904        }
905    }
906
907    /// Returns the underlying integer handle for this database.
908    ///
909    /// ## Deprecated
910    ///
911    /// Renamed to `as_raw()` for consistency.
912    #[deprecated(since = "0.4.4", note = "use as_raw() instead")]
913    pub fn dbi(&self) -> ffi::MDB_dbi {
914        self.db.dbi
915    }
916
917    /// Returns the underlying integer handle for this database.
918    #[inline]
919    pub fn as_raw(&self) -> ffi::MDB_dbi {
920        self.db.dbi
921    }
922
923    /// Consume self, returning the underlying integer handle for this
924    /// database.
925    ///
926    /// If this `Database` owns the database handle, it is not closed, but it
927    /// is removed from the list of handles owned by the `Environment`.
928    pub fn into_raw(mut self) -> ffi::MDB_dbi {
929        self.disown();
930        self.db.dbi
931    }
932
933    /// Prevent the underlying `MDB_dbi` handle from being closed when this
934    /// `Database` is dropped.
935    ///
936    /// This is useful when sharing an `MDB_env` with a native library, as in
937    /// such a context the `MDB_dbi` handles are also involuntarily shared.
938    pub fn disown(&mut self) {
939        use env;
940
941        if self.db.close_on_drop {
942            let mut locked_dbis = env::env_open_dbis(&self.db.env).lock()
943                .expect("open_dbis lock poisoned");
944
945            locked_dbis.remove(&self.db.dbi);
946            self.db.close_on_drop = false;
947        }
948    }
949}
950
951#[cfg(test)]
952mod test {
953    use test_helpers::*;
954    use dbi::*;
955    use tx::*;
956
957    #[test]
958    fn disown_allows_sharing() {
959        let env = create_env();
960        let mut db1 = defdb(&env);
961        db1.disown();
962        let db2 = defdb(&env);
963
964        let tx = WriteTransaction::new(&env).unwrap();
965        tx.access().put(&db1, "foo", "bar", put::Flags::empty()).unwrap();
966        tx.commit().unwrap();
967        drop(db1);
968
969        let tx = ReadTransaction::new(&env).unwrap();
970        assert_eq!("bar", tx.access().get::<str,str>(&db2, "foo").unwrap());
971    }
972
973    #[test]
974    fn borrow_raw_allows_sharing() {
975        let env = create_env();
976        let db1 = defdb(&env);
977        let db2 = unsafe { Database::borrow_raw(&env, db1.as_raw()) };
978
979        let tx = WriteTransaction::new(&env).unwrap();
980        tx.access().put(&db2, "foo", "bar", put::Flags::empty()).unwrap();
981        tx.commit().unwrap();
982        drop(db2);
983
984        let tx = ReadTransaction::new(&env).unwrap();
985        assert_eq!("bar", tx.access().get::<str,str>(&db1, "foo").unwrap());
986    }
987}