mod capacity;
mod kv;
pub mod schema;
#[cfg(test)]
mod tests;
pub use capacity::{
Capacity, DEFAULT_PAGE_SIZE, RESERVE_TREE_DEPTH, batch_growth_bytes, reserve_floor,
};
pub use kv::{GET_MANY_CHUNK, SqlKvStore, TIMER_WINDOW_AFTER, TIMER_WINDOW_START, TimerCursor};
use crate::error::Redacted;
use crate::rt::{MaybeSend, MaybeSync};
use crate::store::StoreError;
pub const MAX_BOUND_PARAMS: usize = 100;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SqlValue {
Null,
Integer(i64),
Text(String),
Blob(Vec<u8>),
}
pub type Row = Vec<SqlValue>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SqlError {
#[error("sql backend error")]
Backend(Redacted),
#[error("sql constraint violated")]
Constraint,
#[error("sql database full")]
Full,
#[error("corrupt sql row: {0}")]
Corrupt(&'static str),
}
impl From<SqlError> for StoreError {
fn from(e: SqlError) -> Self {
match e {
SqlError::Full => Self::Full,
SqlError::Corrupt(what) => Self::Corrupt(what.into()),
e @ (SqlError::Backend(_) | SqlError::Constraint) => Self::unavailable(e),
}
}
}
pub type TxFn<C, T> = Box<dyn FnOnce(C) -> Result<T, SqlError> + 'static>;
pub trait SqlConn: MaybeSend + MaybeSync + Clone + 'static {
fn exec(&self, sql: &str, params: &[SqlValue]) -> Result<u64, SqlError>;
fn query(&self, sql: &str, params: &[SqlValue]) -> Result<Vec<Row>, SqlError>;
fn transaction<T: 'static>(&self, f: TxFn<Self, T>) -> Result<T, SqlError>;
fn now_ms(&self) -> u64;
fn size_bytes(&self) -> Result<u64, SqlError>;
fn set_size_limit(&self, bytes: u64) -> Result<(), SqlError> {
let _ = bytes;
Ok(())
}
fn backup_to(&self, dest: &str) -> Result<(), StoreError> {
let _ = dest;
Err(StoreError::Unsupported(
"this SQL backend has no physical backup; use the portable export".into(),
))
}
}
pub(crate) fn blob(row: &mut Row, i: usize) -> Result<Vec<u8>, SqlError> {
match row.get_mut(i) {
Some(SqlValue::Blob(b)) => Ok(core::mem::take(b)),
_ => Err(SqlError::Corrupt("expected a blob column")),
}
}
pub(crate) fn count(row: &Row, i: usize) -> Result<u64, SqlError> {
match row.get(i) {
Some(SqlValue::Null) => Ok(0),
Some(SqlValue::Integer(n)) => {
u64::try_from(*n).map_err(|_| SqlError::Corrupt("negative count"))
}
_ => Err(SqlError::Corrupt("expected an integer column")),
}
}