use std::borrow::Cow;
use crate::{
Error,
connection::{Connection, ConnectionOwned},
filter,
table::Table,
};
use chuchi_postgres_derive::{FromRow, row};
use tracing::debug;
use types::time::DateTime;
#[derive(Debug, FromRow)]
pub struct ExecutedMigration {
datetime: DateTime,
}
#[derive(Debug, Clone)]
pub struct Migrations {
table: Table,
}
impl Migrations {
pub(super) fn new(table_name: Option<String>) -> Self {
Self {
table: Table::new(
table_name.map(Cow::Owned).unwrap_or("migrations".into()),
),
}
}
pub(super) async fn init(
&self,
db: &mut ConnectionOwned,
) -> Result<(), Error> {
let db = db.transaction().await?;
let conn = db.connection();
let table_exists =
TABLE_EXISTS.replace("migrations", self.table.name());
let [result] =
conn.query_one::<[bool; 1], _>(&table_exists, &[]).await?;
if !result {
let create_table =
CREATE_TABLE.replace("migrations", self.table.name());
conn.batch_execute(&create_table).await?;
}
db.commit().await?;
Ok(())
}
pub async fn add(
&self,
conn: &mut ConnectionOwned,
name: &str,
sql: &str,
) -> Result<(), Error> {
let trans = conn.transaction().await?;
let conn = trans.connection();
let executed = self.get(conn, name).await?;
if let Some(mig) = executed {
debug!("migration {} was executed at {}", name, mig.datetime);
return Ok(());
}
conn.batch_execute(&sql).await?;
self.set(conn, name).await?;
trans.commit().await?;
Ok(())
}
pub async fn get(
&self,
conn: Connection<'_>,
name: &str,
) -> Result<Option<ExecutedMigration>, Error> {
let table = self.table.with_conn(conn);
table.select_opt(filter!(&name)).await
}
pub async fn set(
&self,
conn: Connection<'_>,
name: &str,
) -> Result<(), Error> {
let table = self.table.with_conn(conn);
table
.insert(row! {
name,
"datetime": DateTime::now(),
})
.await?;
Ok(())
}
}
const TABLE_EXISTS: &str = "\
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'migrations'
);";
const CREATE_TABLE: &str = "\
CREATE TABLE migrations (
name text PRIMARY KEY,
datetime timestamp
);
CREATE INDEX ON migrations (datetime);";