use sqlx::{ConnectOptions, Connection};
use crate::Error;
const POSTGRES_LOCK_KEY: i64 = 0x7275_6c6c_7374_6d67;
const POSTGRES_LOCK_SQL: &str = "SELECT CAST(pg_advisory_lock($1) AS TEXT)";
const MYSQL_LOCK_SQL: &str = "SELECT CAST(GET_LOCK(CONCAT('rullst_orm_migrations:', SHA1(COALESCE(DATABASE(), ''))), 10) AS SIGNED)";
type LockConnection = <crate::database::RullstDatabase as sqlx::Database>::Connection;
pub(super) struct MigrationLock {
connection: Option<LockConnection>,
}
impl MigrationLock {
pub(super) async fn acquire(pool: &crate::RullstPool, driver: &str) -> Result<Self, Error> {
if !matches!(driver, "postgres" | "mysql") {
return Ok(Self { connection: None });
}
let mut connection = pool.connect_options().connect().await?;
if driver == "postgres" {
sqlx::query(POSTGRES_LOCK_SQL)
.bind(POSTGRES_LOCK_KEY)
.execute(&mut connection)
.await?;
} else {
loop {
let acquired: Option<i64> = sqlx::query_scalar(MYSQL_LOCK_SQL)
.fetch_one(&mut connection)
.await?;
match acquired {
Some(1) => break,
Some(0) => continue,
_ => {
return Err(Error::DatabaseError(
"the migration runner lock could not be acquired".to_string(),
));
}
}
}
}
Ok(Self {
connection: Some(connection),
})
}
pub(super) async fn release(mut self) {
if let Some(connection) = self.connection.take() {
let _ = connection.close().await;
}
}
}