lmdb_zero/
env.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::collections::HashSet;
11use std::ffi::{CStr, CString};
12use std::mem;
13use std::ptr;
14use std::sync::Mutex;
15use libc::{self, c_char, c_int, c_uint, c_void};
16
17use ffi;
18use ffi2;
19use tx::TxHandle;
20use ::{Fd, FileMode, Result};
21
22/// Flags used when opening an LMDB environment.
23pub mod open {
24    use libc;
25    use ffi;
26
27    bitflags! {
28        /// Flags used when opening an LMDB environment.
29        pub struct Flags : libc::c_uint {
30            /// Use a fixed address for the mmap region. This flag must be
31            /// specified when creating the environment, and is stored
32            /// persistently in the environment. If successful, the memory map
33            /// will always reside at the same virtual address and pointers
34            /// used to reference data items in the database will be constant
35            /// across multiple invocations. This option may not always work,
36            /// depending on how the operating system has allocated memory to
37            /// shared libraries and other uses. The feature is highly
38            /// experimental.
39            const FIXEDMAP = ffi::MDB_FIXEDMAP;
40            /// By default, LMDB creates its environment in a directory whose
41            /// pathname is given in path, and creates its data and lock files
42            /// under that directory. With this option, the `path` passed to
43            /// `EnvBuilder::open` is used as-is for the database main data
44            /// file. The database lock file is the path with "-lock" appended.
45            const NOSUBDIR = ffi::MDB_NOSUBDIR;
46            /// Open the environment in read-only mode. No write operations
47            /// will be allowed. LMDB will still modify the lock file - except
48            /// on read-only filesystems, where LMDB does not use locks.
49            const RDONLY = ffi::MDB_RDONLY;
50            /// Use a writeable memory map unless `RDONLY` is set. This is
51            /// faster and uses fewer mallocs, but loses protection from
52            /// application bugs like wild pointer writes and other bad updates
53            /// into the database. Incompatible with nested transactions. Do
54            /// not mix processes with and without `WRITEMAP` on the same
55            /// environment. This can defeat durability (`Environment::sync`
56            /// etc).
57            const WRITEMAP = ffi::MDB_WRITEMAP;
58            /// Flush system buffers to disk only once per transaction, omit
59            /// the metadata flush. Defer that until the system flushes files
60            /// to disk, or next non-`RDONLY` commit or `Environment::sync()`.
61            /// This optimization maintains database integrity, but a system
62            /// crash may undo the last committed transaction. I.e. it
63            /// preserves the ACI (atomicity, consistency, isolation) but not D
64            /// (durability) database property. This flag may be changed at any
65            /// time using `Environment::set_flags()`.
66            const NOMETASYNC = ffi::MDB_NOMETASYNC;
67            /// Don't flush system buffers to disk when committing a
68            /// transaction. This optimization means a system crash can corrupt
69            /// the database or lose the last transactions if buffers are not
70            /// yet flushed to disk. The risk is governed by how often the
71            /// system flushes dirty buffers to disk and how often
72            /// `Environment::sync()` is called. However, if the filesystem
73            /// preserves write order and the `WRITEMAP` flag is not used,
74            /// transactions exhibit ACI (atomicity, consistency, isolation)
75            /// properties and only lose D (durability). I.e. database
76            /// integrity is maintained, but a system crash may undo the final
77            /// transactions. Note that `(NOSYNC | WRITEMAP)` leaves the system
78            /// with no hint for when to write transactions to disk, unless
79            /// `Environment::sync()` is called. `(MAPASYNC | WRITEMAP)` may be
80            /// preferable. This flag may be changed at any time using
81            /// `Environment::set_flags()`.
82            const NOSYNC = ffi::MDB_NOSYNC;
83            /// When using `WRITEMAP`, use asynchronous flushes to disk. As
84            /// with `NOSYNC`, a system crash can then corrupt the database or
85            /// lose the last transactions. Calling `Environment::sync()`
86            /// ensures on-disk database integrity until next commit. This flag
87            /// may be changed at any time using `Environment::set_flags()`.
88            const MAPASYNC = ffi::MDB_MAPASYNC;
89            /// Don't use Thread-Local Storage. Tie reader locktable slots to
90            /// transaction objects instead of to threads. I.e.
91            /// `Transaction::reset()` keeps the slot reseved for the
92            /// transaction object. A thread may use parallel read-only
93            /// transactions. A read-only transaction may span threads if the
94            /// user synchronizes its use. Applications that multiplex many
95            /// user threads over individual OS threads need this option. Such
96            /// an application must also serialize the write transactions in an
97            /// OS thread, since LMDB's write locking is unaware of the user
98            /// threads.
99            const NOTLS = ffi::MDB_NOTLS;
100            /// Don't do any locking. If concurrent access is anticipated, the
101            /// caller must manage all concurrency itself. For proper operation
102            /// the caller must enforce single-writer semantics, and must
103            /// ensure that no readers are using old transactions while a
104            /// writer is active. The simplest approach is to use an exclusive
105            /// lock so that no readers may be active at all when a writer
106            /// begins.
107            const NOLOCK = ffi::MDB_NOLOCK;
108            /// Turn off readahead. Most operating systems perform readahead on
109            /// read requests by default. This option turns it off if the OS
110            /// supports it. Turning it off may help random read performance
111            /// when the DB is larger than RAM and system RAM is full. The
112            /// option is not implemented on Windows.
113            const NORDAHEAD = ffi::MDB_NORDAHEAD;
114            /// Don't initialize malloc'd memory before writing to unused
115            /// spaces in the data file. By default, memory for pages written
116            /// to the data file is obtained using malloc. While these pages
117            /// may be reused in subsequent transactions, freshly malloc'd
118            /// pages will be initialized to zeroes before use. This avoids
119            /// persisting leftover data from other code (that used the heap
120            /// and subsequently freed the memory) into the data file. Note
121            /// that many other system libraries may allocate and free memory
122            /// from the heap for arbitrary uses. E.g., stdio may use the heap
123            /// for file I/O buffers. This initialization step has a modest
124            /// performance cost so some applications may want to disable it
125            /// using this flag. This option can be a problem for applications
126            /// which handle sensitive data like passwords, and it makes memory
127            /// checkers like Valgrind noisy. This flag is not needed with
128            /// `WRITEMAP`, which writes directly to the mmap instead of using
129            /// malloc for pages. The initialization is also skipped if
130            /// `RESERVE` is used; the caller is expected to overwrite all of
131            /// the memory that was reserved in that case. This flag may be
132            /// changed at any time using `Environment::set_flags()`.
133            const NOMEMINIT = ffi::MDB_NOMEMINIT;
134        }
135    }
136}
137
138/// Flags used when copying an LMDB environment.
139pub mod copy {
140    use ffi2;
141    use libc;
142
143    bitflags! {
144        /// Flags used when copying an LMDB environment.
145        pub struct Flags : libc::c_uint {
146            /// Perform compaction while copying: omit free pages and sequentially
147            /// renumber all pages in output. This option consumes more CPU and
148            /// runs more slowly than the default.
149            const COMPACT = ffi2::MDB_CP_COMPACT;
150        }
151    }
152}
153
154#[derive(Debug)]
155struct EnvHandle(*mut ffi::MDB_env, bool);
156
157unsafe impl Sync for EnvHandle { }
158unsafe impl Send for EnvHandle { }
159impl Drop for EnvHandle {
160    fn drop(&mut self) {
161        if self.1 {
162            unsafe {
163                ffi::mdb_env_close(self.0)
164            }
165        }
166    }
167}
168
169
170/// Handle on an uninitialised LMDB environment to allow configuring pre-open
171/// options.
172#[derive(Debug)]
173pub struct EnvBuilder {
174    env: EnvHandle,
175}
176
177impl EnvBuilder {
178    /// Allocates a new, uninitialised environment.
179    pub fn new() -> Result<Self> {
180        let mut env: *mut ffi::MDB_env = ptr::null_mut();
181        unsafe {
182            lmdb_call!(ffi::mdb_env_create(&mut env));
183            Ok(EnvBuilder { env: EnvHandle(env, true) })
184        }
185    }
186
187    /// Set the size of the memory map to use for this environment.
188    ///
189    /// The size should be a multiple of the OS page size. The default is
190    /// 10485760 bytes. The size of the memory map is also the maximum size of
191    /// the database. The value should be chosen as large as possible, to
192    /// accommodate future growth of the database.
193    ///
194    /// The new size takes effect immediately for the current process but will
195    /// not be persisted to any others until a write transaction has been
196    /// committed by the current process. Also, only mapsize increases are
197    /// persisted into the environment.
198    ///
199    /// See also `Environment::set_mapsize()`.
200    pub fn set_mapsize(&mut self, size: usize) -> Result<()> {
201        unsafe {
202            lmdb_call!(ffi::mdb_env_set_mapsize(
203                self.env.0, size as libc::size_t));
204        }
205
206        Ok(())
207    }
208
209    /// Set the maximum number of threads/reader slots for the environment.
210    ///
211    /// This defines the number of slots in the lock table that is used to
212    /// track readers in the the environment. The default is 126. Starting a
213    /// read-only transaction normally ties a lock table slot to the current
214    /// thread until the environment closes or the thread exits. If `NOTLS` is
215    /// in use, starting a transaction instead ties the slot to the transaction
216    /// object until it or the `Environment` object is destroyed.
217    pub fn set_maxreaders(&mut self, readers: u32) -> Result<()> {
218        unsafe {
219            lmdb_call!(ffi::mdb_env_set_maxreaders(
220                self.env.0, readers as libc::c_uint));
221        }
222
223        Ok(())
224    }
225
226    /// Set the maximum number of named databases for the environment.
227    ///
228    /// This function is only needed if multiple databases will be used in the
229    /// environment. Simpler applications that use the environment as a single
230    /// unnamed database can ignore this option.
231    ///
232    /// Currently a moderate number of slots are cheap but a huge number gets
233    /// expensive: 7-120 words per transaction, and every mdb_dbi_open() does a
234    /// linear search of the opened slots.
235    pub fn set_maxdbs(&mut self, dbs: u32) -> Result<()> {
236        unsafe {
237            lmdb_call!(ffi::mdb_env_set_maxdbs(
238                self.env.0, dbs as libc::c_uint));
239        }
240
241        Ok(())
242    }
243
244    /// Opens the file or directory at `path` with the given `flags` and, on
245    /// UNIX, permissions given by `mode`.
246    ///
247    /// `path` is a `&str` for convenience, and must be convertable to a
248    /// `CString`. The non-use of `&OsStr` or `AsRef<Path>` as normally used in
249    /// `std` is deliberate, since the path must be convertable to a byte
250    /// string. But as a result there is no way to address files whose names
251    /// are not valid UTF-8 through this call.
252    ///
253    /// ## Unsafety
254    ///
255    /// It is the caller's responsibility to ensure that the underlying file
256    /// will be used properly. Since LMDB is built on memory-mapping, these
257    /// responsibilities run quite deep and vary based on `flags`.
258    ///
259    /// - The caller must ensure that no other process writes to the file in
260    ///   any way except going through LMDB. If this is violated, segfaults can
261    ///   occur, or immutable references can be observed to have their referent
262    ///   mutated asynchronously.
263    ///
264    /// - If the caller uses flags which suppress locking, the caller must
265    ///   additionally ensure that LMDB's locking requirements are upheld.
266    ///
267    /// - If the caller uses flags that result in the creation of a sparse
268    ///   file, the caller must ensure there is actually sufficient disk space
269    ///   for all pages of the file or else a segfault may occur.
270    pub unsafe fn open(self, path: &str, flags: open::Flags,
271                       mode: FileMode) -> Result<Environment> {
272        let path_cstr = try!(CString::new(path));
273        lmdb_call!(ffi::mdb_env_open(
274            self.env.0, path_cstr.as_ptr(), flags.bits(), mode));
275        Ok(Environment {
276            env: self.env,
277            open_dbis: Mutex::new(HashSet::new()),
278        })
279    }
280}
281
282/// An LMDB environment which has been opened to a file.
283#[derive(Debug)]
284pub struct Environment {
285    env: EnvHandle,
286    // Track what DBIs are currently in use, so that an open() call that tries
287    // to duplicate one fails.
288    open_dbis: Mutex<HashSet<ffi::MDB_dbi>>,
289}
290
291/// Statistics information about an environment.
292#[derive(Debug,Clone,Copy)]
293pub struct Stat {
294    /// Size of a database page. This is currently the same for all databases.
295    pub psize: u32,
296    /// Depth (height) of the B-tree
297    pub depth: u32,
298    /// Number of internal (non-leaf) pages
299    pub branch_pages: usize,
300    /// Number of leaf pages
301    pub leaf_pages: usize,
302    /// Number of overflow pages
303    pub overflow_pages: usize,
304    /// Number of data items
305    pub entries: usize,
306}
307
308impl From<ffi::MDB_stat> for Stat {
309    fn from(raw: ffi::MDB_stat) -> Stat {
310        Stat {
311            psize: raw.ms_psize as u32,
312            depth: raw.ms_depth as u32,
313            branch_pages: raw.ms_branch_pages as usize,
314            leaf_pages: raw.ms_leaf_pages as usize,
315            overflow_pages: raw.ms_overflow_pages as usize,
316            entries: raw.ms_entries as usize,
317        }
318    }
319}
320
321/// Configuration information about an environment.
322#[derive(Debug,Clone,Copy)]
323pub struct EnvInfo {
324    /// Address of map, if fixed
325    pub mapaddr: *const c_void,
326    /// Size of the data memory map
327    pub mapsize: usize,
328    /// ID of the last used page
329    pub last_pgno: usize,
330    /// ID of the last committed transaction
331    pub last_txnid: usize,
332    /// max reader slots in the environment
333    pub maxreaders: u32,
334    /// max reader slots used in the environment
335    pub numreaders: u32,
336}
337
338impl Environment {
339    /// Wrap a raw LMDB environment handle in an `Environment`.
340    ///
341    /// The `Environment` assumes ownership of the `MDB_env`. If you do not
342    /// want this, see [`borrow_raw()`](#method.borrow_raw) instead.
343    ///
344    /// ## Unsafety
345    ///
346    /// `env` must point to a valid `MDB_env`.
347    ///
348    /// It is the caller's responsibility to ensure that nothing destroys the
349    /// `MDB_env` before [`into_raw()`](#method.into_raw) is called, and that
350    /// nothing attempts to use the `MDB_env` after `Environment` is dropped
351    /// normally.
352    ///
353    /// It is safe for multiple `Environment`s bound to the same `MDB_env` to
354    /// coexist (though the others would need to be created by `borrow_raw`).
355    /// However, care must be taken when using databases since by default the
356    /// `Environment` will assume ownership of those as well.
357    pub unsafe fn from_raw(env: *mut ffi::MDB_env) -> Self {
358        Environment {
359            env: EnvHandle(env, true),
360            open_dbis: Mutex::new(HashSet::new()),
361        }
362    }
363
364    /// Wrap a raw LMDB environment handle in an `Environment` without taking
365    /// ownership.
366    ///
367    /// The `Environment` does not assume ownership of the `MDB_env`, and will
368    /// not destroy it when it is dropped. See [`from_raw()`](#method.from_raw)
369    /// if taking ownership is desired.
370    ///
371    /// Note that this does not affect assumed ownership of `MDB_dbi` handles;
372    /// databases opened by `Database::open` are still presumed owned.
373    ///
374    /// ## Unsafety
375    ///
376    /// `env` must point to a valid `MDB_env`.
377    ///
378    /// It is safe for multiple `Environment`s bound to the same `MDB_env` to
379    /// coexist (though the others would need to be created by `borrow_raw`).
380    /// However, care must be taken when using databases since by default the
381    /// `Environment` will assume ownership of those as well.
382    pub unsafe fn borrow_raw(env: *mut ffi::MDB_env) -> Self {
383        Environment {
384            env: EnvHandle(env, false),
385            open_dbis: Mutex::new(HashSet::new()),
386        }
387    }
388
389    /// Return the underlying `MDB_env` handle.
390    ///
391    /// ## Safety
392    ///
393    /// While this call is in and of itself safe, the caller must ensure that
394    /// operations against the backing store do not violate Rust aliasing
395    /// rules, and must not take any action that would cause the `MDB_env` to
396    /// be destroyed prematurely, or to use it after this `Environment` is
397    /// destroyed.
398    pub fn as_raw(&self) -> *mut ffi::MDB_env {
399        self.env.0
400    }
401
402    /// Consume this `Environment` and return the underlying handle.
403    ///
404    /// After this call, it is the caller's responsibility to ensure that the
405    /// `MDB_env` is eventually destroyed, if it was actually owned by this
406    /// `Environment` (compare [`from_raw`](#method.from_raw) and
407    /// [`borrow_raw`](#method.borrow_raw)).
408    ///
409    /// ## Safety
410    ///
411    /// While this call is in and of itself safe, the caller must ensure that
412    /// operations against the backing store do not violate Rust aliasing
413    /// rules.
414    pub fn into_raw(self) -> *mut ffi::MDB_env {
415        let ret = self.env.0;
416        mem::forget(self.env);
417        ret
418    }
419
420    /// Copy an LMDB environment to the specified path, with options.
421    ///
422    /// This function may be used to make a backup of an existing environment.
423    /// No lockfile is created, since it gets recreated at need.
424    ///
425    /// ## Note
426    ///
427    /// This call can trigger significant file size growth if run in parallel
428    /// with write transactions, because it employs a read-only transaction.
429    /// See long-lived transactions under Caveats.
430    ///
431    /// ## Example
432    ///
433    /// ```
434    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
435    /// # fn main() {
436    /// # let env = create_env();
437    /// let out = tempdir::TempDir::new_in(".", "lmdbcopy").unwrap();
438    /// env.copy(out.path().to_str().unwrap(),
439    ///          lmdb::copy::COMPACT).unwrap();
440    /// // We could now open up an independent environment in `lmdbcopyXXXX`
441    /// // or upload it somewhere, eg, while `env` could continue being
442    /// // modified concurrently.
443    /// # }
444    /// ```
445    pub fn copy(&self, path: &str, flags: copy::Flags) -> Result<()> {
446        let path_cstr = try!(CString::new(path));
447        unsafe {
448            lmdb_call!(ffi2::mdb_env_copy2(
449                self.env.0, path_cstr.as_ptr(), flags.bits()));
450        }
451        Ok(())
452    }
453
454    /// Copy an LMDB environment to the specified file descriptor, with options.
455    ///
456    /// This function may be used to make a backup of an existing environment.
457    /// No lockfile is created, since it gets recreated at need. See
458    /// `copy()` for further details.
459    ///
460    /// ## Note
461    ///
462    /// This call can trigger significant file size growth if run in parallel
463    /// with write transactions, because it employs a read-only transaction.
464    /// See long-lived transactions under Caveats.
465    pub fn copyfd(&self, fd: Fd, flags: copy::Flags) -> Result<()> {
466        unsafe {
467            lmdb_call!(ffi2::mdb_env_copyfd2(self.env.0, fd, flags.bits()));
468        }
469        Ok(())
470    }
471
472    /// Return statistics about the LMDB environment.
473    pub fn stat(&self) -> Result<Stat> {
474        let raw = unsafe {
475            let mut raw = mem::zeroed::<ffi::MDB_stat>();
476            lmdb_call!(ffi::mdb_env_stat(self.env.0, &mut raw));
477            raw
478        };
479        Ok(raw.into())
480    }
481
482    /// Return information about the LMDB environment.
483    pub fn info(&self) -> Result<EnvInfo> {
484        let raw = unsafe {
485            let mut raw = mem::zeroed::<ffi::MDB_envinfo>();
486            lmdb_call!(ffi::mdb_env_info(self.env.0, &mut raw));
487            raw
488        };
489        Ok(EnvInfo {
490            mapaddr: raw.me_mapaddr as *const c_void,
491            mapsize: raw.me_mapsize as usize,
492            last_pgno: raw.me_last_pgno as usize,
493            last_txnid: raw.me_last_txnid as usize,
494            maxreaders: raw.me_maxreaders as u32,
495            numreaders: raw.me_numreaders as u32,
496        })
497    }
498
499    /// Flush the data buffers to disk.
500    ///
501    /// Data is always written to disk when transactions are committed, but the
502    /// operating system may keep it buffered. LMDB always flushes the OS
503    /// buffers upon commit as well, unless the environment was opened with
504    /// `NOSYNC` or in part `NOMETASYNC`. This call is not valid if the
505    /// environment was opened with `RDONLY`.
506    ///
507    /// If `force` is true, force a synchronous flush. Otherwise if the
508    /// environment has the `NOSYNC` flag set the flushes will be omitted, and
509    /// with `MAPASYNC` they will be asynchronous.
510    pub fn sync(&self, force: bool) -> Result<()> {
511        unsafe {
512            lmdb_call!(ffi::mdb_env_sync(self.env.0, force as c_int));
513        }
514        Ok(())
515    }
516
517    /// Set environment flags.
518    ///
519    /// This may be used to set some flags in addition to those from
520    /// `EnvBuilder::open()`, or to unset these flags. If several threads
521    /// change the flags at the same time, the result is undefined.
522    ///
523    /// `flags` specifies the flags to edit, not the new status of all flags.
524    /// If `onoff` is true, all flags in `flags` are set; otherwise, all flags
525    /// in `flags` are cleared.
526    ///
527    /// ## Unsafety
528    ///
529    /// The caller must ensure that multiple threads do not call this
530    /// concurrently with itself or with `get_flags()`. This could not be
531    /// accomplished by using `&mut self`, since any open databases necessarily
532    /// have the environment borrowed already.
533    ///
534    /// ## Example
535    /// ```
536    /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs"));
537    /// # fn main() {
538    /// # let env = create_env();
539    /// unsafe {
540    ///   // Enable the NOMETASYNC and MAPASYNC flags
541    ///   env.set_flags(lmdb::open::NOMETASYNC | lmdb::open::MAPASYNC, true)
542    ///     .unwrap();
543    ///   assert!(env.flags().unwrap().contains(
544    ///     lmdb::open::NOMETASYNC | lmdb::open::MAPASYNC));
545    ///   // Turn MAPASYNC back off, leaving NOMETASYNC set
546    ///   env.set_flags(lmdb::open::MAPASYNC, false).unwrap();
547    ///   assert!(env.flags().unwrap().contains(lmdb::open::NOMETASYNC));
548    ///   assert!(!env.flags().unwrap().contains(lmdb::open::MAPASYNC));
549    /// }
550    /// # }
551    /// ```
552    pub unsafe fn set_flags(&self, flags: open::Flags,
553                            onoff: bool) -> Result<()> {
554        lmdb_call!(ffi::mdb_env_set_flags(
555            self.env.0, flags.bits(), onoff as c_int));
556        Ok(())
557    }
558
559    /// Get environment flags.
560    pub fn flags(&self) -> Result<open::Flags> {
561        let mut raw: c_uint = 0;
562        unsafe {
563            lmdb_call!(ffi::mdb_env_get_flags(self.env.0, &mut raw));
564        }
565        Ok(open::Flags::from_bits_truncate(raw))
566    }
567
568    /// Return the path that was used in `EnvBuilder::open()`.
569    ///
570    /// ## Panics
571    ///
572    /// Panics if LMDB returns success but sets the path to a NULL pointer.
573    pub fn path(&self) -> Result<&CStr> {
574        let mut raw: *mut c_char = ptr::null_mut();
575        unsafe {
576            lmdb_call!(ffi::mdb_env_get_path(self.env.0, &mut raw));
577            if raw.is_null() {
578                panic!("mdb_env_get_path() returned NULL pointer");
579            }
580            Ok(CStr::from_ptr(raw))
581        }
582    }
583
584    /// Return the filedescriptor for the given environment.
585    ///
586    /// ## Unsafety
587    ///
588    /// The caller must ensure that the file descriptor is not used to subvert
589    /// normal LMDB functionality, such as by writing to it or closing it.
590    pub unsafe fn fd(&self) -> Result<Fd> {
591        let mut raw: Fd = 0;
592        lmdb_call!(ffi::mdb_env_get_fd(self.env.0, &mut raw));
593        Ok(raw)
594    }
595
596    /// Set the size of the memory map to use for this environment.
597    ///
598    /// The size should be a multiple of the OS page size. The default is
599    /// 10485760 bytes. The size of the memory map is also the maximum size of
600    /// the database. The value should be chosen as large as possible, to
601    /// accommodate future growth of the database.
602    ///
603    /// The new size takes effect immediately for the current process but will
604    /// not be persisted to any others until a write transaction has been
605    /// committed by the current process. Also, only mapsize increases are
606    /// persisted into the environment.
607    ///
608    /// If the mapsize is increased by another process, and data has grown
609    /// beyond the range of the current mapsize, starting a transaction will
610    /// return `error::MAP_RESIZED`. This function may be called with a size of
611    /// zero to adopt the new size.
612    ///
613    /// ## Unsafety
614    ///
615    /// This may only be called if no transactions are active in the current
616    /// process. Note that the library does not check for this condition, the
617    /// caller must ensure it explicitly.
618    pub unsafe fn set_mapsize(&self, size: usize) -> Result<()> {
619        lmdb_call!(ffi::mdb_env_set_mapsize(self.env.0, size));
620        Ok(())
621    }
622
623    /// Get the maximum number of threads/reader slots for the environment.
624    pub fn maxreaders(&self) -> Result<u32> {
625        let mut raw: c_uint = 0;
626        unsafe {
627            lmdb_call!(ffi::mdb_env_get_maxreaders(self.env.0, &mut raw));
628        }
629        Ok(raw as u32)
630    }
631
632    /// Get the maximum size of keys and `DUPSORT` data we can write.
633    ///
634    /// Depends on the compile-time constant `MDB_MAXKEYSIZE` in LMDB.
635    /// Default 511.
636    pub fn maxkeysize(&self) -> u32 {
637        unsafe {
638            ffi::mdb_env_get_maxkeysize(self.env.0) as u32
639        }
640    }
641
642    /// Check for stale entries in the reader lock table.
643    ///
644    /// Returns the number of stale slots that were cleared.
645    pub fn reader_check(&self) -> Result<i32> {
646        let mut raw: c_int = 0;
647        unsafe {
648            lmdb_call!(ffi::mdb_reader_check(self.env.0, &mut raw));
649        }
650        Ok(raw as i32)
651    }
652}
653
654// Internal API
655pub fn dbi_close(this: &Environment, dbi: ffi::MDB_dbi) {
656    // Hold the lock through the end of the function to also guard the
657    // LMDB's unsynchronised DBI table.
658    let mut locked_dbis = this.open_dbis.lock()
659        .expect("open_dbis lock poisoned");
660    assert!(locked_dbis.remove(&dbi),
661            "closed dbi that wasn't open");
662
663    unsafe {
664        ffi::mdb_dbi_close(this.env.0, dbi);
665    }
666}
667
668// Internal API
669pub fn dbi_delete(this: &Environment, dbi: ffi::MDB_dbi) -> Result<()> {
670    // Hold the lock across the call to `mdb_drop()` to also guard its
671    // unsynchronised DBI table.
672    let mut locked_dbis = this.open_dbis.lock()
673        .expect("open_dbis lock poisoned");
674    unsafe {
675        let mut raw_txn: *mut ffi::MDB_txn = ptr::null_mut();
676        lmdb_call!(ffi::mdb_txn_begin(
677            this.env.0, ptr::null_mut(), 0, &mut raw_txn));
678        let mut txn = TxHandle(raw_txn);
679        lmdb_call!(ffi::mdb_drop(raw_txn, dbi, 1 /* delete */));
680        try!(txn.commit());
681    }
682    assert!(locked_dbis.remove(&dbi),
683            "closed dbi that wasn't open");
684    Ok(())
685}
686
687// Internal API
688pub fn env_ptr(this: &Environment) -> *mut ffi::MDB_env {
689    this.env.0
690}
691
692// Internal API
693pub fn env_open_dbis(this: &Environment) -> &Mutex<HashSet<ffi::MDB_dbi>> {
694    &this.open_dbis
695}
696
697#[cfg(test)]
698mod test {
699    use test_helpers::*;
700    use env::*;
701    use tx::*;
702
703    #[test]
704    fn borrow_raw_doesnt_take_ownership() {
705        let outer_env = create_env();
706        {
707            let inner_env = unsafe {
708                Environment::borrow_raw(outer_env.as_raw())
709            };
710            let db = defdb(&inner_env);
711            let tx = WriteTransaction::new(&inner_env).unwrap();
712            tx.access().put(&db, "foo", "bar", put::Flags::empty()).unwrap();
713            tx.commit().unwrap();
714        }
715
716        let db = defdb(&outer_env);
717        let tx = ReadTransaction::new(&outer_env).unwrap();
718        assert_eq!("bar", tx.access().get::<str,str>(&db, "foo").unwrap());
719    }
720}