use std::cell::Cell;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use libc::c_uint;
use ffi;
use ffi2;
use supercow::{Supercow, NonSyncSupercow};
use env::{self, Environment, Stat};
use dbi::{db, Database};
use error::{Error, Result};
use mdb_vals::*;
use traits::*;
use cursor::{self, Cursor, StaleCursor};
pub mod put {
use ffi;
use libc;
bitflags! {
pub struct Flags : libc::c_uint {
const NODUPDATA = ffi::MDB_NODUPDATA;
const NOOVERWRITE = ffi::MDB_NOOVERWRITE;
const APPEND = ffi::MDB_APPEND;
const APPENDDUP = ffi::MDB_APPENDDUP;
}
}
}
pub mod del {
use ffi;
use libc;
bitflags! {
pub struct Flags : libc::c_uint {
const NODUPDATA = ffi::MDB_NODUPDATA;
}
}
}
#[derive(Debug)]
pub struct TxHandle(pub *mut ffi::MDB_txn);
impl Drop for TxHandle {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
ffi::mdb_txn_abort(self.0);
}
self.0 = ptr::null_mut();
}
}
}
impl TxHandle {
pub unsafe fn commit(&mut self) -> Result<()> {
let txn_p = mem::replace(&mut self.0, ptr::null_mut());
lmdb_call!(ffi::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<'txn> Drop for ConstAccessor<'txn> {
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 ffi::MDB_txn = ptr::null_mut();
unsafe {
lmdb_call!(ffi::mdb_txn_begin(
env::env_ptr(&env), parent.map_or(ptr::null_mut(), |p| p.tx.0),
flags, &mut rawtx));
}
Ok(ConstTransaction {
env: 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 {
ffi2::mdb_txn_id(self.tx.0)
}
}
pub fn db_stat(&self, db: &Database) -> Result<Stat> {
try!(db.assert_same_env(&self.env));
unsafe {
let mut raw: ffi::MDB_stat = mem::zeroed();
lmdb_call!(ffi::mdb_stat(self.tx.0, db.as_raw(), &mut raw));
Ok(raw.into())
}
}
pub fn db_flags(&self, db: &Database) -> Result<db::Flags> {
try!(db.assert_same_env(&self.env));
let mut raw: c_uint = 0;
unsafe {
lmdb_call!(ffi::mdb_dbi_flags(self.tx.0, db.as_raw(), &mut raw));
}
Ok(db::Flags::from_bits_truncate(raw))
}
#[inline]
fn assert_sensible_cursor(&self, cursor: &Cursor)
-> Result<()> {
if self as *const ConstTransaction !=
cursor::txn_ref(cursor) as *const ConstTransaction
{
Err(Error::Mismatch)
} else {
Ok(())
}
}
}
#[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 env as *const Environment != &*txn.env as *const Environment {
Err(Error::Mismatch)
} else {
Ok(())
}
}
#[inline]
pub fn txptr(txn: &ConstTransaction) -> *mut ffi::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(try!(ConstTransaction::new(
env, None, ffi::MDB_RDONLY))))
}
pub fn dissoc_cursor<'txn,'db>(&self, cursor: Cursor<'txn,'db>)
-> Result<StaleCursor<'db>>
where 'env: 'db {
try!(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 { ffi::mdb_txn_reset(self.0.tx.0); }
ResetTransaction(self)
}
}
impl<'env> ResetTransaction<'env> {
pub fn renew(self) -> Result<ReadTransaction<'env>> {
unsafe { lmdb_call!(ffi::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(try!(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(try!(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<'txn> ConstAccessor<'txn> {
#[inline]
pub fn get<K : AsLmdbBytes + ?Sized, V : FromLmdbBytes + ?Sized>(
&self, db: &Database, key: &K) -> Result<&V>
{
try!(db.assert_same_env(self.env()));
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_get(
self.txptr(), db.as_raw(), &mut mv_key, &mut out_val));
}
from_val(self, &out_val)
}
fn txptr(&self) -> *mut ffi::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<'txn> WriteAccessor<'txn> {
#[inline]
pub fn put<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>(
&mut self, db: &Database, key: &K, value: &V,
flags: put::Flags) -> Result<()>
{
try!(db.assert_same_env(self.env()));
let mut mv_key = as_val(key);
let mut mv_val = as_val(value);
unsafe {
lmdb_call!(ffi::mdb_put(
self.txptr(), db.as_raw(), &mut mv_key, &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, mem::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, mem::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>
{
try!(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!(ffi::mdb_put(
self.txptr(), db.as_raw(), &mut mv_key, &mut out_val,
flags.bits() | ffi::MDB_RESERVE));
Ok(from_reserved(self, &out_val))
}
#[inline]
pub fn del_key<K : AsLmdbBytes + ?Sized>(
&mut self, db: &Database, key: &K) -> Result<()>
{
try!(db.assert_same_env(self.env()));
let mut mv_key = as_val(key);
unsafe {
lmdb_call!(ffi::mdb_del(
self.txptr(), db.as_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<()>
{
try!(db.assert_same_env(self.env()));
let mut mv_key = as_val(key);
let mut mv_val = as_val(val);
unsafe {
lmdb_call!(ffi::mdb_del(
self.txptr(), db.as_raw(), &mut mv_key, &mut mv_val));
}
Ok(())
}
pub fn clear_db(&mut self, db: &Database) -> Result<()> {
try!(db.assert_same_env(self.env()));
unsafe {
lmdb_call!(ffi::mdb_drop(self.txptr(), db.as_raw(), 0));
}
Ok(())
}
}