1use std::{convert::Infallible, ffi::c_int, result};
4
5pub type MdbxResult<T, E = MdbxError> = result::Result<T, E>;
7
8pub type ReadResult<T, E = ReadError> = Result<T, E>;
10
11#[derive(thiserror::Error, Debug)]
23pub enum ReadError {
24 #[error(transparent)]
26 Mdbx(#[from] MdbxError),
27 #[error(transparent)]
29 Decoding(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
30}
31
32impl ReadError {
33 pub fn decoding<E>(err: E) -> Self
35 where
36 E: std::error::Error + Send + Sync + 'static,
37 {
38 Self::Decoding(Box::new(err))
39 }
40}
41
42#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
47pub enum MdbxError {
48 #[error("key/data pair already exists")]
50 KeyExist,
51 #[error("no matching key/data pair found")]
53 NotFound,
54 #[error("the cursor is already at the end of data")]
56 NoData,
57 #[error("requested page not found")]
59 PageNotFound,
60 #[error("database is corrupted")]
62 Corrupted,
63 #[error("fatal environment error")]
65 Panic,
66 #[error("DB version mismatch")]
68 VersionMismatch,
69 #[error("file is not an MDBX file")]
71 Invalid,
72 #[error("environment map size limit reached")]
74 MapFull,
75 #[error("too many DBI-handles (maxdbs reached)")]
77 DbsFull,
78 #[error("too many readers (maxreaders reached)")]
80 ReadersFull,
81 #[error("transaction has too many dirty pages (i.e., the transaction is too big)")]
83 TxnFull,
84 #[error("cursor stack limit reached")]
86 CursorFull,
87 #[error("page has no more space")]
89 PageFull,
90 #[error("database engine was unable to extend mapping")]
103 UnableExtendMapSize,
104 #[error("environment or database is not compatible with the requested operation or flags")]
106 Incompatible,
107 #[error("invalid reuse of reader locktable slot")]
109 BadRslot,
110 #[error("transaction is not valid for requested operation")]
112 BadTxn,
113 #[error("invalid size or alignment of key or data for the target database")]
115 BadValSize,
116 #[error("the specified DBI-handle is invalid")]
118 BadDbi,
119 #[error("unexpected internal error")]
121 Problem,
122 #[error("another write transaction is running")]
124 Busy,
125 #[error("the specified key has more than one associated value")]
127 Multival,
128 #[error("wrong signature of a runtime object(s)")]
130 BadSignature,
131 #[error(
134 "database should be recovered, but cannot be done automatically since it's in read-only mode"
135 )]
136 WannaRecovery,
137 #[error("the given key value is mismatched to the current cursor position")]
139 KeyMismatch,
140 #[error("invalid parameter specified")]
142 DecodeError,
143 #[error(
145 "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more"
146 )]
147 Access,
148 #[error("database is too large for the current system")]
150 TooLarge,
151 #[error("invalid parameter specified or active write transaction")]
156 DecodeErrorLenDiff,
157 #[error("nested transactions are not supported with WriteMap")]
161 NestedTransactionsUnsupportedWithWriteMap,
162 #[error("write transactions are not supported in read-only mode")]
166 WriteTransactionUnsupportedInReadOnlyMode,
167 #[error("read transaction has been timed out")]
169 ReadTransactionTimeout,
170 #[error("botched transaction")]
175 BotchedTransaction,
176 #[error("permission denied to setup database")]
178 Permission,
179 #[error("unknown error code: {0}")]
181 Other(i32),
182 #[error("operation requires DUP_SORT flag on database")]
184 RequiresDupSort,
185 #[error("operation requires DUP_FIXED flag on database")]
187 RequiresDupFixed,
188}
189
190impl MdbxError {
191 pub const fn from_err_code(err_code: c_int) -> Self {
193 match err_code {
194 ffi::MDBX_KEYEXIST => Self::KeyExist,
195 ffi::MDBX_NOTFOUND => Self::NotFound,
196 ffi::MDBX_ENODATA => Self::NoData,
197 ffi::MDBX_PAGE_NOTFOUND => Self::PageNotFound,
198 ffi::MDBX_CORRUPTED => Self::Corrupted,
199 ffi::MDBX_PANIC => Self::Panic,
200 ffi::MDBX_VERSION_MISMATCH => Self::VersionMismatch,
201 ffi::MDBX_INVALID => Self::Invalid,
202 ffi::MDBX_MAP_FULL => Self::MapFull,
203 ffi::MDBX_DBS_FULL => Self::DbsFull,
204 ffi::MDBX_READERS_FULL => Self::ReadersFull,
205 ffi::MDBX_TXN_FULL => Self::TxnFull,
206 ffi::MDBX_CURSOR_FULL => Self::CursorFull,
207 ffi::MDBX_PAGE_FULL => Self::PageFull,
208 ffi::MDBX_UNABLE_EXTEND_MAPSIZE => Self::UnableExtendMapSize,
209 ffi::MDBX_INCOMPATIBLE => Self::Incompatible,
210 ffi::MDBX_BAD_RSLOT => Self::BadRslot,
211 ffi::MDBX_BAD_TXN => Self::BadTxn,
212 ffi::MDBX_BAD_VALSIZE => Self::BadValSize,
213 ffi::MDBX_BAD_DBI => Self::BadDbi,
214 ffi::MDBX_PROBLEM => Self::Problem,
215 ffi::MDBX_BUSY => Self::Busy,
216 ffi::MDBX_EMULTIVAL => Self::Multival,
217 ffi::MDBX_WANNA_RECOVERY => Self::WannaRecovery,
218 ffi::MDBX_EKEYMISMATCH => Self::KeyMismatch,
219 ffi::MDBX_EINVAL => Self::DecodeError,
220 ffi::MDBX_EACCESS => Self::Access,
221 ffi::MDBX_TOO_LARGE => Self::TooLarge,
222 ffi::MDBX_EBADSIGN => Self::BadSignature,
223 ffi::MDBX_EPERM => Self::Permission,
224 other => Self::Other(other),
225 }
226 }
227
228 pub const fn to_err_code(&self) -> i32 {
230 match self {
231 Self::KeyExist => ffi::MDBX_KEYEXIST,
232 Self::NotFound => ffi::MDBX_NOTFOUND,
233 Self::NoData => ffi::MDBX_ENODATA,
234 Self::PageNotFound => ffi::MDBX_PAGE_NOTFOUND,
235 Self::Corrupted => ffi::MDBX_CORRUPTED,
236 Self::Panic => ffi::MDBX_PANIC,
237 Self::VersionMismatch => ffi::MDBX_VERSION_MISMATCH,
238 Self::Invalid => ffi::MDBX_INVALID,
239 Self::MapFull => ffi::MDBX_MAP_FULL,
240 Self::DbsFull => ffi::MDBX_DBS_FULL,
241 Self::ReadersFull => ffi::MDBX_READERS_FULL,
242 Self::TxnFull => ffi::MDBX_TXN_FULL,
243 Self::CursorFull => ffi::MDBX_CURSOR_FULL,
244 Self::PageFull => ffi::MDBX_PAGE_FULL,
245 Self::UnableExtendMapSize => ffi::MDBX_UNABLE_EXTEND_MAPSIZE,
246 Self::Incompatible => ffi::MDBX_INCOMPATIBLE,
247 Self::BadRslot => ffi::MDBX_BAD_RSLOT,
248 Self::BadTxn => ffi::MDBX_BAD_TXN,
249 Self::BadValSize => ffi::MDBX_BAD_VALSIZE,
250 Self::BadDbi => ffi::MDBX_BAD_DBI,
251 Self::Problem => ffi::MDBX_PROBLEM,
252 Self::Busy => ffi::MDBX_BUSY,
253 Self::Multival => ffi::MDBX_EMULTIVAL,
254 Self::WannaRecovery => ffi::MDBX_WANNA_RECOVERY,
255 Self::KeyMismatch => ffi::MDBX_EKEYMISMATCH,
256 Self::DecodeErrorLenDiff | Self::DecodeError => ffi::MDBX_EINVAL,
257 Self::TooLarge => ffi::MDBX_TOO_LARGE,
258 Self::BadSignature => ffi::MDBX_EBADSIGN,
259 Self::Access
260 | Self::WriteTransactionUnsupportedInReadOnlyMode
261 | Self::NestedTransactionsUnsupportedWithWriteMap => ffi::MDBX_EACCESS,
262 Self::ReadTransactionTimeout => -96000, Self::BotchedTransaction => -96001,
264 Self::RequiresDupSort => -96002,
265 Self::RequiresDupFixed => -96003,
266 Self::Permission => ffi::MDBX_EPERM,
267 Self::Other(err_code) => *err_code,
268 }
269 }
270}
271
272impl From<MdbxError> for i32 {
273 fn from(value: MdbxError) -> Self {
274 value.to_err_code()
275 }
276}
277
278#[inline]
287pub(crate) const fn mdbx_result(err_code: c_int) -> MdbxResult<bool> {
288 match err_code {
289 ffi::MDBX_SUCCESS => Ok(false),
290 ffi::MDBX_RESULT_TRUE => Ok(true),
291 other => Err(MdbxError::from_err_code(other)),
292 }
293}
294
295impl From<MdbxError> for Infallible {
296 fn from(_value: MdbxError) -> Self {
297 unreachable!()
298 }
299}
300
301#[inline]
309#[allow(dead_code)]
310pub(crate) const fn mdbx_result_unit(err_code: c_int) -> MdbxResult<()> {
311 match err_code {
312 ffi::MDBX_SUCCESS => Ok(()),
313 ffi::MDBX_RESULT_TRUE => Ok(()),
314 other => Err(MdbxError::from_err_code(other)),
315 }
316}
317
318#[macro_export]
321macro_rules! mdbx_try_optional {
322 ($expr:expr) => {{
323 match $expr {
324 Err(MdbxError::NotFound | MdbxError::NoData) => return Ok(None),
325 Err(e) => return Err(e),
326 Ok(v) => v,
327 }
328 }};
329}
330
331#[macro_export]
333macro_rules! codec_try_optional {
334 ($expr:expr) => {{
335 match $expr {
336 Err($crate::error::ReadError::Mdbx(
337 $crate::MdbxError::NotFound | $crate::MdbxError::NoData,
338 )) => {
339 return Ok(None);
340 }
341 Err(e) => return Err(e),
342 Ok(v) => v,
343 }
344 }};
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_description() {
353 assert_eq!(
354 "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more",
355 MdbxError::from_err_code(13).to_string()
356 );
357
358 assert_eq!("file is not an MDBX file", MdbxError::Invalid.to_string());
359 }
360
361 #[test]
362 fn test_conversion() {
363 assert_eq!(MdbxError::from_err_code(ffi::MDBX_KEYEXIST), MdbxError::KeyExist);
364 }
365}