1use std::{ffi::c_int, result};
2
3pub type Result<T> = result::Result<T, Error>;
5
6#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
8pub enum Error {
9 #[error("key/data pair already exists")]
11 KeyExist,
12 #[error("no matching key/data pair found")]
14 NotFound,
15 #[error("the cursor is already at the end of data")]
17 NoData,
18 #[error("requested page not found")]
20 PageNotFound,
21 #[error("database is corrupted")]
23 Corrupted,
24 #[error("fatal environment error")]
26 Panic,
27 #[error("DB version mismatch")]
29 VersionMismatch,
30 #[error("file is not an MDBX file")]
32 Invalid,
33 #[error("environment map size limit reached")]
35 MapFull,
36 #[error("too many DBI-handles (maxdbs reached)")]
38 DbsFull,
39 #[error("too many readers (maxreaders reached)")]
41 ReadersFull,
42 #[error("transaction has too many dirty pages (i.e., the transaction is too big)")]
44 TxnFull,
45 #[error("cursor stack limit reached")]
47 CursorFull,
48 #[error("page has no more space")]
50 PageFull,
51 #[error("database engine was unable to extend mapping")]
61 UnableExtendMapSize,
62 #[error("environment or database is not compatible with the requested operation or flags")]
64 Incompatible,
65 #[error("invalid reuse of reader locktable slot")]
67 BadRslot,
68 #[error("transaction is not valid for requested operation")]
70 BadTxn,
71 #[error("invalid size or alignment of key or data for the target database")]
73 BadValSize,
74 #[error("the specified DBI-handle is invalid")]
76 BadDbi,
77 #[error("unexpected internal error")]
79 Problem,
80 #[error("another write transaction is running")]
82 Busy,
83 #[error("the specified key has more than one associated value")]
85 Multival,
86 #[error("wrong signature of a runtime object(s)")]
88 BadSignature,
89 #[error(
92 "database should be recovered, but cannot be done automatically since it's in read-only mode"
93 )]
94 WannaRecovery,
95 #[error("the given key value is mismatched to the current cursor position")]
97 KeyMismatch,
98 #[error("invalid parameter specified")]
100 DecodeError,
101 #[error(
103 "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more"
104 )]
105 Access,
106 #[error("database is too large for the current system")]
108 TooLarge,
109 #[error("invalid parameter specified or active write transaction")]
113 DecodeErrorLenDiff,
114 #[error("nested transactions are not supported with WriteMap")]
118 NestedTransactionsUnsupportedWithWriteMap,
119 #[error("write transactions are not supported in read-only mode")]
122 WriteTransactionUnsupportedInReadOnlyMode,
123 #[error("read transaction has been timed out")]
125 ReadTransactionTimeout,
126 #[error("botched transaction")]
131 BotchedTransaction,
132 #[error("permission denied to setup database")]
134 Permission,
135 #[error("{}", Error::fmt_other(*.0))]
137 Other(i32),
138}
139
140impl Error {
141 pub const fn from_err_code(err_code: c_int) -> Self {
143 match err_code {
144 ffi::MDBX_KEYEXIST => Self::KeyExist,
145 ffi::MDBX_NOTFOUND => Self::NotFound,
146 ffi::MDBX_ENODATA => Self::NoData,
147 ffi::MDBX_PAGE_NOTFOUND => Self::PageNotFound,
148 ffi::MDBX_CORRUPTED => Self::Corrupted,
149 ffi::MDBX_PANIC => Self::Panic,
150 ffi::MDBX_VERSION_MISMATCH => Self::VersionMismatch,
151 ffi::MDBX_INVALID => Self::Invalid,
152 ffi::MDBX_MAP_FULL => Self::MapFull,
153 ffi::MDBX_DBS_FULL => Self::DbsFull,
154 ffi::MDBX_READERS_FULL => Self::ReadersFull,
155 ffi::MDBX_TXN_FULL => Self::TxnFull,
156 ffi::MDBX_CURSOR_FULL => Self::CursorFull,
157 ffi::MDBX_PAGE_FULL => Self::PageFull,
158 ffi::MDBX_UNABLE_EXTEND_MAPSIZE => Self::UnableExtendMapSize,
159 ffi::MDBX_INCOMPATIBLE => Self::Incompatible,
160 ffi::MDBX_BAD_RSLOT => Self::BadRslot,
161 ffi::MDBX_BAD_TXN => Self::BadTxn,
162 ffi::MDBX_BAD_VALSIZE => Self::BadValSize,
163 ffi::MDBX_BAD_DBI => Self::BadDbi,
164 ffi::MDBX_PROBLEM => Self::Problem,
165 ffi::MDBX_BUSY => Self::Busy,
166 ffi::MDBX_EMULTIVAL => Self::Multival,
167 ffi::MDBX_WANNA_RECOVERY => Self::WannaRecovery,
168 ffi::MDBX_EKEYMISMATCH => Self::KeyMismatch,
169 ffi::MDBX_EINVAL => Self::DecodeError,
170 ffi::MDBX_EACCESS => Self::Access,
171 ffi::MDBX_TOO_LARGE => Self::TooLarge,
172 ffi::MDBX_EBADSIGN => Self::BadSignature,
173 ffi::MDBX_EPERM => Self::Permission,
174 other => Self::Other(other),
175 }
176 }
177
178 pub const fn to_err_code(&self) -> i32 {
180 match self {
181 Self::KeyExist => ffi::MDBX_KEYEXIST,
182 Self::NotFound => ffi::MDBX_NOTFOUND,
183 Self::NoData => ffi::MDBX_ENODATA,
184 Self::PageNotFound => ffi::MDBX_PAGE_NOTFOUND,
185 Self::Corrupted => ffi::MDBX_CORRUPTED,
186 Self::Panic => ffi::MDBX_PANIC,
187 Self::VersionMismatch => ffi::MDBX_VERSION_MISMATCH,
188 Self::Invalid => ffi::MDBX_INVALID,
189 Self::MapFull => ffi::MDBX_MAP_FULL,
190 Self::DbsFull => ffi::MDBX_DBS_FULL,
191 Self::ReadersFull => ffi::MDBX_READERS_FULL,
192 Self::TxnFull => ffi::MDBX_TXN_FULL,
193 Self::CursorFull => ffi::MDBX_CURSOR_FULL,
194 Self::PageFull => ffi::MDBX_PAGE_FULL,
195 Self::UnableExtendMapSize => ffi::MDBX_UNABLE_EXTEND_MAPSIZE,
196 Self::Incompatible => ffi::MDBX_INCOMPATIBLE,
197 Self::BadRslot => ffi::MDBX_BAD_RSLOT,
198 Self::BadTxn => ffi::MDBX_BAD_TXN,
199 Self::BadValSize => ffi::MDBX_BAD_VALSIZE,
200 Self::BadDbi => ffi::MDBX_BAD_DBI,
201 Self::Problem => ffi::MDBX_PROBLEM,
202 Self::Busy => ffi::MDBX_BUSY,
203 Self::Multival => ffi::MDBX_EMULTIVAL,
204 Self::WannaRecovery => ffi::MDBX_WANNA_RECOVERY,
205 Self::KeyMismatch => ffi::MDBX_EKEYMISMATCH,
206 Self::DecodeErrorLenDiff | Self::DecodeError => ffi::MDBX_EINVAL,
207 Self::TooLarge => ffi::MDBX_TOO_LARGE,
208 Self::BadSignature => ffi::MDBX_EBADSIGN,
209 Self::Access |
210 Self::WriteTransactionUnsupportedInReadOnlyMode |
211 Self::NestedTransactionsUnsupportedWithWriteMap => ffi::MDBX_EACCESS,
212 Self::ReadTransactionTimeout => -96000, Self::BotchedTransaction => -96001,
214 Self::Permission => ffi::MDBX_EPERM,
215 Self::Other(err_code) => *err_code,
216 }
217 }
218
219 fn fmt_other(code: i32) -> String {
220 let mut s = String::with_capacity(1024);
221 let desc = unsafe { ffi::mdbx_strerror_r(code, s.as_mut_ptr().cast(), 1024) };
222 let desc = unsafe { std::ffi::CStr::from_ptr(desc) }.to_string_lossy();
223 desc.into_owned()
224 }
225}
226
227impl From<Error> for i32 {
228 fn from(value: Error) -> Self {
229 value.to_err_code()
230 }
231}
232
233#[inline]
242pub(crate) const fn mdbx_result(err_code: c_int) -> Result<bool> {
243 match err_code {
244 ffi::MDBX_SUCCESS => Ok(false),
245 ffi::MDBX_RESULT_TRUE => Ok(true),
246 other => Err(Error::from_err_code(other)),
247 }
248}
249
250#[macro_export]
251macro_rules! mdbx_try_optional {
252 ($expr:expr) => {{
253 match $expr {
254 Err(Error::NotFound | Error::NoData) => return Ok(None),
255 Err(e) => return Err(e),
256 Ok(v) => v,
257 }
258 }};
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_description() {
267 assert_eq!(
268 "the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more",
269 Error::from_err_code(13).to_string()
270 );
271
272 assert_eq!("file is not an MDBX file", Error::Invalid.to_string());
273 }
274
275 #[test]
276 fn test_conversion() {
277 assert_eq!(Error::from_err_code(ffi::MDBX_KEYEXIST), Error::KeyExist);
278 }
279}