use std::collections::HashSet;
use rusqlite::{Connection, params_from_iter};
const PRUNE_BATCH_SIZE: usize = 500;
pub(super) fn prune_stale_evidence_revisions(
conn: &Connection,
completed_ids: &HashSet<i64>,
) -> anyhow::Result<usize> {
let mut completed_ids = completed_ids.iter().copied().collect::<Vec<_>>();
completed_ids.sort_unstable();
let mut deleted = 0;
for batch in completed_ids.chunks(PRUNE_BATCH_SIZE) {
deleted += prune(conn, Some(batch))?;
}
Ok(deleted)
}
pub(super) fn prune_all_stale_evidence_revisions(conn: &Connection) -> anyhow::Result<usize> {
prune(conn, None)
}
fn prune(conn: &Connection, completed_ids: Option<&[i64]>) -> anyhow::Result<usize> {
let scope = completed_ids.map_or_else(String::new, |ids| {
let placeholders = std::iter::repeat_n("?", ids.len())
.collect::<Vec<_>>()
.join(",");
format!(
" AND EXISTS (
SELECT 1
FROM evidence AS touched
WHERE touched.id IN ({placeholders})
AND touched.provider = evidence.provider
AND touched.session_id = evidence.session_id
AND touched.entry_id = evidence.entry_id
)"
)
});
let sql = format!(
"DELETE FROM evidence
WHERE extraction_completed_at IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM claim_evidence
WHERE claim_evidence.evidence_id = evidence.id
)
AND EXISTS (
SELECT 1
FROM evidence AS newer
WHERE newer.provider = evidence.provider
AND newer.session_id = evidence.session_id
AND newer.entry_id = evidence.entry_id
AND newer.extraction_completed_at IS NOT NULL
AND (newer.created_at > evidence.created_at
OR (newer.created_at = evidence.created_at
AND newer.id > evidence.id))
){scope}"
);
let deleted = match completed_ids {
Some(ids) => conn.execute(&sql, params_from_iter(ids.iter()))?,
None => conn.execute(&sql, [])?,
};
Ok(deleted)
}