persistant_sqlx/
migrate.rs1use crate::{PgBackend, classify};
16use persistant_relational::{MigrateError, MigrateReport, Migrates, Migration, RelError};
17use sqlx::SqlSafeStr;
18use sqlx::migrate::{Migration as SqlxMigration, MigrationType, Migrator};
19
20pub(crate) fn migrator(migrations: &[Migration]) -> Migrator {
22 let sqlx_migrations: Vec<SqlxMigration> = migrations
23 .iter()
24 .map(|m| {
25 SqlxMigration::new(
26 m.version,
27 m.description.clone().into(),
28 MigrationType::Simple,
29 sqlx::AssertSqlSafe(m.sql.clone()).into_sql_str(),
30 false,
31 )
32 })
33 .collect();
34 Migrator::with_migrations(sqlx_migrations)
35}
36
37pub(crate) fn map_error(
39 e: sqlx::migrate::MigrateError,
40 classify: fn(sqlx::Error) -> RelError,
41) -> MigrateError {
42 match e {
43 sqlx::migrate::MigrateError::VersionMismatch(version) => {
44 MigrateError::ChecksumMismatch { version }
45 }
46 sqlx::migrate::MigrateError::VersionMissing(version) => {
47 MigrateError::NewerThanProgram { version }
48 }
49 sqlx::migrate::MigrateError::Dirty(version) => MigrateError::Dirty { version },
50 sqlx::migrate::MigrateError::ExecuteMigration(e, version) => MigrateError::Failed {
51 version,
52 error: classify(e),
53 },
54 other => MigrateError::Backend(other.to_string()),
55 }
56}
57
58pub(crate) fn backend_error(e: sqlx::Error) -> MigrateError {
59 MigrateError::Backend(e.to_string())
60}
61
62macro_rules! run_on_own_connection {
68 ($pool:expr, $migrations:expr, $classify:expr) => {{
69 let mut conn = $pool
70 .acquire()
71 .await
72 .map_err(crate::migrate::backend_error)?;
73 match crate::migrate::migrator($migrations)
75 .run_direct(None, &mut *conn, false)
76 .await
77 {
78 Ok(()) => Ok(()),
79 Err(e) => {
80 let _ = sqlx::Connection::close(conn.detach()).await;
81 Err(crate::migrate::map_error(e, $classify))
82 }
83 }
84 }};
85}
86#[cfg(any(feature = "mysql", feature = "sqlite"))]
87pub(crate) use run_on_own_connection;
88
89impl Migrates for PgBackend {
90 async fn migrate(&self, migrations: &[Migration]) -> Result<MigrateReport, MigrateError> {
91 let before = Migrates::applied_versions(self).await?;
92 run_on_own_connection!(self.pool, migrations, classify)?;
93 let after = Migrates::applied_versions(self).await?;
94 let applied = after
95 .iter()
96 .copied()
97 .filter(|v| !before.contains(v))
98 .collect();
99 Ok(MigrateReport {
100 applied,
101 already_applied: before,
102 })
103 }
104
105 async fn applied_versions(&self) -> Result<Vec<i64>, MigrateError> {
106 let exists: bool = sqlx::query_scalar("SELECT to_regclass('_sqlx_migrations') IS NOT NULL")
107 .fetch_one(&self.pool)
108 .await
109 .map_err(backend_error)?;
110 if !exists {
111 return Ok(Vec::new());
112 }
113 sqlx::query_scalar("SELECT version FROM _sqlx_migrations WHERE success ORDER BY version")
114 .fetch_all(&self.pool)
115 .await
116 .map_err(backend_error)
117 }
118}
119
120impl PgBackend {
121 pub async fn migrate(&self, migrations: &[Migration]) -> Result<MigrateReport, MigrateError> {
124 Migrates::migrate(self, migrations).await
125 }
126
127 pub async fn applied_versions(&self) -> Result<Vec<i64>, MigrateError> {
129 Migrates::applied_versions(self).await
130 }
131}