libmdbx_remote/
error.rs

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