use std::marker::PhantomData;
use std::path::Path;
use std::sync::Arc;
use crate::api::ApiError;
use crate::bplustree::iterator::BPlusTreeIter;
use crate::bplustree::transaction::{TxnStatus, WriteTransaction};
use crate::bplustree::tree::SharedBPlusTree;
use crate::codec::kv::{KeyCodec, ValueCodec};
use crate::database::{self, Database};
use crate::keyfmt::KeyFormat;
use crate::keyfmt::raw::RawFormat;
use crate::storage::file_page_storage::FilePageStorage;
use crate::storage::paged_node_storage::PagedNodeStorage;
type InnerTree = SharedBPlusTree<PagedNodeStorage<FilePageStorage>, FilePageStorage>;
pub struct Db {
database: Arc<Database<FilePageStorage>>,
}
impl Db {
pub fn open<P: AsRef<Path>>(dir: P) -> Result<Self, ApiError> {
let db = database::open::<FilePageStorage, _>(dir)
.map_err(|e| ApiError::Internal(e.to_string()))?;
Ok(Self {
database: Arc::new(db),
})
}
pub fn create_tree<K, V>(&self, name: &str, order: u64) -> Result<Tree<K, V>, ApiError>
where
K: KeyCodec,
V: ValueCodec,
{
let key_format = KeyFormat::Raw(RawFormat);
let tree_meta = self
.database
.create_tree(name, K::ENCODING, key_format, order, None)
.map_err(|e| ApiError::Internal(e.to_string()))?;
let inner = self
.database
.bind_tree(&tree_meta)
.map_err(|e| ApiError::Internal(e.to_string()))?;
Ok(Tree {
inner,
_k: PhantomData,
_v: PhantomData,
})
}
pub fn open_tree<K, V>(&self, name: &str) -> Result<Tree<K, V>, ApiError>
where
K: KeyCodec,
V: ValueCodec,
{
let tree_meta = self
.database
.describe_tree(name)
.map_err(|e| ApiError::Internal(e.to_string()))?;
let inner = self
.database
.bind_tree(&tree_meta)
.map_err(|e| ApiError::Internal(e.to_string()))?;
Ok(Tree {
inner,
_k: PhantomData,
_v: PhantomData,
})
}
pub fn tree<K, V>(&self, name: &str, order: u64) -> Result<Tree<K, V>, ApiError>
where
K: KeyCodec,
V: ValueCodec,
{
match self.open_tree(name) {
Ok(t) => Ok(t),
Err(_) => self.create_tree(name, order),
}
}
pub fn rename_tree(&self, old_name: &str, new_name: &str) -> Result<(), ApiError> {
let meta = self
.database
.describe_tree(old_name)
.map_err(|e| ApiError::Internal(e.to_string()))?;
self.database
.rename_tree(&meta.id, new_name)
.map_err(|e| ApiError::Internal(e.to_string()))
}
pub fn drop_tree(&self, name: &str) -> Result<(), ApiError> {
let meta = self
.database
.describe_tree(name)
.map_err(|e| ApiError::Internal(e.to_string()))?;
self.database
.drop_tree(&meta.id)
.map_err(|e| ApiError::Internal(e.to_string()))
}
pub fn list_trees(&self) -> Vec<String> {
self.database.list_trees()
}
pub fn format_version(&self) -> u32 {
self.database.format_version()
}
pub fn close(self) -> Result<(), ApiError> {
self.database
.checkpoint_freelist()
.map_err(|e| ApiError::Internal(e.to_string()))
}
}
impl Drop for Db {
fn drop(&mut self) {
if let Err(e) = self.database.checkpoint_freelist() {
eprintln!("warning: failed to checkpoint freelist on drop: {e}");
}
}
}
pub struct Tree<K, V>
where
K: KeyCodec,
V: ValueCodec,
{
inner: InnerTree,
_k: PhantomData<fn() -> K>,
_v: PhantomData<fn() -> V>,
}
impl<K, V> Tree<K, V>
where
K: KeyCodec,
V: ValueCodec,
{
pub fn put(&self, key: &K, value: &V) -> Result<(), ApiError> {
let mut txn = WriteTransaction::new(self.inner.clone());
txn.insert(key.encode(), value.encode());
match txn.commit(&self.inner)? {
TxnStatus::Committed => Ok(()),
TxnStatus::Aborted => Err(ApiError::TxnAborted),
}
}
pub fn get(&self, key: &K) -> Result<Option<V>, ApiError> {
let kb = key.encode();
match self.inner.search(kb)? {
Some(bytes) => Ok(Some(V::decode(&bytes)?)),
None => Ok(None),
}
}
pub fn contains_key(&self, key: &K) -> Result<bool, ApiError> {
let kb = key.encode();
Ok(self.inner.contains_key(kb)?)
}
pub fn delete(&self, key: &K) -> Result<(), ApiError> {
let mut txn = WriteTransaction::new(self.inner.clone());
txn.delete(key.encode());
match txn.commit(&self.inner)? {
TxnStatus::Committed => Ok(()),
TxnStatus::Aborted => Err(ApiError::TxnAborted),
}
}
pub fn txn(&self) -> WriteTxn<'_, K, V> {
WriteTxn {
inner: WriteTransaction::new(self.inner.clone()),
tree: self,
}
}
pub fn range(&self, start: &K, end: &K) -> Result<RangeIter<'_, K, V>, ApiError> {
let start_bytes = start.encode();
let end_bytes = end.encode();
let inner = self.inner.search_range(&start_bytes, Some(&end_bytes))?;
Ok(RangeIter {
inner,
_k: PhantomData,
_v: PhantomData,
})
}
pub fn range_from(&self, start: &K) -> Result<RangeIter<'_, K, V>, ApiError> {
let start_bytes = start.encode();
let inner = self.inner.search_range(&start_bytes, None)?;
Ok(RangeIter {
inner,
_k: PhantomData,
_v: PhantomData,
})
}
pub fn len(&self) -> u64 {
self.inner.get_size()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn height(&self) -> u64 {
self.inner.get_height()
}
}
pub struct WriteTxn<'t, K, V>
where
K: KeyCodec,
V: ValueCodec,
{
inner: WriteTransaction,
tree: &'t Tree<K, V>,
}
impl<'t, K, V> WriteTxn<'t, K, V>
where
K: KeyCodec,
V: ValueCodec,
{
pub fn insert(&mut self, key: &K, value: &V) {
self.inner.insert(key.encode(), value.encode());
}
pub fn delete(&mut self, key: &K) {
self.inner.delete(key.encode());
}
pub fn commit(mut self) -> Result<(), ApiError> {
match self.inner.commit(&self.tree.inner)? {
TxnStatus::Committed => Ok(()),
TxnStatus::Aborted => Err(ApiError::TxnAborted),
}
}
}
type InnerNodeStorage = PagedNodeStorage<FilePageStorage>;
pub struct RangeIter<'t, K, V>
where
K: KeyCodec,
V: ValueCodec,
{
inner: BPlusTreeIter<'t, InnerNodeStorage>,
_k: PhantomData<fn() -> K>,
_v: PhantomData<fn() -> V>,
}
impl<'t, K, V> Iterator for RangeIter<'t, K, V>
where
K: KeyCodec,
V: ValueCodec,
{
type Item = Result<(K, V), ApiError>;
fn next(&mut self) -> Option<Self::Item> {
let (key_bytes, val_bytes) = match self.inner.next()? {
Ok(pair) => pair,
Err(e) => return Some(Err(e.into())),
};
let key = match K::decode(&key_bytes) {
Ok(k) => k,
Err(e) => return Some(Err(e)),
};
let value = match V::decode(&val_bytes) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
Some(Ok((key, value)))
}
}