use std::fmt::Debug;
use std::future::Future;
use std::time::Instant;
use cdk_common::database::Error;
use crate::database::DatabaseExecutor;
use crate::stmt::query;
const SLOW_QUERY_THRESHOLD_MS: u128 = 20;
#[inline(always)]
pub fn run_db_operation_sync<F, E, E1, T>(
info: &str,
operation: F,
error_map: E,
) -> Result<T, Error>
where
F: FnOnce() -> Result<T, E1>,
E1: Debug,
E: FnOnce(E1) -> Error,
{
let start = Instant::now();
tracing::trace!("Running db operation {}", info);
let result = operation().map_err(|e| {
tracing::error!("Query {} failed with error {:?}", info, e);
error_map(e)
});
let duration = start.elapsed();
if duration.as_millis() > SLOW_QUERY_THRESHOLD_MS {
tracing::warn!("[SLOW QUERY] Took {} ms: {}", duration.as_millis(), info);
}
result
}
#[inline(always)]
pub async fn run_db_operation<Fut, E, E1, T>(
info: &str,
operation: Fut,
error_map: E,
) -> Result<T, Error>
where
Fut: Future<Output = Result<T, E1>>,
E1: Debug,
E: FnOnce(E1) -> Error,
{
let start = Instant::now();
tracing::trace!("Running db operation {}", info);
let result = operation.await.map_err(|e| {
tracing::error!("Query {} failed with error {:?}", info, e);
error_map(e)
});
let duration = start.elapsed();
if duration.as_millis() > SLOW_QUERY_THRESHOLD_MS {
tracing::warn!("[SLOW QUERY] Took {} ms: {}", duration.as_millis(), info);
}
result
}
#[inline(always)]
pub async fn migrate<C>(
conn: &C,
db_prefix: &str,
migrations: &[(&str, &str, &str)],
) -> Result<(), Error>
where
C: DatabaseExecutor,
{
query(
r#"
CREATE TABLE IF NOT EXISTS migrations (
name TEXT PRIMARY KEY,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"#,
)?
.execute(conn)
.await?;
for (prefix, name, sql) in migrations {
if !prefix.is_empty() && *prefix != db_prefix {
continue;
}
let is_missing = query("SELECT name FROM migrations WHERE name = :name")?
.bind("name", name)
.pluck(conn)
.await?
.is_none();
if is_missing {
query(sql)?.batch(conn).await?;
query(r#"INSERT INTO migrations (name) VALUES (:name)"#)?
.bind("name", name)
.execute(conn)
.await?;
}
}
Ok(())
}