use crate::catalog::{Catalog, db_err};
use orbok_core::{OrbokError, OrbokResult, now_iso8601};
struct Migration {
version: i64,
name: &'static str,
sql: &'static str,
}
const MIGRATIONS: &[Migration] = &[
Migration {
version: 1,
name: "baseline",
sql: include_str!("../migrations/0001_baseline.sql"),
},
Migration {
version: 2,
name: "trigram_index",
sql: include_str!("../migrations/0002_trigram_index.sql"),
},
];
pub fn run_pending(catalog: &Catalog) -> OrbokResult<()> {
let mut conn = catalog.lock();
conn.execute(
"CREATE TABLE IF NOT EXISTS schema_migrations (
version INTEGER PRIMARY KEY,
name TEXT NOT NULL,
applied_at TEXT NOT NULL
)",
[],
)
.map_err(db_err)?;
for migration in MIGRATIONS {
let applied: bool = conn
.query_row(
"SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE version = ?1)",
[migration.version],
|row| row.get(0),
)
.map_err(db_err)?;
if applied {
continue;
}
let tx = conn.transaction().map_err(db_err)?;
tx.execute_batch(migration.sql)
.map_err(|e| OrbokError::MigrationFailed {
version: migration.version,
message: e.to_string(),
})?;
tx.execute(
"INSERT INTO schema_migrations (version, name, applied_at) VALUES (?1, ?2, ?3)",
rusqlite::params![migration.version, migration.name, now_iso8601()],
)
.map_err(|e| OrbokError::MigrationFailed {
version: migration.version,
message: e.to_string(),
})?;
tx.commit().map_err(|e| OrbokError::MigrationFailed {
version: migration.version,
message: e.to_string(),
})?;
tracing::info!(version = migration.version, name = migration.name, "applied migration");
}
Ok(())
}
pub fn latest_version() -> i64 {
MIGRATIONS.last().map(|m| m.version).unwrap_or(0)
}