use crate::error::Error;
use async_trait::async_trait;
use refinery_core::Migration;
use refinery_core::traits::r#async::{AsyncMigrate, AsyncQuery, AsyncTransaction};
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
use tokio_postgres::Client;
use tokio_postgres::error::Error as PgError;
const MIGRATIONS: &[(&str, &str)] = &[
(
"V1__initial",
include_str!("../../migrations/V1__initial.sql"),
),
(
"V2__history_cursor",
include_str!("../../migrations/V2__history_cursor.sql"),
),
];
pub(crate) async fn apply(client: &mut Client, prefix: &str) -> Result<(), Error> {
let migrations = MIGRATIONS
.iter()
.map(|(name, sql)| {
let body = sql.replace("{{prefix}}", prefix);
Migration::unapplied(name, &body)
})
.collect::<Result<Vec<_>, _>>()?;
let history_table = format!("{prefix}_refinery_schema_history");
let mut runner = refinery_core::Runner::new(&migrations);
runner.set_migration_table_name(&history_table);
let mut conn = PgMigrationClient(client);
runner.run_async(&mut conn).await?;
Ok(())
}
struct PgMigrationClient<'a>(&'a mut Client);
#[async_trait]
impl AsyncTransaction for PgMigrationClient<'_> {
type Error = PgError;
async fn execute<'a, T: Iterator<Item = &'a str> + Send>(
&mut self,
queries: T,
) -> Result<usize, Self::Error> {
let transaction = self.0.transaction().await?;
let mut count = 0;
for query in queries {
transaction.batch_execute(query).await?;
count += 1;
}
transaction.commit().await?;
Ok(count)
}
}
#[async_trait]
impl AsyncQuery<Vec<Migration>> for PgMigrationClient<'_> {
async fn query(
&mut self,
query: &str,
) -> Result<Vec<Migration>, <Self as AsyncTransaction>::Error> {
let transaction = self.0.transaction().await?;
let rows = transaction.query(query, &[]).await?;
transaction.commit().await?;
let mut applied = Vec::with_capacity(rows.len());
for row in rows {
let version: i32 = row.get(0);
let name: String = row.get(1);
let applied_on: String = row.get(2);
let applied_on = OffsetDateTime::parse(&applied_on, &Rfc3339)
.expect("refinery records applied_on in RFC 3339");
let checksum: String = row.get(3);
let checksum = checksum
.parse::<u64>()
.expect("refinery records checksum as a decimal u64");
applied.push(Migration::applied(version, name, applied_on, checksum));
}
Ok(applied)
}
}
impl AsyncMigrate for PgMigrationClient<'_> {}