1#![allow(non_upper_case_globals)]
42
43use libc::{c_int, c_uint, size_t, c_void};
44use std;
45use std::borrow::ToOwned;
46use std::cell::{UnsafeCell};
47use std::cmp::{Ordering};
48use std::collections::HashMap;
49use std::error::Error;
50use std::ffi::{CString};
51use std::path::Path;
52use std::mem;
53use std::ptr;
54use std::result::Result;
55use std::sync::{Arc, Mutex};
56
57use bitflags::bitflags;
58
59use ffi::{self, MDB_val};
60pub use MdbError::{NotFound, KeyExists, Other, StateError, Corrupted, Panic};
61pub use MdbError::{InvalidPath, TxnFull, CursorFull, PageFull, CacheError};
62use crate::traits::{ToMdbValue, FromMdbValue};
63use crate::utils::{error_msg};
64
65
66macro_rules! lift_mdb {
67 ($e:expr) => (lift_mdb!($e, ()));
68 ($e:expr, $r:expr) => (
69 {
70 let t = $e;
71 match t {
72 ffi::MDB_SUCCESS => Ok($r),
73 _ => return Err(MdbError::new_with_code(t))
74 }
75 })
76}
77
78macro_rules! try_mdb {
79 ($e:expr) => (
80 {
81 let t = $e;
82 match t {
83 ffi::MDB_SUCCESS => (),
84 _ => return Err(MdbError::new_with_code(t))
85 }
86 })
87}
88
89macro_rules! assert_state_eq {
90 ($log:ident, $cur:expr, $exp:expr) =>
91 ({
92 let c = $cur;
93 let e = $exp;
94 if c == e {
95
96 } else {
97 let msg = format!("{} requires {:?}, is in {:?}", stringify!($log), c, e);
98 return Err(StateError(msg))
99 }})
100}
101
102
103
104#[derive(Debug)]
106pub enum MdbError {
107 NotFound,
108 KeyExists,
109 TxnFull,
110 CursorFull,
111 PageFull,
112 Corrupted,
113 Panic,
114 InvalidPath,
115 StateError(String),
116 CacheError,
117 Other(c_int, String)
118}
119
120
121impl MdbError {
122 pub fn new_with_code(code: c_int) -> MdbError {
123 match code {
124 ffi::MDB_NOTFOUND => NotFound,
125 ffi::MDB_KEYEXIST => KeyExists,
126 ffi::MDB_TXN_FULL => TxnFull,
127 ffi::MDB_CURSOR_FULL => CursorFull,
128 ffi::MDB_PAGE_FULL => PageFull,
129 ffi::MDB_CORRUPTED => Corrupted,
130 ffi::MDB_PANIC => Panic,
131 _ => Other(code, error_msg(code))
132 }
133 }
134}
135
136
137impl std::fmt::Display for MdbError {
138 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
139 match self {
140 &NotFound => write!(fmt, "not found"),
141 &KeyExists => write!(fmt, "key exists"),
142 &TxnFull => write!(fmt, "txn full"),
143 &CursorFull => write!(fmt, "cursor full"),
144 &PageFull => write!(fmt, "page full"),
145 &Corrupted => write!(fmt, "corrupted"),
146 &Panic => write!(fmt, "panic"),
147 &InvalidPath => write!(fmt, "invalid path for database"),
148 &CacheError => write!(fmt, "db cache error"),
149 StateError(msg) => write!(fmt, "{}", msg),
150 &Other(code, ref msg) => write!(fmt, "{}: {}", code, msg)
151 }
152 }
153}
154
155impl Error for MdbError {
156 fn description(&self) -> &'static str {
157 match *self {
158 NotFound => "not found",
159 KeyExists => "key exists",
160 TxnFull => "txn full",
161 CursorFull => "cursor full",
162 PageFull => "page full",
163 Corrupted => "corrupted",
164 Panic => "panic",
165 InvalidPath => "invalid path for database",
166 StateError(_) => "state error",
167 CacheError => "db cache error",
168 Other(_, _) => "other error",
169 }
170 }
171}
172
173
174pub type MdbResult<T> = Result<T, MdbError>;
175
176bitflags! {
177 #[doc = "A set of environment flags which could be changed after opening"]
178 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
179 pub struct EnvFlags: c_uint {
180
181 #[doc="Don't flush system buffers to disk when committing a
182 transaction. This optimization means a system crash can
183 corrupt the database or lose the last transactions if buffers
184 are not yet flushed to disk. The risk is governed by how
185 often the system flushes dirty buffers to disk and how often
186 mdb_env_sync() is called. However, if the filesystem
187 preserves write order and the MDB_WRITEMAP flag is not used,
188 transactions exhibit ACI (atomicity, consistency, isolation)
189 properties and only lose D (durability). I.e. database
190 integrity is maintained, but a system crash may undo the
191 final transactions. Note that (MDB_NOSYNC | MDB_WRITEMAP)
192 leaves the system with no hint for when to write transactions
193 to disk, unless mdb_env_sync() is called. (MDB_MAPASYNC |
194 MDB_WRITEMAP) may be preferable. This flag may be changed at
195 any time using mdb_env_set_flags()."]
196 const EnvNoSync = ffi::MDB_NOSYNC;
197
198 #[doc="Flush system buffers to disk only once per transaction,
199 omit the metadata flush. Defer that until the system flushes
200 files to disk, or next non-MDB_RDONLY commit or
201 mdb_env_sync(). This optimization maintains database
202 integrity, but a system crash may undo the last committed
203 transaction. I.e. it preserves the ACI (atomicity,
204 consistency, isolation) but not D (durability) database
205 property. This flag may be changed at any time using
206 mdb_env_set_flags()."]
207 const EnvNoMetaSync = ffi::MDB_NOMETASYNC;
208
209 #[doc="When using MDB_WRITEMAP, use asynchronous flushes to
210 disk. As with MDB_NOSYNC, a system crash can then corrupt the
211 database or lose the last transactions. Calling
212 mdb_env_sync() ensures on-disk database integrity until next
213 commit. This flag may be changed at any time using
214 mdb_env_set_flags()."]
215 const EnvMapAsync = ffi::MDB_MAPASYNC;
216
217 #[doc="Don't initialize malloc'd memory before writing to
218 unused spaces in the data file. By default, memory for pages
219 written to the data file is obtained using malloc. While
220 these pages may be reused in subsequent transactions, freshly
221 malloc'd pages will be initialized to zeroes before use. This
222 avoids persisting leftover data from other code (that used
223 the heap and subsequently freed the memory) into the data
224 file. Note that many other system libraries may allocate and
225 free memory from the heap for arbitrary uses. E.g., stdio may
226 use the heap for file I/O buffers. This initialization step
227 has a modest performance cost so some applications may want
228 to disable it using this flag. This option can be a problem
229 for applications which handle sensitive data like passwords,
230 and it makes memory checkers like Valgrind noisy. This flag
231 is not needed with MDB_WRITEMAP, which writes directly to the
232 mmap instead of using malloc for pages. The initialization is
233 also skipped if MDB_RESERVE is used; the caller is expected
234 to overwrite all of the memory that was reserved in that
235 case. This flag may be changed at any time using
236 mdb_env_set_flags()."]
237 const EnvNoMemInit = ffi::MDB_NOMEMINIT;
238 }
239}
240
241bitflags! {
242 #[doc = "A set of all environment flags"]
243 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
244 pub struct EnvCreateFlags: c_uint {
245 #[doc="Use a fixed address for the mmap region. This flag must be"]
246 #[doc=" specified when creating the environment, and is stored persistently"]
247 #[doc=" in the environment. If successful, the memory map will always reside"]
248 #[doc=" at the same virtual address and pointers used to reference data items"]
249 #[doc=" in the database will be constant across multiple invocations. This "]
250 #[doc="option may not always work, depending on how the operating system has"]
251 #[doc=" allocated memory to shared libraries and other uses. The feature is highly experimental."]
252 const EnvCreateFixedMap = ffi::MDB_FIXEDMAP;
253 #[doc="By default, LMDB creates its environment in a directory whose"]
254 #[doc=" pathname is given in path, and creates its data and lock files"]
255 #[doc=" under that directory. With this option, path is used as-is"]
256 #[doc=" for the database main data file. The database lock file is"]
257 #[doc=" the path with \"-lock\" appended."]
258 const EnvCreateNoSubDir = ffi::MDB_NOSUBDIR;
259 #[doc="Don't flush system buffers to disk when committing a"]
260 #[doc=" transaction. This optimization means a system crash can corrupt"]
261 #[doc=" the database or lose the last transactions if buffers are not"]
262 #[doc=" yet flushed to disk. The risk is governed by how often the"]
263 #[doc=" system flushes dirty buffers to disk and how often"]
264 #[doc=" mdb_env_sync() is called. However, if the filesystem preserves"]
265 #[doc=" write order and the MDB_WRITEMAP flag is not used, transactions"]
266 #[doc=" exhibit ACI (atomicity, consistency, isolation) properties and"]
267 #[doc=" only lose D (durability). I.e. database integrity is"]
268 #[doc=" maintained, but a system crash may undo the final"]
269 #[doc=" transactions. Note that (MDB_NOSYNC | MDB_WRITEMAP) leaves"]
270 #[doc=" the system with no hint for when to write transactions to"]
271 #[doc=" disk, unless mdb_env_sync() is called."]
272 #[doc=" (MDB_MAPASYNC | MDB_WRITEMAP) may be preferable. This flag"]
273 #[doc=" may be changed at any time using mdb_env_set_flags()."]
274 const EnvCreateNoSync = ffi::MDB_NOSYNC;
275 #[doc="Open the environment in read-only mode. No write operations"]
276 #[doc=" will be allowed. LMDB will still modify the lock file - except"]
277 #[doc=" on read-only filesystems, where LMDB does not use locks."]
278 const EnvCreateReadOnly = ffi::MDB_RDONLY;
279 #[doc="Flush system buffers to disk only once per transaction,"]
280 #[doc=" omit the metadata flush. Defer that until the system flushes"]
281 #[doc=" files to disk, or next non-MDB_RDONLY commit or mdb_env_sync()."]
282 #[doc=" This optimization maintains database integrity, but a system"]
283 #[doc=" crash may undo the last committed transaction. I.e. it"]
284 #[doc=" preserves the ACI (atomicity, consistency, isolation) but"]
285 #[doc=" not D (durability) database property. This flag may be changed"]
286 #[doc=" at any time using mdb_env_set_flags()."]
287 const EnvCreateNoMetaSync = ffi::MDB_NOMETASYNC;
288 #[doc="Use a writeable memory map unless MDB_RDONLY is set. This is"]
289 #[doc="faster and uses fewer mallocs, but loses protection from"]
290 #[doc="application bugs like wild pointer writes and other bad updates"]
291 #[doc="into the database. Incompatible with nested"]
292 #[doc="transactions. Processes with and without MDB_WRITEMAP on the"]
293 #[doc="same environment do not cooperate well."]
294 const EnvCreateWriteMap = ffi::MDB_WRITEMAP;
295 #[doc="When using MDB_WRITEMAP, use asynchronous flushes to disk. As"]
296 #[doc="with MDB_NOSYNC, a system crash can then corrupt the database or"]
297 #[doc="lose the last transactions. Calling mdb_env_sync() ensures"]
298 #[doc="on-disk database integrity until next commit. This flag may be"]
299 #[doc="changed at any time using mdb_env_set_flags()."]
300 const EnvCreateMapAsync = ffi::MDB_MAPASYNC;
301 #[doc="Don't use Thread-Local Storage. Tie reader locktable slots to"]
302 #[doc="ffi::MDB_txn objects instead of to threads. I.e. mdb_txn_reset()"]
303 #[doc="keeps the slot reseved for the ffi::MDB_txn object. A thread may"]
304 #[doc="use parallel read-only transactions. A read-only transaction may"]
305 #[doc="span threads if the user synchronizes its use. Applications that"]
306 #[doc="multiplex many user threads over individual OS threads need this"]
307 #[doc="option. Such an application must also serialize the write"]
308 #[doc="transactions in an OS thread, since LMDB's write locking is"]
309 #[doc="unaware of the user threads."]
310 const EnvCreateNoTls = ffi::MDB_NOTLS;
311 #[doc="Don't do any locking. If concurrent access is anticipated, the"]
312 #[doc="caller must manage all concurrency itself. For proper operation"]
313 #[doc="the caller must enforce single-writer semantics, and must ensure"]
314 #[doc="that no readers are using old transactions while a writer is"]
315 #[doc="active. The simplest approach is to use an exclusive lock so"]
316 #[doc="that no readers may be active at all when a writer begins. "]
317 const EnvCreateNoLock = ffi::MDB_NOLOCK;
318 #[doc="Turn off readahead. Most operating systems perform readahead on"]
319 #[doc="read requests by default. This option turns it off if the OS"]
320 #[doc="supports it. Turning it off may help random read performance"]
321 #[doc="when the DB is larger than RAM and system RAM is full. The"]
322 #[doc="option is not implemented on Windows."]
323 const EnvCreateNoReadAhead = ffi::MDB_NORDAHEAD;
324 #[doc="Don't initialize malloc'd memory before writing to unused spaces"]
325 #[doc="in the data file. By default, memory for pages written to the"]
326 #[doc="data file is obtained using malloc. While these pages may be"]
327 #[doc="reused in subsequent transactions, freshly malloc'd pages will"]
328 #[doc="be initialized to zeroes before use. This avoids persisting"]
329 #[doc="leftover data from other code (that used the heap and"]
330 #[doc="subsequently freed the memory) into the data file. Note that"]
331 #[doc="many other system libraries may allocate and free memory from"]
332 #[doc="the heap for arbitrary uses. E.g., stdio may use the heap for"]
333 #[doc="file I/O buffers. This initialization step has a modest"]
334 #[doc="performance cost so some applications may want to disable it"]
335 #[doc="using this flag. This option can be a problem for applications"]
336 #[doc="which handle sensitive data like passwords, and it makes memory"]
337 #[doc="checkers like Valgrind noisy. This flag is not needed with"]
338 #[doc="MDB_WRITEMAP, which writes directly to the mmap instead of using"]
339 #[doc="malloc for pages. The initialization is also skipped if"]
340 #[doc="MDB_RESERVE is used; the caller is expected to overwrite all of"]
341 #[doc="the memory that was reserved in that case. This flag may be"]
342 #[doc="changed at any time using mdb_env_set_flags()."]
343 const EnvCreateNoMemInit = ffi::MDB_NOMEMINIT;
344 }
345}
346
347bitflags! {
348 #[doc = "A set of database flags"]
349 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
350 pub struct DbFlags: c_uint {
351 #[doc="Keys are strings to be compared in reverse order, from the"]
352 #[doc=" end of the strings to the beginning. By default, Keys are"]
353 #[doc=" treated as strings and compared from beginning to end."]
354 const DbReverseKey = ffi::MDB_REVERSEKEY;
355 #[doc="Duplicate keys may be used in the database. (Or, from another"]
356 #[doc="perspective, keys may have multiple data items, stored in sorted"]
357 #[doc="order.) By default keys must be unique and may have only a"]
358 #[doc="single data item."]
359 const DbAllowDups = ffi::MDB_DUPSORT;
360 #[doc="Keys are binary integers in native byte order. Setting this"]
361 #[doc="option requires all keys to be the same size, typically"]
362 #[doc="sizeof(int) or sizeof(size_t)."]
363 const DbIntKey = ffi::MDB_INTEGERKEY;
364 #[doc="This flag may only be used in combination with"]
365 #[doc="ffi::MDB_DUPSORT. This option tells the library that the data"]
366 #[doc="items for this database are all the same size, which allows"]
367 #[doc="further optimizations in storage and retrieval. When all data"]
368 #[doc="items are the same size, the ffi::MDB_GET_MULTIPLE and"]
369 #[doc="ffi::MDB_NEXT_MULTIPLE cursor operations may be used to retrieve"]
370 #[doc="multiple items at once."]
371 const DbDupFixed = ffi::MDB_DUPFIXED;
372 #[doc="This option specifies that duplicate data items are also"]
373 #[doc="integers, and should be sorted as such."]
374 const DbAllowIntDups = ffi::MDB_INTEGERDUP;
375 #[doc="This option specifies that duplicate data items should be"]
376 #[doc=" compared as strings in reverse order."]
377 const DbReversedDups = ffi::MDB_REVERSEDUP;
378 #[doc="Create the named database if it doesn't exist. This option"]
379 #[doc=" is not allowed in a read-only transaction or a read-only"]
380 #[doc=" environment."]
381 const DbCreate = ffi::MDB_CREATE;
382 }
383}
384
385#[derive(Debug)]
387pub struct Database<'a> {
388 handle: ffi::MDB_dbi,
389 txn: &'a NativeTransaction<'a>,
390}
391
392impl<'a> Database<'a> {
396 fn new_with_handle(handle: ffi::MDB_dbi, txn: &'a NativeTransaction<'a>) -> Database<'a> {
397 Database { handle, txn }
398 }
399
400 pub fn stat(&'a self) -> MdbResult<ffi::MDB_stat> {
402 self.txn.stat(self.handle)
403 }
404
405 pub fn get<V: FromMdbValue + 'a>(&'a self, key: &dyn ToMdbValue) -> MdbResult<V> {
407 self.txn.get(self.handle, key)
408 }
409
410 pub fn set(&self, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
412 self.txn.set(self.handle, key, value)
413 }
414
415 pub fn append<K: ToMdbValue, V: ToMdbValue>(&self, key: &K, value: &V) -> MdbResult<()> {
419 self.txn.append(self.handle, key, value)
420 }
421
422 pub fn append_duplicate<K: ToMdbValue, V: ToMdbValue>(&self, key: &K, value: &V) -> MdbResult<()> {
426 self.txn.append_duplicate(self.handle, key, value)
427 }
428
429 pub fn insert(&self, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
431 self.txn.insert(self.handle, key, value)
432 }
433
434 pub fn del(&self, key: &dyn ToMdbValue) -> MdbResult<()> {
436 self.txn.del(self.handle, key)
437 }
438
439 pub fn del_item(&self, key: &dyn ToMdbValue, data: &dyn ToMdbValue) -> MdbResult<()> {
441 self.txn.del_item(self.handle, key, data)
442 }
443
444 pub fn new_cursor(&'a self) -> MdbResult<Cursor<'a>> {
446 self.txn.new_cursor(self.handle)
447 }
448
449 pub fn del_db(self) -> MdbResult<()> {
451 self.txn.del_db(self)
452 }
453
454 pub fn clear(&self) -> MdbResult<()> {
456 self.txn.clear_db(self.handle)
457 }
458
459 pub fn iter(&'a self) -> MdbResult<CursorIterator<'a, CursorIter>> {
461 self.txn.new_cursor(self.handle).map(|c| CursorIterator::wrap(c, CursorIter))
462 }
463
464 pub fn keyrange_from<'c, K: ToMdbValue + 'c>(&'c self, start_key: &'c K) -> MdbResult<CursorIterator<'c, CursorFromKeyIter<'c>>> {
466 let cursor = self.txn.new_cursor(self.handle)?;
467 let key_range = CursorFromKeyIter::new(start_key);
468 let wrap = CursorIterator::wrap(cursor, key_range);
469 Ok(wrap)
470 }
471
472 pub fn keyrange_to<'c, K: ToMdbValue + 'c>(&'c self, end_key: &'c K) -> MdbResult<CursorIterator<'c, CursorToKeyIter<'c>>> {
474 let cursor = self.txn.new_cursor(self.handle)?;
475 let key_range = CursorToKeyIter::new(end_key);
476 let wrap = CursorIterator::wrap(cursor, key_range);
477 Ok(wrap)
478 }
479
480 pub fn keyrange_from_to<'c, K: ToMdbValue + 'c>(&'c self, start_key: &'c K, end_key: &'c K)
483 -> MdbResult<CursorIterator<'c, CursorKeyRangeIter<'c>>>
484 {
485 let cursor = self.txn.new_cursor(self.handle)?;
486 let key_range = CursorKeyRangeIter::new(start_key, end_key, false);
487 let wrap = CursorIterator::wrap(cursor, key_range);
488 Ok(wrap)
489 }
490
491 pub fn keyrange<'c, K: ToMdbValue + 'c>(&'c self, start_key: &'c K, end_key: &'c K)
496 -> MdbResult<CursorIterator<'c, CursorKeyRangeIter<'c>>>
497 {
498 let cursor = self.txn.new_cursor(self.handle)?;
499 let key_range = CursorKeyRangeIter::new(start_key, end_key, true);
500 let wrap = CursorIterator::wrap(cursor, key_range);
501 Ok(wrap)
502 }
503
504 pub fn item_iter<'c, 'db: 'c, K: ToMdbValue>(&'db self, key: &'c K) -> MdbResult<CursorIterator<'c, CursorItemIter<'c>>> {
506 let cursor = self.txn.new_cursor(self.handle)?;
507 let inner_iter = CursorItemIter::<'c>::new(key);
508 Ok(CursorIterator::<'c>::wrap(cursor, inner_iter))
509 }
510
511 pub fn set_compare(&self, cmp_fn: extern "C" fn(*const MDB_val, *const MDB_val) -> c_int) -> MdbResult<()> {
523 lift_mdb!(unsafe {
524 ffi::mdb_set_compare(self.txn.handle, self.handle, cmp_fn)
525 })
526 }
527
528 pub fn set_dupsort(&self, cmp_fn: extern "C" fn(*const MDB_val, *const MDB_val) -> c_int) -> MdbResult<()> {
541 lift_mdb!(unsafe {
542 ffi::mdb_set_dupsort(self.txn.handle, self.handle, cmp_fn)
543 })
544 }
545}
546
547
548#[derive(Copy, Clone, Debug)]
553pub struct EnvBuilder {
554 flags: EnvCreateFlags,
555 max_readers: Option<usize>,
556 max_dbs: Option<usize>,
557 map_size: Option<u64>,
558 autocreate_dir: bool,
559}
560
561impl Default for EnvBuilder {
562 fn default() -> Self {
563 Self::new()
564 }
565}
566
567impl EnvBuilder {
568 pub fn new() -> EnvBuilder {
569 EnvBuilder {
570 flags: EnvCreateFlags::empty(),
571 max_readers: None,
572 max_dbs: None,
573 map_size: None,
574 autocreate_dir: true,
575 }
576 }
577
578 pub fn flags(mut self, flags: EnvCreateFlags) -> EnvBuilder {
580 self.flags = flags;
581 self
582 }
583
584 pub fn max_readers(mut self, max_readers: usize) -> EnvBuilder {
586 self.max_readers = Some(max_readers);
587 self
588 }
589
590 pub fn max_dbs(mut self, max_dbs: usize) -> EnvBuilder {
592 self.max_dbs = Some(max_dbs);
593 self
594 }
595
596 pub fn map_size(mut self, map_size: u64) -> EnvBuilder {
599 self.map_size = Some(map_size);
600 self
601 }
602
603 pub fn autocreate_dir(mut self, autocreate_dir: bool) -> EnvBuilder {
606 self.autocreate_dir = autocreate_dir;
607 self
608 }
609
610 pub fn open<P: AsRef<Path>>(self, path: P, perms: u32) -> MdbResult<Environment> {
612 let changeable_flags: EnvCreateFlags = EnvCreateFlags::EnvCreateMapAsync | EnvCreateFlags::EnvCreateNoMemInit | EnvCreateFlags::EnvCreateNoSync | EnvCreateFlags::EnvCreateNoMetaSync;
613
614 let env: *mut ffi::MDB_env = ptr::null_mut();
615 unsafe {
616 let p_env: *mut *mut ffi::MDB_env = &env as *const *mut ffi::MDB_env as *mut *mut ffi::MDB_env;
617 try_mdb!(ffi::mdb_env_create(p_env));
618 }
619
620 try_mdb!(unsafe { ffi::mdb_env_set_flags(env, self.flags.bits() & changeable_flags.bits(), 1)});
622
623 if let Some(map_size) = self.map_size {
624 try_mdb!(unsafe { ffi::mdb_env_set_mapsize(env, map_size as size_t)});
625 }
626
627 if let Some(max_readers) = self.max_readers {
628 try_mdb!(unsafe { ffi::mdb_env_set_maxreaders(env, max_readers as u32)});
629 }
630
631 if let Some(max_dbs) = self.max_dbs {
632 try_mdb!(unsafe { ffi::mdb_env_set_maxdbs(env, max_dbs as u32)});
633 }
634
635 if self.autocreate_dir {
636 EnvBuilder::check_path(&path, self.flags)?;
637 }
638
639 let is_readonly = self.flags.contains(EnvCreateFlags::EnvCreateReadOnly);
640
641 let res = unsafe {
642 let path_str = path.as_ref().to_str().ok_or(MdbError::InvalidPath)?;
645 let c_path = CString::new(path_str).map_err(|_| MdbError::InvalidPath)?;
646
647 ffi::mdb_env_open(env, c_path.as_ref().as_ptr(), self.flags.bits(),
648 perms as ffi::mdb_mode_t)
649 };
650
651 let _ = self;
652 match res {
653 ffi::MDB_SUCCESS => {
654 Ok(Environment::from_raw(env, is_readonly))
655 },
656 _ => {
657 unsafe { ffi::mdb_env_close(env); }
658 Err(MdbError::new_with_code(res))
659 }
660 }
661
662 }
663
664 fn check_path<P: AsRef<Path>>(path: P, flags: EnvCreateFlags) -> MdbResult<()> {
665 use std::{fs, io};
666
667 if flags.contains(EnvCreateFlags::EnvCreateNoSubDir) {
668 warn!("checking for path in NoSubDir mode isn't implemented yet");
670 return Ok(());
671 }
672
673 match fs::metadata(&path) {
675 Ok(meta) => {
676 if meta.is_dir() {
677 Ok(())
678 } else {
679 Err(MdbError::InvalidPath)
680 }
681 },
682 Err(e) => {
683 if e.kind() == io::ErrorKind::NotFound {
684 fs::create_dir_all(path.as_ref()).map_err(|e| {
685 error!("failed to auto create dir: {}", e);
686 MdbError::InvalidPath
687 })
688 } else {
689 Err(MdbError::InvalidPath)
690 }
691 }
692 }
693 }
694}
695
696#[derive(Debug)]
697struct EnvHandle(*mut ffi::MDB_env);
698
699unsafe impl Send for EnvHandle {}
700unsafe impl Sync for EnvHandle {}
701
702impl Drop for EnvHandle {
703 fn drop(&mut self) {
704 unsafe {
705 if !self.0.is_null() {
706 ffi::mdb_env_close(self.0);
707 }
708 }
709 }
710}
711
712#[derive(Debug)]
714pub struct Environment {
715 env: Arc<EnvHandle>,
716 db_cache: Arc<Mutex<UnsafeCell<HashMap<String, ffi::MDB_dbi>>>>,
717 is_readonly: bool, }
719
720impl Environment {
721 pub fn builder() -> EnvBuilder {
722 EnvBuilder::new()
723 }
724
725 fn from_raw(env: *mut ffi::MDB_env, is_readonly: bool) -> Environment {
726 Environment {
727 env: Arc::new(EnvHandle(env)),
728 db_cache: Arc::new(Mutex::new(UnsafeCell::new(HashMap::new()))),
729 is_readonly,
730 }
731 }
732
733 pub fn reader_check(&self) -> MdbResult<c_int> {
737 let mut dead: c_int = 0;
738 lift_mdb!(unsafe { ffi::mdb_reader_check(self.env.0, &mut dead as *mut c_int)}, dead)
739 }
740
741 pub fn stat(&self) -> MdbResult<ffi::MDB_stat> {
743 let mut tmp: ffi::MDB_stat = unsafe { std::mem::zeroed() };
744 lift_mdb!(unsafe { ffi::mdb_env_stat(self.env.0, &mut tmp)}, tmp)
745 }
746
747 pub fn info(&self) -> MdbResult<ffi::MDB_envinfo> {
748 let mut tmp: ffi::MDB_envinfo = unsafe { std::mem::zeroed() };
749 lift_mdb!(unsafe { ffi::mdb_env_info(self.env.0, &mut tmp)}, tmp)
750 }
751
752 pub fn sync(&self, force: bool) -> MdbResult<()> {
754 lift_mdb!(unsafe { ffi::mdb_env_sync(self.env.0, if force {1} else {0})})
755 }
756
757 pub fn set_mapsize(&self, map_size: usize) -> MdbResult<()> {
760 lift_mdb!(unsafe { ffi::mdb_env_set_mapsize(self.env.0, map_size as size_t)})
761 }
762
763 pub fn set_flags(&mut self, flags: EnvFlags, turn_on: bool) -> MdbResult<()> {
766 lift_mdb!(unsafe {
767 ffi::mdb_env_set_flags(self.env.0, flags.bits(), if turn_on {1} else {0})
768 })
769 }
770
771 pub fn get_flags(&self) -> MdbResult<EnvFlags> {
774 let tmp = self.get_all_flags()?;
775 Ok(EnvFlags::from_bits_truncate(tmp.bits()))
776 }
777
778 pub fn get_all_flags(&self) -> MdbResult<EnvCreateFlags> {
781 let mut flags: c_uint = 0;
782 lift_mdb!(unsafe {ffi::mdb_env_get_flags(self.env.0, &mut flags)}, EnvCreateFlags::from_bits_truncate(flags))
783 }
784
785 pub fn get_maxreaders(&self) -> MdbResult<c_uint> {
786 let mut max_readers: c_uint = 0;
787 lift_mdb!(unsafe {
788 ffi::mdb_env_get_maxreaders(self.env.0, &mut max_readers)
789 }, max_readers)
790 }
791
792 pub fn get_maxkeysize(&self) -> c_int {
793 unsafe {ffi::mdb_env_get_maxkeysize(self.env.0)}
794 }
795
796 pub fn copy_to_fd(&self, fd: ffi::mdb_filehandle_t) -> MdbResult<()> {
798 lift_mdb!(unsafe { ffi::mdb_env_copyfd(self.env.0, fd) })
799 }
800
801 pub fn get_fd(&self) -> MdbResult<ffi::mdb_filehandle_t> {
803 let mut fd = 0;
804 lift_mdb!({ unsafe { ffi::mdb_env_get_fd(self.env.0, &mut fd) }}, fd)
805 }
806
807 pub fn copy_to_path<P: AsRef<Path>>(&self, path: P) -> MdbResult<()> {
810 let path_str = path.as_ref().to_str().ok_or(MdbError::InvalidPath)?;
813 let c_path = CString::new(path_str).map_err(|_| MdbError::InvalidPath)?;
814
815 unsafe {
816 lift_mdb!(ffi::mdb_env_copy(self.env.0, c_path.as_ref().as_ptr()))
817 }
818 }
819
820 fn create_transaction(&self, parent: Option<NativeTransaction>, flags: c_uint) -> MdbResult<NativeTransaction<'_>> {
821 let mut handle: *mut ffi::MDB_txn = ptr::null_mut();
822 let parent_handle = match parent {
823 Some(t) => t.handle,
824 _ => ptr::null_mut()
825 };
826
827 lift_mdb!(unsafe { ffi::mdb_txn_begin(self.env.0, parent_handle, flags, &mut handle) },
828 NativeTransaction::new_with_handle(handle, flags as usize, self))
829 }
830
831 pub fn new_transaction(&self) -> MdbResult<Transaction<'_>> {
835 if self.is_readonly {
836 return Err(MdbError::StateError("Error: creating read-write transaction in read-only environment".to_owned()))
837 }
838 self.create_transaction(None, 0).map(Transaction::new_with_native)
839 }
840
841 pub fn get_reader(&self) -> MdbResult<ReadonlyTransaction<'_>> {
843 self.create_transaction(None, ffi::MDB_RDONLY).map(ReadonlyTransaction::new_with_native)
844 }
845
846 fn _open_db(&self, db_name: & str, flags: DbFlags, force_creation: bool) -> MdbResult<ffi::MDB_dbi> {
847 debug!("Opening {} (create={}, read_only={})", db_name, force_creation, self.is_readonly);
848 match self.db_cache.lock() {
855 Err(_) => Err(MdbError::CacheError),
856 Ok(guard) => {
857 let cell = &(*guard);
858 let cache = cell.get();
859
860 unsafe {
861 if let Some(db) = (*cache).get(db_name) {
862 debug!("Cached value for {}: {}", db_name, *db);
863 return Ok(*db);
864 }
865 }
866
867 let mut txn = {
868 let txflags = if self.is_readonly { ffi::MDB_RDONLY } else { 0 };
869 self.create_transaction(None, txflags)?
870 };
871 let opt_name = if !db_name.is_empty() {Some(db_name)} else {None};
872 let flags = if force_creation {flags | DbFlags::DbCreate} else {flags - DbFlags::DbCreate};
873
874 let mut db: ffi::MDB_dbi = 0;
875 let db_res = match opt_name {
876 None => unsafe { ffi::mdb_dbi_open(txn.handle, ptr::null(), flags.bits(), &mut db) },
877 Some(db_name) => {
878 let db_name = CString::new(db_name.as_bytes()).unwrap();
879 unsafe {
880 ffi::mdb_dbi_open(txn.handle, db_name.as_ptr(), flags.bits(), &mut db)
881 }
882 }
883 };
884
885 try_mdb!(db_res);
886 txn.commit()?;
887
888 debug!("Caching: {} -> {}", db_name, db);
889 unsafe {
890 (*cache).insert(db_name.to_owned(), db);
891 };
892
893 Ok(db)
894 }
895 }
896 }
897
898 pub fn get_db(& self, db_name: &str, flags: DbFlags) -> MdbResult<DbHandle> {
900 let db = self._open_db(db_name, flags, false)?;
901 Ok(DbHandle {handle: db, flags})
902 }
903
904 pub fn create_db(&self, db_name: &str, flags: DbFlags) -> MdbResult<DbHandle> {
906 let db = self._open_db(db_name, flags, true)?;
907 Ok(DbHandle {handle: db, flags})
908 }
909
910 pub fn get_default_db(&self, flags: DbFlags) -> MdbResult<DbHandle> {
912 self.get_db("", flags)
913 }
914
915 fn drop_db_from_cache(&self, handle: ffi::MDB_dbi) {
916 match self.db_cache.lock() {
917 Err(_) => (),
918 Ok(guard) => {
919 let cell = &(*guard);
920
921 unsafe {
922 let cache = cell.get();
923
924 let mut key = None;
925 for (k, v) in (*cache).iter() {
926 if *v == handle {
927 key = Some(k);
928 break;
929 }
930 }
931
932 if let Some(key) = key {
933 (*cache).remove(key);
934 }
935 }
936 }
937 }
938 }
939}
940
941unsafe impl Sync for Environment {}
942unsafe impl Send for Environment {}
943
944impl Clone for Environment {
945 fn clone(&self) -> Environment {
946 Environment {
947 env: self.env.clone(),
948 db_cache: self.db_cache.clone(),
949 is_readonly: self.is_readonly,
950 }
951 }
952}
953
954#[allow(dead_code)]
955#[derive(Copy, Clone, Debug)]
956pub struct DbHandle {
962 handle: ffi::MDB_dbi,
963 flags: DbFlags
964}
965
966unsafe impl Sync for DbHandle {}
967unsafe impl Send for DbHandle {}
968
969#[derive(Copy, PartialEq, Debug, Eq, Clone)]
970enum TransactionState {
971 Normal, Released, Invalid, }
975
976#[derive(Debug)]
977struct NativeTransaction<'a> {
978 handle: *mut ffi::MDB_txn,
979 env: &'a Environment,
980 flags: usize,
981 state: TransactionState,
982}
983
984impl<'a> NativeTransaction<'a> {
985 fn new_with_handle(h: *mut ffi::MDB_txn, flags: usize, env: &Environment) -> NativeTransaction<'_> {
986 NativeTransaction {
988 handle: h,
989 flags,
990 state: TransactionState::Normal,
991 env,
992 }
993 }
994
995 fn is_readonly(&self) -> bool {
996 (self.flags as u32 & ffi::MDB_RDONLY) == ffi::MDB_RDONLY
997 }
998
999 fn commit(&mut self) -> MdbResult<()> {
1000 assert_state_eq!(txn, self.state, TransactionState::Normal);
1001 debug!("commit txn");
1002 self.state = if self.is_readonly() {
1003 TransactionState::Released
1004 } else {
1005 TransactionState::Invalid
1006 };
1007 try_mdb!(unsafe { ffi::mdb_txn_commit(self.handle) } );
1008 Ok(())
1009 }
1010
1011 fn abort(&mut self) {
1012 if self.state != TransactionState::Normal {
1013 debug!("Can't abort transaction: current state {:?}", self.state)
1014 } else {
1015 debug!("abort txn");
1016 unsafe { ffi::mdb_txn_abort(self.handle); }
1017 self.state = if self.is_readonly() {
1018 TransactionState::Released
1019 } else {
1020 TransactionState::Invalid
1021 };
1022 }
1023 }
1024
1025 fn reset(&mut self) {
1028 if self.state != TransactionState::Normal {
1029 debug!("Can't reset transaction: current state {:?}", self.state);
1030 } else {
1031 unsafe { ffi::mdb_txn_reset(self.handle); }
1032 self.state = TransactionState::Released;
1033 }
1034 }
1035
1036 fn renew(&mut self) -> MdbResult<()> {
1038 assert_state_eq!(txn, self.state, TransactionState::Released);
1039 try_mdb!(unsafe {ffi::mdb_txn_renew(self.handle)});
1040 self.state = TransactionState::Normal;
1041 Ok(())
1042 }
1043
1044 fn new_child(&self, flags: c_uint) -> MdbResult<NativeTransaction<'_>> {
1045 let mut out: *mut ffi::MDB_txn = ptr::null_mut();
1046 try_mdb!(unsafe { ffi::mdb_txn_begin(ffi::mdb_txn_env(self.handle), self.handle, flags, &mut out) });
1047 Ok(NativeTransaction::new_with_handle(out, flags as usize, self.env))
1048 }
1049
1050 fn silent_abort(&mut self) {
1052 if self.state == TransactionState::Normal {
1053 debug!("silent abort");
1054 unsafe {ffi::mdb_txn_abort(self.handle);}
1055 self.state = TransactionState::Invalid;
1056 }
1057 }
1058
1059 fn get_value<V: FromMdbValue + 'a>(&'a self, db: ffi::MDB_dbi, key: &dyn ToMdbValue) -> MdbResult<V> {
1060 let mut key_val = key.to_mdb_value();
1061 unsafe {
1062 let mut data_val: MdbValue = std::mem::zeroed();
1063 try_mdb!(ffi::mdb_get(self.handle, db, &mut key_val.value, &mut data_val.value));
1064 Ok(FromMdbValue::from_mdb_value(&data_val))
1065 }
1066 }
1067
1068 fn get<V: FromMdbValue + 'a>(&'a self, db: ffi::MDB_dbi, key: &dyn ToMdbValue) -> MdbResult<V> {
1069 assert_state_eq!(txn, self.state, TransactionState::Normal);
1070 self.get_value(db, key)
1071 }
1072
1073 fn set_value(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
1074 self.set_value_with_flags(db, key, value, 0)
1075 }
1076
1077 fn set_value_with_flags(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue, flags: c_uint) -> MdbResult<()> {
1078 unsafe {
1079 let mut key_val = key.to_mdb_value();
1080 let mut data_val = value.to_mdb_value();
1081
1082 lift_mdb!(ffi::mdb_put(self.handle, db, &mut key_val.value, &mut data_val.value, flags))
1083 }
1084 }
1085
1086 fn set(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
1091 assert_state_eq!(txn, self.state, TransactionState::Normal);
1092 self.set_value(db, key, value)
1093 }
1094
1095 fn append(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
1096 assert_state_eq!(txn, self.state, TransactionState::Normal);
1097 self.set_value_with_flags(db, key, value, ffi::MDB_APPEND)
1098 }
1099
1100 fn append_duplicate(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
1101 assert_state_eq!(txn, self.state, TransactionState::Normal);
1102 self.set_value_with_flags(db, key, value, ffi::MDB_APPENDDUP)
1103 }
1104
1105 fn insert(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, value: &dyn ToMdbValue) -> MdbResult<()> {
1108 assert_state_eq!(txn, self.state, TransactionState::Normal);
1109 self.set_value_with_flags(db, key, value, ffi::MDB_NOOVERWRITE)
1110 }
1111
1112 fn del_value(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue) -> MdbResult<()> {
1114 unsafe {
1115 let mut key_val = key.to_mdb_value();
1116 lift_mdb!(ffi::mdb_del(self.handle, db, &mut key_val.value, ptr::null_mut()))
1117 }
1118 }
1119
1120 fn del_item(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue, data: &dyn ToMdbValue) -> MdbResult<()> {
1122 assert_state_eq!(txn, self.state, TransactionState::Normal);
1123 unsafe {
1124 let mut key_val = key.to_mdb_value();
1125 let mut data_val = data.to_mdb_value();
1126
1127 lift_mdb!(ffi::mdb_del(self.handle, db, &mut key_val.value, &mut data_val.value))
1128 }
1129 }
1130
1131 fn del(&self, db: ffi::MDB_dbi, key: &dyn ToMdbValue) -> MdbResult<()> {
1133 assert_state_eq!(txn, self.state, TransactionState::Normal);
1134 self.del_value(db, key)
1135 }
1136
1137 fn new_cursor(&'a self, db: ffi::MDB_dbi) -> MdbResult<Cursor<'a>> {
1139 Cursor::new(self, db)
1140 }
1141
1142 fn del_db(&self, db: Database) -> MdbResult<()> {
1144 assert_state_eq!(txn, self.state, TransactionState::Normal);
1145 unsafe {
1146 self.env.drop_db_from_cache(db.handle);
1147 lift_mdb!(ffi::mdb_drop(self.handle, db.handle, 1))
1148 }
1149 }
1150
1151 fn clear_db(&self, db: ffi::MDB_dbi) -> MdbResult<()> {
1153 assert_state_eq!(txn, self.state, TransactionState::Normal);
1154 unsafe {
1155 lift_mdb!(ffi::mdb_drop(self.handle, db, 0))
1156 }
1157 }
1158
1159 fn stat(&self, db: ffi::MDB_dbi) -> MdbResult<ffi::MDB_stat> {
1161 let mut tmp: ffi::MDB_stat = unsafe { std::mem::zeroed() };
1162 lift_mdb!(unsafe { ffi::mdb_stat(self.handle, db, &mut tmp)}, tmp)
1163 }
1164
1165 }
1178
1179impl Drop for NativeTransaction<'_> {
1180 fn drop(&mut self) {
1181 self.silent_abort();
1183 }
1184}
1185
1186#[derive(Debug)]
1187pub struct Transaction<'a> {
1188 inner: NativeTransaction<'a>,
1189}
1190
1191impl<'a> Transaction<'a> {
1192 fn new_with_native(txn: NativeTransaction<'a>) -> Transaction<'a> {
1193 Transaction {
1194 inner: txn
1195 }
1196 }
1197
1198 pub fn new_child(&self) -> MdbResult<Transaction<'_>> {
1199 self.inner.new_child(0).map(Transaction::new_with_native)
1200 }
1201
1202 pub fn new_ro_child(&self) -> MdbResult<ReadonlyTransaction<'_>> {
1203 self.inner.new_child(ffi::MDB_RDONLY).map(ReadonlyTransaction::new_with_native)
1204 }
1205
1206 pub fn commit(self) -> MdbResult<()> {
1208 let mut t = self;
1210 t.inner.commit()
1211 }
1212
1213 pub fn abort(self) {
1215 let mut t = self;
1216 t.inner.abort();
1217 }
1218
1219 pub fn bind(&self, db_handle: &DbHandle) -> Database<'_> {
1220 Database::new_with_handle(db_handle.handle, &self.inner)
1221 }
1222}
1223
1224
1225#[derive(Debug)]
1226pub struct ReadonlyTransaction<'a> {
1227 inner: NativeTransaction<'a>,
1228}
1229
1230
1231impl<'a> ReadonlyTransaction<'a> {
1232 fn new_with_native(txn: NativeTransaction<'a>) -> ReadonlyTransaction<'a> {
1233 ReadonlyTransaction {
1234 inner: txn,
1235 }
1236 }
1237
1238 pub fn new_ro_child(&self) -> MdbResult<ReadonlyTransaction<'_>> {
1239 self.inner.new_child(ffi::MDB_RDONLY).map(ReadonlyTransaction::new_with_native)
1240
1241 }
1242
1243 pub fn abort(&mut self) {
1246 self.inner.abort();
1247 }
1248
1249 pub fn reset(&mut self) {
1252 self.inner.reset();
1253 }
1254
1255 pub fn renew(&mut self) -> MdbResult<()> {
1258 self.inner.renew()
1259 }
1260
1261 pub fn bind(&self, db_handle: &DbHandle) -> Database<'_> {
1262 Database::new_with_handle(db_handle.handle, &self.inner)
1263 }
1264}
1265
1266trait IsLess {
1269 fn is_less(&self, or_equal: bool) -> bool;
1270}
1271
1272impl IsLess for Ordering {
1273 fn is_less(&self, or_equal: bool) -> bool {
1274 matches!((*self, or_equal), (Ordering::Less, _) | (Ordering::Equal, true))
1275 }
1276}
1277
1278impl IsLess for MdbResult<Ordering> {
1279 fn is_less(&self, or_equal: bool) -> bool {
1280 match *self {
1281 Ok(ord) => ord.is_less(or_equal),
1282 Err(_) => false,
1283 }
1284 }
1285}
1286
1287#[derive(Debug)]
1288pub struct Cursor<'txn> {
1289 handle: *mut ffi::MDB_cursor,
1290 data_val: ffi::MDB_val,
1291 key_val: ffi::MDB_val,
1292 txn: &'txn NativeTransaction<'txn>,
1293 db: ffi::MDB_dbi,
1294 valid_key: bool,
1295}
1296
1297
1298impl<'txn> Cursor<'txn> {
1299 fn new(txn: &'txn NativeTransaction, db: ffi::MDB_dbi) -> MdbResult<Cursor<'txn>> {
1300 debug!("Opening cursor in {}", db);
1301 let mut tmp: *mut ffi::MDB_cursor = std::ptr::null_mut();
1302 try_mdb!(unsafe { ffi::mdb_cursor_open(txn.handle, db, &mut tmp) });
1303 Ok(Cursor {
1304 handle: tmp,
1305 data_val: unsafe { std::mem::zeroed() },
1306 key_val: unsafe { std::mem::zeroed() },
1307 txn,
1308 db,
1309 valid_key: false,
1310 })
1311 }
1312
1313 fn navigate(&mut self, op: ffi::MDB_cursor_op) -> MdbResult<()> {
1314 self.valid_key = false;
1315
1316 let res = unsafe {
1317 ffi::mdb_cursor_get(self.handle, &mut self.key_val, &mut self.data_val, op)
1318 };
1319 match res {
1320 ffi::MDB_SUCCESS => {
1321 self.valid_key = op != ffi::MDB_cursor_op::MDB_SET;
1328 Ok(())
1329 },
1330 e => Err(MdbError::new_with_code(e))
1331 }
1332 }
1333
1334 fn move_to<K, V>(&mut self, key: &K, value: Option<&V>, op: ffi::MDB_cursor_op) -> MdbResult<()>
1335 where K: ToMdbValue, V: ToMdbValue {
1336 self.key_val = key.to_mdb_value().value;
1337 self.data_val = match value {
1338 Some(v) => v.to_mdb_value().value,
1339 _ => unsafe {std::mem::zeroed() }
1340 };
1341
1342 self.navigate(op)
1343 }
1344
1345 pub fn to_first(&mut self) -> MdbResult<()> {
1347 self.navigate(ffi::MDB_cursor_op::MDB_FIRST)
1348 }
1349
1350 pub fn to_last(&mut self) -> MdbResult<()> {
1352 self.navigate(ffi::MDB_cursor_op::MDB_LAST)
1353 }
1354
1355 pub fn to_key<'k, K: ToMdbValue>(&mut self, key: &'k K) -> MdbResult<()> {
1357 self.move_to(key, None::<&MdbValue<'k>>, ffi::MDB_cursor_op::MDB_SET_KEY)
1358 }
1359
1360 pub fn to_gte_key<'k, K: ToMdbValue>(&mut self, key: &'k K) -> MdbResult<()> {
1363 self.move_to(key, None::<&MdbValue<'k>>, ffi::MDB_cursor_op::MDB_SET_RANGE)
1364 }
1365
1366 pub fn to_item<K, V>(&mut self, key: &K, value: & V) -> MdbResult<()> where K: ToMdbValue, V: ToMdbValue {
1370 self.move_to(key, Some(value), ffi::MDB_cursor_op::MDB_GET_BOTH)
1371 }
1372
1373 pub fn to_gte_item<K, V>(&mut self, key: &K, value: & V) -> MdbResult<()> where K: ToMdbValue, V: ToMdbValue {
1375 self.move_to(key, Some(value), ffi::MDB_cursor_op::MDB_GET_BOTH_RANGE)
1376 }
1377
1378 pub fn to_next_key(&mut self) -> MdbResult<()> {
1381 self.navigate(ffi::MDB_cursor_op::MDB_NEXT_NODUP)
1382 }
1383
1384 pub fn to_next_item(&mut self) -> MdbResult<()> {
1386 self.navigate(ffi::MDB_cursor_op::MDB_NEXT_DUP)
1387 }
1388
1389 pub fn to_prev_key(&mut self) -> MdbResult<()> {
1392 self.navigate(ffi::MDB_cursor_op::MDB_PREV_NODUP)
1393 }
1394
1395 pub fn to_prev_item(&mut self) -> MdbResult<()> {
1397 self.navigate(ffi::MDB_cursor_op::MDB_PREV_DUP)
1398 }
1399
1400 pub fn to_first_item(&mut self) -> MdbResult<()> {
1402 self.navigate(ffi::MDB_cursor_op::MDB_FIRST_DUP)
1403 }
1404
1405 pub fn to_last_item(&mut self) -> MdbResult<()> {
1407 self.navigate(ffi::MDB_cursor_op::MDB_LAST_DUP)
1408 }
1409
1410 pub fn get<'a, T: FromMdbValue + 'a, U: FromMdbValue + 'a>(&'a mut self) -> MdbResult<(T, U)> {
1412 let (k, v) = self.get_plain()?;
1413
1414 unsafe {
1415 Ok((FromMdbValue::from_mdb_value(mem::transmute::<&MdbValue<'_>, &MdbValue<'_>>(&k)),
1416 FromMdbValue::from_mdb_value(mem::transmute::<&MdbValue<'_>, &MdbValue<'_>>(&v))))
1417 }
1418 }
1419
1420 pub fn get_value<'a, V: FromMdbValue + 'a>(&'a mut self) -> MdbResult<V> {
1422 let (_, v) = self.get_plain()?;
1423
1424 unsafe {
1425 Ok(FromMdbValue::from_mdb_value(mem::transmute::<&MdbValue<'_>, &MdbValue<'_>>(&v)))
1426 }
1427 }
1428
1429 pub fn get_key<'a, K: FromMdbValue + 'a>(&'a mut self) -> MdbResult<K> {
1431 let (k, _) = self.get_plain()?;
1432
1433 unsafe {
1434 Ok(FromMdbValue::from_mdb_value(mem::transmute::<&MdbValue<'_>, &MdbValue<'_>>(&k)))
1435 }
1436 }
1437
1438 #[inline]
1440 fn cmp_key(&mut self, other: &MdbValue) -> MdbResult<Ordering> {
1441 let (k, _) = self.get_plain()?;
1442 let mut kval = k.value;
1443 let cmp = unsafe {
1444 ffi::mdb_cmp(self.txn.handle, self.db, &mut kval, mem::transmute(other))
1445 };
1446 Ok(match cmp {
1447 n if n < 0 => Ordering::Less,
1448 n if n > 0 => Ordering::Greater,
1449 _ => Ordering::Equal,
1450 })
1451 }
1452
1453 #[inline]
1454 fn ensure_key_valid(&mut self) -> MdbResult<()> {
1455 if !self.valid_key {
1458 unsafe {
1459 try_mdb!(ffi::mdb_cursor_get(self.handle, &mut self.key_val,
1460 ptr::null_mut(),
1461 ffi::MDB_cursor_op::MDB_GET_CURRENT));
1462 }
1463 self.valid_key = true;
1464 }
1465 Ok(())
1466 }
1467
1468 #[inline]
1469 fn get_plain(&mut self) -> MdbResult<(MdbValue<'txn>, MdbValue<'txn>)> {
1470 self.ensure_key_valid()?;
1471 let k = MdbValue {value: self.key_val, marker: ::std::marker::PhantomData};
1472 let v = MdbValue {value: self.data_val, marker: ::std::marker::PhantomData};
1473
1474 Ok((k, v))
1475 }
1476
1477
1478
1479 fn set_value<V: ToMdbValue>(&mut self, value: &V, flags: c_uint) -> MdbResult<()> {
1480 self.ensure_key_valid()?;
1481 self.data_val = value.to_mdb_value().value;
1482 lift_mdb!(unsafe {ffi::mdb_cursor_put(self.handle, &mut self.key_val, &mut self.data_val, flags)})
1483 }
1484
1485 pub fn set<K: ToMdbValue, V: ToMdbValue>(&mut self, key: &K, value: &V, flags: c_uint) -> MdbResult<()> {
1486 self.key_val = key.to_mdb_value().value;
1487 self.valid_key = true;
1488 let res = self.set_value(value, flags);
1489 self.valid_key = false;
1490 res
1491 }
1492
1493 pub fn replace<V: ToMdbValue>(&mut self, value: &V) -> MdbResult<()> {
1496 let res = self.set_value(value, ffi::MDB_CURRENT);
1497 self.valid_key = false;
1498 res
1499 }
1500
1501 pub fn add_item<V: ToMdbValue>(&mut self, value: &V) -> MdbResult<()> {
1503 let res = self.set_value(value, 0);
1504 self.valid_key = false;
1505 res
1506 }
1507
1508 fn del_value(&mut self, flags: c_uint) -> MdbResult<()> {
1509 lift_mdb!(unsafe { ffi::mdb_cursor_del(self.handle, flags) })
1510 }
1511
1512 pub fn del(&mut self) -> MdbResult<()> {
1514 self.del_all()
1515 }
1516
1517 pub fn del_item(&mut self) -> MdbResult<()> {
1523 let res = self.del_value(0);
1524 self.valid_key = false;
1525 res
1526 }
1527
1528 pub fn del_all(&mut self) -> MdbResult<()> {
1530 self.del_value(ffi::MDB_NODUPDATA)
1531 }
1532
1533 pub fn item_count(&self) -> MdbResult<size_t> {
1535 let mut tmp: size_t = 0;
1536 lift_mdb!(unsafe {ffi::mdb_cursor_count(self.handle, &mut tmp)}, tmp)
1537 }
1538
1539 pub fn get_item<'k, K: ToMdbValue>(self, k: &'k K) -> CursorItemAccessor<'txn, 'k, K> {
1540 CursorItemAccessor {
1541 cursor: self,
1542 key: k
1543 }
1544 }
1545}
1546
1547impl Drop for Cursor<'_> {
1548 fn drop(&mut self) {
1549 unsafe { ffi::mdb_cursor_close(self.handle) };
1550 }
1551}
1552
1553#[derive(Debug)]
1554pub struct CursorItemAccessor<'c, 'k, K: 'k> {
1555 cursor: Cursor<'c>,
1556 key: &'k K,
1557}
1558
1559impl<'k, 'c: 'k, K: ToMdbValue> CursorItemAccessor<'c, 'k, K> {
1560 pub fn get<'a, V: FromMdbValue + 'a>(&'a mut self) -> MdbResult<V> {
1561 self.cursor.to_key(self.key)?;
1562 self.cursor.get_value()
1563 }
1564
1565 pub fn add<V: ToMdbValue>(&mut self, v: &V) -> MdbResult<()> {
1566 self.cursor.set(self.key, v, 0)
1567 }
1568
1569 pub fn del<V: ToMdbValue>(&mut self, v: &V) -> MdbResult<()> {
1570 self.cursor.to_item(self.key, v)?;
1571 self.cursor.del_item()
1572 }
1573
1574 pub fn del_all(&mut self) -> MdbResult<()> {
1575 self.cursor.to_key(self.key)?;
1576 self.cursor.del_all()
1577 }
1578
1579 pub fn into_inner(self) -> Cursor<'c> {
1580 let tmp = self;
1581 tmp.cursor
1582 }
1583}
1584
1585
1586#[derive(Debug)]
1587pub struct CursorValue<'cursor> {
1588 key: MdbValue<'cursor>,
1589 value: MdbValue<'cursor>,
1590 marker: ::std::marker::PhantomData<&'cursor ()>,
1591}
1592
1593impl<'cursor> CursorValue<'cursor> {
1597 pub fn get_key<T: FromMdbValue + 'cursor>(&'cursor self) -> T {
1598 FromMdbValue::from_mdb_value(&self.key)
1599 }
1600
1601 pub fn get_value<T: FromMdbValue + 'cursor>(&'cursor self) -> T {
1602 FromMdbValue::from_mdb_value(&self.value)
1603 }
1604
1605 pub fn get<T: FromMdbValue + 'cursor, U: FromMdbValue + 'cursor>(&'cursor self) -> (T, U) {
1606 (FromMdbValue::from_mdb_value(&self.key),
1607 FromMdbValue::from_mdb_value(&self.value))
1608 }
1609}
1610
1611pub trait IterateCursor {
1613 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: &mut Cursor<'b>) -> bool;
1616
1617 fn move_to_next<'iter, 'cursor: 'iter>(&'iter self, cursor: &'cursor mut Cursor<'cursor>) -> bool;
1619
1620 fn get_size_hint(&self, _cursor: &Cursor) -> (usize, Option<usize>) {
1622 (0, None)
1623 }
1624}
1625
1626
1627#[derive(Debug)]
1628pub struct CursorIterator<'c, I> {
1629 inner: I,
1630 has_data: bool,
1631 cursor: Cursor<'c>,
1632 marker: ::std::marker::PhantomData<&'c ()>,
1633}
1634
1635impl<'c, I: IterateCursor + 'c> CursorIterator<'c, I> {
1636 fn wrap(cursor: Cursor<'c>, inner: I) -> CursorIterator<'c, I> {
1637 let mut cursor = cursor;
1638 let has_data = inner.init_cursor(&mut cursor);
1639 CursorIterator {
1640 inner,
1641 has_data,
1642 cursor,
1643 marker: ::std::marker::PhantomData,
1644 }
1645 }
1646
1647 #[allow(dead_code)]
1648 fn unwrap(self) -> Cursor<'c> {
1649 self.cursor
1650 }
1651}
1652
1653impl<'c, I: IterateCursor + 'c> Iterator for CursorIterator<'c, I> {
1654 type Item = CursorValue<'c>;
1655
1656 fn next(&mut self) -> Option<CursorValue<'c>> {
1657 if !self.has_data {
1658 None
1659 } else {
1660 match self.cursor.get_plain() {
1661 Err(_) => None,
1662 Ok((k, v)) => {
1663 self.has_data = unsafe { self.inner.move_to_next(mem::transmute::<&mut Cursor<'_>, &mut Cursor<'_>>(&mut self.cursor)) };
1664 Some(CursorValue {
1665 key: k,
1666 value: v,
1667 marker: ::std::marker::PhantomData
1668 })
1669 }
1670 }
1671 }
1672 }
1673
1674 fn size_hint(&self) -> (usize, Option<usize>) {
1675 self.inner.get_size_hint(&self.cursor)
1676 }
1677}
1678
1679#[derive(Debug)]
1680pub struct CursorKeyRangeIter<'a> {
1681 start_key: MdbValue<'a>,
1682 end_key: MdbValue<'a>,
1683 end_inclusive: bool,
1684 marker: ::std::marker::PhantomData<&'a ()>,
1685}
1686
1687impl<'a> CursorKeyRangeIter<'a> {
1688 pub fn new<K: ToMdbValue+'a>(start_key: &'a K, end_key: &'a K, end_inclusive: bool) -> CursorKeyRangeIter<'a> {
1689 CursorKeyRangeIter {
1690 start_key: start_key.to_mdb_value(),
1691 end_key: end_key.to_mdb_value(),
1692 end_inclusive,
1693 marker: ::std::marker::PhantomData,
1694 }
1695 }
1696}
1697
1698impl IterateCursor for CursorKeyRangeIter<'_> {
1699 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: & mut Cursor<'b>) -> bool {
1700 let ok = unsafe {
1701 cursor.to_gte_key(mem::transmute::<&'a MdbValue<'a>, &'b MdbValue<'b>>(&self.start_key)).is_ok()
1702 };
1703 ok && cursor.cmp_key(&self.end_key).is_less(self.end_inclusive)
1704 }
1705
1706 fn move_to_next<'i, 'c: 'i>(&'i self, cursor: &'c mut Cursor<'c>) -> bool {
1707 let moved = cursor.to_next_key().is_ok();
1708 if !moved {
1709 false
1710 } else {
1711 cursor.cmp_key(&self.end_key).is_less(self.end_inclusive)
1712 }
1713 }
1714}
1715
1716#[derive(Debug)]
1717pub struct CursorFromKeyIter<'a> {
1718 start_key: MdbValue<'a>,
1719 marker: ::std::marker::PhantomData<&'a ()>,
1720}
1721
1722
1723impl<'a> CursorFromKeyIter<'a> {
1724 pub fn new<K: ToMdbValue+'a>(start_key: &'a K) -> CursorFromKeyIter<'a> {
1725 CursorFromKeyIter {
1726 start_key: start_key.to_mdb_value(),
1727 marker: ::std::marker::PhantomData
1728 }
1729 }
1730}
1731
1732impl IterateCursor for CursorFromKeyIter<'_> {
1733 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: & mut Cursor<'b>) -> bool {
1734 unsafe {
1735 cursor.to_gte_key(mem::transmute::<&'a MdbValue<'a>, &'b MdbValue<'b>>(&self.start_key)).is_ok()
1736 }
1737 }
1738
1739 fn move_to_next<'i, 'c: 'i>(&'i self, cursor: &'c mut Cursor<'c>) -> bool {
1740 cursor.to_next_key().is_ok()
1741 }
1742}
1743
1744
1745#[derive(Debug)]
1746pub struct CursorToKeyIter<'a> {
1747 end_key: MdbValue<'a>,
1748 marker: ::std::marker::PhantomData<&'a ()>,
1749}
1750
1751
1752impl<'a> CursorToKeyIter<'a> {
1753 pub fn new<K: ToMdbValue+'a>(end_key: &'a K) -> CursorToKeyIter<'a> {
1754 CursorToKeyIter {
1755 end_key: end_key.to_mdb_value(),
1756 marker: ::std::marker::PhantomData,
1757 }
1758 }
1759}
1760
1761impl IterateCursor for CursorToKeyIter<'_> {
1762 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: & mut Cursor<'b>) -> bool {
1763 let ok = cursor.to_first().is_ok();
1764 ok && cursor.cmp_key(&self.end_key).is_less(false)
1765 }
1766
1767 fn move_to_next<'i, 'c: 'i>(&'i self, cursor: &'c mut Cursor<'c>) -> bool {
1768 let moved = cursor.to_next_key().is_ok();
1769 if !moved {
1770 false
1771 } else {
1772 cursor.cmp_key(&self.end_key).is_less(false)
1773 }
1774 }
1775}
1776
1777#[allow(missing_copy_implementations)]
1778#[derive(Debug)]
1779pub struct CursorIter;
1780
1781
1782impl IterateCursor for CursorIter {
1783 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: & mut Cursor<'b>) -> bool {
1784 cursor.to_first().is_ok()
1785 }
1786
1787 fn move_to_next<'i, 'c: 'i>(&'i self, cursor: &'c mut Cursor<'c>) -> bool {
1788 cursor.to_next_key().is_ok()
1789 }
1790}
1791
1792
1793#[derive(Debug)]
1794pub struct CursorItemIter<'a> {
1795 key: MdbValue<'a>,
1796 marker: ::std::marker::PhantomData<&'a ()>,
1797}
1798
1799
1800impl<'a> CursorItemIter<'a> {
1801 pub fn new<K: ToMdbValue+'a>(key: &'a K) -> CursorItemIter<'a> {
1802 CursorItemIter {
1803 key: key.to_mdb_value(),
1804 marker: ::std::marker::PhantomData
1805 }
1806 }
1807}
1808
1809impl IterateCursor for CursorItemIter<'_> {
1810 fn init_cursor<'a, 'b: 'a>(&'a self, cursor: & mut Cursor<'b>) -> bool {
1811 unsafe {
1812 cursor.to_key(mem::transmute::<&MdbValue, &'b MdbValue<'b>>(&self.key)).is_ok()
1813 }
1814 }
1815
1816 fn move_to_next<'i, 'c: 'i>(&'i self, cursor: &'c mut Cursor<'c>) -> bool {
1817 cursor.to_next_item().is_ok()
1818 }
1819
1820 fn get_size_hint(&self, c: &Cursor) -> (usize, Option<usize>) {
1821 match c.item_count() {
1822 Err(_) => (0, None),
1823 Ok(cnt) => (0, Some(cnt))
1824 }
1825 }
1826}
1827
1828
1829#[derive(Copy, Clone, Debug)]
1830pub struct MdbValue<'a> {
1831 value: MDB_val,
1832 marker: ::std::marker::PhantomData<&'a ()>,
1833}
1834
1835impl<'a> MdbValue<'a> {
1836 #[inline]
1840 pub unsafe fn new(data: *const c_void, len: usize) -> MdbValue<'a> {
1841 MdbValue {
1842 value: MDB_val {
1843 mv_data: data,
1844 mv_size: len as size_t
1845 },
1846 marker: ::std::marker::PhantomData
1847 }
1848 }
1849
1850 #[inline]
1854 pub unsafe fn from_raw(mdb_val: *const ffi::MDB_val) -> MdbValue<'a> {
1855 MdbValue::new((*mdb_val).mv_data, (*mdb_val).mv_size)
1856 }
1857
1858 #[inline]
1859 pub fn new_from_sized<T>(data: &'a T) -> MdbValue<'a> {
1860 unsafe {
1861 MdbValue::new(data as *const T as *const c_void, mem::size_of::<T>())
1862 }
1863 }
1864
1865 #[inline]
1869 pub unsafe fn get_ref(&'a self) -> *const c_void {
1870 self.value.mv_data
1871 }
1872
1873 #[inline]
1874 pub fn get_size(&self) -> usize {
1875 self.value.mv_size
1876 }
1877}