Current section
Files
Jump to
Current section
Files
native/lean_lmdb_nif/src/crud.rs
use crate::atoms;
use crate::lifecycle::{DatabaseResource, EnvironmentState};
use heed::MdbError;
use rustler::{Binary, Decoder, ResourceArc, Term};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum CrudError {
InvalidDatabase,
InvalidKey,
InvalidValue,
ReadOnly,
MapFull,
MapResized,
ReadersFull,
TransactionFull,
OutOfMemory,
Io,
Database,
InvalidBatch,
BatchTooLarge,
MixedEnvironments,
InvalidExpected,
InvalidReplacement,
}
impl CrudError {
pub(crate) fn atom(self) -> rustler::Atom {
match self {
Self::InvalidDatabase => atoms::invalid_database(),
Self::InvalidKey => atoms::invalid_key(),
Self::InvalidValue => atoms::invalid_value(),
Self::ReadOnly => atoms::read_only(),
Self::MapFull => atoms::map_full(),
Self::MapResized => atoms::map_resized(),
Self::ReadersFull => atoms::readers_full(),
Self::TransactionFull => atoms::transaction_full(),
Self::OutOfMemory => atoms::out_of_memory(),
Self::Io => atoms::io_error(),
Self::Database => atoms::database_error(),
Self::InvalidBatch => atoms::invalid_batch(),
Self::BatchTooLarge => atoms::batch_too_large(),
Self::MixedEnvironments => atoms::mixed_environments(),
Self::InvalidExpected => atoms::invalid_expected(),
Self::InvalidReplacement => atoms::invalid_replacement(),
}
}
}
pub(crate) fn map_heed_error(error: heed::Error) -> CrudError {
match error {
heed::Error::Io(error) if error.kind() == std::io::ErrorKind::OutOfMemory => {
CrudError::OutOfMemory
}
heed::Error::Io(_) => CrudError::Io,
heed::Error::Mdb(MdbError::MapFull) => CrudError::MapFull,
heed::Error::Mdb(MdbError::MapResized) => CrudError::MapResized,
heed::Error::Mdb(MdbError::ReadersFull) => CrudError::ReadersFull,
heed::Error::Mdb(MdbError::TxnFull) => CrudError::TransactionFull,
heed::Error::Mdb(MdbError::BadDbi | MdbError::Incompatible) => CrudError::InvalidDatabase,
heed::Error::Mdb(MdbError::BadValSize) => CrudError::InvalidKey,
heed::Error::Mdb(_) | heed::Error::Encoding(_) | heed::Error::Decoding(_) => {
CrudError::Database
}
heed::Error::EnvAlreadyOpened => CrudError::Database,
}
}
pub(crate) fn decode_crud_database(
term: Term<'_>,
) -> Result<ResourceArc<DatabaseResource>, CrudError> {
term.decode().map_err(|_| CrudError::InvalidDatabase)
}
pub(crate) fn decode_binary<'a>(term: Term<'a>, error: CrudError) -> Result<Binary<'a>, CrudError> {
Binary::decode(term).map_err(|_| error)
}
pub(crate) fn validate_key<'a>(
state: &EnvironmentState,
key: &'a [u8],
) -> Result<&'a [u8], CrudError> {
if key.is_empty() || key.len() > state.env.max_key_size() {
Err(CrudError::InvalidKey)
} else {
Ok(key)
}
}
pub(crate) fn put_binary(
database: &DatabaseResource,
key: &[u8],
value: &[u8],
) -> Result<(), CrudError> {
validate_key(&database.state, key)?;
if database.state.options.read_only {
return Err(CrudError::ReadOnly);
}
let mut transaction = database.state.env.write_txn().map_err(map_heed_error)?;
database
.database
.put(&mut transaction, key, value)
.map_err(map_heed_error)?;
transaction.commit().map_err(map_heed_error)
}
pub(crate) fn clear_stale_readers(state: &EnvironmentState) -> Result<usize, CrudError> {
state.env.clear_stale_readers().map_err(map_heed_error)
}
pub(crate) fn delete_binary(database: &DatabaseResource, key: &[u8]) -> Result<(), CrudError> {
validate_key(&database.state, key)?;
if database.state.options.read_only {
return Err(CrudError::ReadOnly);
}
let mut transaction = database.state.env.write_txn().map_err(map_heed_error)?;
database
.database
.delete(&mut transaction, key)
.map_err(map_heed_error)?;
transaction.commit().map_err(map_heed_error)
}
pub(crate) fn copy_bytes(bytes: &[u8]) -> Result<Vec<u8>, CrudError> {
let mut copied = Vec::new();
copied
.try_reserve_exact(bytes.len())
.map_err(|_| CrudError::OutOfMemory)?;
copied.extend_from_slice(bytes);
Ok(copied)
}
#[cfg(test)]
pub(crate) fn get_binary(
database: &DatabaseResource,
key: &[u8],
) -> Result<Option<Vec<u8>>, CrudError> {
validate_key(&database.state, key)?;
let transaction = database.state.env.read_txn().map_err(map_heed_error)?;
let value = database
.database
.get(&transaction, key)
.map_err(map_heed_error)?
.map(<[u8]>::to_vec);
transaction.commit().map_err(map_heed_error)?;
Ok(value)
}