use super::*;
use std;
use std::path::Path;
use std::io::Read;
#[cfg(feature = "d-mysql")]
use ::mysql::{prelude::*, Conn, Opts};
#[cfg(not(feature = "d-mysql"))]
mod m {
use super::*;
pub fn can_connect(conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn migration_table_exists(conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn migration_setup(conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn select_migrations(conn_str: &str) -> Result<Vec<String>> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn insert_migration_tag(conn_str: &str, tag: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn remove_migration_tag(conn_str: &str, tag: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn run_migration(conn_str: &str, filename: &Path) -> Result<()> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
pub fn run_migration_str(conn_str: &str, stmt: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-mysql feature");
}
}
#[cfg(feature = "d-mysql")]
mod m {
use super::*;
pub fn can_connect(conn_str: &str) -> Result<bool> {
let conn_opts = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
Conn::new(conn_opts).chain_err(|| {
format!(
"Unable to connect to mysql database with conn str: {:?}",
conn_str
)
})?;
Ok(true)
}
pub fn migration_table_exists(conn_str: &str) -> Result<bool> {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
let rows: Vec<u32> = conn.query(sql::MYSQL_MIGRATION_TABLE_EXISTS)?;
assert_eq!(
rows.len(),
1,
"Migration table check: Expected 1 returned row"
);
Ok(rows[0] == 1)
}
pub fn migration_setup(conn_str: &str) -> Result<bool> {
if !migration_table_exists(conn_str)? {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
conn.query_drop(sql::MYSQL_CREATE_TABLE)
.chain_err(|| "Error setting up migration table")?;
return Ok(true);
}
Ok(false)
}
pub fn select_migrations(conn_str: &str) -> Result<Vec<String>> {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
Ok(conn.query(sql::GET_MIGRATIONS)?)
}
pub fn insert_migration_tag(conn_str: &str, tag: &str) -> Result<()> {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
conn.exec_drop("insert into __migrant_migrations (tag) values (?)", (tag,))?;
Ok(())
}
pub fn remove_migration_tag(conn_str: &str, tag: &str) -> Result<()> {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
conn.exec_drop("delete from __migrant_migrations where tag = ?", (tag,))?;
Ok(())
}
pub fn run_migration(conn_str: &str, filename: &Path) -> Result<()> {
let mut file = std::fs::File::open(filename)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
conn.query_drop(&buf)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
Ok(())
}
pub fn run_migration_str(conn_str: &str, stmt: &str) -> Result<()> {
let conn_str = Opts::from_url(conn_str)
.chain_err(|| "Error parsing mysql connection string".to_string())?;
let mut conn = Conn::new(conn_str).chain_err(|| "Connection Error")?;
conn.query_drop(stmt)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
Ok(())
}
}
pub use self::m::*;
#[cfg(feature = "d-mysql")]
#[cfg(test)]
mod test {
use super::*;
use std;
macro_rules! _try {
($exp:expr) => {
match $exp {
Ok(v) => v,
Err(e) => {
eprintln!("Caught: {}", e);
panic!("{}", e)
}
}
};
}
#[test]
fn mysql() {
let conn_str = std::env::var("MYSQL_TEST_CONN_STR")
.expect("MYSQL_TEST_CONN_STR env variable required");
can_connect(&conn_str).unwrap();
let is_setup = _try!(migration_table_exists(&conn_str));
assert!(!is_setup, "Assert migration table does not exist");
let was_setup = _try!(migration_setup(&conn_str));
assert!(
was_setup,
"Assert `migration_setup` initializes migration table"
);
let was_setup = _try!(migration_setup(&conn_str));
assert!(!was_setup, "Assert `migration_setup` is idempotent");
let is_setup = _try!(migration_table_exists(&conn_str));
assert!(is_setup, "Assert migration table exists");
_try!(insert_migration_tag(&conn_str, "initial"));
_try!(insert_migration_tag(&conn_str, "alter1"));
_try!(insert_migration_tag(&conn_str, "alter2"));
let migs = _try!(select_migrations(&conn_str));
assert_eq!(3, migs.len(), "Assert 3 migrations applied");
_try!(remove_migration_tag(&conn_str, "alter2"));
let migs = _try!(select_migrations(&conn_str));
assert_eq!(2, migs.len(), "Assert 2 migrations applied");
_try!(remove_migration_tag(&conn_str, "alter1"));
_try!(remove_migration_tag(&conn_str, "initial"));
let migs = _try!(select_migrations(&conn_str));
assert_eq!(0, migs.len(), "Assert all migrations removed");
}
}