use libc::c_uint;
use std::cell::Cell;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use supercow::{NonSyncSupercow, Supercow};
use crate::cursor::{self, Cursor, StaleCursor};
use crate::dbi::{Database, db};
use crate::env::{self, Environment, Stat};
use crate::error::{Error, Result};
use crate::mdb_vals::{EMPTY_VAL, as_val, from_reserved, from_val};
use crate::traits::{AsLmdbBytes, FromLmdbBytes, FromReservedLmdbBytes, LmdbRaw};
pub mod put {
use liblmdb;
bitflags! {
#[derive(Clone)]
pub struct Flags: u32 {
const NODUPDATA = liblmdb::MDB_NODUPDATA;
const NOOVERWRITE = liblmdb::MDB_NOOVERWRITE;
const APPEND = liblmdb::MDB_APPEND;
const APPENDDUP = liblmdb::MDB_APPENDDUP;
}
}
}
pub mod del {
use libc;
use liblmdb;
bitflags! {
pub struct Flags : libc::c_uint {
const NODUPDATA = liblmdb::MDB_NODUPDATA;
}
}
}
#[derive(Debug)]
pub struct TxHandle(pub *mut liblmdb::MDB_txn);
impl Drop for TxHandle {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
liblmdb::mdb_txn_abort(self.0);
}
self.0 = ptr::null_mut();
}
}
}
impl TxHandle {
pub unsafe fn commit(&mut self) -> Result<()> {
unsafe {
let txn_p = mem::replace(&mut self.0, ptr::null_mut());
lmdb_call!(liblmdb::mdb_txn_commit(txn_p));
Ok(())
}
}
}
#[derive(Debug)]
pub struct ConstTransaction<'env> {
env: NonSyncSupercow<'env, Environment>,
tx: TxHandle,
has_yielded_accessor: Cell<bool>,
}
#[derive(Debug)]
pub struct ReadTransaction<'env>(ConstTransaction<'env>);
#[derive(Debug)]
pub struct WriteTransaction<'env>(ConstTransaction<'env>);
#[derive(Debug)]
pub struct ResetTransaction<'env>(ReadTransaction<'env>);
#[derive(Debug)]
pub struct ConstAccessor<'txn>(&'txn ConstTransaction<'txn>);
impl Drop for ConstAccessor<'_> {
fn drop(&mut self) {
self.0.has_yielded_accessor.set(false);
}
}
#[derive(Debug)]
pub struct WriteAccessor<'txn>(ConstAccessor<'txn>);
impl<'env> ConstTransaction<'env> {
fn new<'outer: 'env, E>(
env: E,
parent: Option<&'env mut ConstTransaction<'outer>>,
flags: c_uint,
) -> Result<Self>
where
E: Into<NonSyncSupercow<'env, Environment>>,
{
let env: NonSyncSupercow<'env, Environment> = env.into();
let mut rawtx: *mut liblmdb::MDB_txn = ptr::null_mut();
unsafe {
lmdb_call!(liblmdb::mdb_txn_begin(
env::env_ptr_no_sync(&env),
parent.map_or(ptr::null_mut(), |p| p.tx.0),
flags,
&raw mut rawtx
));
}
Ok(ConstTransaction {
env,
tx: TxHandle(rawtx),
has_yielded_accessor: Cell::new(false),
})
}
#[inline]
pub fn access(&self) -> ConstAccessor<'_> {
assert!(
!self.has_yielded_accessor.get(),
"Transaction accessor already returned"
);
self.has_yielded_accessor.set(true);
ConstAccessor(self)
}
#[inline]
pub fn cursor<'txn, 'db, DB>(&'txn self, db: DB) -> Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
Cursor::construct(Supercow::borrowed(self), db.into())
}
pub fn id(&self) -> usize {
unsafe { liblmdb::mdb_txn_id(self.tx.0) }
}
pub fn db_stat(&self, db: &Database) -> Result<Stat> {
db.assert_same_env(&self.env)?;
unsafe {
let mut raw: liblmdb::MDB_stat = mem::zeroed();
lmdb_call!(liblmdb::mdb_stat(self.tx.0, db.as_raw(), &raw mut raw));
Ok(raw.into())
}
}
pub fn db_flags(&self, db: &Database) -> Result<db::Flags> {
db.assert_same_env(&self.env)?;
let mut raw: c_uint = 0;
unsafe {
lmdb_call!(liblmdb::mdb_dbi_flags(self.tx.0, db.as_raw(), &raw mut raw));
}
Ok(db::Flags::from_bits_truncate(raw))
}
#[inline]
fn assert_sensible_cursor(&self, cursor: &Cursor) -> Result<()> {
if ptr::from_ref::<ConstTransaction>(self)
== ptr::from_ref::<ConstTransaction>(cursor::txn_ref(cursor))
{
Ok(())
} else {
Err(Error::Mismatch)
}
}
}
#[inline]
pub fn assert_sensible_cursor(access: &ConstAccessor, cursor: &Cursor) -> Result<()> {
access.0.assert_sensible_cursor(cursor)
}
#[inline]
pub fn assert_same_env(txn: &ConstTransaction, db: &Database) -> Result<()> {
db.assert_same_env(&txn.env)
}
#[inline]
pub fn assert_in_env(txn: &ConstTransaction, env: &Environment) -> Result<()> {
if ptr::from_ref::<Environment>(env) == &raw const *txn.env {
Ok(())
} else {
Err(Error::Mismatch)
}
}
#[inline]
pub fn txptr(txn: &ConstTransaction) -> *mut liblmdb::MDB_txn {
txn.tx.0
}
impl<'env> Deref for ReadTransaction<'env> {
type Target = ConstTransaction<'env>;
fn deref(&self) -> &ConstTransaction<'env> {
&self.0
}
}
impl<'env> DerefMut for ReadTransaction<'env> {
fn deref_mut(&mut self) -> &mut ConstTransaction<'env> {
&mut self.0
}
}
impl<'env> ReadTransaction<'env> {
pub fn new<E>(env: E) -> Result<Self>
where
E: Into<NonSyncSupercow<'env, Environment>>,
{
Ok(ReadTransaction(ConstTransaction::new(
env,
None,
liblmdb::MDB_RDONLY,
)?))
}
#[allow(clippy::missing_panics_doc)]
pub fn dissoc_cursor<'txn, 'db>(&self, cursor: Cursor<'txn, 'db>) -> Result<StaleCursor<'db>>
where
'env: 'db,
{
self.assert_sensible_cursor(&cursor)?;
let env = Supercow::clone_non_owned(&self.env)
.expect("Cannot use owned `Environment` with `dissoc_cursor`");
Ok(cursor::to_stale(cursor, env))
}
pub fn assoc_cursor<'txn, 'db>(
&'txn self,
cursor: StaleCursor<'db>,
) -> Result<Cursor<'txn, 'db>> {
let self_as_const: &'txn ConstTransaction = self;
Cursor::from_stale(cursor, NonSyncSupercow::borrowed(self_as_const))
}
pub fn reset(self) -> ResetTransaction<'env> {
unsafe {
liblmdb::mdb_txn_reset(self.0.tx.0);
}
ResetTransaction(self)
}
}
impl<'env> ResetTransaction<'env> {
pub fn renew(self) -> Result<ReadTransaction<'env>> {
unsafe {
lmdb_call!(liblmdb::mdb_txn_renew(self.0.0.tx.0));
}
Ok(self.0)
}
}
impl<'env> Deref for WriteTransaction<'env> {
type Target = ConstTransaction<'env>;
fn deref(&self) -> &ConstTransaction<'env> {
&self.0
}
}
impl<'env> DerefMut for WriteTransaction<'env> {
fn deref_mut(&mut self) -> &mut ConstTransaction<'env> {
&mut self.0
}
}
impl<'env> WriteTransaction<'env> {
pub fn new<E>(env: E) -> Result<Self>
where
E: Into<NonSyncSupercow<'env, Environment>>,
{
Ok(WriteTransaction(ConstTransaction::new(env, None, 0)?))
}
pub fn child_tx<'a>(&'a mut self) -> Result<WriteTransaction<'a>>
where
'env: 'a,
{
let env = Supercow::share(&mut self.0.env);
Ok(WriteTransaction(ConstTransaction::new(
env,
Some(&mut *self),
0,
)?))
}
pub fn commit(mut self) -> Result<()> {
unsafe { self.0.tx.commit() }
}
#[inline]
pub fn access(&self) -> WriteAccessor<'_> {
WriteAccessor(self.0.access())
}
}
impl ConstAccessor<'_> {
#[inline]
pub fn get<K: AsLmdbBytes + ?Sized, V: FromLmdbBytes + ?Sized>(
&self,
db: &Database,
key: &K,
) -> Result<&V> {
db.assert_same_env(self.env())?;
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(liblmdb::mdb_get(
self.txptr(),
db.as_raw(),
&raw mut mv_key,
&raw mut out_val
));
}
from_val(self, &out_val)
}
fn txptr(&self) -> *mut liblmdb::MDB_txn {
self.0.tx.0
}
fn env(&self) -> &Environment {
&self.0.env
}
}
impl<'txn> Deref for WriteAccessor<'txn> {
type Target = ConstAccessor<'txn>;
fn deref(&self) -> &ConstAccessor<'txn> {
&self.0
}
}
impl WriteAccessor<'_> {
#[inline]
pub fn put<K: AsLmdbBytes + ?Sized, V: AsLmdbBytes + ?Sized>(
&mut self,
db: &Database,
key: &K,
value: &V,
flags: &put::Flags,
) -> Result<()> {
db.assert_same_env(self.env())?;
let mut mv_key = as_val(key);
let mut mv_val = as_val(value);
unsafe {
lmdb_call!(liblmdb::mdb_put(
self.txptr(),
db.as_raw(),
&raw mut mv_key,
&raw mut mv_val,
flags.bits()
));
}
Ok(())
}
#[inline]
pub fn put_reserve<K: AsLmdbBytes + ?Sized, V: FromReservedLmdbBytes + Sized>(
&mut self,
db: &Database,
key: &K,
flags: &put::Flags,
) -> Result<&mut V> {
unsafe { self.put_reserve_unsized(db, key, size_of::<V>(), flags) }
}
#[inline]
pub fn put_reserve_array<K: AsLmdbBytes + ?Sized, V: LmdbRaw>(
&mut self,
db: &Database,
key: &K,
count: usize,
flags: &put::Flags,
) -> Result<&mut [V]> {
unsafe { self.put_reserve_unsized(db, key, size_of::<V>() * count, flags) }
}
#[inline]
pub unsafe fn put_reserve_unsized<
K: AsLmdbBytes + ?Sized,
V: FromReservedLmdbBytes + ?Sized,
>(
&mut self,
db: &Database,
key: &K,
size: usize,
flags: &put::Flags,
) -> Result<&mut V> {
unsafe {
db.assert_same_env(self.env())?;
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
out_val.mv_size = size;
lmdb_call!(liblmdb::mdb_put(
self.txptr(),
db.as_raw(),
&raw mut mv_key,
&raw mut out_val,
flags.bits() | liblmdb::MDB_RESERVE
));
Ok(from_reserved(self, &mut out_val))
}
}
#[inline]
pub fn del_key<K: AsLmdbBytes + ?Sized>(&mut self, db: &Database, key: &K) -> Result<()> {
db.assert_same_env(self.env())?;
let mut mv_key = as_val(key);
unsafe {
lmdb_call!(liblmdb::mdb_del(
self.txptr(),
db.as_raw(),
&raw mut mv_key,
ptr::null_mut()
));
}
Ok(())
}
#[inline]
pub fn del_item<K: AsLmdbBytes + ?Sized, V: AsLmdbBytes + ?Sized>(
&mut self,
db: &Database,
key: &K,
val: &V,
) -> Result<()> {
db.assert_same_env(self.env())?;
let mut mv_key = as_val(key);
let mut mv_val = as_val(val);
unsafe {
lmdb_call!(liblmdb::mdb_del(
self.txptr(),
db.as_raw(),
&raw mut mv_key,
&raw mut mv_val
));
}
Ok(())
}
pub fn clear_db(&mut self, db: &Database) -> Result<()> {
db.assert_same_env(self.env())?;
unsafe {
lmdb_call!(liblmdb::mdb_drop(self.txptr(), db.as_raw(), 0));
}
Ok(())
}
}