use crate::catalog::{Catalog, db_err};
use orbok_core::{CleanupAction, CleanupPlan, OrbokError, OrbokResult, now_iso8601};
use rusqlite::params;
#[derive(Debug, Clone, Default)]
pub struct CleanupOutcome {
pub deleted_rows: u64,
}
pub struct CleanupExecutor<'a> {
catalog: &'a Catalog,
}
impl<'a> CleanupExecutor<'a> {
pub fn new(catalog: &'a Catalog) -> Self {
Self { catalog }
}
pub fn run_safe(&self, plan: &CleanupPlan) -> OrbokResult<CleanupOutcome> {
plan.assert_safe_for_ordinary_cleanup()?;
match plan.action {
CleanupAction::ClearExpiredSearchCache => self.clear_expired_search_cache(),
CleanupAction::ClearSnippetCache => self.clear_snippet_cache(),
CleanupAction::ClearTemporaryExtraction => Ok(CleanupOutcome::default()),
CleanupAction::RemoveReplacedStaleIndexes => self.remove_replaced_stale_indexes(),
_ => Err(OrbokError::CleanupWouldTouchPersistentData),
}
}
pub fn run_reset_catalog(
&self,
plan: &CleanupPlan,
keep_settings: bool,
) -> OrbokResult<CleanupOutcome> {
if plan.action != CleanupAction::ResetCatalog {
return Err(OrbokError::Database(
"reset requires a ResetCatalog plan".into(),
));
}
let mut conn = self.catalog.lock();
let tx = conn.transaction().map_err(db_err)?;
let mut deleted = 0u64;
for table in [
"sources",
"index_jobs",
"search_queries",
"snippet_cache",
"app_events",
"storage_accounting",
"cache_engines",
"models",
] {
deleted += tx
.execute(&format!("DELETE FROM {table}"), [])
.map_err(db_err)? as u64;
}
if !keep_settings {
deleted += tx.execute("DELETE FROM app_settings", []).map_err(db_err)? as u64;
}
tx.execute("INSERT INTO chunk_fts(chunk_fts) VALUES('delete-all')", [])
.map_err(db_err)?;
tx.commit().map_err(db_err)?;
Ok(CleanupOutcome {
deleted_rows: deleted,
})
}
fn clear_expired_search_cache(&self) -> OrbokResult<CleanupOutcome> {
let now = now_iso8601();
let conn = self.catalog.lock();
let mut deleted = conn
.execute(
"DELETE FROM search_result_cache WHERE expires_at IS NOT NULL AND expires_at < ?1",
params![now],
)
.map_err(db_err)? as u64;
deleted += conn
.execute(
"DELETE FROM search_queries WHERE expires_at IS NOT NULL AND expires_at < ?1",
params![now],
)
.map_err(db_err)? as u64;
Ok(CleanupOutcome {
deleted_rows: deleted,
})
}
fn clear_snippet_cache(&self) -> OrbokResult<CleanupOutcome> {
let conn = self.catalog.lock();
let deleted = conn
.execute("DELETE FROM snippet_cache", [])
.map_err(db_err)? as u64;
Ok(CleanupOutcome {
deleted_rows: deleted,
})
}
fn remove_replaced_stale_indexes(&self) -> OrbokResult<CleanupOutcome> {
let conn = self.catalog.lock();
let deleted = conn
.execute(
"DELETE FROM chunks WHERE chunk_status IN ('stale','deleted') AND file_id IN \
(SELECT file_id FROM chunks WHERE chunk_status = 'active')",
[],
)
.map_err(db_err)? as u64;
Ok(CleanupOutcome {
deleted_rows: deleted,
})
}
}