Skip to main content

systemprompt_database/lifecycle/migrations/
repair.rs

1//! Migration checksum-drift repair.
2//!
3//! When an already-applied migration file is edited in place, its stored
4//! checksum stops matching the file and the runner refuses to proceed.
5//! [`MigrationService::repair_drift`] reconciles the `extension_migrations`
6//! tracking table by dropping the drifted rows and re-applying those
7//! migrations — every migration is idempotent (guarded seeds or
8//! `CREATE ... IF NOT EXISTS`), so re-running them re-records the current
9//! checksum without touching real data.
10
11use super::{ChecksumDrift, MigrationService};
12use systemprompt_extension::{Extension, LoaderError};
13
14#[derive(Debug, Default, Clone)]
15pub struct RepairResult {
16    pub repaired: Vec<ChecksumDrift>,
17    pub migrations_run: usize,
18}
19
20impl MigrationService<'_> {
21    pub async fn repair_drift(
22        &self,
23        extension: &dyn Extension,
24    ) -> Result<RepairResult, LoaderError> {
25        let ext_id = extension.metadata().id;
26        let status = self.status(extension).await?;
27
28        if status.drift.is_empty() {
29            return Ok(RepairResult::default());
30        }
31
32        for drift in &status.drift {
33            self.db
34                .execute(
35                    &"DELETE FROM extension_migrations WHERE extension_id = $1 AND version = $2",
36                    &[&drift.extension_id, &drift.version],
37                )
38                .await
39                .map_err(|e| LoaderError::MigrationFailed {
40                    extension: ext_id.to_string(),
41                    message: format!(
42                        "Failed to drop drifted migration record {} ('{}'): {e}",
43                        drift.version, drift.name
44                    ),
45                })?;
46        }
47
48        let result = self.run_pending_migrations(extension).await?;
49
50        Ok(RepairResult {
51            repaired: status.drift,
52            migrations_run: result.migrations_run,
53        })
54    }
55}