use std::collections::HashSet;
use sqlx::PgPool;
const MIGRATIONS: &[(i32, &str, &str)] = &[
(
1,
"init",
include_str!("../../../migrations_pg/0001_init.sql"),
),
(
2,
"code_embeddings",
include_str!("../../../migrations_pg/0002_code_embeddings.sql"),
),
(
3,
"indexes",
include_str!("../../../migrations_pg/0003_indexes.sql"),
),
];
const MIGRATION_LOCK_KEY: i64 = 0x006D_6170_726F_6F6D;
const TRACKING_DDL: &str = "CREATE TABLE IF NOT EXISTS schema_migrations (\
version INTEGER PRIMARY KEY, \
name TEXT NOT NULL, \
applied_at TIMESTAMPTZ NOT NULL DEFAULT now())";
pub async fn run(pool: PgPool) -> anyhow::Result<()> {
use sqlx::Executor;
let mut tx = pool.begin().await?;
(&mut *tx)
.execute(
"SET LOCAL statement_timeout = 0; \
SET LOCAL lock_timeout = 0; \
SET LOCAL idle_in_transaction_session_timeout = 0",
)
.await?;
sqlx::query("SELECT pg_advisory_xact_lock($1)")
.bind(MIGRATION_LOCK_KEY)
.execute(&mut *tx)
.await?;
sqlx::query(TRACKING_DDL).execute(&mut *tx).await?;
let versions: Vec<i32> = sqlx::query_scalar("SELECT version FROM schema_migrations")
.fetch_all(&mut *tx)
.await?;
let applied: HashSet<i32> = versions.into_iter().collect();
for (version, name, sql) in MIGRATIONS {
if applied.contains(version) {
continue;
}
let batch = format!(
"{sql}\nINSERT INTO schema_migrations (version, name) VALUES ({version}, '{name}');"
);
(&mut *tx).execute(batch.as_str()).await?;
}
tx.commit().await?;
Ok(())
}
pub async fn get_applied(pool: &PgPool) -> anyhow::Result<HashSet<i32>> {
let table_exists: Option<i32> = sqlx::query_scalar(
"SELECT 1 FROM information_schema.tables \
WHERE table_schema = 'public' AND table_name = 'schema_migrations'",
)
.fetch_optional(pool)
.await?;
if table_exists.is_none() {
return Ok(HashSet::new());
}
let versions: Vec<i32> = sqlx::query_scalar("SELECT version FROM schema_migrations")
.fetch_all(pool)
.await?;
Ok(versions.into_iter().collect())
}