use sqlx::SqlitePool;
use super::{terminal_state_labels, PurgeReport, RetentionPolicy};
pub async fn purge(
pool: &SqlitePool,
table: &'static str,
journal: Option<&'static str>,
policy: &RetentionPolicy,
) -> Result<PurgeReport, sqlx::Error> {
let labels = terminal_state_labels();
let cutoff = format!("-{} seconds", policy.terminal_max_age.as_secs());
let batch = i64::from(policy.effective_batch_size());
let delete_sql = format!(
"DELETE FROM {table} WHERE rowid IN ( \
SELECT rowid FROM {table} \
WHERE state IN (?1, ?2, ?3, ?4) \
AND updated_at < strftime('%Y-%m-%d %H:%M:%f', 'now', ?5) \
LIMIT ?6 \
)"
);
let mut report = PurgeReport::default();
loop {
if policy.max_batches.is_some_and(|max| report.batches >= max) {
report.complete = false;
break;
}
let mut query = sqlx::query(&delete_sql);
for label in &labels {
query = query.bind(label);
}
let deleted = query
.bind(&cutoff)
.bind(batch)
.execute(pool)
.await?
.rows_affected();
if deleted == 0 {
report.complete = true;
break;
}
report.tasks_deleted += deleted;
report.batches += 1;
}
if let Some(journal) = journal {
if report.tasks_deleted > 0 {
let sql =
format!("DELETE FROM {journal} WHERE task_id NOT IN (SELECT id FROM {table})");
report.journal_orphans_deleted = sqlx::query(&sql).execute(pool).await?.rows_affected();
}
}
Ok(report)
}