Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benches/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn bench_get_seq_iter(c: &mut Criterion) {
count += 1;
}

fn iterate<K: TransactionKind>(cursor: &mut Cursor<K>) -> Result<()> {
fn iterate<K: TransactionKind>(cursor: &mut Cursor<K>) -> ReadResult<()> {
let mut i = 0;
for result in cursor.iter::<ObjectLength, ObjectLength>() {
let (key_len, data_len) = result?;
Expand Down
36 changes: 20 additions & 16 deletions src/codec.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
use crate::{Error, Transaction, TransactionKind};
use crate::error::ReadResult;
use crate::{MdbxError, Transaction, TransactionKind};
use std::{borrow::Cow, slice};

/// A trait for types that can be deserialized from a database value without
/// borrowing.
pub trait TableObjectOwned: for<'de> TableObject<'de> {}

impl<T> TableObjectOwned for T where T: for<'de> TableObject<'de> {}

/// Implement this to be able to decode data values
pub trait TableObject<'a>: Sized {
/// Decodes the object from the given bytes.
fn decode(data_val: &[u8]) -> Result<Self, Error>;
fn decode(data_val: &[u8]) -> ReadResult<Self>;

/// Decodes the value directly from the given MDBX_val pointer.
#[doc(hidden)]
fn decode_val<K: TransactionKind>(
_: &'a Transaction<K>,
data_val: ffi::MDBX_val,
) -> Result<Self, Error> {
) -> ReadResult<Self> {
// SAFETY: the data val is borrowed from the inner mdbx transaction,
// so it is valid for the lifetime of the transaction.
let s = unsafe { slice::from_raw_parts(data_val.iov_base as *const u8, data_val.iov_len) };
Expand All @@ -20,15 +27,15 @@ pub trait TableObject<'a>: Sized {
}

impl<'a> TableObject<'a> for Cow<'a, [u8]> {
fn decode(_: &[u8]) -> Result<Self, Error> {
fn decode(_: &[u8]) -> ReadResult<Self> {
unreachable!()
}

#[doc(hidden)]
fn decode_val<K: TransactionKind>(
_txn: &'a Transaction<K>,
data_val: ffi::MDBX_val,
) -> Result<Self, Error> {
) -> ReadResult<Self> {
let s = unsafe { slice::from_raw_parts(data_val.iov_base as *const u8, data_val.iov_len) };

#[cfg(feature = "return-borrowed")]
Expand All @@ -48,21 +55,18 @@ impl<'a> TableObject<'a> for Cow<'a, [u8]> {
}
}

impl<'a> TableObject<'a> for Vec<u8> {
fn decode(data_val: &[u8]) -> Result<Self, Error> {
impl TableObject<'_> for Vec<u8> {
fn decode(data_val: &[u8]) -> ReadResult<Self> {
Ok(data_val.to_vec())
}
}

impl<'a> TableObject<'a> for () {
fn decode(_: &[u8]) -> Result<Self, Error> {
fn decode(_: &[u8]) -> ReadResult<Self> {
Ok(())
}

fn decode_val<K: TransactionKind>(
_: &'a Transaction<K>,
_: ffi::MDBX_val,
) -> Result<Self, Error> {
fn decode_val<K: TransactionKind>(_: &'a Transaction<K>, _: ffi::MDBX_val) -> ReadResult<Self> {
Ok(())
}
}
Expand All @@ -71,16 +75,16 @@ impl<'a> TableObject<'a> for () {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ObjectLength(pub usize);

impl<'a> TableObject<'a> for ObjectLength {
fn decode(data_val: &[u8]) -> Result<Self, Error> {
impl TableObject<'_> for ObjectLength {
fn decode(data_val: &[u8]) -> ReadResult<Self> {
Ok(Self(data_val.len()))
}
}

impl<'a, const LEN: usize> TableObject<'a> for [u8; LEN] {
fn decode(data_val: &[u8]) -> Result<Self, Error> {
fn decode(data_val: &[u8]) -> ReadResult<Self> {
if data_val.len() != LEN {
return Err(Error::DecodeErrorLenDiff);
return Err(MdbxError::DecodeErrorLenDiff.into());
}
let mut a = [0; LEN];
a[..].copy_from_slice(data_val);
Expand Down
99 changes: 87 additions & 12 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
use std::{ffi::c_int, result};
use std::{convert::Infallible, ffi::c_int, result};

/// An MDBX result.
pub type Result<T> = result::Result<T, Error>;
pub type MdbxResult<T, E = MdbxError> = result::Result<T, E>;

/// Result type for codec operations.
pub type ReadResult<T, E = ReadError> = Result<T, E>;

/// Error type for reading from the database.
///
/// This encapsulates errors that can occur during DB operations via
/// `Self::Mdbx` as well as post-read during the [`TableObject`] decoding
/// step via `Self::Decoding`.
///
/// For simplicity, the decoding error is boxed. [`Self::decoding`] can be used
/// to create such an error from any error type fits the bounds. E.g.
/// `result.map_err(ReadError::decoding)`.
///
/// [`TableObject`]: crate::codec::TableObject
#[derive(thiserror::Error, Debug)]
pub enum ReadError {
/// Mdbx error during decoding.
#[error(transparent)]
Mdbx(#[from] MdbxError),
/// Type-associated error while decoding.
#[error(transparent)]
Decoding(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}

impl ReadError {
/// Creates a new decoding error from a boxed error.
pub fn decoding<E>(err: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Decoding(Box::new(err))
}
}

/// An MDBX error kind.
///
/// This represents various error conditions that can occur when interacting
/// with the MDBX database.
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
pub enum Error {
pub enum MdbxError {
/// Key/data pair already exists.
#[error("key/data pair already exists")]
KeyExist,
Expand Down Expand Up @@ -137,7 +174,7 @@ pub enum Error {
Other(i32),
}

impl Error {
impl MdbxError {
/// Converts a raw error code to an [Error].
pub const fn from_err_code(err_code: c_int) -> Self {
match err_code {
Expand Down Expand Up @@ -217,8 +254,8 @@ impl Error {
}
}

impl From<Error> for i32 {
fn from(value: Error) -> Self {
impl From<MdbxError> for i32 {
fn from(value: MdbxError) -> Self {
value.to_err_code()
}
}
Expand All @@ -232,19 +269,57 @@ impl From<Error> for i32 {
/// The most unintuitive case is `mdbx_txn_commit` which returns `Ok(true)`
/// when the commit has been aborted.
#[inline]
pub(crate) const fn mdbx_result(err_code: c_int) -> Result<bool> {
pub(crate) const fn mdbx_result(err_code: c_int) -> MdbxResult<bool> {
match err_code {
ffi::MDBX_SUCCESS => Ok(false),
ffi::MDBX_RESULT_TRUE => Ok(true),
other => Err(Error::from_err_code(other)),
other => Err(MdbxError::from_err_code(other)),
}
}

impl From<MdbxError> for Infallible {
fn from(_value: MdbxError) -> Self {
unreachable!()
}
}

/// Parses an MDBX error code into a result type.
///
/// This function returns `Ok(())` on both `MDBX_SUCCESS` and
/// `MDBX_RESULT_TRUE`, effectively treating them both as non-error outcomes.
/// This is useful in scenarios where the distinction between these two
/// success codes is not relevant to the caller, e.g. on a `get` operation
/// where either outcome indicates a successful operation.
#[inline]
#[allow(dead_code)]
pub(crate) const fn mdbx_result_unit(err_code: c_int) -> MdbxResult<()> {
match err_code {
ffi::MDBX_SUCCESS => Ok(()),
ffi::MDBX_RESULT_TRUE => Ok(()),
other => Err(MdbxError::from_err_code(other)),
}
}

#[macro_export]
macro_rules! mdbx_try_optional {
($expr:expr) => {{
match $expr {
Err(Error::NotFound | Error::NoData) => return Ok(None),
Err(MdbxError::NotFound | MdbxError::NoData) => return Ok(None),
Err(e) => return Err(e),
Ok(v) => v,
}
}};
}

#[macro_export]
macro_rules! codec_try_optional {
($expr:expr) => {{
match $expr {
Err($crate::error::ReadError::Mdbx(
$crate::MdbxError::NotFound | $crate::MdbxError::NoData,
)) => {
return Ok(None);
}
Err(e) => return Err(e),
Ok(v) => v,
}
Expand All @@ -259,14 +334,14 @@ mod tests {
fn test_description() {
assert_eq!(
"the environment opened in read-only, check <https://reth.rs/run/troubleshooting.html> for more",
Error::from_err_code(13).to_string()
MdbxError::from_err_code(13).to_string()
);

assert_eq!("file is not an MDBX file", Error::Invalid.to_string());
assert_eq!("file is not an MDBX file", MdbxError::Invalid.to_string());
}

#[test]
fn test_conversion() {
assert_eq!(Error::from_err_code(ffi::MDBX_KEYEXIST), Error::KeyExist);
assert_eq!(MdbxError::from_err_code(ffi::MDBX_KEYEXIST), MdbxError::KeyExist);
}
}
6 changes: 2 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use codec::*;
pub use crate::sys::read_transactions::MaxReadTransactionDuration;

mod error;
pub use error::{Error, Result};
pub use error::{MdbxError, MdbxResult, ReadError, ReadResult};

mod flags;
pub use flags::*;
Expand All @@ -30,9 +30,7 @@ pub use sys::{
};

mod tx;
pub use tx::{
CommitLatency, Cursor, Database, Iter, IterDup, RO, RW, Transaction, TransactionKind,
};
pub use tx::{CommitLatency, Cursor, Database, RO, RW, Transaction, TransactionKind, iter};

#[cfg(test)]
mod test {
Expand Down
Loading