use super::*;
use std;
use std::path::Path;
#[cfg(feature = "d-postgres")]
use postgres::{Client, NoTls};
use std::io::Read;
#[cfg(not(feature = "d-postgres"))]
mod m {
use super::*;
pub fn can_connect(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn migration_table_exists(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn migration_setup(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn select_migrations(cert: Option<&Path>, conn_str: &str) -> Result<Vec<String>> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn insert_migration_tag(cert: Option<&Path>, conn_str: &str, tag: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn remove_migration_tag(cert: Option<&Path>, conn_str: &str, tag: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn run_migration(cert: Option<&Path>, conn_str: &str, filename: &Path) -> Result<()> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
pub fn run_migration_str(cert: Option<&Path>, conn_str: &str, stmt: &str) -> Result<()> {
unimplemented!("migrant_lib: must enable d-postgres feature");
}
}
#[cfg(feature = "d-postgres")]
mod m {
use super::*;
macro_rules! make_connector {
($file:expr) => {{
let cert = std::fs::read($file)
.map_err(|e| format_err!(ErrorKind::Migration, "postgres cert file error {}", e))?;
let cert = native_tls::Certificate::from_pem(&cert)
.map_err(|e| format_err!(ErrorKind::Migration, "postgres cert load error {}", e))?;
let connector = native_tls::TlsConnector::builder()
.add_root_certificate(cert)
.build()
.map_err(|e| {
format_err!(ErrorKind::Migration, "postgres tls-connection error {}", e)
})?;
postgres_native_tls::MakeTlsConnector::new(connector)
}};
}
pub fn can_connect(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
match cert {
None => match Client::connect(conn_str, NoTls) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
},
Some(cert) => match Client::connect(conn_str, make_connector!(cert)) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
},
}
}
macro_rules! make_connection {
($cert:expr, $conn_str:expr) => {{
match $cert {
None => Client::connect($conn_str, NoTls),
Some(cert) => Client::connect($conn_str, make_connector!(cert)),
}
}};
}
pub fn migration_table_exists(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
let mut conn = make_connection!(cert, conn_str)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
let rows = conn
.query(sql::PG_MIGRATION_TABLE_EXISTS, &[])
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
let exists: bool = rows
.get(0)
.expect("expected a result from postgres when checking for migration table")
.get(0);
Ok(exists)
}
pub fn migration_setup(cert: Option<&Path>, conn_str: &str) -> Result<bool> {
if !migration_table_exists(cert, conn_str)? {
let mut conn = make_connection!(cert, conn_str)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
conn.execute(sql::CREATE_TABLE, &[])
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
return Ok(true);
}
Ok(false)
}
pub fn select_migrations(cert: Option<&Path>, conn_str: &str) -> Result<Vec<String>> {
let mut conn = make_connection!(cert, conn_str)?;
let rows = conn.query(sql::GET_MIGRATIONS, &[])?;
Ok(rows.iter().map(|row| row.get(0)).collect())
}
pub fn insert_migration_tag(cert: Option<&Path>, conn_str: &str, tag: &str) -> Result<()> {
let mut conn = make_connection!(cert, conn_str)?;
conn.execute(
"insert into __migrant_migrations (tag) values ($1)",
&[&tag],
)?;
Ok(())
}
pub fn remove_migration_tag(cert: Option<&Path>, conn_str: &str, tag: &str) -> Result<()> {
let mut conn = make_connection!(cert, conn_str)?;
conn.execute("delete from __migrant_migrations where tag = $1", &[&tag])?;
Ok(())
}
pub fn run_migration(cert: Option<&Path>, 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 mut conn = make_connection!(cert, conn_str)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
conn.batch_execute(&buf)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
Ok(())
}
pub fn run_migration_str(cert: Option<&Path>, conn_str: &str, stmt: &str) -> Result<()> {
let mut conn = make_connection!(cert, conn_str)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
conn.batch_execute(stmt)
.map_err(|e| format_err!(ErrorKind::Migration, "{}", e))?;
Ok(())
}
}
pub use self::m::*;
#[cfg(feature = "d-postgres")]
#[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 postgres() {
let conn_str = std::env::var("POSTGRES_TEST_CONN_STR")
.expect("POSTGRES_TEST_CONN_STR env variable required");
assert!(can_connect(None, &conn_str).is_ok());
let is_setup = _try!(migration_table_exists(None, &conn_str));
assert!(!is_setup, "Assert migration table does not exist");
let was_setup = _try!(migration_setup(None, &conn_str));
assert!(
was_setup,
"Assert `migration_setup` initializes migration table"
);
let was_setup = _try!(migration_setup(None, &conn_str));
assert!(!was_setup, "Assert `migration_setup` is idempotent");
let is_setup = _try!(migration_table_exists(None, &conn_str));
assert!(is_setup, "Assert migration table exists");
_try!(insert_migration_tag(None, &conn_str, "initial"));
_try!(insert_migration_tag(None, &conn_str, "alter1"));
_try!(insert_migration_tag(None, &conn_str, "alter2"));
let migs = _try!(select_migrations(None, &conn_str));
assert_eq!(3, migs.len(), "Assert 3 migrations applied");
_try!(remove_migration_tag(None, &conn_str, "alter2"));
let migs = _try!(select_migrations(None, &conn_str));
assert_eq!(2, migs.len(), "Assert 2 migrations applied");
_try!(remove_migration_tag(None, &conn_str, "alter1"));
_try!(remove_migration_tag(None, &conn_str, "initial"));
let migs = _try!(select_migrations(None, &conn_str));
assert_eq!(0, migs.len(), "Assert all migrations removed");
}
}