heed/mdb/
lmdb_error.rs

1use std::error::Error as StdError;
2use std::ffi::CStr;
3use std::os::raw::c_char;
4use std::{fmt, str};
5
6use libc::c_int;
7use lmdb_master_sys as ffi;
8
9/// An LMDB error kind.
10#[derive(Debug, Copy, Clone, Eq, PartialEq)]
11pub enum Error {
12    /// A key/data pair already exists.
13    ///
14    /// May be also returned by append functions. Data passed
15    /// doesn't respect the database ordering.
16    KeyExist,
17    /// A key/data pair was not found (EOF).
18    NotFound,
19    /// Requested page not found - this usually indicates corruption.
20    PageNotFound,
21    /// Located page was wrong type.
22    Corrupted,
23    /// Update of meta page failed or environment had fatal error.
24    Panic,
25    /// Environment version mismatch.
26    VersionMismatch,
27    /// File is not a valid LMDB file.
28    Invalid,
29    /// Environment mapsize reached.
30    MapFull,
31    /// Environment maxdbs reached.
32    DbsFull,
33    /// Environment maxreaders reached.
34    ReadersFull,
35    /// Too many TLS keys in use - Windows only.
36    TlsFull,
37    /// Txn has too many dirty pages.
38    TxnFull,
39    /// Cursor stack too deep - internal error.
40    CursorFull,
41    /// Page has not enough space - internal error.
42    PageFull,
43    /// Database contents grew beyond environment mapsize.
44    MapResized,
45    /// Operation and DB incompatible, or DB type changed. This can mean:
46    ///   - The operation expects an MDB_DUPSORT / MDB_DUPFIXED database.
47    ///   - Opening a named DB when the unnamed DB has MDB_DUPSORT / MDB_INTEGERKEY.
48    ///   - Accessing a data record as a database, or vice versa.
49    ///   - The database was dropped and recreated with different flags.
50    Incompatible,
51    /// Invalid reuse of reader locktable slot.
52    BadRslot,
53    /// Transaction cannot recover - it must be aborted.
54    BadTxn,
55    /// Unsupported size of key/DB name/data, or wrong DUP_FIXED size.
56    ///
57    /// Common causes of this error:
58    ///   - You tried to store a zero-length key
59    ///   - You tried to store a key longer than the max allowed key (511 bytes by default)
60    ///   - You are using [DUP_SORT](crate::DatabaseFlags::DUP_SORT) and trying to store a
61    ///     value longer than the max allowed key size (511 bytes by default)
62    ///
63    /// In the last two cases you can enable the `longer-keys` feature to increase the max allowed key size.
64    BadValSize,
65    /// The specified DBI was changed unexpectedly.
66    BadDbi,
67    /// Unexpected problem - transaction should abort.
68    Problem,
69    /// Other error.
70    Other(c_int),
71}
72
73impl Error {
74    /// Returns `true` if the given error is [`Error::NotFound`].
75    pub fn not_found(&self) -> bool {
76        *self == Error::NotFound
77    }
78
79    /// Converts a raw error code to an `Error`.
80    pub fn from_err_code(err_code: c_int) -> Error {
81        match err_code {
82            ffi::MDB_KEYEXIST => Error::KeyExist,
83            ffi::MDB_NOTFOUND => Error::NotFound,
84            ffi::MDB_PAGE_NOTFOUND => Error::PageNotFound,
85            ffi::MDB_CORRUPTED => Error::Corrupted,
86            ffi::MDB_PANIC => Error::Panic,
87            ffi::MDB_VERSION_MISMATCH => Error::VersionMismatch,
88            ffi::MDB_INVALID => Error::Invalid,
89            ffi::MDB_MAP_FULL => Error::MapFull,
90            ffi::MDB_DBS_FULL => Error::DbsFull,
91            ffi::MDB_READERS_FULL => Error::ReadersFull,
92            ffi::MDB_TLS_FULL => Error::TlsFull,
93            ffi::MDB_TXN_FULL => Error::TxnFull,
94            ffi::MDB_CURSOR_FULL => Error::CursorFull,
95            ffi::MDB_PAGE_FULL => Error::PageFull,
96            ffi::MDB_MAP_RESIZED => Error::MapResized,
97            ffi::MDB_INCOMPATIBLE => Error::Incompatible,
98            ffi::MDB_BAD_RSLOT => Error::BadRslot,
99            ffi::MDB_BAD_TXN => Error::BadTxn,
100            ffi::MDB_BAD_VALSIZE => Error::BadValSize,
101            ffi::MDB_BAD_DBI => Error::BadDbi,
102            ffi::MDB_PROBLEM => Error::Problem,
103            other => Error::Other(other),
104        }
105    }
106
107    /// Converts an `Error` to the raw error code.
108    #[allow(clippy::trivially_copy_pass_by_ref)]
109    pub fn to_err_code(&self) -> c_int {
110        match *self {
111            Error::KeyExist => ffi::MDB_KEYEXIST,
112            Error::NotFound => ffi::MDB_NOTFOUND,
113            Error::PageNotFound => ffi::MDB_PAGE_NOTFOUND,
114            Error::Corrupted => ffi::MDB_CORRUPTED,
115            Error::Panic => ffi::MDB_PANIC,
116            Error::VersionMismatch => ffi::MDB_VERSION_MISMATCH,
117            Error::Invalid => ffi::MDB_INVALID,
118            Error::MapFull => ffi::MDB_MAP_FULL,
119            Error::DbsFull => ffi::MDB_DBS_FULL,
120            Error::ReadersFull => ffi::MDB_READERS_FULL,
121            Error::TlsFull => ffi::MDB_TLS_FULL,
122            Error::TxnFull => ffi::MDB_TXN_FULL,
123            Error::CursorFull => ffi::MDB_CURSOR_FULL,
124            Error::PageFull => ffi::MDB_PAGE_FULL,
125            Error::MapResized => ffi::MDB_MAP_RESIZED,
126            Error::Incompatible => ffi::MDB_INCOMPATIBLE,
127            Error::BadRslot => ffi::MDB_BAD_RSLOT,
128            Error::BadTxn => ffi::MDB_BAD_TXN,
129            Error::BadValSize => ffi::MDB_BAD_VALSIZE,
130            Error::BadDbi => ffi::MDB_BAD_DBI,
131            Error::Problem => ffi::MDB_PROBLEM,
132            Error::Other(err_code) => err_code,
133        }
134    }
135}
136
137impl fmt::Display for Error {
138    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
139        let description = unsafe {
140            // This is safe since the error messages returned from mdb_strerror are static.
141            let err: *const c_char = ffi::mdb_strerror(self.to_err_code()) as *const c_char;
142            str::from_utf8_unchecked(CStr::from_ptr(err).to_bytes())
143        };
144
145        fmt.write_str(description)
146    }
147}
148
149impl StdError for Error {}
150
151pub fn mdb_result(err_code: c_int) -> Result<(), Error> {
152    if err_code == ffi::MDB_SUCCESS {
153        Ok(())
154    } else {
155        Err(Error::from_err_code(err_code))
156    }
157}
158
159#[cfg(test)]
160mod test {
161    use super::*;
162
163    #[test]
164    fn test_description() {
165        assert_eq!("Permission denied", Error::from_err_code(13).to_string());
166        assert_eq!("MDB_NOTFOUND: No matching key/data pair found", Error::NotFound.to_string());
167    }
168}