systemprompt_database/lifecycle/migrations/
down.rs1use super::exec::execute_statements_transactional;
7use super::{MigrationResult, MigrationService};
8use crate::services::SqlExecutor;
9use systemprompt_extension::{Extension, LoaderError, Migration};
10use tracing::info;
11
12impl MigrationService<'_> {
13 pub async fn run_down_migrations(
14 &self,
15 extension: &dyn Extension,
16 count: u32,
17 ) -> Result<MigrationResult, LoaderError> {
18 if count == 0 {
19 return Ok(MigrationResult::default());
20 }
21
22 let ext_id = extension.metadata().id;
23 self.ensure_migrations_table_exists().await?;
24
25 let result = self
26 .db
27 .query_raw_with(
28 &"SELECT version FROM extension_migrations WHERE extension_id = $1 ORDER BY \
29 version DESC LIMIT $2",
30 &[&ext_id, &count],
31 )
32 .await
33 .map_err(|e| LoaderError::MigrationFailed {
34 extension: ext_id.to_owned(),
35 message: format!("Failed to query applied migrations for revert: {e}"),
36 })?;
37
38 let versions: Vec<u32> = result
39 .rows
40 .iter()
41 .filter_map(|row| row.get("version")?.as_i64().map(|v| v as u32))
42 .collect();
43
44 if versions.is_empty() {
45 return Ok(MigrationResult::default());
46 }
47
48 let migrations = extension.migrations();
49 let mut migrations_run = 0;
50
51 for version in versions {
52 self.revert_version(ext_id, version, &migrations).await?;
53 migrations_run += 1;
54 }
55
56 Ok(MigrationResult {
57 migrations_run,
58 migrations_skipped: 0,
59 })
60 }
61
62 async fn revert_version(
63 &self,
64 ext_id: &str,
65 version: u32,
66 migrations: &[Migration],
67 ) -> Result<(), LoaderError> {
68 let migration = migrations
69 .iter()
70 .find(|m| m.version == version)
71 .ok_or_else(|| LoaderError::MigrationFailed {
72 extension: ext_id.to_owned(),
73 message: format!(
74 "Cannot revert migration {version}: not declared in Extension::migrations()"
75 ),
76 })?;
77
78 let down_sql = migration
79 .down
80 .ok_or_else(|| LoaderError::MigrationNotReversible {
81 extension: ext_id.to_owned(),
82 version,
83 })?;
84
85 info!(
86 extension = %ext_id,
87 version = migration.version,
88 name = %migration.name,
89 "Reverting migration"
90 );
91
92 let statements = SqlExecutor::parse_sql_statements(down_sql).map_err(|e| {
93 LoaderError::MigrationFailed {
94 extension: ext_id.to_owned(),
95 message: format!(
96 "Failed to parse down migration {} ({}): {e}",
97 migration.version, migration.name
98 ),
99 }
100 })?;
101 execute_statements_transactional(self.db, &statements, ext_id, migration).await?;
102
103 self.delete_migration_record(ext_id, version).await
104 }
105
106 async fn delete_migration_record(&self, ext_id: &str, version: u32) -> Result<(), LoaderError> {
107 self.db
108 .execute(
109 &"DELETE FROM extension_migrations WHERE extension_id = $1 AND version = $2",
110 &[&ext_id, &version],
111 )
112 .await
113 .map_err(|e| LoaderError::MigrationFailed {
114 extension: ext_id.to_owned(),
115 message: format!("Failed to delete migration record {version}: {e}"),
116 })?;
117
118 Ok(())
119 }
120}