use crate::config::IsolationLevel;
use crate::db::ColumnFamily;
use crate::error::{check_result, Error, Result};
use crate::ffi;
use crate::iterator::Iterator;
use std::ffi::CString;
use std::ptr;
pub struct Transaction {
txn: *mut ffi::tidesdb_txn_t,
committed: bool,
}
unsafe impl Send for Transaction {}
impl Transaction {
pub(crate) fn new(txn: *mut ffi::tidesdb_txn_t) -> Self {
Transaction {
txn,
committed: false,
}
}
pub fn put(&self, cf: &ColumnFamily, key: &[u8], value: &[u8], ttl: i64) -> Result<()> {
let key_ptr = if key.is_empty() {
ptr::null()
} else {
key.as_ptr()
};
let value_ptr = if value.is_empty() {
ptr::null()
} else {
value.as_ptr()
};
let result = unsafe {
ffi::tidesdb_txn_put(
self.txn,
cf.cf,
key_ptr,
key.len(),
value_ptr,
value.len(),
ttl as libc::time_t,
)
};
check_result(result, "failed to put key-value pair")
}
pub fn get(&self, cf: &ColumnFamily, key: &[u8]) -> Result<Vec<u8>> {
let key_ptr = if key.is_empty() {
ptr::null()
} else {
key.as_ptr()
};
let mut value_ptr: *mut u8 = ptr::null_mut();
let mut value_len: usize = 0;
let result = unsafe {
ffi::tidesdb_txn_get(
self.txn,
cf.cf,
key_ptr,
key.len(),
&mut value_ptr,
&mut value_len,
)
};
check_result(result, "failed to get value")?;
if value_ptr.is_null() {
return Err(Error::NullPointer("value"));
}
let value = unsafe {
let slice = std::slice::from_raw_parts(value_ptr, value_len);
let vec = slice.to_vec();
ffi::tidesdb_free(value_ptr as *mut std::ffi::c_void);
vec
};
Ok(value)
}
pub fn delete(&self, cf: &ColumnFamily, key: &[u8]) -> Result<()> {
let key_ptr = if key.is_empty() {
ptr::null()
} else {
key.as_ptr()
};
let result = unsafe { ffi::tidesdb_txn_delete(self.txn, cf.cf, key_ptr, key.len()) };
check_result(result, "failed to delete key")
}
#[cfg(any(feature = "v9_1_0", feature = "v9_2_0"))]
pub fn single_delete(&self, cf: &ColumnFamily, key: &[u8]) -> Result<()> {
let key_ptr = if key.is_empty() {
ptr::null()
} else {
key.as_ptr()
};
let result =
unsafe { ffi::tidesdb_txn_single_delete(self.txn, cf.cf, key_ptr, key.len()) };
check_result(result, "failed to single-delete key")
}
pub fn commit(&mut self) -> Result<()> {
let result = unsafe { ffi::tidesdb_txn_commit(self.txn) };
self.committed = true;
check_result(result, "failed to commit transaction")
}
pub fn rollback(&mut self) -> Result<()> {
let result = unsafe { ffi::tidesdb_txn_rollback(self.txn) };
self.committed = true; check_result(result, "failed to rollback transaction")
}
pub fn reset(&mut self, isolation: IsolationLevel) -> Result<()> {
let result = unsafe { ffi::tidesdb_txn_reset(self.txn, isolation as i32) };
check_result(result, "failed to reset transaction")?;
self.committed = false;
Ok(())
}
pub fn savepoint(&self, name: &str) -> Result<()> {
let c_name = CString::new(name)?;
let result = unsafe { ffi::tidesdb_txn_savepoint(self.txn, c_name.as_ptr()) };
check_result(result, "failed to create savepoint")
}
pub fn rollback_to_savepoint(&self, name: &str) -> Result<()> {
let c_name = CString::new(name)?;
let result =
unsafe { ffi::tidesdb_txn_rollback_to_savepoint(self.txn, c_name.as_ptr()) };
check_result(result, "failed to rollback to savepoint")
}
pub fn release_savepoint(&self, name: &str) -> Result<()> {
let c_name = CString::new(name)?;
let result = unsafe { ffi::tidesdb_txn_release_savepoint(self.txn, c_name.as_ptr()) };
check_result(result, "failed to release savepoint")
}
pub fn new_iterator(&self, cf: &ColumnFamily) -> Result<Iterator> {
let mut iter: *mut ffi::tidesdb_iter_t = ptr::null_mut();
let result = unsafe { ffi::tidesdb_iter_new(self.txn, cf.cf, &mut iter) };
check_result(result, "failed to create iterator")?;
if iter.is_null() {
return Err(Error::NullPointer("iterator handle"));
}
Ok(Iterator::new(iter))
}
}
impl Drop for Transaction {
fn drop(&mut self) {
if !self.txn.is_null() {
unsafe {
ffi::tidesdb_txn_free(self.txn);
}
self.txn = ptr::null_mut();
}
}
}