reth_libmdbx/
error.rs

1use std::{ffi::c_int, result};
2
3/// An MDBX result.
4pub type Result<T> = result::Result<T, Error>;
5
6/// An MDBX error kind.
7#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
8pub enum Error {
9    /// Key/data pair already exists.
10    #[error("key/data pair already exists")]
11    KeyExist,
12    /// No matching key/data pair found.
13    #[error("no matching key/data pair found")]
14    NotFound,
15    /// The cursor is already at the end of data.
16    #[error("the cursor is already at the end of data")]
17    NoData,
18    /// Requested page not found.
19    #[error("requested page not found")]
20    PageNotFound,
21    /// Database is corrupted.
22    #[error("database is corrupted")]
23    Corrupted,
24    /// Fatal environment error.
25    #[error("fatal environment error")]
26    Panic,
27    /// DB version mismatch.
28    #[error("DB version mismatch")]
29    VersionMismatch,
30    /// File is not an MDBX file.
31    #[error("file is not an MDBX file")]
32    Invalid,
33    /// Environment map size limit reached.
34    #[error("environment map size limit reached")]
35    MapFull,
36    /// Too many DBI-handles (maxdbs reached).
37    #[error("too many DBI-handles (maxdbs reached)")]
38    DbsFull,
39    /// Too many readers (maxreaders reached).
40    #[error("too many readers (maxreaders reached)")]
41    ReadersFull,
42    /// Transaction has too many dirty pages (i.e., the transaction is too big).
43    #[error("transaction has too many dirty pages (i.e., the transaction is too big)")]
44    TxnFull,
45    /// Cursor stack limit reached.
46    #[error("cursor stack limit reached")]
47    CursorFull,
48    /// Page has no more space.
49    #[error("page has no more space")]
50    PageFull,
51    /// The database engine was unable to extend mapping, e.g. the address space is unavailable or
52    /// busy.
53    ///
54    /// This can mean:
55    /// - The database size was extended by other processes beyond the environment map size, and
56    ///   the engine was unable to extend the mapping while starting a read transaction. The
57    ///   environment should be re-opened to continue.
58    /// - The engine was unable to extend the mapping during a write transaction or an explicit
59    ///   call to change the geometry of the environment.
60    #[error("database engine was unable to extend mapping")]
61    UnableExtendMapSize,
62    /// Environment or database is not compatible with the requested operation or flags.
63    #[error("environment or database is not compatible with the requested operation or flags")]
64    Incompatible,
65    /// Invalid reuse of reader locktable slot.
66    #[error("invalid reuse of reader locktable slot")]
67    BadRslot,
68    /// Transaction is not valid for requested operation.
69    #[error("transaction is not valid for requested operation")]
70    BadTxn,
71    /// Invalid size or alignment of key or data for the target database.
72    #[error("invalid size or alignment of key or data for the target database")]
73    BadValSize,
74    /// The specified DBI-handle is invalid.
75    #[error("the specified DBI-handle is invalid")]
76    BadDbi,
77    /// Unexpected internal error.
78    #[error("unexpected internal error")]
79    Problem,
80    /// Another write transaction is running.
81    #[error("another write transaction is running")]
82    Busy,
83    /// The specified key has more than one associated value.
84    #[error("the specified key has more than one associated value")]
85    Multival,
86    /// Wrong signature of a runtime object(s).
87    #[error("wrong signature of a runtime object(s)")]
88    BadSignature,
89    /// Database should be recovered, but cannot be done automatically since it's in read-only
90    /// mode.
91    #[error(
92        "database should be recovered, but cannot be done automatically since it's in read-only mode"
93    )]
94    WannaRecovery,
95    /// The given key value is mismatched to the current cursor position.
96    #[error("the given key value is mismatched to the current cursor position")]
97    KeyMismatch,
98    /// Decode error: An invalid parameter was specified.
99    #[error("invalid parameter specified")]
100    DecodeError,
101    /// The environment opened in read-only.
102    #[error(
103        "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more"
104    )]
105    Access,
106    /// Database is too large for the current system.
107    #[error("database is too large for the current system")]
108    TooLarge,
109    /// Decode error length difference:
110    ///
111    /// An invalid parameter was specified, or the environment has an active write transaction.
112    #[error("invalid parameter specified or active write transaction")]
113    DecodeErrorLenDiff,
114    /// If the [Environment](crate::Environment) was opened with
115    /// [`EnvironmentKind::WriteMap`](crate::EnvironmentKind::WriteMap) flag, nested transactions
116    /// are not supported.
117    #[error("nested transactions are not supported with WriteMap")]
118    NestedTransactionsUnsupportedWithWriteMap,
119    /// If the [Environment](crate::Environment) was opened with in read-only mode
120    /// [`Mode::ReadOnly`](crate::flags::Mode::ReadOnly), write transactions can't be opened.
121    #[error("write transactions are not supported in read-only mode")]
122    WriteTransactionUnsupportedInReadOnlyMode,
123    /// Read transaction has been timed out.
124    #[error("read transaction has been timed out")]
125    ReadTransactionTimeout,
126    /// Permission defined
127    #[error("permission denied to setup database")]
128    Permission,
129    /// Unknown error code.
130    #[error("unknown error code: {0}")]
131    Other(i32),
132}
133
134impl Error {
135    /// Converts a raw error code to an [Error].
136    pub const fn from_err_code(err_code: c_int) -> Self {
137        match err_code {
138            ffi::MDBX_KEYEXIST => Self::KeyExist,
139            ffi::MDBX_NOTFOUND => Self::NotFound,
140            ffi::MDBX_ENODATA => Self::NoData,
141            ffi::MDBX_PAGE_NOTFOUND => Self::PageNotFound,
142            ffi::MDBX_CORRUPTED => Self::Corrupted,
143            ffi::MDBX_PANIC => Self::Panic,
144            ffi::MDBX_VERSION_MISMATCH => Self::VersionMismatch,
145            ffi::MDBX_INVALID => Self::Invalid,
146            ffi::MDBX_MAP_FULL => Self::MapFull,
147            ffi::MDBX_DBS_FULL => Self::DbsFull,
148            ffi::MDBX_READERS_FULL => Self::ReadersFull,
149            ffi::MDBX_TXN_FULL => Self::TxnFull,
150            ffi::MDBX_CURSOR_FULL => Self::CursorFull,
151            ffi::MDBX_PAGE_FULL => Self::PageFull,
152            ffi::MDBX_UNABLE_EXTEND_MAPSIZE => Self::UnableExtendMapSize,
153            ffi::MDBX_INCOMPATIBLE => Self::Incompatible,
154            ffi::MDBX_BAD_RSLOT => Self::BadRslot,
155            ffi::MDBX_BAD_TXN => Self::BadTxn,
156            ffi::MDBX_BAD_VALSIZE => Self::BadValSize,
157            ffi::MDBX_BAD_DBI => Self::BadDbi,
158            ffi::MDBX_PROBLEM => Self::Problem,
159            ffi::MDBX_BUSY => Self::Busy,
160            ffi::MDBX_EMULTIVAL => Self::Multival,
161            ffi::MDBX_WANNA_RECOVERY => Self::WannaRecovery,
162            ffi::MDBX_EKEYMISMATCH => Self::KeyMismatch,
163            ffi::MDBX_EINVAL => Self::DecodeError,
164            ffi::MDBX_EACCESS => Self::Access,
165            ffi::MDBX_TOO_LARGE => Self::TooLarge,
166            ffi::MDBX_EBADSIGN => Self::BadSignature,
167            ffi::MDBX_EPERM => Self::Permission,
168            other => Self::Other(other),
169        }
170    }
171
172    /// Converts an [Error] to the raw error code.
173    pub const fn to_err_code(&self) -> i32 {
174        match self {
175            Self::KeyExist => ffi::MDBX_KEYEXIST,
176            Self::NotFound => ffi::MDBX_NOTFOUND,
177            Self::NoData => ffi::MDBX_ENODATA,
178            Self::PageNotFound => ffi::MDBX_PAGE_NOTFOUND,
179            Self::Corrupted => ffi::MDBX_CORRUPTED,
180            Self::Panic => ffi::MDBX_PANIC,
181            Self::VersionMismatch => ffi::MDBX_VERSION_MISMATCH,
182            Self::Invalid => ffi::MDBX_INVALID,
183            Self::MapFull => ffi::MDBX_MAP_FULL,
184            Self::DbsFull => ffi::MDBX_DBS_FULL,
185            Self::ReadersFull => ffi::MDBX_READERS_FULL,
186            Self::TxnFull => ffi::MDBX_TXN_FULL,
187            Self::CursorFull => ffi::MDBX_CURSOR_FULL,
188            Self::PageFull => ffi::MDBX_PAGE_FULL,
189            Self::UnableExtendMapSize => ffi::MDBX_UNABLE_EXTEND_MAPSIZE,
190            Self::Incompatible => ffi::MDBX_INCOMPATIBLE,
191            Self::BadRslot => ffi::MDBX_BAD_RSLOT,
192            Self::BadTxn => ffi::MDBX_BAD_TXN,
193            Self::BadValSize => ffi::MDBX_BAD_VALSIZE,
194            Self::BadDbi => ffi::MDBX_BAD_DBI,
195            Self::Problem => ffi::MDBX_PROBLEM,
196            Self::Busy => ffi::MDBX_BUSY,
197            Self::Multival => ffi::MDBX_EMULTIVAL,
198            Self::WannaRecovery => ffi::MDBX_WANNA_RECOVERY,
199            Self::KeyMismatch => ffi::MDBX_EKEYMISMATCH,
200            Self::DecodeErrorLenDiff | Self::DecodeError => ffi::MDBX_EINVAL,
201            Self::TooLarge => ffi::MDBX_TOO_LARGE,
202            Self::BadSignature => ffi::MDBX_EBADSIGN,
203            Self::Access |
204            Self::WriteTransactionUnsupportedInReadOnlyMode |
205            Self::NestedTransactionsUnsupportedWithWriteMap => ffi::MDBX_EACCESS,
206            Self::ReadTransactionTimeout => -96000, // Custom non-MDBX error code
207            Self::Permission => ffi::MDBX_EPERM,
208            Self::Other(err_code) => *err_code,
209        }
210    }
211}
212
213impl From<Error> for i32 {
214    fn from(value: Error) -> Self {
215        value.to_err_code()
216    }
217}
218
219#[inline]
220pub(crate) const fn mdbx_result(err_code: c_int) -> Result<bool> {
221    match err_code {
222        ffi::MDBX_SUCCESS => Ok(false),
223        ffi::MDBX_RESULT_TRUE => Ok(true),
224        other => Err(Error::from_err_code(other)),
225    }
226}
227
228#[macro_export]
229macro_rules! mdbx_try_optional {
230    ($expr:expr) => {{
231        match $expr {
232            Err(Error::NotFound | Error::NoData) => return Ok(None),
233            Err(e) => return Err(e),
234            Ok(v) => v,
235        }
236    }};
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn test_description() {
245        assert_eq!(
246            "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more",
247            Error::from_err_code(13).to_string()
248        );
249
250        assert_eq!("file is not an MDBX file", Error::Invalid.to_string());
251    }
252
253    #[test]
254    fn test_conversion() {
255        assert_eq!(Error::from_err_code(ffi::MDBX_KEYEXIST), Error::KeyExist);
256    }
257}