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("database should be recovered, but cannot be done automatically since it's in read-only mode")]
92    WannaRecovery,
93    /// The given key value is mismatched to the current cursor position.
94    #[error("the given key value is mismatched to the current cursor position")]
95    KeyMismatch,
96    /// Decode error: An invalid parameter was specified.
97    #[error("invalid parameter specified")]
98    DecodeError,
99    /// The environment opened in read-only.
100    #[error("the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more")]
101    Access,
102    /// Database is too large for the current system.
103    #[error("database is too large for the current system")]
104    TooLarge,
105    /// Decode error length difference:
106    ///
107    /// An invalid parameter was specified, or the environment has an active write transaction.
108    #[error("invalid parameter specified or active write transaction")]
109    DecodeErrorLenDiff,
110    /// If the [Environment](crate::Environment) was opened with
111    /// [`EnvironmentKind::WriteMap`](crate::EnvironmentKind::WriteMap) flag, nested transactions
112    /// are not supported.
113    #[error("nested transactions are not supported with WriteMap")]
114    NestedTransactionsUnsupportedWithWriteMap,
115    /// If the [Environment](crate::Environment) was opened with in read-only mode
116    /// [`Mode::ReadOnly`](crate::flags::Mode::ReadOnly), write transactions can't be opened.
117    #[error("write transactions are not supported in read-only mode")]
118    WriteTransactionUnsupportedInReadOnlyMode,
119    /// Read transaction has been timed out.
120    #[error("read transaction has been timed out")]
121    ReadTransactionTimeout,
122    /// Permission defined
123    #[error("permission denied to setup database")]
124    Permission,
125    /// Unknown error code.
126    #[error("unknown error code: {0}")]
127    Other(i32),
128}
129
130impl Error {
131    /// Converts a raw error code to an [Error].
132    pub const fn from_err_code(err_code: c_int) -> Self {
133        match err_code {
134            ffi::MDBX_KEYEXIST => Self::KeyExist,
135            ffi::MDBX_NOTFOUND => Self::NotFound,
136            ffi::MDBX_ENODATA => Self::NoData,
137            ffi::MDBX_PAGE_NOTFOUND => Self::PageNotFound,
138            ffi::MDBX_CORRUPTED => Self::Corrupted,
139            ffi::MDBX_PANIC => Self::Panic,
140            ffi::MDBX_VERSION_MISMATCH => Self::VersionMismatch,
141            ffi::MDBX_INVALID => Self::Invalid,
142            ffi::MDBX_MAP_FULL => Self::MapFull,
143            ffi::MDBX_DBS_FULL => Self::DbsFull,
144            ffi::MDBX_READERS_FULL => Self::ReadersFull,
145            ffi::MDBX_TXN_FULL => Self::TxnFull,
146            ffi::MDBX_CURSOR_FULL => Self::CursorFull,
147            ffi::MDBX_PAGE_FULL => Self::PageFull,
148            ffi::MDBX_UNABLE_EXTEND_MAPSIZE => Self::UnableExtendMapSize,
149            ffi::MDBX_INCOMPATIBLE => Self::Incompatible,
150            ffi::MDBX_BAD_RSLOT => Self::BadRslot,
151            ffi::MDBX_BAD_TXN => Self::BadTxn,
152            ffi::MDBX_BAD_VALSIZE => Self::BadValSize,
153            ffi::MDBX_BAD_DBI => Self::BadDbi,
154            ffi::MDBX_PROBLEM => Self::Problem,
155            ffi::MDBX_BUSY => Self::Busy,
156            ffi::MDBX_EMULTIVAL => Self::Multival,
157            ffi::MDBX_WANNA_RECOVERY => Self::WannaRecovery,
158            ffi::MDBX_EKEYMISMATCH => Self::KeyMismatch,
159            ffi::MDBX_EINVAL => Self::DecodeError,
160            ffi::MDBX_EACCESS => Self::Access,
161            ffi::MDBX_TOO_LARGE => Self::TooLarge,
162            ffi::MDBX_EBADSIGN => Self::BadSignature,
163            ffi::MDBX_EPERM => Self::Permission,
164            other => Self::Other(other),
165        }
166    }
167
168    /// Converts an [Error] to the raw error code.
169    pub const fn to_err_code(&self) -> i32 {
170        match self {
171            Self::KeyExist => ffi::MDBX_KEYEXIST,
172            Self::NotFound => ffi::MDBX_NOTFOUND,
173            Self::NoData => ffi::MDBX_ENODATA,
174            Self::PageNotFound => ffi::MDBX_PAGE_NOTFOUND,
175            Self::Corrupted => ffi::MDBX_CORRUPTED,
176            Self::Panic => ffi::MDBX_PANIC,
177            Self::VersionMismatch => ffi::MDBX_VERSION_MISMATCH,
178            Self::Invalid => ffi::MDBX_INVALID,
179            Self::MapFull => ffi::MDBX_MAP_FULL,
180            Self::DbsFull => ffi::MDBX_DBS_FULL,
181            Self::ReadersFull => ffi::MDBX_READERS_FULL,
182            Self::TxnFull => ffi::MDBX_TXN_FULL,
183            Self::CursorFull => ffi::MDBX_CURSOR_FULL,
184            Self::PageFull => ffi::MDBX_PAGE_FULL,
185            Self::UnableExtendMapSize => ffi::MDBX_UNABLE_EXTEND_MAPSIZE,
186            Self::Incompatible => ffi::MDBX_INCOMPATIBLE,
187            Self::BadRslot => ffi::MDBX_BAD_RSLOT,
188            Self::BadTxn => ffi::MDBX_BAD_TXN,
189            Self::BadValSize => ffi::MDBX_BAD_VALSIZE,
190            Self::BadDbi => ffi::MDBX_BAD_DBI,
191            Self::Problem => ffi::MDBX_PROBLEM,
192            Self::Busy => ffi::MDBX_BUSY,
193            Self::Multival => ffi::MDBX_EMULTIVAL,
194            Self::WannaRecovery => ffi::MDBX_WANNA_RECOVERY,
195            Self::KeyMismatch => ffi::MDBX_EKEYMISMATCH,
196            Self::DecodeErrorLenDiff | Self::DecodeError => ffi::MDBX_EINVAL,
197            Self::TooLarge => ffi::MDBX_TOO_LARGE,
198            Self::BadSignature => ffi::MDBX_EBADSIGN,
199            Self::Access |
200            Self::WriteTransactionUnsupportedInReadOnlyMode |
201            Self::NestedTransactionsUnsupportedWithWriteMap => ffi::MDBX_EACCESS,
202            Self::ReadTransactionTimeout => -96000, // Custom non-MDBX error code
203            Self::Permission => ffi::MDBX_EPERM,
204            Self::Other(err_code) => *err_code,
205        }
206    }
207}
208
209impl From<Error> for i32 {
210    fn from(value: Error) -> Self {
211        value.to_err_code()
212    }
213}
214
215#[inline]
216pub(crate) const fn mdbx_result(err_code: c_int) -> Result<bool> {
217    match err_code {
218        ffi::MDBX_SUCCESS => Ok(false),
219        ffi::MDBX_RESULT_TRUE => Ok(true),
220        other => Err(Error::from_err_code(other)),
221    }
222}
223
224#[macro_export]
225macro_rules! mdbx_try_optional {
226    ($expr:expr) => {{
227        match $expr {
228            Err(Error::NotFound | Error::NoData) => return Ok(None),
229            Err(e) => return Err(e),
230            Ok(v) => v,
231        }
232    }};
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_description() {
241        assert_eq!(
242            "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more",
243            Error::from_err_code(13).to_string()
244        );
245
246        assert_eq!("file is not an MDBX file", Error::Invalid.to_string());
247    }
248
249    #[test]
250    fn test_conversion() {
251        assert_eq!(Error::from_err_code(ffi::MDBX_KEYEXIST), Error::KeyExist);
252    }
253}