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}