Skip to main content

heed/
lib.rs

1#![doc(
2    html_favicon_url = "https://raw.githubusercontent.com/meilisearch/heed/main/assets/heed-pigeon.ico?raw=true"
3)]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/meilisearch/heed/main/assets/heed-pigeon-logo.png?raw=true"
6)]
7
8//! `heed` is a high-level wrapper of [LMDB].
9//!
10//! The [cookbook] will give you a variety of complete Rust programs to use with heed.
11//!
12//! ----
13//!
14//! This crate simply facilitates the use of LMDB by providing a mechanism to store and
15//! retrieve Rust types. It abstracts away some of the complexities of the raw LMDB usage
16//! while retaining its performance characteristics. The functionality is achieved with the help
17//! of the serde library for data serialization concerns.
18//!
19//! LMDB stands for Lightning Memory-Mapped Database, which utilizes memory-mapped files
20//! for efficient data storage and retrieval by mapping file content directly into the virtual
21//! address space. `heed` derives its efficiency from the underlying LMDB without imposing
22//! additional runtime costs.
23//!
24//! [LMDB]: https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database
25//!
26//! # Examples
27//!
28//! Open a database that will support some typed key/data and ensure, at compile time,
29//! that you'll write those types and not others.
30//!
31//! ```
32//! use std::fs;
33//! use std::path::Path;
34//! use heed::{EnvOpenOptions, Database};
35//! use heed::types::*;
36//!
37//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
38//! let dir = tempfile::tempdir()?;
39//! let env = unsafe { EnvOpenOptions::new().open(dir.path())? };
40//!
41//! // we will open the default unnamed database
42//! let mut wtxn = env.write_txn()?;
43//! let db: Database<Str, U32<byteorder::NativeEndian>> = env.create_database(&mut wtxn, None)?;
44//!
45//! // opening a write transaction
46//! db.put(&mut wtxn, "seven", &7)?;
47//! db.put(&mut wtxn, "zero", &0)?;
48//! db.put(&mut wtxn, "five", &5)?;
49//! db.put(&mut wtxn, "three", &3)?;
50//! wtxn.commit()?;
51//!
52//! // opening a read transaction
53//! // to check if those values are now available
54//! let mut rtxn = env.read_txn()?;
55//!
56//! let ret = db.get(&rtxn, "zero")?;
57//! assert_eq!(ret, Some(0));
58//!
59//! let ret = db.get(&rtxn, "five")?;
60//! assert_eq!(ret, Some(5));
61//! # Ok(()) }
62//! ```
63#![warn(missing_docs)]
64
65pub mod cookbook;
66mod cursor;
67mod database;
68mod env;
69pub mod iteration_method;
70mod iterator;
71mod mdb;
72mod reserved_space;
73mod txn;
74mod write_admission;
75
76use std::ffi::CStr;
77use std::{error, fmt, io, mem, result};
78
79use heed_traits as traits;
80pub use {byteorder, heed_types as types};
81
82use self::cursor::{RoCursor, RwCursor};
83pub use self::database::{Database, DatabaseOpenOptions, DatabaseStat};
84pub use self::env::{
85    env_closing_event, CompactionOption, DefaultComparator, Env, EnvClosingEvent, EnvInfo,
86    EnvOpenOptions, FlagSetMode, PinnedLmdbFileIdentity, PinnedLmdbIdentity,
87};
88pub use self::iterator::{
89    RoIter, RoPrefix, RoRange, RoRevIter, RoRevPrefix, RoRevRange, RwIter, RwPrefix, RwRange,
90    RwRevIter, RwRevPrefix, RwRevRange,
91};
92pub use self::mdb::error::Error as MdbError;
93use self::mdb::ffi::{from_val, into_val};
94pub use self::mdb::flags::{DatabaseFlags, EnvFlags, PutFlags};
95pub use self::reserved_space::ReservedSpace;
96pub use self::traits::{BoxedError, BytesDecode, BytesEncode, Comparator, LexicographicComparator};
97pub use self::txn::{RoTxn, RwTxn};
98pub use self::write_admission::WriteAdmission;
99
100/// The underlying LMDB library version information.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
102pub struct LmdbVersion {
103    /// The library version as a string.
104    pub string: &'static str,
105    /// The library major version number.
106    pub major: i32,
107    /// The library minor version number.
108    pub minor: i32,
109    /// The library patch version number.
110    pub patch: i32,
111}
112
113/// Return the LMDB library version information.
114///
115/// ```
116/// use heed::{lmdb_version, LmdbVersion};
117///
118/// let expected = LmdbVersion {
119///     string: "LMDB 0.9.70: (December 19, 2015)",
120///     major: 0,
121///     minor: 9,
122///     patch: 70,
123/// };
124/// assert_eq!(lmdb_version(), expected);
125/// ```
126pub fn lmdb_version() -> LmdbVersion {
127    let mut major = mem::MaybeUninit::uninit();
128    let mut minor = mem::MaybeUninit::uninit();
129    let mut patch = mem::MaybeUninit::uninit();
130
131    unsafe {
132        let string_ptr =
133            mdb::ffi::mdb_version(major.as_mut_ptr(), minor.as_mut_ptr(), patch.as_mut_ptr());
134        LmdbVersion {
135            string: CStr::from_ptr(string_ptr).to_str().unwrap(),
136            major: major.assume_init(),
137            minor: minor.assume_init(),
138            patch: patch.assume_init(),
139        }
140    }
141}
142
143/// An error that encapsulates all possible errors in this crate.
144#[derive(Debug)]
145pub enum Error {
146    /// I/O error: can come from the standard library or be a rewrapped [`MdbError`].
147    Io(io::Error),
148    /// LMDB error.
149    Mdb(MdbError),
150    /// Encoding error.
151    Encoding(BoxedError),
152    /// Decoding error.
153    Decoding(BoxedError),
154    /// Database closing in progress.
155    DatabaseClosing,
156    /// Attempt to open [`Env`] with different options.
157    BadOpenOptions {
158        /// The options that were used to originally open this env.
159        options: EnvOpenOptions,
160        /// The env opened with the original options.
161        env: Env,
162    },
163}
164
165impl fmt::Display for Error {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        match self {
168            Error::Io(error) => write!(f, "{}", error),
169            Error::Mdb(error) => write!(f, "{}", error),
170            Error::Encoding(error) => write!(f, "error while encoding: {}", error),
171            Error::Decoding(error) => write!(f, "error while decoding: {}", error),
172            Error::DatabaseClosing => {
173                f.write_str("database is in a closing phase, you can't open it at the same time")
174            }
175            Error::BadOpenOptions { .. } => {
176                f.write_str("an environment is already opened with different options")
177            }
178        }
179    }
180}
181
182impl error::Error for Error {}
183
184impl From<MdbError> for Error {
185    fn from(error: MdbError) -> Error {
186        match error {
187            MdbError::Other(e) => Error::Io(io::Error::from_raw_os_error(e)),
188            _ => Error::Mdb(error),
189        }
190    }
191}
192
193impl From<io::Error> for Error {
194    fn from(error: io::Error) -> Error {
195        Error::Io(error)
196    }
197}
198
199/// Either a success or an [`Error`].
200pub type Result<T> = result::Result<T, Error>;
201
202/// An unspecified type.
203///
204/// It is used as placeholders when creating a database.
205/// It does not implement the [`BytesEncode`] and [`BytesDecode`] traits
206/// and therefore can't be used as codecs. You must use the [`Database::remap_types`]
207/// to properly define them.
208pub enum Unspecified {}
209
210macro_rules! assert_eq_env_db_txn {
211    ($database:ident, $txn:ident) => {
212        assert!(
213            $database.env_ident == $txn.env_mut_ptr() as usize,
214            "The database environment doesn't match the transaction's environment"
215        );
216    };
217}
218
219macro_rules! assert_eq_env_txn {
220    ($env:expr, $txn:ident) => {
221        assert!(
222            $env.env_mut_ptr() == $txn.env_mut_ptr(),
223            "The environment doesn't match the transaction's environment"
224        );
225    };
226}
227
228pub(crate) use {assert_eq_env_db_txn, assert_eq_env_txn};
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    #[test]
235    fn error_is_send_sync() {
236        fn give_me_send_sync<T: Send + Sync>(_: T) {}
237
238        let error = Error::Encoding(Box::from("There is an issue, you know?"));
239        give_me_send_sync(error);
240    }
241}