#![allow(clippy::mutable_key_type)]
use crate::types::BoringResult;
use crate::{low_level::LowLevel, DbError};
use std::sync::Arc;
use bytes::Bytes;
use rusqlite::{params, OptionalExtension};
struct DictInner {
low_level: Arc<LowLevel>,
read_statement: String,
write_statement: String,
delete_statement: String,
table_name: String,
}
impl DictInner {
fn new(low_level: Arc<LowLevel>, table_name: &str) -> Self {
let write_statement = format!("insert into {}(key, value) values ($1, $2) on conflict(key) do update set value = excluded.value", table_name);
let delete_statement = format!("delete from {} where key = $1", table_name);
Self {
low_level,
read_statement: format!("select value from {} where key = $1", table_name),
table_name: table_name.to_string(),
write_statement,
delete_statement,
}
}
fn write(&self, key: Bytes, value: Bytes) -> BoringResult<Option<Bytes>> {
self.low_level
.transaction(|txn| {
let existing: Option<Vec<u8>> = txn
.prepare_cached(&self.read_statement)?
.query_row(params![key.as_ref()], |r| r.get(0))
.optional()?;
txn.prepare_cached(&self.write_statement)?
.execute(params![key.as_ref(), value.as_ref()])?;
txn.commit()?;
if let Some(existing) = existing {
Ok(Some(existing.into()))
} else {
Ok(None)
}
})
.map_err(DbError::SqliteFailed)
}
fn remove(&self, key: &[u8]) -> BoringResult<Option<Bytes>> {
self.low_level
.transaction(|txn| {
let existing: Option<Vec<u8>> = txn
.prepare_cached(&self.read_statement)?
.query_row(params![key], |r| r.get(0))
.optional()?;
txn.prepare_cached(&self.delete_statement)?
.execute(params![key])?;
txn.commit()?;
if let Some(existing) = existing {
Ok(Some(existing.into()))
} else {
Ok(None)
}
})
.map_err(DbError::SqliteFailed)
}
fn read(&self, key: &[u8]) -> BoringResult<Option<Bytes>> {
let read_statement = self.read_statement.clone();
let result: Option<Vec<u8>> = self.low_level.transaction(move |transaction| {
let mut statement: rusqlite::CachedStatement =
transaction.prepare_cached(&read_statement)?;
statement.query_row(&[key], |r| r.get(0)).optional()
})?;
Ok(result.map(|v| v.into()))
}
}
#[derive(Clone)]
pub struct Dict {
inner: Arc<DictInner>,
}
impl Dict {
pub fn get(&self, key: &[u8]) -> BoringResult<Option<Bytes>> {
self.inner.read(key)
}
pub fn insert(
&self,
key: impl Into<Bytes>,
val: impl Into<Bytes>,
) -> BoringResult<Option<Bytes>> {
self.inner.write(key.into(), val.into())
}
pub fn remove(&self, key: &[u8]) -> BoringResult<Option<Bytes>> {
self.inner.remove(key)
}
pub(crate) fn new(low_level: Arc<LowLevel>, table_name: &str) -> Self {
Self {
inner: Arc::new(DictInner::new(low_level, table_name)),
}
}
pub fn flush(&self) -> BoringResult<()> {
Ok(())
}
}