use lru::LruCache;
use std::{
hash::{DefaultHasher, Hasher},
num::NonZeroUsize,
};
use crate::{
Result,
common::SqliteStr,
query::{Execute, StatementRef},
sqlite::{Database, DatabaseExt, OpenFlag, SqliteHandle, StatementHandle, error::OpenError},
};
#[derive(Debug)]
pub struct Connection {
stmts: LruCache<u64, StatementHandle>,
handle: SqliteHandle,
}
unsafe impl Send for Connection {}
unsafe impl Sync for Connection {}
impl Connection {
pub fn open<P: SqliteStr>(path: P) -> Result<Self> {
Self::open_with(path, <_>::default())
}
pub fn open_in_memory() -> Result<Self> {
Self::open_with(c":memory:", <_>::default())
}
pub fn open_with<P: SqliteStr>(path: P, flags: OpenFlag) -> Result<Self> {
if !crate::sqlite::is_threadsafe() {
Err(OpenError::NotSerializeMode)?;
}
let path = path.to_nul_string().map_err(OpenError::from)?;
let mut handle = SqliteHandle::open_v2(&path, flags)?;
handle.extended_result_codes(true)?;
handle.busy_timeout(std::time::Duration::from_secs(5))?;
Ok(Self {
handle,
stmts: LruCache::new(NonZeroUsize::new(24).unwrap()),
})
}
}
impl<'s> Execute<'s> for &'s mut Connection {
fn prepare<S: SqliteStr>(self, sql: S) -> Result<StatementRef<'s>> {
let mut hash = DefaultHasher::new();
sql.hash(&mut hash);
let key = hash.finish();
let stmt = self.stmts.try_get_or_insert(key, || {
StatementHandle::prepare_v2(self.handle.as_ptr(), sql)
})?;
Ok(StatementRef::Borrow(stmt))
}
}
impl Database for Connection {
fn as_ptr(&self) -> *mut libsqlite3_sys::sqlite3 {
self.handle.as_ptr()
}
}