systemprompt_database/lifecycle/migrations/
compat.rs1use systemprompt_extension::LoaderError;
14use systemprompt_identifiers::ExtensionId;
15
16use super::MigrationService;
17
18const RECORD_BREAKING_SQL: &str = "INSERT INTO extension_migration_compat (extension_id, \
19 version, breaks_older) VALUES ($1, $2, TRUE) ON CONFLICT \
20 (extension_id, version) DO UPDATE SET breaks_older = TRUE";
21
22const LEDGER_EXISTS_SQL: &str =
23 "SELECT to_regclass('extension_migration_compat') IS NOT NULL AS present";
24
25const BREAKING_VERSIONS_SQL: &str = "SELECT version FROM extension_migration_compat WHERE \
26 extension_id = $1 AND breaks_older ORDER BY version";
27
28impl MigrationService<'_> {
29 pub(super) async fn record_breaking(
30 &self,
31 ext_id: &ExtensionId,
32 version: u32,
33 ) -> Result<(), LoaderError> {
34 let ledger = include_str!("../../../schema/extension_migration_compat.sql");
35 crate::services::SqlExecutor::execute_statements_parsed(self.db, ledger)
36 .await
37 .map_err(|e| LoaderError::MigrationStepFailed {
38 extension: ext_id.clone(),
39 context: "Failed to ensure the migration compatibility ledger exists".to_owned(),
40 source: Box::new(e),
41 })?;
42 self.db
43 .execute(&RECORD_BREAKING_SQL, &[&ext_id, &version])
44 .await
45 .map_err(|e| LoaderError::MigrationStepFailed {
46 extension: ext_id.clone(),
47 context: format!("Failed to record migration {version} as breaking"),
48 source: Box::new(e),
49 })?;
50 Ok(())
51 }
52
53 pub async fn breaking_versions(&self, ext_id: &ExtensionId) -> Result<Vec<u32>, LoaderError> {
57 let ledger = self
58 .db
59 .query_raw_with(&LEDGER_EXISTS_SQL, &[])
60 .await
61 .map_err(|e| LoaderError::MigrationStepFailed {
62 extension: ext_id.clone(),
63 context: "Failed to look up the migration compatibility ledger".to_owned(),
64 source: Box::new(e),
65 })?;
66 let present = ledger
67 .rows
68 .first()
69 .and_then(|row| row.get("present"))
70 .and_then(serde_json::Value::as_bool)
71 .unwrap_or(false);
72 if !present {
73 return Ok(Vec::new());
74 }
75 let result = self
76 .db
77 .query_raw_with(&BREAKING_VERSIONS_SQL, &[&ext_id])
78 .await
79 .map_err(|e| LoaderError::MigrationStepFailed {
80 extension: ext_id.clone(),
81 context: "Failed to read the migration compatibility ledger".to_owned(),
82 source: Box::new(e),
83 })?;
84 Ok(result
85 .rows
86 .iter()
87 .filter_map(|row| row.get("version").and_then(serde_json::Value::as_i64))
88 .filter_map(|v| u32::try_from(v).ok())
89 .collect())
90 }
91}