123 lines
3.6 KiB
Rust
123 lines
3.6 KiB
Rust
|
use Connection;
|
||
|
use Error;
|
||
|
use Result;
|
||
|
|
||
|
use diesel::connection::{Connection as Conn, SimpleConnection};
|
||
|
use migrations_internals::{setup_database, MigrationConnection};
|
||
|
|
||
|
use std::path::Path;
|
||
|
|
||
|
#[allow(dead_code)] //variants might not be constructed if not required by current migrations
|
||
|
enum Action {
|
||
|
Sql(&'static str),
|
||
|
Function(&'static Fn(&Connection, &Path) -> Result<()>),
|
||
|
}
|
||
|
|
||
|
impl Action {
|
||
|
fn run(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||
|
match self {
|
||
|
Action::Sql(sql) => conn.batch_execute(sql).map_err(Error::from),
|
||
|
Action::Function(f) => f(conn, path),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct ComplexMigration {
|
||
|
name: &'static str,
|
||
|
up: &'static [Action],
|
||
|
down: &'static [Action],
|
||
|
}
|
||
|
|
||
|
impl ComplexMigration {
|
||
|
fn run(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||
|
println!("Running migration {}", self.name);
|
||
|
for step in self.up {
|
||
|
step.run(conn, path)?
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn revert(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||
|
println!("Reverting migration {}", self.name);
|
||
|
for step in self.down {
|
||
|
step.run(conn, path)?
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct ImportedMigrations(&'static [ComplexMigration]);
|
||
|
|
||
|
impl ImportedMigrations {
|
||
|
pub fn run_pending_migrations(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||
|
use diesel::dsl::sql;
|
||
|
use diesel::sql_types::Bool;
|
||
|
use diesel::{select, RunQueryDsl};
|
||
|
#[cfg(feature = "postgres")]
|
||
|
let schema_exists: bool = select(sql::<Bool>(
|
||
|
"EXISTS \
|
||
|
(SELECT 1 \
|
||
|
FROM information_schema.tables \
|
||
|
WHERE table_name = '__diesel_schema_migrations')",
|
||
|
))
|
||
|
.get_result(conn)?;
|
||
|
#[cfg(feature = "sqlite")]
|
||
|
let schema_exists: bool = select(sql::<Bool>(
|
||
|
"EXISTS \
|
||
|
(SELECT 1 \
|
||
|
FROM sqlite_master \
|
||
|
WHERE type = 'table' \
|
||
|
AND name = '__diesel_schema_migrations')",
|
||
|
))
|
||
|
.get_result(conn)?;
|
||
|
|
||
|
if !schema_exists {
|
||
|
setup_database(conn)?;
|
||
|
}
|
||
|
|
||
|
let latest_migration = conn.latest_run_migration_version()?;
|
||
|
let latest_id = if let Some(migration) = latest_migration {
|
||
|
self.0
|
||
|
.binary_search_by_key(&migration.as_str(), |mig| mig.name)
|
||
|
.map(|id| id + 1)
|
||
|
.map_err(|_| Error::NotFound)?
|
||
|
} else {
|
||
|
0
|
||
|
};
|
||
|
|
||
|
let to_run = &self.0[latest_id..];
|
||
|
for migration in to_run {
|
||
|
conn.transaction(|| {
|
||
|
migration.run(conn, path)?;
|
||
|
conn.insert_new_migration(migration.name)
|
||
|
.map_err(Error::from)
|
||
|
})?;
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn is_pending(&self, conn: &Connection) -> Result<bool> {
|
||
|
let latest_migration = conn.latest_run_migration_version()?;
|
||
|
if let Some(migration) = latest_migration {
|
||
|
Ok(self.0.last().expect("no migrations found").name != migration)
|
||
|
} else {
|
||
|
Ok(true)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn rerun_last_migration(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||
|
let latest_migration = conn.latest_run_migration_version()?;
|
||
|
let id = latest_migration
|
||
|
.and_then(|m| self.0.binary_search_by_key(&m.as_str(), |m| m.name).ok())?;
|
||
|
let migration = &self.0[id];
|
||
|
conn.transaction(|| {
|
||
|
migration.revert(conn, path)?;
|
||
|
migration.run(conn, path)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub const IMPORTED_MIGRATIONS: ImportedMigrations = {
|
||
|
import_migrations! {}
|
||
|
};
|