Bulk-delete memories and their embeddings in one transaction.
(
conn: &mut Connection,
memory_ids: &[i64],
)
| 3588 | |
| 3589 | /// Bulk-delete memories and their embeddings in one transaction. |
| 3590 | pub fn bulk_delete_memory( |
| 3591 | conn: &mut Connection, |
| 3592 | memory_ids: &[i64], |
| 3593 | ) -> Result<usize, rusqlite::Error> { |
| 3594 | if memory_ids.is_empty() { |
| 3595 | return Ok(0); |
| 3596 | } |
| 3597 | |
| 3598 | // Phase A: one read round-trip for all target rows, then identity normalization lock-free. |
| 3599 | let phase_a_targets = fetch_memory_mutation_targets(conn, memory_ids)?; |
| 3600 | let phase_a_paths: HashMap<i64, String> = phase_a_targets |
| 3601 | .iter() |
| 3602 | .map(|(id, target)| (*id, target.project_path.clone())) |
| 3603 | .collect(); |
| 3604 | let placeholders = memory_ids.iter().map(|_| "?").collect::<Vec<_>>().join(","); |
| 3605 | |
| 3606 | // Phase B: bulk re-verify, queue one delete per memory, then delete. |
| 3607 | let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate)?; |
| 3608 | verify_bulk_memory_project_paths_unchanged(&tx, &phase_a_paths)?; |
| 3609 | for id in memory_ids { |
| 3610 | if let Some(target) = phase_a_targets.get(id) { |
| 3611 | queue_memory_mutation( |
| 3612 | &tx, |
| 3613 | &target.project_path, |
| 3614 | "delete", |
| 3615 | *id, |
| 3616 | None, |
| 3617 | target.category.as_deref(), |
| 3618 | None, |
| 3619 | )?; |
| 3620 | } |
| 3621 | } |
| 3622 | { |
| 3623 | let sql = format!("DELETE FROM memory_embeddings WHERE memory_id IN ({placeholders})"); |
| 3624 | tx.execute(&sql, params_from_iter(memory_ids.iter()))?; |
| 3625 | } |
| 3626 | let affected = { |
| 3627 | let sql = format!("DELETE FROM memories WHERE id IN ({placeholders})"); |
| 3628 | tx.execute(&sql, params_from_iter(memory_ids.iter()))? |
| 3629 | }; |
| 3630 | tx.commit()?; |
| 3631 | Ok(affected) |
| 3632 | } |
| 3633 | |
| 3634 | // ── Session queries ───────────────────────────────────────── |
| 3635 |