mmtkvdb/lib.rs
1//! Key-value database using LMDB
2//!
3//! # Caveats / Safety
4//!
5//! Because of how memory-mapped I/O is being used and also because of certain
6//! assumptions of the underlying LMDB API, opening environments and databases
7//! requires `unsafe` Rust when using this library.
8//!
9//! Do not open the same environment twice in the same process at the same time
10//! (see documentation of [`EnvBuilder`]). The file format is platform
11//! specific, so endianness and word size must not change across use.
12//!
13//! When opening an already existing database, all options must be compatible
14//! with the previously used options (see documentation of [`DbBuilder`]).
15//!
16//! Stale readers might need to be cleared manually, see
17//! [`Env::clear_stale_readers`].
18//!
19//! # Example
20//!
21//! ```
22//! # fn doit() -> std::io::Result<()> {
23//! // question mark operator may return with `std::io::Error`
24//! use mmtkvdb::{self as kv, traits::*};
25//! use tempfile::tempdir;
26//! let location = tempdir()?;
27//! let db1_opts = kv::DbBuilder::new().name("db1");
28//! let db2_opts = kv::DbBuilder::new().key_type::<str>().value_type::<i32>().name("db2");
29//! let env_builder = kv::EnvBuilder::new().dir(location.path()).max_dbs(2);
30//! {
31//! // SAFETY:
32//! // * database isn't opened twice in this process at the same time
33//! // * it is assumed no other processes access the environment
34//! // * since environment is newly created, it matches the platform
35//! let mut env = unsafe { env_builder.open_rw()? };
36//! // SAFETY: database doesn't exist yet (or would need compatible options)
37//! let db1 = unsafe { env.create_db(&db1_opts)? };
38//! // SAFETY: database doesn't exist yet (or would need compatible options)
39//! let db2 = unsafe { env.create_db(&db2_opts)? };
40//! let mut txn = env.txn_rw()?;
41//! txn.put(&db1, "key1".as_bytes(), "value1".as_bytes())?;
42//! txn.put(&db2, "Prime", &17)?;
43//! txn.commit()?;
44//! }
45//! {
46//! // SAFETY:
47//! // * database isn't opened twice in this process at the same time
48//! // * it is assumed no other processes access the environment
49//! // * the environment has been created above, thus it matches the platform
50//! let env = unsafe { env_builder.open_ro()? };
51//! // SAFETY: database options are compatible with the ones used when
52//! // creating the database
53//! let db1 = unsafe { env.open_db(&db1_opts)? };
54//! // SAFETY: database options are compatible with the ones used when
55//! // creating the database
56//! let db2 = unsafe { env.open_db(&db2_opts)? };
57//! let txn = env.txn_ro()?;
58//! assert_eq!(txn.get(&db1, "key1".as_bytes())?, Some("value1".as_bytes()));
59//! assert_eq!(txn.get_owned(&db2, "Prime")?, Some(17));
60//! }
61//! location.close()?;
62//! # Ok(())
63//! # }
64//! # doit().expect("error during database access")
65//! ```
66
67#![deny(unsafe_op_in_unsafe_fn)]
68#![warn(missing_docs)]
69#![allow(unused_macro_rules)]
70
71pub mod cow;
72mod helpers;
73pub mod lmdb;
74pub mod storable;
75mod syncutils;
76
77#[cfg(test)]
78mod tests;
79
80pub use cow::{GenericCow, Owned};
81use helpers::*;
82pub use storable::{BorrowStorable, NoKey, NoValue, Storable, StorableConstBytesLen, StorableRef};
83use syncutils::*;
84
85use std::cell::UnsafeCell;
86use std::collections::{HashMap, HashSet};
87use std::ffi::{c_int, c_uint, CStr, CString};
88use std::fmt;
89use std::hash::{Hash, Hasher};
90use std::io;
91use std::marker::PhantomData;
92use std::mem::{forget, take, MaybeUninit};
93use std::ops::{Deref, DerefMut};
94use std::path::Path;
95use std::ptr::{null, null_mut};
96use std::slice;
97use std::str;
98use std::sync::atomic::{AtomicBool, AtomicU64, Ordering::Relaxed};
99use std::sync::{Arc, Mutex, Weak};
100
101// TODO: Ensure that `usize` is really `c_size_t` without depending on unstable
102// features.
103#[allow(non_camel_case_types)]
104type c_size_t = usize;
105/*
106// Compile-time check enforcing `usize` is same size as `c_size_t`
107const _: fn() = || {
108 let _ = core::mem::transmute::<usize, std::ffi::c_size_t>;
109};
110*/
111
112/// Important traits re-exported without name
113pub mod traits {
114 pub use super::Env as _;
115 pub use super::EnvRef as _;
116 pub use super::Txn as _;
117}
118
119/// Converts an error code from LMDB into a `Result`
120///
121/// # Safety
122///
123/// * If `code` is negative, it must be a valid LMDB error code.
124/// * `lmdb::mdb_strerror` is assumed to be thread-safe for valid negative LMDB
125/// error codes. This is currently not guaranteed by LMDB's API
126/// documentation. See also: `https://bugs.openldap.org/show_bug.cgi?id=8361`
127unsafe fn check_err_code(code: c_int) -> io::Result<()> {
128 if code > 0 {
129 Err(io::Error::from_raw_os_error(code))
130 } else if code < 0 {
131 Err(io::Error::new(
132 io::ErrorKind::Other,
133 // SAFETY: because `code` is negative and an existent LMDB error
134 // code, `lmdb::mdb_strerror` returns a pointer to a static
135 // C string (not guaranteed by the API docs, but believed to not
136 // change in future versions of LMDB)
137 unsafe { CStr::from_ptr(lmdb::mdb_strerror(code)) }
138 .to_str()
139 .unwrap_or("unknown error (LMDB error string is not valid UTF-8)"),
140 ))
141 } else {
142 Ok(())
143 }
144}
145
146/// Type alias for [`c_uint`] as used by LMDB to specify flags
147type LmdbFlags = c_uint;
148
149/// Macro checking if an `lmdb_flags` field has a certain flag set
150macro_rules! flag_get {
151 ($self:ident, $flag:ident) => {
152 ($self.lmdb_flags & (lmdb::$flag as LmdbFlags) != 0)
153 };
154}
155
156/// Macro setting or clearing a flag in the `lmdb_flags` field
157macro_rules! flag_set {
158 ($self:ident, $flag:ident, $value:expr) => {
159 if $value {
160 $self.lmdb_flags |= lmdb::$flag as LmdbFlags;
161 } else {
162 $self.lmdb_flags &= !(lmdb::$flag as LmdbFlags);
163 }
164 };
165}
166
167static ENV_COUNTER: AtomicU64 = AtomicU64::new(0);
168
169/// Internal struct with LMDB environment handle as raw pointer
170///
171/// On drop, the environment is closed. Contains an additional mutex to
172/// synchronize opening/creating/closing databases.
173#[derive(Debug)]
174struct EnvBackend {
175 inner: *mut lmdb::MDB_env,
176 id: u64,
177 max_keysize: usize,
178 nestable_txns: bool,
179 /// `Mutex` ensures synchronization of `lmdb::mdb_dbi_open` and contained
180 /// `HashMap` avoids re-creating a new `DbBackend` for a database that is
181 /// already open (because databases must not be closed more than once)
182 dbis: Mutex<HashMap<lmdb::MDB_dbi, Weak<DbBackend>>>,
183}
184
185// SAFETY: `lmdb::MDB_env` is generally thread-safe, but particular function
186// must be synchronized
187unsafe impl Send for EnvBackend {}
188// SAFETY: `lmdb::MDB_env` is generally thread-safe, but particular function
189// must be synchronized
190unsafe impl Sync for EnvBackend {}
191
192impl PartialEq for EnvBackend {
193 fn eq(&self, other: &Self) -> bool {
194 self.inner == other.inner
195 }
196}
197
198impl Eq for EnvBackend {}
199
200impl Hash for EnvBackend {
201 fn hash<H: Hasher>(&self, state: &mut H) {
202 self.inner.hash(state);
203 }
204}
205
206impl Drop for EnvBackend {
207 fn drop(&mut self) {
208 // LMDB's API specification is contradictory and claims both that
209 // databases must be closed before calling `mdb_env_close` and that
210 // "closing a database handle is not necessary". We close them anyway,
211 // just to be sure:
212 for dbi in take(&mut *self.dbis.lock().unwrap()).into_keys() {
213 // SAFETY:
214 // * due to `&mut self`, it is not possible to call this function
215 // (for a given environment) from different threads at the same
216 // time
217 // * the database can't have been closed already, because the
218 // `lmdb::MDB_dbi` is always removed from `EnvBackend::dbis` in
219 // that case
220 // * all transactions have been closed at this point because
221 // `TxnRo<'a>` and `TxnRw<'a>` have a lifetime `'a` which has
222 // ended by now
223 // * all cursors have been closed at this point because closing
224 // all transactions also closes all cursors
225 unsafe {
226 lmdb::mdb_dbi_close(self.inner, dbi);
227 }
228 }
229 // SAFETY:
230 // * due to `&mut self`, it is not possible to call this function
231 // (for a given environment) from different threads at the same
232 // time
233 // * all transactions, cursors, and databases have been closed (see
234 // above)
235 unsafe { lmdb::mdb_env_close(self.inner) }
236 }
237}
238
239/// Read-only handle for accessing environment that stores key-value databases
240///
241/// An environment can be opened using [`EnvBuilder`].
242///
243/// The methods for read-only access are accessible through the [`Env`] trait
244/// (which is implemented by `EnvRo` and [`EnvRw`]).
245///
246/// Use [`Env::open_db`] to retrieve database handles and [`Env::txn_ro`] to
247/// start a read-only transaction.
248///
249/// It's possible to clone the handle, in which case the environment will be
250/// closed when the last handle is dropped. It's also possible to create a
251/// read-only handle from a read-write handle ([`EnvRw`]) by invoking
252/// [`Env::clone_ro`].
253#[derive(Clone, PartialEq, Eq, Hash, Debug)]
254pub struct EnvRo {
255 backend: Arc<EnvBackend>,
256}
257
258/// Read-write handle for accessing environment that stores key-value databases
259///
260/// An environment can be opened using [`EnvBuilder`].
261///
262/// The methods for read-only access are accessible through the [`Env`] trait
263/// (which is implemented by [`EnvRo`] and `EnvRw`).
264/// Methods for write access, however, are available directly on the `EnvRw`
265/// struct.
266///
267/// Use [`Env::open_db`] or [`EnvRw::create_db`] to retrieve database handles
268/// and [`EnvRw::txn_rw`] to start a read-write transaction.
269///
270/// It's also possible to create a read-only handle from a read-write handle by
271/// invoking [`Env::clone_ro`].
272#[derive(PartialEq, Eq, Hash, Debug)]
273pub struct EnvRw {
274 env_ro: EnvRo,
275}
276
277impl AsRef<EnvRo> for EnvRo {
278 fn as_ref(&self) -> &EnvRo {
279 &self
280 }
281}
282
283impl AsRef<EnvRo> for EnvRw {
284 fn as_ref(&self) -> &EnvRo {
285 &self.env_ro
286 }
287}
288
289/// Abstraction over [`&EnvRo`](EnvRo) and [`&mut EnvRw`](EnvRw), which allows
290/// creating databases in the latter case if non-existing
291///
292/// This trait is only needed if you want to create generic code which works on
293/// either `&EnvRo` or `&mut EnvRw` and creates databases in the latter case if
294/// non-existing.
295/// In most cases, `EnvRo` or `EnvRw` can be used directly. (Note that you can
296/// call `EnvRo`'s methods on `EnvRw` automatically due to [`Deref`] coercion.)
297pub trait EnvRef {
298 /// Open database in environment and, if possible, create if non-existing
299 ///
300 /// Note that the implementation of `EnvRef` for [`&EnvRo`]() does *not*
301 /// create any database if non-existing.
302 ///
303 /// # Safety
304 ///
305 /// * If a database exists already, it must have been created with
306 /// compatible options.
307 /// * Implementation of [`Storable`] for the used
308 /// [keys](DbBuilder::key_type) and [values](DbBuilder::value_type) must
309 /// behave the same as when the database was created.
310 unsafe fn open_or_create_db<K, V, C>(
311 &mut self,
312 options: &DbSpec<K, V, C>,
313 ) -> io::Result<Db<K, V, C>>
314 where
315 K: Storable + ?Sized,
316 V: Storable + ?Sized,
317 C: Constraint;
318}
319
320impl<T: Env> EnvRef for &T {
321 unsafe fn open_or_create_db<K, V, C>(
322 &mut self,
323 options: &DbSpec<K, V, C>,
324 ) -> io::Result<Db<K, V, C>>
325 where
326 K: Storable + ?Sized,
327 V: Storable + ?Sized,
328 C: Constraint,
329 {
330 // SAFETY: all requirements of `EnvRo::open_db` are listed in
331 // documentation of `EnvRef:open_or_create_db` as well
332 unsafe { self.open_db(options) }
333 }
334}
335
336impl EnvRef for &mut EnvRw {
337 unsafe fn open_or_create_db<K, V, C>(
338 &mut self,
339 options: &DbSpec<K, V, C>,
340 ) -> io::Result<Db<K, V, C>>
341 where
342 K: Storable + ?Sized,
343 V: Storable + ?Sized,
344 C: Constraint,
345 {
346 // SAFETY: all requirements of `EnvRw::create_db` are listed in
347 // documentation of `EnvRef:open_or_create_db` as well
348 unsafe { self.create_db(options) }
349 }
350}
351
352/// Internal struct with LMDB transaction handle as raw pointer
353///
354/// On drop, all associated cursors (which haven't been dropped and thus closed
355/// yet) will be closed.
356#[derive(Debug)]
357struct TxnBackend<'a> {
358 env_ro: &'a EnvRo,
359 inner: *mut lmdb::MDB_txn,
360 cursors: WeakVec<CursorBackend>,
361}
362
363impl<'a> TxnBackend<'a> {
364 fn close_cursors(&mut self) {
365 for cursor in take(&mut self.cursors).iter() {
366 cursor.closed.store(true, Relaxed);
367 // SAFETY:
368 // * Holding a shared reference to `cursor` means that its drop
369 // handler did not run yet. Thus the cursor has not been closed
370 // by the drop handler. Storing `true` in `closed` ensures that
371 // when the drop handler later obtains a mutable reference, it
372 // will read `true` and doesn't close the cursor twice.
373 // * The cursor also won't be closed by this method twice,
374 // because `self.cursors` is emptied above.
375 // * There is no other place where the cursor could have been
376 // closed.
377 // * All callers of `TxnBackend::close_cursors` will drop or
378 // forget the `TxnBackend` afterwards. Thus the cursor can't be
379 // used after calling `lmdb::mdb_cursor_close` below, because
380 // using the cursor requires a `TxnRo` or `TxnRw` handle. In
381 // this context, `CursorBackend::assert_txn_backend` ensures
382 // that a cursor handle really belongs to the transaction for
383 // which it has been created. Even a `Relaxed` store of `true`
384 // to `cursor.closed` will happen-before subsequent `Relaxed`
385 // loads in `CursorBackend::assert_txn_backend` if the address
386 // `TxnBackend.inner` has been reused. That is because
387 // releasing the allocated memory in LMDB synchronizes-with an
388 // allocation from within the same memory region.
389 unsafe {
390 lmdb::mdb_cursor_close(cursor.inner);
391 }
392 }
393 }
394 /// Drop without aborting the transaction (closes all cursors)
395 fn dont_abort(mut self) {
396 self.close_cursors();
397 // NOTE: doesn't leak because `self.cursors` isn't holding any
398 // allocation
399 forget(self);
400 }
401}
402
403impl<'a> Drop for TxnBackend<'a> {
404 fn drop(&mut self) {
405 self.close_cursors();
406 // SAFETY: transaction cannot be used further and all cursors have been
407 // closed
408 unsafe {
409 lmdb::mdb_txn_abort(self.inner);
410 }
411 }
412}
413
414/// Read-only transaction
415///
416/// A read-only transaction can be started with [`Env::txn_ro`].
417///
418/// The methods for read-only access to the database are accessible through the
419/// [`Txn`] trait (which is implemented by `TxnRo` and [`TxnRw`]).
420///
421/// Read-only transactions do not need to be committed or aborted; their
422/// handles can be simply dropped when not needed anymore.
423///
424/// Opposed to read-write transactions ([`TxnRw`]), read-only transactions
425/// (`TxnRo`) are [`Send`]. However, they are not [`Sync`]. Thus, if you want
426/// to use a read-only transaction from several threads concurrently, you have
427/// to synchronize the use, e.g. by wrapping the `TxnRo` in a [`Mutex`].
428#[derive(Debug)]
429pub struct TxnRo<'a> {
430 backend: UnsafeCell<TxnBackend<'a>>,
431}
432
433// SAFETY: Option `lmdb::MDB_NOTLS` is used, thus `TxnRo` can be used from
434// different threads. However, the `UnsafeCell` demands `!Sync`.
435unsafe impl<'a> Send for TxnRo<'a> {}
436
437/// Helper type to store `HashSet<ArcByAddr<DbBackend>>` as owned value or
438/// mutable reference
439#[derive(Debug)]
440enum UsedDbs<'a> {
441 Toplevel(HashSet<ArcByAddr<DbBackend>>),
442 Nested(&'a mut HashSet<ArcByAddr<DbBackend>>),
443}
444
445impl<'a> Deref for UsedDbs<'a> {
446 type Target = HashSet<ArcByAddr<DbBackend>>;
447 fn deref(&self) -> &Self::Target {
448 match self {
449 UsedDbs::Toplevel(x) => x,
450 UsedDbs::Nested(x) => x,
451 }
452 }
453}
454
455impl<'a> DerefMut for UsedDbs<'a> {
456 fn deref_mut(&mut self) -> &mut Self::Target {
457 match self {
458 UsedDbs::Toplevel(x) => x,
459 UsedDbs::Nested(x) => x,
460 }
461 }
462}
463
464/// Read-write transaction
465///
466/// A read-write transaction can be started with [`EnvRw::txn_rw`].
467///
468/// The methods for read-only access to the database are accessible through the
469/// [`Txn`] trait (which is implemented by [`TxnRo`] and `TxnRw`).
470/// Methods for write access, however, are available directly on the `TxnRw`
471/// struct.
472///
473/// Read-write transactions are [aborted](TxnRw::abort) when dropped. To make
474/// changes permanent, [`TxnRw::commit`] must be called.
475///
476/// Read-write transactions are neither [`Send`] nor [`Sync`].
477pub struct TxnRw<'a> {
478 backend: UnsafeCell<TxnBackend<'a>>,
479 used_dbs: UsedDbs<'a>,
480 commit_handlers: Vec<Box<dyn FnOnce(&mut Self) -> io::Result<()> + 'a>>,
481}
482
483impl<'a> fmt::Debug for TxnRw<'a> {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 f.debug_struct("Point")
486 .field("backend", &self.backend)
487 .field("used_dbs", &self.used_dbs)
488 .finish_non_exhaustive()
489 }
490}
491
492/// Internal struct with LMDB database handle
493///
494/// `DbBackend` can be wrapped in an [`ArcByAddr`] to allow for equality checks
495/// and hashing.
496///
497/// There is no strong reference to [`EnvBackend`] to avoid a lifetime argument
498/// (particularly such that databases can persist beyond mutation of
499/// [`EnvRw`]).
500///
501/// It is an error if a database handle is used on a different environment.
502/// Method [`DbBackend::assert_env_backend`] can be used to panic in that case.
503///
504/// Note that `DbBackend`s must not be dropped while there is an open
505/// transaction which modified the respective database. Hence clones of
506/// `ArcByAddr<DbBackend>` must be stored in [`TxnRw::used_dbs`] before
507/// modifying any data in the database. This will defer invocation of
508/// [`DbBackend::drop`] and [`lmdb::mdb_dbi_close`].
509#[derive(Debug)]
510struct DbBackend {
511 env_backend: Weak<EnvBackend>,
512 env_id: u64,
513 inner: lmdb::MDB_dbi,
514}
515
516impl DbBackend {
517 /// Drop without running normal destructor
518 fn dont_close(mut self) {
519 self.env_backend = Default::default();
520 // NOTE: doesn't leak because `self.env_backend` doesn't hold any
521 // allocation
522 forget(self);
523 }
524 /// Panic if database handle does not belong to environment
525 fn assert_env_backend(&self, env_backend: &EnvBackend) {
526 if self.env_id != env_backend.id {
527 panic!("database does not belong to environment");
528 }
529 }
530}
531
532impl Drop for DbBackend {
533 fn drop(&mut self) {
534 if let Some(env_backend) = Weak::upgrade(&self.env_backend) {
535 let mut dbis = env_backend.dbis.lock().unwrap();
536 // If `env_backend.dbis` does not contain the `lmdb::MDB_dbi`, then
537 // `lmdb::mdb_dbi_close` has already been called.
538 if let Some(weak) = dbis.get(&self.inner) {
539 // If `weak.strong_count` is not zero, then the database has
540 // been reopened after the last `Arc<DbBackend> was dropped.
541 if weak.strong_count() == 0 {
542 // Removing the `lmdb::MDB_dbi` from `dbis` ensures that
543 // the database doesn't get closed twice.
544 dbis.remove(&self.inner);
545 // SAFETY:
546 // * due to `env_backend.dbis.lock()`, it is not possible
547 // to call this function (for a given environment) from
548 // different threads at the same time
549 // * because `weak.strong_count()` is zero, no new
550 // `DbBackend` (which is still in use) with the same
551 // `DbBackend::inner` may have been created by
552 // `EnvRo::open_or_opt_create_db`
553 // * removing the `lmdb::MDB_dbi` from `dbis` ensures that
554 // `mdb_dbi_close` isn't called twice, even if a
555 // recreated `DbBackend` gets dropped later
556 // * `mdb_cursor_close` isn't called later because all
557 // `CursorBackend`s must have been dropped before the last
558 // `Db` handle and before thus the `DbBackend` is dropped
559 // (see SAFETY comment on `CursorBackend`)
560 unsafe {
561 lmdb::mdb_dbi_close(env_backend.inner, self.inner);
562 }
563 }
564 }
565 }
566 }
567}
568
569/// Database handle
570///
571/// These handles are created using [`DbBuilder`] builder, which can be passed
572/// by (shared) reference to [`Env::open_db`] or [`EnvRw::create_db`].
573///
574/// The type arguments `K`, `V`, and `C` reflect the type used for keys and
575/// values, and whether duplicate keys are allowed (`C` being [`KeysUnique`] or
576/// [`KeysDuplicate`]), respectively.
577///
578/// Operations on databases is done using the methods of the [`Txn`] trait or
579/// [`TxnRw`] struct. The database handle must be passed to these methods by
580/// (shared) reference.
581///
582/// Using a [`Db`] handle in a different environment (or in an environment that
583/// has been closed and re-opened) will cause a panic.
584#[derive(Debug)]
585pub struct Db<K: ?Sized, V: ?Sized, C> {
586 key: PhantomData<fn(&K) -> &K>,
587 value: PhantomData<fn(&V) -> &V>,
588 constraint: C,
589 backend: ArcByAddr<DbBackend>,
590}
591
592impl<K: ?Sized, V: ?Sized, C> Clone for Db<K, V, C>
593where
594 C: Constraint,
595{
596 fn clone(&self) -> Self {
597 Db {
598 key: PhantomData,
599 value: PhantomData,
600 constraint: self.constraint,
601 backend: self.backend.clone(),
602 }
603 }
604}
605
606impl<K: ?Sized, V: ?Sized, C> PartialEq for Db<K, V, C> {
607 fn eq(&self, other: &Self) -> bool {
608 self.backend == other.backend
609 }
610}
611
612impl<K: ?Sized, V: ?Sized, C> Eq for Db<K, V, C> {}
613
614/// Internal struct with LMDB cursor handle
615///
616/// There is no lifetime argument, but cursors will be closed when
617/// [`TxnBackend`] gets dropped. In that case, this struct will be marked as
618/// closed by setting [`CursorBackend::closed`] to true.
619///
620/// `CursorBackend` can be wrapped in an [`ArcByAddr`] to allow for equality
621/// checks and hashing. (Equality can't be checked by comparing the inner raw
622/// pointer because the address behind the raw pointer could be reused after
623/// the cursor has been closed.)
624///
625/// Like database handles, cursor handles have no lifetime argument.
626///
627/// It is an error if a cursor handle is used on a different transaction.
628/// Method [`CursorBackend::assert_txn_backend`] can be used to panic in that
629/// case.
630///
631/// This struct implements `Send` and `Sync`, thus proper synchronization must
632/// be ensured when using it (which is done by making operations work on
633/// transaction handles).
634//
635// SAFETY: Creators must ensure that a corresponding `Db` handle exists which
636// gets dropped after `CursorBackend` gets dropped. See `CursorBackend::drop`.
637#[derive(Debug)]
638struct CursorBackend {
639 inner: *mut lmdb::MDB_cursor,
640 closed: AtomicBool,
641 inner_txn: *mut lmdb::MDB_txn,
642}
643
644// SAFETY: `Cursor`s can only be used with a `TxnRo` or `TxnRw`. Those
645// transaction handles implement `Send` or `!Send` as necessary.
646unsafe impl Send for CursorBackend {}
647// SAFETY: `Cursor`s can only be used with a `TxnRo` or `TxnRw`. Those
648// transaction handles implement `Sync` or `!Sync` as necessary.
649unsafe impl Sync for CursorBackend {}
650
651impl CursorBackend {
652 /// Panic if cursor handle does not belong to transaction
653 fn assert_txn_backend(&self, txn_backend: &TxnBackend) {
654 if self.closed.load(Relaxed) || self.inner_txn != txn_backend.inner {
655 panic!("cursor does not belong to transaction");
656 }
657 }
658}
659
660impl Drop for CursorBackend {
661 fn drop(&mut self) {
662 // NOTE: mutex should never be poisoned
663 if !*self.closed.get_mut() {
664 // SAFETY:
665 // * If the cursor was closed before, then
666 // `TxnBackend::close_cursors` would read as `true` because
667 // the only other place where the cursor might have been closed
668 // is `TxnBackend::close_cursors`.
669 // * `CursorBackend` is always dropped before the last associated
670 // `Db` handle and thus before the `DbBackend` is dropped. See
671 // SAFETY comment on `CursorBackend`. Therefore,
672 // `lmdb::mdb_cursor_close` gets called before
673 // `lmdb::mdb_dbi_close`. Note that the LMDB API specification
674 // is unclear about whether this is a requirement for read-only
675 // transactions. For read-write transactions where there has
676 // been an actual write to the database, this is ensured also
677 // additionally due to storing the `DbBackend` in
678 // `TxnRw:used_dbs`.
679 unsafe {
680 lmdb::mdb_cursor_close(self.inner);
681 }
682 }
683 }
684}
685
686#[derive(Debug)]
687/// Cursor for reading from or writing to a particular database
688///
689/// A new cursor can be created using [`Txn::new_cursor`]. It is used by
690/// invoking one of the cursor methods in [`Txn`] or [`TxnRw`].
691pub struct Cursor<K: ?Sized, V: ?Sized, C> {
692 // SAFETY: field order matters, see `CursorBackend::drop`
693 backend: ArcByAddr<CursorBackend>,
694 db: Db<K, V, C>,
695}
696
697impl<K: ?Sized, V: ?Sized, C> Clone for Cursor<K, V, C>
698where
699 C: Constraint,
700{
701 fn clone(&self) -> Self {
702 Cursor {
703 db: self.db.clone(),
704 backend: self.backend.clone(),
705 }
706 }
707}
708
709impl<K: ?Sized, V: ?Sized, C> PartialEq for Cursor<K, V, C> {
710 fn eq(&self, other: &Self) -> bool {
711 self.backend == other.backend
712 }
713}
714
715impl<K: ?Sized, V: ?Sized, C> Eq for Cursor<K, V, C> {}
716
717/// Options for opening an environment
718///
719/// This struct follows the builder pattern. Start with [`EnvBuilder::new`] and
720/// open the environment with [`EnvBuilder::open_ro`] (for read-only access) or
721/// [`EnvBuilder::open_rw`] (for read-write access).
722///
723/// # Caveats / Safety
724///
725/// Do not open the same environment twice in the same process at the same
726/// time.
727///
728/// Since this cannot be easily ensured (and because the environment must be
729/// free of corruption on the storage medium), [`open_ro`](EnvBuilder::open_ro)
730/// and [`open_rw`](EnvBuilder::open_rw) are marked as `unsafe`.
731/// Use [`Env::clone_ro`] to (re-)open an already open environment in read-only
732/// mode.
733///
734/// The file format is platform specific, so endianness and word size must not
735/// change across use.
736///
737/// # Example
738///
739/// ```
740/// # fn doit() -> std::io::Result<()> {
741/// // question mark operator may return with `std::io::Error`
742/// use mmtkvdb::{self as kv, traits::*};
743/// use tempfile::tempdir;
744/// let location = tempdir()?;
745/// let env_builder = kv::EnvBuilder::new().dir(location.path()).max_dbs(1);
746/// // SAFETY:
747/// // * database isn't opened twice in this process at the same time
748/// // * it is assumed no other processes access the environment
749/// // * since environment is newly created, it matches the platform
750/// let mut env_rw = unsafe { env_builder.open_rw() }?;
751/// let env_ro = env_rw.clone_ro();
752/// # Ok(())
753/// # }
754/// # doit().expect("error during database access")
755/// ```
756#[derive(Clone, Debug)]
757pub struct EnvBuilder<P> {
758 path: P,
759 lmdb_flags: LmdbFlags,
760 max_size: usize,
761 max_readers: usize,
762 max_dbs: usize,
763 unix_file_mode: u32,
764}
765
766/// Constraints on database (type argument `C` to [`DbBuilder`] and [`Db`])
767pub trait Constraint: Copy + 'static {
768 /// Duplicate keys allowed?
769 const DUPLICATE_KEYS: bool;
770}
771
772/// Type argument to [`DbBuilder`] and [`Db`] indicating unique keys
773#[derive(Clone, Copy, Debug)]
774pub struct KeysUnique;
775
776impl Constraint for KeysUnique {
777 const DUPLICATE_KEYS: bool = false;
778}
779
780/// Type argument to [`DbBuilder`] and [`Db`] indicating non-unique keys
781#[derive(Clone, Copy, Debug)]
782pub struct KeysDuplicate;
783
784impl Constraint for KeysDuplicate {
785 const DUPLICATE_KEYS: bool = true;
786}
787
788/// Type alias for `DbBuilder` which has a [name](DbBuilder::name) set (or
789/// unnamed database selected)
790///
791/// Values of type `DbSpec<K, V, C>` are created by creating a new
792/// [`DbBuilder`] with [`DbBuilder::new`], setting the desired options, and
793/// then invoking [`DbBuilder::name`] or [`DbBuilder::unnamed`].
794///
795/// Values of type `DbSpec<K, V, C>` can be passed to the following methods in
796/// order to create and/or open databases:
797///
798/// * [`Env::open_db`]
799/// * [`EnvRw::create_db`] (same as `Env::open_db` but creates the database if
800/// not existent)
801/// * [`EnvRef::open_or_create_db`] (never creates the database if called on
802/// `&EnvRo`)
803pub type DbSpec<K, V, C> = DbBuilder<K, V, C, Option<CString>>;
804
805/// Options for opening a database
806///
807/// This struct follows the builder pattern. Start with [`DbBuilder::new`] and
808/// pass completed builder by (shared) reference to [`Env::open_db`] or
809/// [`EnvRw::create_db`] to open or create a database.
810///
811/// # Caveats / Safety
812///
813/// When opening an already existing database, all options must be compatible
814/// with the previously used options. It has be be ensured by the caller that
815/// this is true. Thus [`Env::open_db`] and [`EnvRw::create_db`] are marked as
816/// `unsafe`.
817///
818/// [`DbBuilder::name`] has to be called last (after setting all other
819/// options). This is due to some restrictions regarding `const fn`s.
820/// A compiler error will be reported if the methods are invoked in the wrong
821/// order.
822///
823/// # Type arguments
824///
825/// The type arguments `K`, `V`, and `C` reflect the type used for keys and
826/// values, and whether duplicate keys are allowed (`C` being [`KeysUnique`] or
827/// [`KeysDuplicate`]), respectively.
828///
829/// The fourth type parameter `N` determines if a [name](DbBuilder::name)
830/// <small>(or the [unnamed database](DbBuilder::unnamed))</small>
831/// has been set, or if a name must be selected yet. `N` is `()` if no database
832/// name has been selected yet. Once a name has been selected, you can use the
833/// type alias [`DbSpec<K, V, C>`] to refer to the completed builder.
834///
835/// # Example
836///
837/// ```
838/// # fn doit() -> std::io::Result<()> {
839/// // question mark operator may return with `std::io::Error`
840/// use mmtkvdb::{self as kv, Txn as _};
841/// use tempfile::tempdir;
842/// let location = tempdir()?;
843/// let env_builder = kv::EnvBuilder::new().dir(location.path()).max_dbs(1);
844/// // SAFETY:
845/// // * database isn't opened twice in this process at the same time
846/// // * it is assumed no other processes access the environment
847/// // * since environment is newly created, it matches the platform
848/// let mut env_rw = unsafe { env_builder.open_rw() }?;
849/// let db_opts = kv::DbBuilder::new()
850/// .key_type::<str>()
851/// .value_type::<i32>()
852/// .name("account_balance");
853/// // SAFETY: database doesn't exist yet (or would need compatible options)
854/// let db = unsafe { env_rw.create_db(&db_opts) }?;
855/// # Ok(())
856/// # }
857/// # doit().expect("error during database access")
858/// ```
859#[derive(Clone, Debug)]
860pub struct DbBuilder<K: ?Sized, V: ?Sized, C, N> {
861 key: PhantomData<fn(&K) -> &K>,
862 value: PhantomData<fn(&V) -> &V>,
863 constraint: C,
864 lmdb_flags: LmdbFlags,
865 name: N,
866}
867
868/// Some default values used for [`EnvBuilder`]
869pub mod env_builder_defaults {
870 /// Default value for `max_size`
871 pub const MAX_SIZE: usize = 1024 * 1024 * 1024;
872 /// Default value for `max_readers`
873 pub const MAX_READERS: usize = 126;
874 /// Default value for `max_dbs`
875 pub const MAX_DBS: usize = 0;
876 /// Default value for `unix_file_mode`
877 pub const UNIX_FILE_MODE: u32 = 0o600;
878}
879
880impl EnvBuilder<()> {
881 /// Default options for environment
882 pub const fn new() -> Self {
883 Self {
884 path: (),
885 // SAFETY: `lmdb::MDB_NOTLS` is required for `TxnRo` to be `Send`
886 // and for `EnvRo::open_or_opt_create_db` to be called with its
887 // `create` argument set to `false` while other transactions are
888 // open
889 lmdb_flags: lmdb::MDB_NORDAHEAD as LmdbFlags | lmdb::MDB_NOTLS as LmdbFlags,
890 max_size: env_builder_defaults::MAX_SIZE,
891 max_readers: env_builder_defaults::MAX_READERS,
892 max_dbs: env_builder_defaults::MAX_DBS,
893 unix_file_mode: env_builder_defaults::UNIX_FILE_MODE,
894 }
895 }
896}
897
898impl<P> EnvBuilder<P> {
899 /// Set directory location
900 ///
901 /// Overwrites a previously set directory or file location.
902 pub fn dir<'a>(mut self, path: &'a (impl AsRef<Path> + ?Sized)) -> EnvBuilder<&'a Path> {
903 flag_set!(self, MDB_NOSUBDIR, false);
904 EnvBuilder {
905 path: path.as_ref(),
906 lmdb_flags: self.lmdb_flags,
907 max_size: self.max_size,
908 max_readers: self.max_readers,
909 max_dbs: self.max_dbs,
910 unix_file_mode: self.unix_file_mode,
911 }
912 }
913 /// Set file location
914 ///
915 /// Overwrites a previously set directory or file location.
916 pub fn file<'a>(mut self, path: &'a (impl AsRef<Path> + ?Sized)) -> EnvBuilder<&'a Path> {
917 flag_set!(self, MDB_NOSUBDIR, true);
918 EnvBuilder {
919 path: path.as_ref(),
920 lmdb_flags: self.lmdb_flags,
921 max_size: self.max_size,
922 max_readers: self.max_readers,
923 max_dbs: self.max_dbs,
924 unix_file_mode: self.unix_file_mode,
925 }
926 }
927 /// Get write-map option
928 pub const fn get_writemap(&self) -> bool {
929 flag_get!(self, MDB_WRITEMAP)
930 }
931 /// Set or clear write-map option
932 ///
933 /// Enabling this option is incompatible with nested transactions
934 /// (see [`TxnRw::nested`]).
935 pub const fn writemap(mut self, flag: bool) -> Self {
936 flag_set!(self, MDB_WRITEMAP, flag);
937 self
938 }
939 /// Get no-meta-sync option
940 pub const fn get_nometasync(&self) -> bool {
941 flag_get!(self, MDB_NOMETASYNC)
942 }
943 /// Set or clear no-meta-sync option
944 pub const fn nometasync(mut self, flag: bool) -> Self {
945 flag_set!(self, MDB_NOMETASYNC, flag);
946 self
947 }
948 /// Get no-sync option
949 pub const fn get_nosync(&self) -> bool {
950 flag_get!(self, MDB_NOSYNC)
951 }
952 /// Set or clear no-sync option
953 pub const fn nosync(mut self, flag: bool) -> Self {
954 flag_set!(self, MDB_NOSYNC, flag);
955 self
956 }
957 /// Get map-async option
958 pub const fn get_mapasync(&self) -> bool {
959 flag_get!(self, MDB_MAPASYNC)
960 }
961 /// Set or clear map-async option
962 pub const fn mapasync(mut self, flag: bool) -> Self {
963 flag_set!(self, MDB_MAPASYNC, flag);
964 self
965 }
966 /// Get no-read-ahead option
967 pub const fn get_nordahead(&self) -> bool {
968 flag_get!(self, MDB_NORDAHEAD)
969 }
970 /// Set or clear no-read-ahead option
971 pub const fn nordahead(mut self, flag: bool) -> Self {
972 flag_set!(self, MDB_NORDAHEAD, flag);
973 self
974 }
975 /// Get maximum environment size
976 pub const fn get_max_size(&self) -> usize {
977 self.max_size
978 }
979 /// Set maximum environment size
980 pub const fn max_size(mut self, max_size: usize) -> Self {
981 self.max_size = max_size;
982 self
983 }
984 /// Get maximum number of concurrent readers
985 pub const fn get_max_readers(&self) -> usize {
986 self.max_readers
987 }
988 /// Set maximum number of concurrent readers
989 pub const fn max_readers(mut self, max_readers: usize) -> Self {
990 self.max_readers = max_readers;
991 self
992 }
993 /// Get maximum number of named databases
994 pub const fn get_max_dbs(&self) -> usize {
995 self.max_dbs
996 }
997 /// Set maximum number of named databases
998 pub const fn max_dbs(mut self, max_dbs: usize) -> Self {
999 self.max_dbs = max_dbs;
1000 self
1001 }
1002 /// Get file mode
1003 pub const fn get_unix_file_mode(&self) -> u32 {
1004 self.unix_file_mode
1005 }
1006 /// Set file mode
1007 pub const fn unix_file_mode(mut self, mode: u32) -> Self {
1008 self.unix_file_mode = mode;
1009 self
1010 }
1011 /// Check if transactions are nestable with current options
1012 ///
1013 /// See [`TxnRw::nested`].
1014 pub const fn nestable_txns(&self) -> bool {
1015 !self.get_writemap()
1016 }
1017}
1018
1019impl<'a> EnvBuilder<&'a Path> {
1020 /// Open environment
1021 ///
1022 /// # Safety
1023 ///
1024 /// See [`EnvBuilder::open_ro`] and [`EnvBuilder::open_rw`]
1025 unsafe fn open_with_readonly_flag(&self, readonly: bool) -> io::Result<EnvRo> {
1026 let path = path_to_cstring(self.path)?;
1027 let mut inner = MaybeUninit::<*mut lmdb::MDB_env>::uninit();
1028 // SAFETY:
1029 // * `lmdb::mdb_env_create` doesn't have any requirements
1030 // * the status passed to `check_err_code` is valid
1031 unsafe {
1032 check_err_code(lmdb::mdb_env_create(inner.as_mut_ptr()))?;
1033 }
1034 // SAFETY: call to `lmdb::mdb_env_create` above has been successful as
1035 // otherwise `check_err_code` would have returned an `Err` resulting in
1036 // early return from this function
1037 let inner = unsafe { inner.assume_init() };
1038 let backend = EnvBackend {
1039 inner: inner,
1040 id: ENV_COUNTER.fetch_add(1, Relaxed),
1041 max_keysize: unsafe { lmdb::mdb_env_get_maxkeysize(inner) }
1042 .try_into()
1043 .unwrap_or(usize::MAX),
1044 nestable_txns: self.nestable_txns(),
1045 dbis: Default::default(),
1046 };
1047 // SAFETY:
1048 // * `lmdb::mdb_env_set_mapsize` doesn't have any requirements
1049 // * the status passed to `check_err_code` is valid
1050 unsafe {
1051 check_err_code(lmdb::mdb_env_set_mapsize(
1052 backend.inner,
1053 self.max_size.try_into().map_err(|_| {
1054 io::Error::new(io::ErrorKind::InvalidInput, "maximum size invalid")
1055 })?,
1056 ))?;
1057 }
1058 // SAFETY:
1059 // * `lmdb::mdb_env_set_maxreaders` doesn't have any requirements
1060 // (other than being called before `mdb_env_open` which is the case
1061 // and would otherwise only result in EINVAL being returned)
1062 // * the status passed to `check_err_code` is valid
1063 unsafe {
1064 check_err_code(lmdb::mdb_env_set_maxreaders(
1065 backend.inner,
1066 self.max_readers.try_into().map_err(|_| {
1067 io::Error::new(io::ErrorKind::InvalidInput, "maximum reader count invalid")
1068 })?,
1069 ))?;
1070 }
1071 // SAFETY:
1072 // * `lmdb::mdb_env_set_maxdbs` doesn't have any requirements
1073 // (other than being called before `mdb_env_open` which is the case
1074 // and would otherwise only result in EINVAL being returned)
1075 // * the status passed to `check_err_code` is valid
1076 unsafe {
1077 check_err_code(lmdb::mdb_env_set_maxdbs(
1078 backend.inner,
1079 self.max_dbs.try_into().map_err(|_| {
1080 io::Error::new(
1081 io::ErrorKind::InvalidInput,
1082 "maximum number of named databases invalid",
1083 )
1084 })?,
1085 ))?;
1086 }
1087 // SAFETY:
1088 // * safety requirements are documented in `EnvBuilder::env_ro` and
1089 // `EnvBuilder::env_rw`
1090 // * the status passed to `check_err_code` is valid
1091 unsafe {
1092 check_err_code(lmdb::mdb_env_open(
1093 backend.inner,
1094 path.as_ptr(),
1095 if readonly {
1096 self.lmdb_flags | lmdb::MDB_RDONLY as LmdbFlags
1097 } else {
1098 self.lmdb_flags
1099 },
1100 self.unix_file_mode.try_into().map_err(|_| {
1101 io::Error::new(
1102 io::ErrorKind::InvalidInput,
1103 "file mode bits integer invalid",
1104 )
1105 })?,
1106 ))?;
1107 }
1108 Ok(EnvRo {
1109 backend: Arc::new(backend),
1110 })
1111 }
1112 /// Open environment in read-only mode
1113 ///
1114 /// # Safety
1115 ///
1116 /// * Do not open the same environment twice in the same process at the
1117 /// same time.
1118 /// * The environment must be free of corruption on the storage medium and
1119 /// no other processes may corrupt the environment on the storage medium.
1120 /// * The file format is platform specific, so endianness and word size
1121 /// must not change across use.
1122 pub unsafe fn open_ro(&self) -> io::Result<EnvRo> {
1123 // SAFETY: requirements of `EnvBuilder::open_with_readonly_flag` are
1124 // the same as documented by `EnvBuilder::open_ro`
1125 Ok(unsafe { self.open_with_readonly_flag(true) }?)
1126 }
1127 /// Open environment in read-write mode
1128 ///
1129 /// # Safety
1130 ///
1131 /// * Do not open the same environment twice in the same process at the
1132 /// same time.
1133 /// * The environment must be free of corruption on the storage medium and
1134 /// no other processes may corrupt the environment on the storage medium.
1135 /// * The file format is platform specific, so endianness and word size
1136 /// must not change across use.
1137 pub unsafe fn open_rw(&self) -> io::Result<EnvRw> {
1138 // SAFETY: requirements of `EnvBuilder::open_with_readonly_flag` are
1139 // the same as documented by `EnvBuilder::open_rw`
1140 Ok(EnvRw {
1141 env_ro: unsafe { self.open_with_readonly_flag(false) }?,
1142 })
1143 }
1144}
1145
1146impl DbBuilder<[u8], [u8], KeysUnique, ()> {
1147 /// Default options for database
1148 pub const fn new() -> Self {
1149 Self {
1150 key: PhantomData,
1151 value: PhantomData,
1152 constraint: KeysUnique,
1153 lmdb_flags: 0,
1154 name: (),
1155 }
1156 }
1157}
1158
1159impl<K: ?Sized, V: ?Sized, C> DbBuilder<K, V, C, ()>
1160where
1161 C: Constraint,
1162{
1163 /// Clear `dupsort` option (also clears `reversedup` option)
1164 ///
1165 /// See [`DbBuilder::keys_duplicate`] for setting `dupsort` option and
1166 /// [`DbBuilder::has_duplicate_keys`] for getting current state of flag.
1167 pub const fn keys_unique(mut self) -> DbBuilder<K, V, KeysUnique, ()> {
1168 flag_set!(self, MDB_DUPSORT, false);
1169 flag_set!(self, MDB_REVERSEDUP, false);
1170 DbBuilder {
1171 constraint: KeysUnique,
1172 key: PhantomData,
1173 value: PhantomData,
1174 lmdb_flags: self.lmdb_flags,
1175 name: self.name,
1176 }
1177 }
1178 /// Set `dupsort` option
1179 ///
1180 /// Note: Using this option will put an additional constraint on the size
1181 /// of each value stored in the database (see [`Env::max_keysize`]).
1182 ///
1183 /// See [`DbBuilder::keys_unique`] for clearing `dupsort` option and
1184 /// [`DbBuilder::has_duplicate_keys`] for getting current state of flag.
1185 pub const fn keys_duplicate(mut self) -> DbBuilder<K, V, KeysDuplicate, ()> {
1186 flag_set!(self, MDB_DUPSORT, true);
1187 DbBuilder {
1188 constraint: KeysDuplicate,
1189 key: PhantomData,
1190 value: PhantomData,
1191 lmdb_flags: self.lmdb_flags,
1192 name: self.name,
1193 }
1194 }
1195 /// Set stored key type
1196 ///
1197 /// It's possible to use [`NoKey`] as type parameter for databases which
1198 /// contain only a single value.
1199 pub const fn key_type<T>(mut self) -> DbBuilder<T, V, C, ()>
1200 where
1201 T: ?Sized + Storable,
1202 {
1203 flag_set!(self, MDB_INTEGERKEY, T::OPTIMIZE_INT);
1204 DbBuilder {
1205 key: PhantomData,
1206 value: PhantomData,
1207 constraint: self.constraint,
1208 lmdb_flags: self.lmdb_flags,
1209 name: self.name,
1210 }
1211 }
1212 /// Set stored value type
1213 ///
1214 /// It's possible to use [`NoValue`] as type parameter for databases which
1215 /// only contain keys but no values.
1216 pub const fn value_type<T>(mut self) -> DbBuilder<K, T, C, ()>
1217 where
1218 T: ?Sized + Storable,
1219 {
1220 flag_set!(self, MDB_DUPFIXED, T::CONST_BYTES_LEN);
1221 flag_set!(self, MDB_INTEGERDUP, T::OPTIMIZE_INT);
1222 DbBuilder {
1223 value: PhantomData,
1224 key: PhantomData,
1225 constraint: self.constraint,
1226 lmdb_flags: self.lmdb_flags,
1227 name: self.name,
1228 }
1229 }
1230}
1231
1232impl<K: ?Sized, V: ?Sized, C, N> DbBuilder<K, V, C, N>
1233where
1234 C: Constraint,
1235{
1236 /// Get `reversekey` option
1237 pub const fn get_reversekey(&self) -> bool {
1238 flag_get!(self, MDB_REVERSEKEY)
1239 }
1240 /// Set or clear `reversekey` option
1241 pub const fn reversekey(mut self, flag: bool) -> Self {
1242 flag_set!(self, MDB_REVERSEKEY, flag);
1243 self
1244 }
1245 /// Return sanitized flags for LMDB
1246 /// (clear flags that are only used for for databases with duplicate keys
1247 /// if unique keys have been selected)
1248 const fn cleansed_lmdb_flags(&self) -> LmdbFlags {
1249 if flag_get!(self, MDB_DUPSORT) {
1250 self.lmdb_flags
1251 } else {
1252 self.lmdb_flags
1253 & !((lmdb::MDB_REVERSEDUP | lmdb::MDB_DUPFIXED | lmdb::MDB_INTEGERDUP) as LmdbFlags)
1254 }
1255 }
1256 /// Use unnamed database
1257 ///
1258 /// Should be used as last builder method because some other builder
1259 /// methods are not available anymore after calling this method.
1260 pub fn unnamed(self) -> DbSpec<K, V, C> {
1261 DbBuilder {
1262 value: PhantomData,
1263 key: PhantomData,
1264 constraint: self.constraint,
1265 lmdb_flags: self.lmdb_flags,
1266 name: None,
1267 }
1268 }
1269 /// Use named database (name must not contain a null byte)
1270 ///
1271 /// Should be used as last builder method because some other builder
1272 /// methods are not available anymore after calling this method.
1273 pub fn name<T: Into<Vec<u8>>>(self, name: T) -> DbSpec<K, V, C> {
1274 let name = CString::new(name).expect("database name must not contain NULL byte");
1275 DbBuilder {
1276 value: PhantomData,
1277 key: PhantomData,
1278 constraint: self.constraint,
1279 lmdb_flags: self.lmdb_flags,
1280 name: Some(name),
1281 }
1282 }
1283 /// Remove name information
1284 ///
1285 /// NOTE: This does not select the unnamed database. To select the unnamed
1286 /// database, use [`DbBuilder::unnamed`].
1287 pub fn strip_name<T: Into<Vec<u8>>>(self) -> DbBuilder<K, V, C, ()> {
1288 DbBuilder {
1289 value: PhantomData,
1290 key: PhantomData,
1291 constraint: self.constraint,
1292 lmdb_flags: self.lmdb_flags,
1293 name: (),
1294 }
1295 }
1296}
1297
1298impl<K: ?Sized, V: ?Sized, C: Constraint, N> DbBuilder<K, V, C, N> {
1299 /// Returns true if duplicate keys are allowed (i.e. is `dupsort` option set?)
1300 ///
1301 /// See [`DbBuilder::keys_unique`] and [`DbBuilder::keys_duplicate`].
1302 pub fn has_duplicate_keys(&self) -> bool {
1303 C::DUPLICATE_KEYS
1304 }
1305}
1306
1307impl<K: ?Sized, V: ?Sized, N> DbBuilder<K, V, KeysDuplicate, N> {
1308 /// Get `reversedup` option
1309 pub const fn get_reversedup(&self) -> bool {
1310 flag_get!(self, MDB_REVERSEDUP)
1311 }
1312 /// Set or clear `reversedup` option
1313 pub const fn reversedup(mut self, flag: bool) -> Self {
1314 flag_set!(self, MDB_REVERSEDUP, flag);
1315 self
1316 }
1317}
1318
1319/// Function passed to [`lmdb::mdb_set_compare`] to compare two keys or values
1320/// in the database
1321///
1322/// # Safety
1323///
1324/// * Pointers `a` and `b` must be valid.
1325/// * For each `a` and `b`, [`lmdb::MDB_val::mv_data`] must be valid for reads
1326/// for the corresponding [`lmdb::MDB_val::mv_size`] bytes (no alignment
1327/// required and `mv_size` may differ for `a` and `b`) until the function
1328/// returns.
1329/// * The pointed to bytes must have been created with
1330/// [`<T as Storable>::to_bytes`](Storable::to_bytes) on the same platform
1331/// (same endianess / word size).
1332///
1333/// Note: Regardless of whether it is considered to be UB to unwind from an
1334/// `extern "C" fn`, this function can never unwind if implementors of
1335/// [`Storable::cmp_bytes_unchecked`] ensure that `cmp_bytes_unchecked` never
1336/// unwinds (which is demanded by the corresponding `unsafe` trait
1337/// documentation).
1338unsafe extern "C" fn compare_function<T>(a: *const lmdb::MDB_val, b: *const lmdb::MDB_val) -> c_int
1339where
1340 T: ?Sized + Storable,
1341{
1342 // SAFETY: `a` and `b` fulfill the documented requirements of
1343 // `compare_function`
1344 unsafe {
1345 let a: &[u8] = slice::from_raw_parts((*a).mv_data as *const u8, (*a).mv_size);
1346 let b: &[u8] = slice::from_raw_parts((*b).mv_data as *const u8, (*b).mv_size);
1347 T::cmp_bytes_unchecked(a, b) as c_int
1348 }
1349}
1350
1351impl EnvRo {
1352 /// Return true if given size (in bytes) is valid for a key (or value in a
1353 /// database with duplicate keys)
1354 fn size_valid_keysize(&self, size: usize) -> bool {
1355 (1..=self.max_keysize()).contains(&size)
1356 }
1357 /// Return true if given slice has valid size/length as key (or value in a
1358 /// database with duplicate keys)
1359 fn slice_valid_keysize(&self, key: &[u8]) -> bool {
1360 self.size_valid_keysize(key.len())
1361 }
1362 /// Return error if slice doesn't have valid size/length as key
1363 fn assert_valid_keysize(&self, key: &[u8]) -> io::Result<()> {
1364 if self.slice_valid_keysize(key) {
1365 Ok(())
1366 } else {
1367 Err(io::Error::new(
1368 io::ErrorKind::InvalidData,
1369 "invalid key size",
1370 ))
1371 }
1372 }
1373 /// Return error if slice doesn't have valid size/length as value in a
1374 /// database with duplicate keys
1375 fn assert_valid_valuesize(&self, value: &[u8]) -> io::Result<()> {
1376 if self.slice_valid_keysize(value) {
1377 Ok(())
1378 } else {
1379 Err(io::Error::new(
1380 io::ErrorKind::InvalidData,
1381 "invalid value size",
1382 ))
1383 }
1384 }
1385 /// Opens several databases at once (which must be of the same key and
1386 /// value type and key constraint)
1387 ///
1388 /// # Safety
1389 ///
1390 /// * If a database exists already, it must have been created with
1391 /// compatible options.
1392 /// * Implementation of [`Storable`] for the used
1393 /// [keys](DbBuilder::key_type) and [values](DbBuilder::value_type) must
1394 /// behave the same as when the database was created.
1395 /// * If `create` is true, then this method call must be synchronized and
1396 /// no write transaction must be currently open by the current thread.
1397 unsafe fn open_or_opt_create_db<'a, K, V, C>(
1398 &self,
1399 db_spec: &DbSpec<K, V, C>,
1400 create: bool,
1401 ) -> io::Result<Db<K, V, C>>
1402 where
1403 K: ?Sized + Storable + 'a,
1404 V: ?Sized + Storable + 'a,
1405 C: Constraint,
1406 {
1407 let mut dbis = self.backend.dbis.lock().unwrap();
1408 let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
1409 // SAFETY:
1410 // * It's allowed to open several read-only transactions (`create` is
1411 // `false`).
1412 // * If `create` is true, then documentation requires that this
1413 // method is synchronized and no write transaction must be
1414 // currently open by the current thread.
1415 // * The transaction is only used by the current thread (only within
1416 // this function as `TxnBackend` is dropped before this function
1417 // returns or panics).
1418 // * There are no cursors created here which could be used by other
1419 // threads or span transactions.
1420 // * The status passed to `check_err_code` is valid.
1421 unsafe {
1422 check_err_code(lmdb::mdb_txn_begin(
1423 self.backend.inner,
1424 null_mut(),
1425 if create {
1426 0 as LmdbFlags
1427 } else {
1428 lmdb::MDB_RDONLY as LmdbFlags
1429 },
1430 txn_inner.as_mut_ptr(),
1431 ))?;
1432 }
1433 // SAFETY: call to `lmdb::mdb_txn_begin` above has been successful as
1434 // otherwise `check_err_code` would have returned an `Err` resulting in
1435 // early return from this function
1436 let txn_inner = unsafe { txn_inner.assume_init() };
1437 let txn = TxnBackend {
1438 env_ro: self,
1439 inner: txn_inner,
1440 cursors: Default::default(),
1441 };
1442 let mut db_inner = MaybeUninit::<lmdb::MDB_dbi>::uninit();
1443 let mut flags = db_spec.cleansed_lmdb_flags();
1444 if create {
1445 flags |= lmdb::MDB_CREATE as LmdbFlags;
1446 }
1447 // SAFETY:
1448 // * Mutex `EnvBackend::dbis` is locked and ensures that
1449 // `lmdb::mdb_dbi_close` is not called concurrently. This is
1450 // important because we must determine below if the returned
1451 // `lmdb::MDB_dbi` integer belongs to an already open database,
1452 // in which case it must be ensured that it is only closed
1453 // once.
1454 // * The database handle, if a new one, can only be used by other
1455 // transactions after the corresponding transaction is
1456 // successfully committed below.
1457 // * If the transaction is aborted, `lmdb::mdb_dbi_close` will not be
1458 // called because the database handle will be closed automatically.
1459 // * If and only if the transaction is committed (and the
1460 // `lmdb::MDB_dbi` handle is a new handle), a `DbBackend` is
1461 // created which will take care of closing the database handle when
1462 // dropped.
1463 // * `lmdb::mdb_dbi_open` is only called here and the locked mutex
1464 // `EnvBackend::dbis` ensures that `lmdb::mdb_dbi_open` is not
1465 // called from multiple concurrent transactions in the same process
1466 // (for the same environment).
1467 // * Documentation of this function (`open_or_opt_create_db`) demands
1468 // that if a database exists already, it must have been created
1469 // with compatible options. Documentation of `EnvBuilder::open_ro`
1470 // and `EnvBuilder::open_rw` makes additional demands in regard to
1471 // the database being free of corruption and having been
1472 // created on a compatible platform. In addition with the
1473 // requirements for implementing the unsafe trait `Storable` for
1474 // key and value types, as well as invoking
1475 // `DbBuilder::cleansed_lmdb_flags` above, it is ensured that the
1476 // `flags` are valid and that they match any previous creation of
1477 // the database.
1478 // * The status passed to `check_err_code` is valid.
1479 unsafe {
1480 check_err_code(lmdb::mdb_dbi_open(
1481 txn.inner,
1482 db_spec.name.as_ref().map_or(null(), |x| x.as_ptr()),
1483 flags,
1484 db_inner.as_mut_ptr(),
1485 ))?;
1486 }
1487 // SAFETY: call to `lmdb::mdb_dbi_open` above has been successful as
1488 // otherwise `check_err_code` would have returned an `Err` resulting in
1489 // early return from this function
1490 let db_inner = unsafe { db_inner.assume_init() };
1491 let backend = match dbis.get(&db_inner).and_then(Weak::upgrade) {
1492 Some(arc) => {
1493 txn.commit()?;
1494 arc
1495 }
1496 None => {
1497 // SAFETY:
1498 // * Functions `mdb_set_compare` and `mdb_set_dupsort` are
1499 // always called before any data access through the
1500 // database handle can happen.
1501 // * Documentation of this function (`open_or_opt_create_db`)
1502 // demands that if a database exists already, it must have
1503 // been created with compatible options. Documentation of
1504 // `EnvBuilder::open_ro` and `EnvBuilder::open_rw` makes
1505 // additional demands in regard to the database being free
1506 // of corruption and having been created on a compatible
1507 // platform. This ensures that `mdb_set_compare` and
1508 // `mdb_set_dupsort` are set consistently in regard to when
1509 // the database was created.
1510 // * `mdb_set_compare` and `mdb_set_dupsort` are expected to
1511 // ensure that only valid pointers are passed to the
1512 // `compare_function`.
1513 // * `compare_function` does not unwind. (If it would unwind,
1514 // it could leave the environment in an invalid state.
1515 // TODO: Adjust this remark, the corresponding comment on
1516 // `compare_function`, and the `Storable` trait when/if
1517 // Rust implements an abort-on-panic shim for
1518 // `extern "C" fn`.)
1519 // * The status passed to `check_err_code` is valid.
1520 unsafe {
1521 if !K::TRIVIAL_CMP && !K::OPTIMIZE_INT {
1522 check_err_code(lmdb::mdb_set_compare(
1523 txn.inner,
1524 db_inner,
1525 Some(compare_function::<K>),
1526 ))?;
1527 }
1528 if !V::TRIVIAL_CMP && !V::OPTIMIZE_INT && C::DUPLICATE_KEYS {
1529 check_err_code(lmdb::mdb_set_dupsort(
1530 txn.inner,
1531 db_inner,
1532 Some(compare_function::<V>),
1533 ))?;
1534 }
1535 }
1536 let env_backend = Arc::downgrade(&self.backend);
1537 let env_id = self.backend.id;
1538 txn.commit()?;
1539 let db_backend = DbBackend {
1540 env_backend,
1541 env_id,
1542 inner: db_inner,
1543 };
1544 let arc = Arc::new(db_backend);
1545 dbis.insert(db_inner, Arc::downgrade(&arc));
1546 arc
1547 }
1548 };
1549 let db = Db {
1550 key: PhantomData,
1551 value: PhantomData,
1552 constraint: db_spec.constraint,
1553 backend: ArcByAddr::from(backend),
1554 };
1555 drop(dbis);
1556 Ok(db)
1557 }
1558}
1559
1560/// Read-write or read-only handle for accessing environment that stores
1561/// key-value databases
1562///
1563/// This trait is implemented by [`EnvRo`] and [`EnvRw`] and provides the
1564/// methods for read-only access to the environment. Read-write access is
1565/// performed by methods directly implemented on `EnvRw`.
1566pub trait Env: AsRef<EnvRo> {
1567 /// Reference conversion to [`EnvRo`]
1568 ///
1569 /// To obtain an owned [`EnvRo`], you can use [`Env::clone_ro`] as a
1570 /// shorthand for `.as_env_ro().clone()`.
1571 fn as_env_ro(&self) -> &EnvRo {
1572 self.as_ref()
1573 }
1574 /// Clone as [`EnvRo`]
1575 fn clone_ro(&self) -> EnvRo {
1576 self.as_env_ro().clone()
1577 }
1578 /// Start read-only transaction
1579 fn txn_ro(&self) -> io::Result<TxnRo<'_>>;
1580 /// Get maximum size of keys and duplicate data
1581 fn max_keysize(&self) -> usize;
1582 /// Checks if key or duplicate data has valid size
1583 fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
1584 where
1585 K: ?Sized + Storable + 'kr,
1586 KRef: StorableRef<'kr, K>;
1587 /// Open database in environment
1588 ///
1589 /// # Safety
1590 ///
1591 /// * If a database exists already, it must have been created with
1592 /// compatible options.
1593 /// * Implementation of [`Storable`] for the used
1594 /// [keys](DbBuilder::key_type) and [values](DbBuilder::value_type) must
1595 /// behave the same as when the database was created.
1596 unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
1597 where
1598 K: ?Sized + Storable,
1599 V: ?Sized + Storable,
1600 C: Constraint;
1601 /// Clear stale readers
1602 ///
1603 /// Refer to LMDB's documentation when to clear stale readers
1604 fn clear_stale_readers(&self) -> io::Result<usize>;
1605}
1606
1607impl Env for EnvRo {
1608 fn txn_ro(&self) -> io::Result<TxnRo<'_>> {
1609 unsafe {
1610 let mut inner_txn = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
1611 // SAFETY:
1612 // * `mdb_txn_begin` doesn't have any special requirements for
1613 // read-only transactions because `lmdb::MDB_NOTLS` is set
1614 // (and no child transactions are created for read-only
1615 // transactions either).
1616 // * The status passed to `check_err_code` is valid.
1617 check_err_code(lmdb::mdb_txn_begin(
1618 self.backend.inner,
1619 null_mut(),
1620 lmdb::MDB_RDONLY as LmdbFlags,
1621 inner_txn.as_mut_ptr(),
1622 ))?;
1623 // SAFETY: call to `lmdb::mdb_txn_begin` above has been successful
1624 // as otherwise `check_err_code` would have returned an `Err`
1625 // resulting in early return from this function
1626 let inner_txn = inner_txn.assume_init();
1627 Ok(TxnRo {
1628 backend: UnsafeCell::new(TxnBackend {
1629 env_ro: &self,
1630 inner: inner_txn,
1631 cursors: Default::default(),
1632 }),
1633 })
1634 }
1635 }
1636 fn max_keysize(&self) -> usize {
1637 self.backend.max_keysize
1638 }
1639 fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
1640 where
1641 K: ?Sized + Storable + 'kr,
1642 KRef: StorableRef<'kr, K>,
1643 {
1644 self.size_valid_keysize(key.ref_bytes_len())
1645 }
1646 unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
1647 where
1648 K: ?Sized + Storable,
1649 V: ?Sized + Storable,
1650 C: Constraint,
1651 {
1652 // SAFETY:
1653 // * Documentation of `Env::open_db` demands that if the database
1654 // exists already, it must have been created with compatible
1655 // options.
1656 // * Second argument of `open_or_opt_create_db` is set to `false`.
1657 unsafe { self.open_or_opt_create_db(options, false) }
1658 }
1659 fn clear_stale_readers(&self) -> io::Result<usize> {
1660 unsafe {
1661 let mut dead = MaybeUninit::<c_int>::uninit();
1662 // SAFETY:
1663 // * `mdb_reader_check` doesn't have any special requirements
1664 // * status passed to `check_err_code` is valid
1665 check_err_code(lmdb::mdb_reader_check(
1666 self.backend.inner,
1667 dead.as_mut_ptr(),
1668 ))?;
1669 // SAFETY: call to `lmdb::mdb_reader_check` above has been
1670 // successful as otherwise `check_err_code` would have returned an
1671 // `Err` resulting in early return from this function
1672 let dead = dead.assume_init();
1673 Ok(dead
1674 .try_into()
1675 .expect("reported number of stale reader transactions does not fit into usize"))
1676 }
1677 }
1678}
1679
1680impl Env for EnvRw {
1681 fn txn_ro(&self) -> io::Result<TxnRo<'_>> {
1682 self.as_env_ro().txn_ro()
1683 }
1684 fn max_keysize(&self) -> usize {
1685 self.as_env_ro().max_keysize()
1686 }
1687 fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
1688 where
1689 K: ?Sized + Storable + 'kr,
1690 KRef: StorableRef<'kr, K>,
1691 {
1692 self.as_env_ro().valid_keysize(key)
1693 }
1694 unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
1695 where
1696 K: ?Sized + Storable,
1697 V: ?Sized + Storable,
1698 C: Constraint,
1699 {
1700 // SAFETY: calls same trait method which has the same requirements
1701 unsafe { self.as_env_ro().open_db(options) }
1702 }
1703 fn clear_stale_readers(&self) -> io::Result<usize> {
1704 self.as_env_ro().clear_stale_readers()
1705 }
1706}
1707
1708impl EnvRw {
1709 /// Check if transactions are nestable
1710 ///
1711 /// See [`TxnRw::nested`].
1712 pub fn nestable_txns(&self) -> bool {
1713 self.env_ro.backend.nestable_txns
1714 }
1715 /// Open database in environment and create if non-existing
1716 ///
1717 /// # Safety
1718 ///
1719 /// * If a database exists already, it must have been created with
1720 /// compatible options.
1721 /// * Implementation of [`Storable`] for the used
1722 /// [keys](DbBuilder::key_type) and [values](DbBuilder::value_type) must
1723 /// behave the same as when the database was created.
1724 pub unsafe fn create_db<K, V, C>(
1725 &mut self,
1726 options: &DbSpec<K, V, C>,
1727 ) -> io::Result<Db<K, V, C>>
1728 where
1729 K: ?Sized + Storable,
1730 V: ?Sized + Storable,
1731 C: Constraint,
1732 {
1733 // SAFETY:
1734 // * Documentation of `EnvRw::create_db` demands that if the database
1735 // exists already, it must have been created with compatible
1736 // options.
1737 // * Calling `EnvRo::open_or_opt_create_db` is synchronized due to
1738 // `&mut self` reference.
1739 // * No write transaction can be open because `EnvRw::txn_rw` also
1740 // requires `&mut self`.
1741 unsafe { self.env_ro.open_or_opt_create_db(options, true) }
1742 }
1743 /// Delete database in environment
1744 ///
1745 /// This method panics if there are clones of the [`Db`] handle.
1746 /// Use [`TxnRw::delete_all`] to only delete the contents of the database.
1747 pub fn drop_db<K, V, C>(&mut self, db: Db<K, V, C>) -> io::Result<()>
1748 where
1749 K: ?Sized + Storable,
1750 V: ?Sized + Storable,
1751 C: Constraint,
1752 {
1753 let db_backend = ArcByAddr::try_unwrap(db.backend).map_err(|_| {
1754 io::Error::new(io::ErrorKind::Other, "database in use by current process")
1755 })?;
1756 db_backend.assert_env_backend(&self.env_ro.backend);
1757 let dbis = self.env_ro.backend.dbis.lock().unwrap();
1758 unsafe {
1759 let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
1760 // SAFETY:
1761 // * `&mut self` ensures that no other write transaction (for the
1762 // given environment) can be open by the current thread (and
1763 // whole process).
1764 // * The transaction is only used by the current thread because
1765 // it is only used within this function.
1766 // * There are no cursors created here which could be used by other
1767 // threads or span transactions.
1768 // * The status passed to `check_err_code` is valid.
1769 check_err_code(lmdb::mdb_txn_begin(
1770 self.env_ro.backend.inner,
1771 null_mut(),
1772 0 as LmdbFlags,
1773 txn_inner.as_mut_ptr(),
1774 ))?;
1775 // SAFETY: call to `lmdb::mdb_txn_begin` above has been successful
1776 // as otherwise `check_err_code` would have returned an `Err`
1777 // resulting in early return from this function
1778 let txn_inner = txn_inner.assume_init();
1779 let txn = TxnBackend {
1780 env_ro: &self.env_ro,
1781 inner: txn_inner,
1782 cursors: Default::default(),
1783 };
1784 let db_inner = db_backend.inner;
1785 db_backend.dont_close();
1786 // SAFETY:
1787 // * `&mut self` ensures that `lmdb::mdb_drop` is only called by
1788 // a single thread at the same time (for the given
1789 // environment), because no write transaction can be open.
1790 // * No other threads can use the database or any of its cursors
1791 // in a read-only transaction because the `DbBackend` was
1792 // successfully unwrapped from the `Arc` above.
1793 // * No other write transaction which could have modified the
1794 // database can be open because of holding a `&mut self` which
1795 // ensures that no other write transaction is open.
1796 // * The above call of `DbBackend::dont_close` ensures that the
1797 // database handle isn't closed twice.
1798 // * The status passed to `check_err_code` is valid.
1799 check_err_code(lmdb::mdb_drop(txn.inner, db_inner, 1))?;
1800 txn.commit()?;
1801 }
1802 drop(dbis);
1803 Ok(())
1804 }
1805 /// Start read-write transaction
1806 pub fn txn_rw(&mut self) -> io::Result<TxnRw<'_>> {
1807 let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
1808 // SAFETY:
1809 // * `&mut self` ensures that no other write transaction (for the
1810 // given environment) can be open by the current thread (and
1811 // whole process).
1812 // * The status passed to `check_err_code` is valid.
1813 unsafe {
1814 check_err_code(lmdb::mdb_txn_begin(
1815 self.env_ro.backend.inner,
1816 null_mut(),
1817 0,
1818 txn_inner.as_mut_ptr(),
1819 ))?;
1820 }
1821 // SAFETY: call to `lmdb::mdb_txn_begin` above has been successful as
1822 // otherwise `check_err_code` would have returned an `Err` resulting in
1823 // early return from this function
1824 let txn_inner = unsafe { txn_inner.assume_init() };
1825 Ok(TxnRw {
1826 backend: UnsafeCell::new(TxnBackend {
1827 env_ro: &self.env_ro,
1828 inner: txn_inner,
1829 cursors: Default::default(),
1830 }),
1831 used_dbs: UsedDbs::Toplevel(Default::default()),
1832 commit_handlers: Default::default(),
1833 })
1834 }
1835}
1836
1837impl<'a> TxnBackend<'a> {
1838 /// Commit transaction
1839 fn commit(self) -> io::Result<()> {
1840 let inner = self.inner;
1841 self.dont_abort();
1842 // SAFETY:
1843 // * transaction cannot be used further
1844 // * all cursors have been closed by `TxnBackend::dont_abort(self)`
1845 // * the status passed to `check_err_code` is valid
1846 unsafe {
1847 check_err_code(lmdb::mdb_txn_commit(inner))?;
1848 }
1849 Ok(())
1850 }
1851 /// Get reference to value in database
1852 ///
1853 /// # Safety
1854 ///
1855 /// * The value is only valid until end of a read-only transaction or, in
1856 /// case of a read-write transaction, until the transaction ends or
1857 /// modifies data. The caller has to ensure that the lifetime `'b` is
1858 /// short enough.
1859 unsafe fn get_unsafe<'b, 'kr, K, V, C, KRef>(
1860 &mut self,
1861 db: &Db<K, V, C>,
1862 key: KRef,
1863 ) -> io::Result<Option<V::AlignedRef<'b>>>
1864 where
1865 K: ?Sized + Storable + 'kr,
1866 V: ?Sized + Storable,
1867 C: Constraint,
1868 KRef: StorableRef<'kr, K>,
1869 {
1870 db.backend.assert_env_backend(&self.env_ro.backend);
1871 let key: K::BytesRef<'_> = key.ref_to_bytes();
1872 let lmdb_key = lmdb::MDB_val {
1873 mv_size: key.len(),
1874 mv_data: key.as_ptr() as *mut _,
1875 };
1876 let mut lmdb_data = MaybeUninit::<lmdb::MDB_val>::uninit();
1877 Ok(
1878 // SAFETY:
1879 // * `lmdb_key.mv_data` is a valid pointer to `lmdb_key.mv_size`
1880 // bytes of memory for reading.
1881 // * The memory pointed to by `lmdb_data.mv_data` is only read.
1882 // * Documentation requires that `'b` must not live longer than
1883 // the end of the read-only transaction or until the read-write
1884 // transaction ends or modifies data. The lifetime of the
1885 // reference returned by `slice::from_raw_parts` below is
1886 // inferred as being `'b`. Thus the memory won't be accessed
1887 // afterwards.
1888 match unsafe {
1889 lmdb::mdb_get(
1890 self.inner,
1891 db.backend.inner,
1892 &lmdb_key as *const _ as *mut lmdb::MDB_val,
1893 lmdb_data.as_mut_ptr(),
1894 )
1895 } {
1896 lmdb::MDB_NOTFOUND => None,
1897 status => {
1898 // SAFETY: `status` is a valid LMDB return code, and if
1899 // negative, it is a valid LMDB error code
1900 unsafe { check_err_code(status) }?;
1901 // SAFETY: call to `lmdb::mdb_get` above has been
1902 // successful as otherwise `check_err_code` would have
1903 // returned an `Err` resulting in early return from this
1904 // function
1905 let lmdb_data = unsafe { lmdb_data.assume_init() };
1906 // SAFETY:
1907 // * The call to `lmdb::mdb_get` has been successful and
1908 // is expected to have written valid data to
1909 // `lmdb_data` (see SAFETY comment on `lmdb::mdb_get`
1910 // above regarding lifetimes).
1911 // * Documentation of all methods opening databases
1912 // demand that if a database exists already, it must
1913 // have been created with compatible options.
1914 // Documentation of `EnvBuilder::open_ro` and
1915 // `EnvBuilder::open_rw` makes additional demands in
1916 // regard to the database being free of corruption and
1917 // having been created on a compatible platform. It is
1918 // thus safe to call `Storable::from_bytes_unchecked`.
1919 Some(unsafe {
1920 V::from_bytes_unchecked(slice::from_raw_parts(
1921 lmdb_data.mv_data as *const u8,
1922 lmdb_data.mv_size,
1923 ))
1924 })
1925 }
1926 },
1927 )
1928 }
1929 /// Create a new cursor
1930 fn new_cursor<K, V, C>(&mut self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
1931 where
1932 K: ?Sized,
1933 V: ?Sized,
1934 C: Constraint,
1935 {
1936 db.backend.assert_env_backend(&self.env_ro.backend);
1937 let mut inner_cursor = MaybeUninit::<*mut lmdb::MDB_cursor>::uninit();
1938 // SAFETY:
1939 // * Database handles are only closed when the `DbBackend` or
1940 // `EnvBackend` is dropped. Because the `Cursor` struct contains a
1941 // clone of the `Db` handle (after the `CursorBackend`, see SAFETY
1942 // comment on `Cursor`), the database handle won't be closed while
1943 // the cursor may be used.
1944 // * The cursor is not used after the transaction has ended because
1945 // this modules API always requires a `TxnRo` or `TxnRw` for using
1946 // the cursor. In regard to closing the cursor, see SAFETY comments
1947 // where `lmdb::mdb_cursor_close` is invoked.
1948 unsafe {
1949 check_err_code(lmdb::mdb_cursor_open(
1950 self.inner,
1951 db.backend.inner,
1952 inner_cursor.as_mut_ptr(),
1953 ))?;
1954 }
1955 // SAFETY: call to `lmdb::mdb_cursor_open` above has been successful as
1956 // otherwise `check_err_code` would have returned an `Err` resulting in
1957 // early return from this function
1958 let inner_cursor = unsafe { inner_cursor.assume_init() };
1959 // SAFETY: clone `db` first, see `CursorBackend::drop`
1960 let db = db.clone();
1961 let cursor_backend = Arc::new(CursorBackend {
1962 inner: inner_cursor,
1963 closed: Default::default(),
1964 inner_txn: self.inner,
1965 });
1966 self.cursors.push(Arc::downgrade(&cursor_backend));
1967 Ok(Cursor {
1968 backend: ArcByAddr::from(cursor_backend),
1969 db,
1970 })
1971 }
1972 /// Execute cursor operation
1973 ///
1974 /// The returned key or value may be retrieved by calling an `FnOnce`
1975 /// closure when needed.
1976 ///
1977 /// # Safety
1978 ///
1979 /// * Any returned keys or values are only valid until end of a
1980 /// read-only transaction or until a read-write transaction ends or
1981 /// modifies data. The caller has to ensure that the lifetime `'b` is
1982 /// short enough.
1983 unsafe fn cursor_op_unsafe<'b, 'kr, 'vr, K, V, C, KRef, VRef>(
1984 &mut self,
1985 cursor: &Cursor<K, V, C>,
1986 key: Option<KRef>,
1987 value: Option<VRef>,
1988 op: lmdb::MDB_cursor_op,
1989 ) -> io::Result<
1990 Option<(
1991 impl FnOnce() -> K::AlignedRef<'b>,
1992 impl FnOnce() -> V::AlignedRef<'b>,
1993 )>,
1994 >
1995 where
1996 K: ?Sized + Storable + 'kr,
1997 V: ?Sized + Storable + 'vr,
1998 C: Constraint,
1999 KRef: StorableRef<'kr, K>,
2000 VRef: StorableRef<'vr, V>,
2001 {
2002 cursor.backend.assert_txn_backend(self);
2003 let mut lmdb_key = match key {
2004 Some(key) => {
2005 let key: K::BytesRef<'_> = key.ref_to_bytes();
2006 lmdb::MDB_val {
2007 mv_size: key.len(),
2008 mv_data: key.as_ptr() as *mut _,
2009 }
2010 }
2011 None => lmdb::MDB_val {
2012 mv_size: 0,
2013 mv_data: null_mut(),
2014 },
2015 };
2016 let mut lmdb_data = match value {
2017 Some(value) => {
2018 let value: V::BytesRef<'_> = value.ref_to_bytes();
2019 lmdb::MDB_val {
2020 mv_size: value.len(),
2021 mv_data: value.as_ptr() as *mut _,
2022 }
2023 }
2024 None => lmdb::MDB_val {
2025 mv_size: 0,
2026 mv_data: null_mut(),
2027 },
2028 };
2029 Ok(
2030 // SAFETY:
2031 // * `lmdb_key.mv_data` and `lmdb_data.mv_data` are valid
2032 // pointers to `lmdb_key.mv_size` and `lmdb_data.mv_size`
2033 // bytes of memory, respectively, for reading.
2034 // * The memory pointed to by `lmdb_key.mv_data` and
2035 // `lmdb_data.mv_data` is only read subsequently.
2036 // * Documentation requires that `'b` must not live longer than
2037 // the end of the read-only transaction or until the read-write
2038 // transaction ends or modifies data. The lifetime of the
2039 // reference returned by `slice::from_raw_parts` below is
2040 // inferred as being `'b`. Thus the memory won't be accessed
2041 // afterwards.
2042 match unsafe {
2043 lmdb::mdb_cursor_get(cursor.backend.inner, &mut lmdb_key, &mut lmdb_data, op)
2044 } {
2045 lmdb::MDB_NOTFOUND => None,
2046 status => {
2047 // SAFETY: `status` is a valid LMDB return code, and if
2048 // negative, it is a valid LMDB error code
2049 unsafe { check_err_code(status) }?;
2050 // SAFETY:
2051 // * The call to `lmdb::mdb_cursor_get` has been
2052 // successful and is expected to either have written
2053 // valid data to `lmdb_key` and `lmdb_data` or to not
2054 // have written anything to one or both of them (see
2055 // SAFETY comment on `lmdb::mdb_cursor_get` above
2056 // regarding lifetimes).
2057 // * Documentation of all methods opening databases
2058 // demand that if a database exists already, it must
2059 // have been created with compatible options.
2060 // Documentation of `EnvBuilder::open_ro` and
2061 // `EnvBuilder::open_rw` makes additional demands in
2062 // regard to the database being free of corruption and
2063 // having been created on a compatible platform. It is
2064 // thus safe to call `Storable::from_bytes_unchecked`.
2065 Some(unsafe {
2066 (
2067 move || {
2068 K::from_bytes_unchecked(slice::from_raw_parts(
2069 lmdb_key.mv_data as *const u8,
2070 lmdb_key.mv_size,
2071 ))
2072 },
2073 move || {
2074 V::from_bytes_unchecked(slice::from_raw_parts(
2075 lmdb_data.mv_data as *const u8,
2076 lmdb_data.mv_size,
2077 ))
2078 },
2079 )
2080 })
2081 }
2082 },
2083 )
2084 }
2085 /// Get number of values for current cursor position
2086 fn cursor_get_current_value_count<K, V>(
2087 &mut self,
2088 cursor: &Cursor<K, V, KeysDuplicate>,
2089 ) -> io::Result<usize>
2090 where
2091 K: ?Sized + Storable,
2092 V: ?Sized + Storable,
2093 {
2094 cursor.backend.assert_txn_backend(self);
2095 let mut count = MaybeUninit::<usize>::uninit();
2096 // SAFETY:
2097 // * `lmdb::mdb_cursor_count` is only called on databases using
2098 // `lmdb::MDB_DUPSORT` (because the third type parameter of
2099 // `cursor` is `KeysDuplicate`).
2100 // * The status passed to `check_err_code` is valid.
2101 unsafe {
2102 check_err_code(lmdb::mdb_cursor_count(
2103 cursor.backend.inner,
2104 count.as_mut_ptr(),
2105 ))?;
2106 }
2107 // SAFETY: call to `lmdb::mdb_cursor_count` above has been successful as
2108 // otherwise `check_err_code` would have returned an `Err` resulting in
2109 // early return from this function
2110 Ok(unsafe { count.assume_init() })
2111 }
2112}
2113
2114/// Helper macro to create return type of cursor methods
2115macro_rules! cursor_return_type {
2116 (bool $(,)?) => (bool);
2117 (key $(,)?) => (Option<K::AlignedRef<'_>>);
2118 (value $(,)?) => (Option<V::AlignedRef<'_>>);
2119 ((key, value $(,)?)) => (Option<(K::AlignedRef<'_>, V::AlignedRef<'_>)>);
2120}
2121
2122/// Create signature for cursor method
2123macro_rules! cursor_method_def {
2124 ($($meta:meta)*, $name:ident, (), $retval:tt $(,)?) => {
2125 $(#[$meta])*
2126 fn $name<K, V, C>(
2127 &self,
2128 cursor: &Cursor<K, V, C>,
2129 ) -> io::Result<cursor_return_type!($retval)>
2130 where
2131 K: ?Sized + Storable,
2132 V: ?Sized + Storable,
2133 C: Constraint;
2134 };
2135 ($($meta:meta)*, $name:ident, (key), $retval:tt $(,)?) => {
2136 $(#[$meta])*
2137 fn $name<'kr, K, V, C, KRef>(
2138 &self,
2139 cursor: &Cursor<K, V, C>,
2140 key: KRef,
2141 ) -> io::Result<cursor_return_type!($retval)>
2142 where
2143 K: ?Sized + Storable + 'kr,
2144 V: ?Sized + Storable,
2145 C: Constraint,
2146 KRef: StorableRef<'kr, K>;
2147 };
2148 ($($meta:meta)*, $name:ident, (key, value), $retval:tt $(,)?) => {
2149 $(#[$meta])*
2150 fn $name<'kr, 'vr, K, V, C, KRef, VRef>(
2151 &self,
2152 cursor: &Cursor<K, V, C>,
2153 key: KRef,
2154 value: VRef,
2155 ) -> io::Result<cursor_return_type!($retval)>
2156 where
2157 K: ?Sized + Storable + 'kr,
2158 V: ?Sized + Storable + 'vr,
2159 C: Constraint,
2160 KRef: StorableRef<'kr, K>,
2161 VRef: StorableRef<'vr, V>;
2162 };
2163}
2164
2165/// Create signatures for several cursor methods
2166macro_rules! cursor_method_defs {
2167 {
2168 $(
2169 $(#[$meta:meta])*
2170 fn $name:ident $args:tt -> $retval:tt;
2171 )*
2172 } => {
2173 $(
2174 cursor_method_def!($($meta)*, $name, $args, $retval);
2175 )*
2176 };
2177}
2178
2179/// Create signature for cursor method on databases with duplicate keys
2180macro_rules! cursor_method_dupkey_def {
2181 ($($meta:meta)*, $name:ident, (), $retval:tt $(,)?) => {
2182 $(#[$meta])*
2183 fn $name<K, V>(
2184 &self,
2185 cursor: &Cursor<K, V, KeysDuplicate>,
2186 ) -> io::Result<cursor_return_type!($retval)>
2187 where
2188 K: ?Sized + Storable,
2189 V: ?Sized + Storable;
2190 };
2191 ($($meta:meta)*, $name:ident, (key), $retval:tt $(,)?) => {
2192 $(#[$meta])*
2193 fn $name<'kr, K, V, KRef>(
2194 &self,
2195 cursor: &Cursor<K, V, KeysDuplicate>,
2196 key: KRef,
2197 ) -> io::Result<cursor_return_type!($retval)>
2198 where
2199 K: ?Sized + Storable + 'kr,
2200 V: ?Sized + Storable,
2201 KRef: StorableRef<'kr, K>;
2202 };
2203 ($($meta:meta)*, $name:ident, (key, value), $retval:tt $(,)?) => {
2204 $(#[$meta])*
2205 fn $name<'kr, 'vr, K, V, KRef, VRef>(
2206 &self,
2207 cursor: &Cursor<K, V, KeysDuplicate>,
2208 key: KRef,
2209 value: VRef,
2210 ) -> io::Result<cursor_return_type!($retval)>
2211 where
2212 K: ?Sized + Storable + 'kr,
2213 V: ?Sized + Storable + 'vr,
2214 KRef: StorableRef<'kr, K>,
2215 VRef: StorableRef<'vr, V>;
2216 };
2217}
2218
2219/// Create signatures for several cursor methods on databases with duplicate
2220/// keys
2221macro_rules! cursor_method_dupkey_defs {
2222 {
2223 $(
2224 $(#[$meta:meta])*
2225 fn $name:ident $args:tt -> $retval:tt;
2226 )*
2227 } => {
2228 $(
2229 cursor_method_dupkey_def!($($meta)*, $name, $args, $retval);
2230 )*
2231 };
2232}
2233
2234/// Read-write or read-only transaction
2235///
2236/// This trait provides the methods for read-access to databases.
2237/// The simplest interface is provided by [`Txn::get`], which returns a
2238/// [reference](Storable::AlignedRef) to the stored value for a given key
2239/// (or [`None`] if the key does not exist).
2240/// If an owned value is desired, use [`Txn::get_owned`] instead, which
2241/// automatically converts the reference (e.g. `&str`) into an owned value
2242/// (e.g. `String`) using [`GenericCow::into_owned`].
2243///
2244/// [`Txn::get`] and [`Txn::get_owned`] will only retrieve the first value in
2245/// case of [duplicate keys](DbBuilder::keys_duplicate). For more sophisticated
2246/// access, use [`Txn::new_cursor`] to create a new [`Cursor`] which then can
2247/// be used with one of the cursor methods in the `Txn` trait (or the [`TxnRw`]
2248/// struct in case of write access, respectively).
2249pub trait Txn {
2250 /// Get reference to value in database
2251 fn get<'kr, K, V, C, KRef>(
2252 &self,
2253 db: &Db<K, V, C>,
2254 key: KRef,
2255 ) -> io::Result<Option<V::AlignedRef<'_>>>
2256 where
2257 K: ?Sized + Storable + 'kr,
2258 V: ?Sized + Storable,
2259 C: Constraint,
2260 KRef: StorableRef<'kr, K>;
2261 /// Get owned value from database
2262 fn get_owned<'a, 'kr, K, V, C, KRef>(
2263 &'a self,
2264 db: &Db<K, V, C>,
2265 key: KRef,
2266 ) -> io::Result<Option<<V as ToOwned>::Owned>>
2267 where
2268 K: ?Sized + Storable + 'kr,
2269 V: ?Sized + Storable + ToOwned,
2270 C: Constraint,
2271 KRef: StorableRef<'kr, K>,
2272 {
2273 Ok(self.get(db, key)?.map(|x| x.into_owned()))
2274 }
2275 /// Create a new cursor
2276 fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
2277 where
2278 K: ?Sized + Storable,
2279 V: ?Sized + Storable,
2280 C: Constraint;
2281 /// Get number of values for current cursor position
2282 fn cursor_get_current_value_count<K, V>(
2283 &self,
2284 cursor: &Cursor<K, V, KeysDuplicate>,
2285 ) -> io::Result<usize>
2286 where
2287 K: ?Sized + Storable,
2288 V: ?Sized + Storable;
2289 cursor_method_defs! {
2290 /// Set cursor to first entry in database
2291 ///
2292 /// Returns `false` if database is empty.
2293 fn cursor_set_first() -> bool;
2294 /// Set cursor to first entry in database and get pair
2295 fn cursor_set_first_get_pair() -> (key, value);
2296 /// Set cursor to last entry in database
2297 ///
2298 /// Returns `false` if database is empty.
2299 fn cursor_set_last() -> bool;
2300 /// Set cursor to last entry in database and get pair
2301 fn cursor_set_last_get_pair() -> (key, value);
2302 /// Get key at current cursor position
2303 fn cursor_get_current_key() -> key;
2304 /// Get value at current cursor position
2305 fn cursor_get_current_value() -> value;
2306 /// Get key-value pair at current cursor position
2307 fn cursor_get_current_pair() -> (key, value);
2308 /// Set cursor to key
2309 ///
2310 /// Returns `false` if key does not exist.
2311 /// Note that in this case it's *not* guaranteed that subsequent
2312 /// accesses to the cursor using `cursor_get_current_` methods return
2313 /// `None`.
2314 fn cursor_set_key(key) -> bool;
2315 /// Set cursor to key and get value
2316 fn cursor_set_key_get_value(key) -> value;
2317 /// Set cursor to key or next greater key if not existent
2318 ///
2319 /// Returns `false` if no matching key has been found.
2320 /// Note that in this case it's *not* guaranteed that subsequent
2321 /// accesses to the cursor using `cursor_get_current_` methods return
2322 /// `None`.
2323 fn cursor_search_key(key) -> bool;
2324 /// Set cursor to key or next greater key if not existent and get pair
2325 fn cursor_search_key_get_pair(key) -> (key, value);
2326 /// Move cursor to next entry in database
2327 ///
2328 /// Returns `false` if there is no next entry.
2329 /// Note that in this case it's *not* guaranteed that subsequent
2330 /// accesses to the cursor using `cursor_get_current_` methods return
2331 /// `None`.
2332 fn cursor_set_next() -> bool;
2333 /// Move cursor to next entry in database and get pair
2334 fn cursor_set_next_get_pair() -> (key, value);
2335 /// Move cursor to previous entry in database
2336 ///
2337 /// Returns `false` if there is no previous entry.
2338 /// Note that in this case it's *not* guaranteed that subsequent
2339 /// accesses to the cursor using `cursor_get_current_` methods return
2340 /// `None`.
2341 fn cursor_set_prev() -> bool;
2342 /// Move cursor to previous entry in database and get pair
2343 fn cursor_set_prev_get_pair() -> (key, value);
2344 /// Move cursor to first value of next key
2345 ///
2346 /// Returns `false` if there is no next key.
2347 /// Note that in this case it's *not* guaranteed that subsequent
2348 /// accesses to the cursor using `cursor_get_current_` methods return
2349 /// `None`.
2350 fn cursor_set_next_key() -> bool;
2351 /// Move cursor to first value of next key and get pair
2352 fn cursor_set_next_key_get_pair() -> (key, value);
2353 /// Move cursor to last value of previous key
2354 ///
2355 /// Returns `false` if there is no previous key.
2356 /// Note that in this case it's *not* guaranteed that subsequent
2357 /// accesses to the cursor using `cursor_get_current_` methods return
2358 /// `None`.
2359 fn cursor_set_prev_key() -> bool;
2360 /// Move cursor to last value of previous key and get pair
2361 fn cursor_set_prev_key_get_pair() -> (key, value);
2362 }
2363 cursor_method_dupkey_defs! {
2364 /// Move cursor to first value of current key
2365 ///
2366 /// Returns `false` if no value has been found.
2367 /// Note that in this case it's *not* guaranteed that subsequent
2368 /// accesses to the cursor using `cursor_get_current_` methods return
2369 /// `None`.
2370 fn cursor_set_first_value() -> bool;
2371 /// Move cursor to first value of current key and get value
2372 fn cursor_set_first_value_get_value() -> value;
2373 /// Move cursor to last value of current key
2374 ///
2375 /// Returns `false` if no value has been found.
2376 /// Note that in this case it's *not* guaranteed that subsequent
2377 /// accesses to the cursor using `cursor_get_current_` methods return
2378 /// `None`.
2379 fn cursor_set_last_value() -> bool;
2380 /// Move cursor to last value of current key and get value
2381 fn cursor_set_last_value_get_value() -> value;
2382 /// Set cursor to key-value pair
2383 ///
2384 /// Returns `false` if not found.
2385 /// Note that in this case it's *not* guaranteed that subsequent
2386 /// accesses to the cursor using `cursor_get_current_` methods return
2387 /// `None`.
2388 fn cursor_set_pair(key, value) -> bool;
2389 /// Set cursor to key and value or next greater value if not existent
2390 ///
2391 /// Returns `false` if no matching value has been found for the given key.
2392 /// Note that in this case it's *not* guaranteed that subsequent
2393 /// accesses to the cursor using `cursor_get_current_` methods return
2394 /// `None`.
2395 fn cursor_set_key_search_value(key, value) -> bool;
2396 /// Set cursor to key and value or next greater value if not existent and get value
2397 fn cursor_set_key_search_value_get_value(key, value) -> value;
2398 /// Move cursor to next value of current key
2399 ///
2400 /// Returns `false` if there is no next value for the current key.
2401 /// Note that in this case it's *not* guaranteed that subsequent
2402 /// accesses to the cursor using `cursor_get_current_` methods return
2403 /// `None`.
2404 fn cursor_set_next_value() -> bool;
2405 /// Move cursor to next value of current key and get value
2406 fn cursor_set_next_value_get_value() -> value;
2407 /// Move cursor to next value of current key
2408 ///
2409 /// Returns `false` if there is no previous value for the current key.
2410 /// Note that in this case it's *not* guaranteed that subsequent
2411 /// accesses to the cursor using `cursor_get_current_` methods return
2412 /// `None`.
2413 fn cursor_set_prev_value() -> bool;
2414 /// Move cursor to next value of current key and get value
2415 fn cursor_set_prev_value_get_value() -> value;
2416 }
2417}
2418
2419/// Helper macro to extract return value for cursor methods
2420macro_rules! cursor_return_value {
2421 (bool, $x:expr $(,)?) => {
2422 match $x {
2423 Some(_) => true,
2424 None => false,
2425 }
2426 };
2427 (key, $x:expr $(,)?) => {
2428 $x.map(|(k, _)| k())
2429 };
2430 (value, $x:expr $(,)?) => {
2431 $x.map(|(_, v)| v())
2432 };
2433 ((key, value), $x:expr $(,)?) => {
2434 $x.map(|(k, v)| (k(), v()))
2435 };
2436}
2437
2438/// Create implementation for cursor method
2439macro_rules! cursor_method_impl {
2440 ($name:ident, (), $retval:tt, $op:ident $(,)?) => {
2441 fn $name<K, V, C>(
2442 &self,
2443 cursor: &Cursor<K, V, C>,
2444 ) -> io::Result<cursor_return_type!($retval)>
2445 where
2446 K: ?Sized + Storable,
2447 V: ?Sized + Storable,
2448 C: Constraint,
2449 {
2450 // SAFETY:
2451 // * Neither `TxnRo` nor `TxnRw` is `Sync` and `self.backend` is
2452 // not accessed by any method calling this method.
2453 // * The macro `cursor_return_type!` gives a return type with
2454 // `'_` as lifetime, which corresponds to the elided lifetime
2455 // of `&self`. This lifetime cannot live longer than the end of
2456 // this transaction and ends before any data is modified by the
2457 // transaction. It is passed to `TxnBackend::cursor_op_unsafe`
2458 // by inference.
2459 Ok(cursor_return_value!($retval, unsafe {
2460 let backend = &mut *self.backend.get();
2461 backend.cursor_op_unsafe::<K, V, C, &K, &V>(cursor, None, None, lmdb::$op)?
2462 }))
2463 }
2464 };
2465 ($name:ident, (key), $retval:tt, $op:ident) => {
2466 fn $name<'kr, K, V, C, KRef>(
2467 &self,
2468 cursor: &Cursor<K, V, C>,
2469 key: KRef,
2470 ) -> io::Result<cursor_return_type!($retval)>
2471 where
2472 K: ?Sized + Storable + 'kr,
2473 V: ?Sized + Storable,
2474 C: Constraint,
2475 KRef: StorableRef<'kr, K>,
2476 {
2477 // SAFETY: see above
2478 Ok(cursor_return_value!($retval, unsafe {
2479 let backend = &mut *self.backend.get();
2480 backend.cursor_op_unsafe::<K, V, C, KRef, &V>(cursor, Some(key), None, lmdb::$op)?
2481 }))
2482 }
2483 };
2484 ($name:ident, (key, value), $retval:tt, $op:ident) => {
2485 fn $name<'kr, 'vr, K, V, C, KRef, VRef>(
2486 &self,
2487 cursor: &Cursor<K, V, C>,
2488 key: KRef,
2489 value: VRef,
2490 ) -> io::Result<cursor_return_type!($retval)>
2491 where
2492 K: ?Sized + Storable + 'kr,
2493 V: ?Sized + Storable + 'vr,
2494 C: Constraint,
2495 KRef: StorableRef<'kr, K>,
2496 VRef: StorableRef<'vr, V>,
2497 {
2498 // SAFETY: see above
2499 Ok(cursor_return_value!($retval, unsafe {
2500 let backend = &mut *self.backend.get();
2501 backend.cursor_op_unsafe::<K, V, C, KRef, VRef>(
2502 cursor,
2503 Some(key),
2504 Some(value),
2505 lmdb::$op,
2506 )?
2507 }))
2508 }
2509 };
2510}
2511
2512/// Create implementation for cursor method for databases with duplicate keys
2513macro_rules! cursor_method_dupkey_impl {
2514 ($name:ident, (), $retval:tt, $op:ident $(,)?) => {
2515 fn $name<K, V>(
2516 &self,
2517 cursor: &Cursor<K, V, KeysDuplicate>,
2518 ) -> io::Result<cursor_return_type!($retval)>
2519 where
2520 K: ?Sized + Storable,
2521 V: ?Sized + Storable,
2522 {
2523 // SAFETY: see `cursor_method_impl!`
2524 Ok(cursor_return_value!($retval, unsafe {
2525 let backend = &mut *self.backend.get();
2526 backend.cursor_op_unsafe::<K, V, KeysDuplicate, &K, &V>(
2527 cursor,
2528 None,
2529 None,
2530 lmdb::$op,
2531 )?
2532 }))
2533 }
2534 };
2535 ($name:ident, (key), $retval:tt, $op:ident) => {
2536 fn $name<'kr, K, V, KRef>(
2537 &self,
2538 cursor: &Cursor<K, V, KeysDuplicate>,
2539 key: KRef,
2540 ) -> io::Result<cursor_return_type!($retval)>
2541 where
2542 K: ?Sized + Storable + 'kr,
2543 V: ?Sized + Storable,
2544 KRef: StorableRef<'kr, K>,
2545 {
2546 // SAFETY: see `cursor_method_impl!`
2547 Ok(cursor_return_value!($retval, unsafe {
2548 let backend = &mut *self.backend.get();
2549 backend.cursor_op_unsafe::<K, V, KeysDuplicate, KRef, &V>(
2550 cursor,
2551 Some(key),
2552 None,
2553 lmdb::$op,
2554 )?
2555 }))
2556 }
2557 };
2558 ($name:ident, (key, value), $retval:tt, $op:ident) => {
2559 fn $name<'kr, 'vr, K, V, KRef, VRef>(
2560 &self,
2561 cursor: &Cursor<K, V, KeysDuplicate>,
2562 key: KRef,
2563 value: VRef,
2564 ) -> io::Result<cursor_return_type!($retval)>
2565 where
2566 K: ?Sized + Storable + 'kr,
2567 V: ?Sized + Storable + 'vr,
2568 KRef: StorableRef<'kr, K>,
2569 VRef: StorableRef<'vr, V>,
2570 {
2571 // SAFETY: see `cursor_method_impl!`
2572 Ok(cursor_return_value!($retval, unsafe {
2573 let backend = &mut *self.backend.get();
2574 backend.cursor_op_unsafe::<K, V, KeysDuplicate, KRef, VRef>(
2575 cursor,
2576 Some(key),
2577 Some(value),
2578 lmdb::$op,
2579 )?
2580 }))
2581 }
2582 };
2583}
2584
2585/// Create implementations for all cursor methods
2586macro_rules! cursor_method_impls {
2587 ($macro:ident, $dupkey_macro:ident) => {
2588 $macro!(cursor_set_first, (), bool, MDB_cursor_op_MDB_FIRST);
2589 $macro!(
2590 cursor_set_first_get_pair,
2591 (),
2592 (key, value),
2593 MDB_cursor_op_MDB_FIRST
2594 );
2595 $macro!(cursor_set_last, (), bool, MDB_cursor_op_MDB_LAST);
2596 $macro!(
2597 cursor_set_last_get_pair,
2598 (),
2599 (key, value),
2600 MDB_cursor_op_MDB_LAST
2601 );
2602 $macro!(
2603 cursor_get_current_key,
2604 (),
2605 key,
2606 MDB_cursor_op_MDB_GET_CURRENT
2607 );
2608 $macro!(
2609 cursor_get_current_value,
2610 (),
2611 value,
2612 MDB_cursor_op_MDB_GET_CURRENT
2613 );
2614 $macro!(
2615 cursor_get_current_pair,
2616 (),
2617 (key, value),
2618 MDB_cursor_op_MDB_GET_CURRENT
2619 );
2620 $macro!(cursor_set_key, (key), bool, MDB_cursor_op_MDB_SET);
2621 $macro!(
2622 cursor_set_key_get_value,
2623 (key),
2624 value,
2625 MDB_cursor_op_MDB_SET
2626 );
2627 $macro!(cursor_search_key, (key), bool, MDB_cursor_op_MDB_SET_RANGE);
2628 $macro!(
2629 cursor_search_key_get_pair,
2630 (key),
2631 (key, value),
2632 MDB_cursor_op_MDB_SET_RANGE
2633 );
2634 $macro!(cursor_set_next, (), bool, MDB_cursor_op_MDB_NEXT);
2635 $macro!(
2636 cursor_set_next_get_pair,
2637 (),
2638 (key, value),
2639 MDB_cursor_op_MDB_NEXT
2640 );
2641 $macro!(cursor_set_prev, (), bool, MDB_cursor_op_MDB_PREV);
2642 $macro!(
2643 cursor_set_prev_get_pair,
2644 (),
2645 (key, value),
2646 MDB_cursor_op_MDB_PREV
2647 );
2648 $macro!(cursor_set_next_key, (), bool, MDB_cursor_op_MDB_NEXT_NODUP);
2649 $macro!(
2650 cursor_set_next_key_get_pair,
2651 (),
2652 (key, value),
2653 MDB_cursor_op_MDB_NEXT_NODUP
2654 );
2655 $macro!(cursor_set_prev_key, (), bool, MDB_cursor_op_MDB_PREV_NODUP);
2656 $macro!(
2657 cursor_set_prev_key_get_pair,
2658 (),
2659 (key, value),
2660 MDB_cursor_op_MDB_PREV_NODUP
2661 );
2662 $dupkey_macro!(
2663 cursor_set_first_value,
2664 (),
2665 bool,
2666 MDB_cursor_op_MDB_FIRST_DUP
2667 );
2668 $dupkey_macro!(
2669 cursor_set_first_value_get_value,
2670 (),
2671 value,
2672 MDB_cursor_op_MDB_FIRST_DUP
2673 );
2674 $dupkey_macro!(cursor_set_last_value, (), bool, MDB_cursor_op_MDB_LAST_DUP);
2675 $dupkey_macro!(
2676 cursor_set_last_value_get_value,
2677 (),
2678 value,
2679 MDB_cursor_op_MDB_LAST_DUP
2680 );
2681 $dupkey_macro!(
2682 cursor_set_pair,
2683 (key, value),
2684 bool,
2685 MDB_cursor_op_MDB_GET_BOTH
2686 );
2687 $dupkey_macro!(
2688 cursor_set_key_search_value,
2689 (key, value),
2690 bool,
2691 MDB_cursor_op_MDB_GET_BOTH_RANGE
2692 );
2693 $dupkey_macro!(
2694 cursor_set_key_search_value_get_value,
2695 (key, value),
2696 value,
2697 MDB_cursor_op_MDB_GET_BOTH_RANGE
2698 );
2699 $dupkey_macro!(cursor_set_next_value, (), bool, MDB_cursor_op_MDB_NEXT_DUP);
2700 $dupkey_macro!(
2701 cursor_set_next_value_get_value,
2702 (),
2703 value,
2704 MDB_cursor_op_MDB_NEXT_DUP
2705 );
2706 $dupkey_macro!(cursor_set_prev_value, (), bool, MDB_cursor_op_MDB_PREV_DUP);
2707 $dupkey_macro!(
2708 cursor_set_prev_value_get_value,
2709 (),
2710 value,
2711 MDB_cursor_op_MDB_PREV_DUP
2712 );
2713 };
2714}
2715
2716impl<'a> Txn for TxnRo<'a> {
2717 fn get<'kr, K, V, C, KRef>(
2718 &self,
2719 db: &Db<K, V, C>,
2720 key: KRef,
2721 ) -> io::Result<Option<V::AlignedRef<'_>>>
2722 where
2723 K: ?Sized + Storable + 'kr,
2724 V: ?Sized + Storable,
2725 C: Constraint,
2726 KRef: StorableRef<'kr, K>,
2727 {
2728 // SAFETY:
2729 // * `TxnRo` is `!Sync` and `self.backend` is not accessed by any
2730 // method calling this method.
2731 // * The return type of this function has `'_` as lifetime, which
2732 // corresponds to the elided lifetime of `&self`. This lifetime
2733 // cannot live longer than the end of this transaction and ends
2734 // before any data is modified by the transaction. It is passed
2735 // to `TxnBackend::get_unsafe` by inference.
2736 unsafe {
2737 let backend = &mut *self.backend.get();
2738 backend.get_unsafe::<K, V, C, KRef>(db, key)
2739 }
2740 }
2741 fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
2742 where
2743 K: ?Sized + Storable,
2744 V: ?Sized + Storable,
2745 C: Constraint,
2746 {
2747 // SAFETY: `TxnRo` is `!Sync` and `self.backend` is not accessed by any
2748 // method calling this method
2749 let backend = unsafe { &mut *self.backend.get() };
2750 backend.new_cursor(db)
2751 }
2752 fn cursor_get_current_value_count<K, V>(
2753 &self,
2754 cursor: &Cursor<K, V, KeysDuplicate>,
2755 ) -> io::Result<usize>
2756 where
2757 K: ?Sized + Storable,
2758 V: ?Sized + Storable,
2759 {
2760 // SAFETY: `TxnRo` is `!Sync` and `self.backend` is not accessed by any
2761 // method calling this method
2762 let backend = unsafe { &mut *self.backend.get() };
2763 backend.cursor_get_current_value_count(cursor)
2764 }
2765 cursor_method_impls!(cursor_method_impl, cursor_method_dupkey_impl);
2766}
2767
2768impl<'a> Txn for TxnRw<'a> {
2769 fn get<'kr, K, V, C, KRef>(
2770 &self,
2771 db: &Db<K, V, C>,
2772 key: KRef,
2773 ) -> io::Result<Option<V::AlignedRef<'_>>>
2774 where
2775 K: ?Sized + Storable + 'kr,
2776 V: ?Sized + Storable,
2777 C: Constraint,
2778 KRef: StorableRef<'kr, K>,
2779 {
2780 // SAFETY:
2781 // * `TxnRw` is `!Sync` and `self.backend` is not accessed by any
2782 // method calling this method.
2783 // * The return type of this function has `'_` as lifetime, which
2784 // corresponds to the elided lifetime of `&self`. This lifetime
2785 // cannot live longer than the end of this transaction and ends
2786 // before any data is modified by the transaction. It is passed
2787 // to `TxnBackend::get_unsafe` by inference.
2788 unsafe {
2789 let backend = &mut *self.backend.get();
2790 backend.get_unsafe::<K, V, C, KRef>(db, key)
2791 }
2792 }
2793 fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
2794 where
2795 K: ?Sized + Storable,
2796 V: ?Sized + Storable,
2797 C: Constraint,
2798 {
2799 // SAFETY: `TxnRw` is `!Sync` and `self.backend` is not accessed by any
2800 // method calling this method
2801 let backend = unsafe { &mut *self.backend.get() };
2802 backend.new_cursor(db)
2803 }
2804 fn cursor_get_current_value_count<K, V>(
2805 &self,
2806 cursor: &Cursor<K, V, KeysDuplicate>,
2807 ) -> io::Result<usize>
2808 where
2809 K: ?Sized + Storable,
2810 V: ?Sized + Storable,
2811 {
2812 // SAFETY: `TxnRw` is `!Sync` and `self.backend` is not accessed by any
2813 // method calling this method
2814 let backend = unsafe { &mut *self.backend.get() };
2815 backend.cursor_get_current_value_count(cursor)
2816 }
2817 cursor_method_impls!(cursor_method_impl, cursor_method_dupkey_impl);
2818}
2819
2820impl<'a> TxnRw<'a> {
2821 /// Add commit handler to be executed on [`TxnRw::commit`] before the
2822 /// transaction is actually committed
2823 pub fn on_commit<F>(&mut self, commit_handler: F)
2824 where
2825 F: FnOnce(&mut Self) -> io::Result<()> + 'a,
2826 {
2827 self.commit_handlers.push(Box::new(commit_handler));
2828 }
2829 /// Commit transaction
2830 ///
2831 /// Previously added commit handlers (see [`TxnRw::on_commit`]) are
2832 /// executed in the same order as they have been added. If one commit
2833 /// handler reports an error, the transaction is aborted.
2834 pub fn commit(mut self) -> io::Result<()> {
2835 for handler in take(&mut self.commit_handlers) {
2836 handler(&mut self)?;
2837 }
2838 self.backend.into_inner().commit()
2839 }
2840 /// Abort transaction (same as [`drop`])
2841 pub fn abort(self) {}
2842 /// Start nested transaction
2843 ///
2844 /// Panics if environment does not support nested transactions (which is
2845 /// the case when the [`EnvBuilder::writemap`] flag has been set).
2846 pub fn nested(&mut self) -> io::Result<TxnRw<'_>> {
2847 let backend = self.backend.get_mut();
2848 assert!(
2849 backend.env_ro.backend.nestable_txns,
2850 "environment does not support nested transactions"
2851 );
2852 let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
2853 // SAFETY:
2854 // * A mutable borrow of the parent transaction (`&mut self`) ensures
2855 // that the parent transaction doesn't issue any operations while
2856 // the child is active and that as long as the child transaction is
2857 // active, the same rules apply as if the parent transaction is
2858 // still open (in particular: no calls to `lmdb::mdb_txn_begin`
2859 // with parent set to NULL can happen).
2860 // * The status passed to `check_err_code` is valid.
2861 unsafe {
2862 check_err_code(lmdb::mdb_txn_begin(
2863 backend.env_ro.backend.inner,
2864 backend.inner,
2865 0,
2866 txn_inner.as_mut_ptr(),
2867 ))?;
2868 }
2869 // SAFETY: call to `lmdb::mdb_txn_begin` above has been successful as
2870 // otherwise `check_err_code` would have returned an `Err` resulting in
2871 // early return from this function
2872 let txn_inner = unsafe { txn_inner.assume_init() };
2873 Ok(TxnRw {
2874 backend: UnsafeCell::new(TxnBackend {
2875 env_ro: &backend.env_ro,
2876 inner: txn_inner,
2877 cursors: Default::default(),
2878 }),
2879 used_dbs: UsedDbs::Nested(&mut self.used_dbs),
2880 commit_handlers: Default::default(),
2881 })
2882 }
2883 /// Implementation for various `put` related methods
2884 ///
2885 /// # Safety
2886 ///
2887 /// Passed `flags` must be valid and correctly chosen for the given `db`.
2888 unsafe fn put_with_flags<'kr, 'vr, K, V, C, KRef, VRef>(
2889 &mut self,
2890 db: &Db<K, V, C>,
2891 key: KRef,
2892 value: VRef,
2893 flags: LmdbFlags,
2894 ) -> io::Result<bool>
2895 where
2896 K: ?Sized + Storable + 'kr,
2897 V: ?Sized + Storable + 'vr,
2898 C: Constraint,
2899 KRef: StorableRef<'kr, K>,
2900 VRef: StorableRef<'vr, V>,
2901 {
2902 let backend = self.backend.get_mut();
2903 db.backend.assert_env_backend(&backend.env_ro.backend);
2904 let key: K::BytesRef<'_> = key.ref_to_bytes();
2905 let value: V::BytesRef<'_> = value.ref_to_bytes();
2906 backend.env_ro.assert_valid_keysize(&key)?;
2907 if C::DUPLICATE_KEYS {
2908 backend.env_ro.assert_valid_valuesize(&value)?;
2909 }
2910 // TODO: use `get_or_insert_owned` if stabilized
2911 //self.used_dbs.get_or_insert_owned(&db.backend);
2912 if !self.used_dbs.contains(&db.backend) {
2913 self.used_dbs.insert(db.backend.to_owned());
2914 }
2915 let lmdb_key = lmdb::MDB_val {
2916 mv_size: key.len(),
2917 mv_data: key.as_ptr() as *mut _,
2918 };
2919 let mut lmdb_data = lmdb::MDB_val {
2920 mv_size: value.len(),
2921 mv_data: value.as_ptr() as *mut _,
2922 };
2923 Ok(
2924 // SAFETY:
2925 // * `lmdb_key.mv_data` is a valid pointer to `lmdb_key.mv_size`
2926 // bytes of memory for reading.
2927 // * `lmdb_data.mv_data` is a valid pointer to
2928 // `lmdb_data.mv_size` bytes of memory for reading and writing
2929 // (writing is required when some `flags` are set).
2930 // * The memory pointed to by `lmdb_data.mv_data` is not read
2931 // after the transaction ends or modifies data (because it is
2932 // not read at all).
2933 // * Documentation demands that `flags` are valid and correctly
2934 // chosen for the database.
2935 match unsafe {
2936 lmdb::mdb_put(
2937 backend.inner,
2938 db.backend.inner,
2939 &lmdb_key as *const _ as *mut lmdb::MDB_val,
2940 &mut lmdb_data,
2941 flags,
2942 )
2943 } {
2944 lmdb::MDB_KEYEXIST => false,
2945 status => {
2946 // SAFETY: `status` is a valid LMDB return code, and if
2947 // negative, it is a valid LMDB error code
2948 unsafe { check_err_code(status) }?;
2949 true
2950 }
2951 },
2952 )
2953 }
2954 /// Put value into database
2955 ///
2956 /// This will overwrite an existing value if the database does not use the
2957 /// [`DbBuilder::keys_duplicate`] option.
2958 pub fn put<'kr, 'vr, K, V, C, KRef, VRef>(
2959 &mut self,
2960 db: &Db<K, V, C>,
2961 key: KRef,
2962 value: VRef,
2963 ) -> io::Result<()>
2964 where
2965 K: ?Sized + Storable + 'kr,
2966 V: ?Sized + Storable + 'vr,
2967 C: Constraint,
2968 KRef: StorableRef<'kr, K>,
2969 VRef: StorableRef<'vr, V>,
2970 {
2971 // SAFETY: passing no flags (`0`) is valid for any database
2972 unsafe {
2973 self.put_with_flags(db, key, value, 0)?;
2974 }
2975 Ok(())
2976 }
2977 /// Put value into database unless key exists
2978 ///
2979 /// Returns `Ok(false)` if key exists.
2980 pub fn put_unless_key_exists<'kr, 'vr, K, V, C, KRef, VRef>(
2981 &mut self,
2982 db: &Db<K, V, C>,
2983 key: KRef,
2984 value: VRef,
2985 ) -> io::Result<bool>
2986 where
2987 K: ?Sized + Storable + 'kr,
2988 V: ?Sized + Storable + 'vr,
2989 C: Constraint,
2990 KRef: StorableRef<'kr, K>,
2991 VRef: StorableRef<'vr, V>,
2992 {
2993 // SAFETY: passing `lmdb::MDB_NOOVERWRITE` is valid for any database
2994 unsafe { self.put_with_flags(db, key, value, lmdb::MDB_NOOVERWRITE) }
2995 }
2996 /// Put value into database unless key-value pair exists
2997 ///
2998 /// Returns `Ok(false)` if key-value pair exists.
2999 pub fn put_unless_pair_exists<'kr, 'vr, K, V, KRef, VRef>(
3000 &mut self,
3001 db: &Db<K, V, KeysDuplicate>,
3002 key: KRef,
3003 value: VRef,
3004 ) -> io::Result<bool>
3005 where
3006 K: ?Sized + Storable + 'kr,
3007 V: ?Sized + Storable + 'vr,
3008 KRef: StorableRef<'kr, K>,
3009 VRef: StorableRef<'vr, V>,
3010 {
3011 // SAFETY: passing `lmdb::MDB_NODUPDATA` is valid for databases with
3012 // the `KeysDuplicate` option, which is set for the given `db`
3013 unsafe { self.put_with_flags(db, key, value, lmdb::MDB_NODUPDATA) }
3014 }
3015 /// Delete all values from database that match a given key
3016 pub fn delete_key<'kr, K, V, C, KRef>(
3017 &mut self,
3018 db: &Db<K, V, C>,
3019 key: KRef,
3020 ) -> io::Result<bool>
3021 where
3022 K: ?Sized + Storable + 'kr,
3023 V: ?Sized + Storable,
3024 C: Constraint,
3025 KRef: StorableRef<'kr, K>,
3026 {
3027 let backend = self.backend.get_mut();
3028 db.backend.assert_env_backend(&backend.env_ro.backend);
3029 let key: K::BytesRef<'_> = key.ref_to_bytes();
3030 // TODO: use `get_or_insert_owned` if stabilized
3031 //self.used_dbs.get_or_insert_owned(&db.backend);
3032 if !self.used_dbs.contains(&db.backend) {
3033 self.used_dbs.insert(db.backend.to_owned());
3034 }
3035 let lmdb_key = lmdb::MDB_val {
3036 mv_size: key.len(),
3037 mv_data: key.as_ptr() as *mut _,
3038 };
3039 Ok(
3040 // SAFETY:
3041 // * `lmdb_key.mv_data` is a valid pointer to `lmdb_key.mv_size`
3042 // bytes of memory for reading.
3043 // * It is allowed to pass a NULL pointer as last argument.
3044 match unsafe {
3045 lmdb::mdb_del(
3046 backend.inner,
3047 db.backend.inner,
3048 &lmdb_key as *const _ as *mut lmdb::MDB_val,
3049 null_mut(),
3050 )
3051 } {
3052 lmdb::MDB_NOTFOUND => false,
3053 status => {
3054 // SAFETY: `status` is a valid LMDB return code, and if
3055 // negative, it is a valid LMDB error code
3056 unsafe { check_err_code(status) }?;
3057 true
3058 }
3059 },
3060 )
3061 }
3062 /// Delete key-value pair from database
3063 pub fn delete_pair<'kr, 'vr, K, V, KRef, VRef>(
3064 &mut self,
3065 db: &Db<K, V, KeysDuplicate>,
3066 key: KRef,
3067 value: VRef,
3068 ) -> io::Result<bool>
3069 where
3070 K: ?Sized + Storable + 'kr,
3071 V: ?Sized + Storable + 'vr,
3072 KRef: StorableRef<'kr, K>,
3073 VRef: StorableRef<'vr, V>,
3074 {
3075 let backend = self.backend.get_mut();
3076 db.backend.assert_env_backend(&backend.env_ro.backend);
3077 let key: K::BytesRef<'_> = key.ref_to_bytes();
3078 let value: V::BytesRef<'_> = value.ref_to_bytes();
3079 // TODO: use `get_or_insert_owned` if stabilized
3080 //self.used_dbs.get_or_insert_owned(&db.backend);
3081 if !self.used_dbs.contains(&db.backend) {
3082 self.used_dbs.insert(db.backend.to_owned());
3083 }
3084 let lmdb_key = lmdb::MDB_val {
3085 mv_size: key.len(),
3086 mv_data: key.as_ptr() as *mut _,
3087 };
3088 let lmdb_value = lmdb::MDB_val {
3089 mv_size: value.len(),
3090 mv_data: value.as_ptr() as *mut _,
3091 };
3092 Ok(
3093 // SAFETY:
3094 // * `lmdb_key.mv_data` and `lmdb_data.mv_data` are valid
3095 // pointers to `lmdb_key.mv_size` and `lmdb_data.mv_size`
3096 // bytes of memory, respectively, for reading.
3097 match unsafe {
3098 lmdb::mdb_del(
3099 backend.inner,
3100 db.backend.inner,
3101 &lmdb_key as *const _ as *mut lmdb::MDB_val,
3102 &lmdb_value as *const _ as *mut lmdb::MDB_val,
3103 )
3104 } {
3105 lmdb::MDB_NOTFOUND => false,
3106 status => {
3107 // SAFETY: `status` is a valid LMDB return code, and if
3108 // negative, it is a valid LMDB error code
3109 unsafe { check_err_code(status) }?;
3110 true
3111 }
3112 },
3113 )
3114 }
3115 /// Delete all entries in a database
3116 ///
3117 /// Use [`EnvRw::drop_db`] to completely drop the database.
3118 pub fn delete_all<K, V, C>(&mut self, db: &Db<K, V, C>) -> io::Result<()>
3119 where
3120 K: ?Sized + Storable,
3121 V: ?Sized + Storable,
3122 C: Constraint,
3123 {
3124 let backend = self.backend.get_mut();
3125 db.backend.assert_env_backend(&backend.env_ro.backend);
3126 // SAFETY:
3127 // * Passing zero (`0`) as a last argument to `lmdb::mdb_drop` means
3128 // the database doesn't get closed but just emptied; thus no
3129 // special requirements for calling the function exist.
3130 // * The status passed to `check_err_code` is valid.
3131 unsafe {
3132 check_err_code(lmdb::mdb_drop(backend.inner, db.backend.inner, 0))?;
3133 }
3134 Ok(())
3135 }
3136 /// Delete key-value pair at current cursor position
3137 pub fn cursor_delete_current<K, V, C>(&mut self, cursor: &Cursor<K, V, C>) -> io::Result<()>
3138 where
3139 K: ?Sized + Storable,
3140 V: ?Sized + Storable,
3141 C: Constraint,
3142 {
3143 // TODO: use `get_or_insert_owned` if stabilized
3144 //self.used_dbs.get_or_insert_owned(&cursor.db.backend);
3145 if !self.used_dbs.contains(&cursor.db.backend) {
3146 self.used_dbs.insert(cursor.db.backend.to_owned());
3147 }
3148 let backend = self.backend.get_mut();
3149 cursor.backend.assert_txn_backend(backend);
3150 // SAFETY:
3151 // * Regarding closing the cursor or its transaction, refer to the
3152 // corresponding other SAFETY comments on closing the cursor
3153 // and/or the transaction, respectively.
3154 // * Passing no flags (`0`) as last argument to
3155 // `lmdb::mdb_cursor_del`is valid for any database
3156 // * The status passed to `check_err_code` is valid.
3157 unsafe { check_err_code(lmdb::mdb_cursor_del(cursor.backend.inner, 0)) }
3158 }
3159 /// Delete all values with same key at current cursor position
3160 pub fn cursor_delete_current_key<K, V>(
3161 &mut self,
3162 cursor: &Cursor<K, V, KeysDuplicate>,
3163 ) -> io::Result<()>
3164 where
3165 K: ?Sized + Storable,
3166 V: ?Sized + Storable,
3167 {
3168 // TODO: use `get_or_insert_owned` if stabilized
3169 //self.used_dbs.get_or_insert_owned(&cursor.db.backend);
3170 if !self.used_dbs.contains(&cursor.db.backend) {
3171 self.used_dbs.insert(cursor.db.backend.to_owned());
3172 }
3173 let backend = self.backend.get_mut();
3174 cursor.backend.assert_txn_backend(backend);
3175 // SAFETY:
3176 // * Regarding closing the cursor or its transaction, refer to the
3177 // corresponding other SAFETY comments on closing the cursor
3178 // and/or the transaction, respectively.
3179 // * Passing `lmdb::MDB_NODUPDATA` is valid for databases with
3180 // the `KeysDuplicate` option, which is set for the given `cursor`
3181 // and thus also for the corresponding database.
3182 // * The status passed to `check_err_code` is valid.
3183 unsafe {
3184 check_err_code(lmdb::mdb_cursor_del(
3185 cursor.backend.inner,
3186 lmdb::MDB_NODUPDATA as LmdbFlags,
3187 ))
3188 }
3189 }
3190}
3191
3192impl<K, V, C> Cursor<K, V, C> {
3193 /// Associated database
3194 pub fn db(&self) -> &Db<K, V, C> {
3195 &self.db
3196 }
3197}
3198
3199/// Version of underlying LMDB library
3200#[derive(Eq, PartialEq, Clone, Debug)]
3201pub struct LmdbVersion {
3202 /// Descriptive string
3203 pub string: &'static str,
3204 /// Major version number
3205 pub major: i32,
3206 /// Minor version number
3207 pub minor: i32,
3208 /// Patch version number
3209 pub patch: i32,
3210}
3211
3212/// Retrieve version of underlying LMDB library
3213pub fn lmdb_version() -> LmdbVersion {
3214 let mut major: c_int = 0;
3215 let mut minor: c_int = 0;
3216 let mut patch: c_int = 0;
3217 // SAFETY: `lmdb::mdb_version` is expected to return a static C string
3218 // as the API documentation does not put any constraints on the usage of
3219 // the returned string
3220 let string: &CStr = unsafe {
3221 CStr::from_ptr(lmdb::mdb_version(
3222 &mut major as *mut c_int,
3223 &mut minor as *mut c_int,
3224 &mut patch as *mut c_int,
3225 ))
3226 };
3227 LmdbVersion {
3228 string: string.to_str().unwrap(),
3229 major: major.try_into().unwrap(),
3230 minor: minor.try_into().unwrap(),
3231 patch: patch.try_into().unwrap(),
3232 }
3233}