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}