use std::any::Any;
use std::future::Future;
use std::pin::Pin;
use async_trait::async_trait;
use crate::types::{PageRequest, SqlRow, SqlStatement, SqlValue, StorageResult};
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub type AtomicUnitOp = Box<
dyn for<'w> FnOnce(&'w mut dyn SqlWriter) -> BoxFuture<'w, StorageResult<Box<dyn Any + Send>>>
+ Send,
>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TopLevelMaintenance {
Vacuum,
WalCheckpointTruncate,
}
impl TopLevelMaintenance {
pub const fn as_sql(self) -> &'static str {
match self {
Self::Vacuum => "VACUUM;",
Self::WalCheckpointTruncate => "PRAGMA wal_checkpoint(TRUNCATE);",
}
}
}
#[async_trait]
pub trait SqlReader: Send + 'static {
async fn query_row(&mut self, statement: SqlStatement) -> StorageResult<Option<SqlRow>>;
async fn query_all(&mut self, statement: SqlStatement) -> StorageResult<Vec<SqlRow>>;
async fn query_page(
&mut self,
statement: SqlStatement,
page: PageRequest,
) -> StorageResult<Vec<SqlRow>> {
let rows = self.query_all(statement).await?;
let offset = usize::try_from(page.offset).unwrap_or(usize::MAX);
let limit = usize::try_from(page.limit).unwrap_or(usize::MAX);
Ok(rows.into_iter().skip(offset).take(limit).collect())
}
async fn query_scalar(&mut self, statement: SqlStatement) -> StorageResult<Option<SqlValue>>;
async fn explain(&mut self, statement: SqlStatement) -> StorageResult<Vec<SqlRow>>;
}
#[async_trait]
pub trait SqlWriter: SqlReader + Send + 'static {
async fn execute(&mut self, statement: SqlStatement) -> StorageResult<u64>;
async fn execute_batch(&mut self, statements: Vec<SqlStatement>) -> StorageResult<u64>;
async fn execute_script(&mut self, script: String) -> StorageResult<()>;
async fn execute_script_top_level(
&mut self,
maintenance: TopLevelMaintenance,
) -> StorageResult<()> {
self.execute_script(maintenance.as_sql().to_owned()).await
}
}
#[async_trait]
pub trait SqlAccess: Send + Sync + 'static {
fn database_path(&self) -> Option<std::path::PathBuf> {
None
}
async fn reader(&self) -> StorageResult<Box<dyn SqlReader>>;
async fn writer(&self) -> StorageResult<Box<dyn SqlWriter>>;
async fn atomic_unit(&self, op: AtomicUnitOp) -> StorageResult<Box<dyn Any + Send>>;
}