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