refac: add struct for db connection
This commit is contained in:
parent
40c0a43dab
commit
8f834264d8
6 changed files with 97 additions and 62 deletions
|
@ -1,12 +1,12 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::database;
|
use crate::database::DatabaseConnection;
|
||||||
use crate::opts::ApplyCommandOpt;
|
use crate::opts::ApplyCommandOpt;
|
||||||
use crate::path::PathBuilder;
|
use crate::path::PathBuilder;
|
||||||
use crate::StdResult;
|
use crate::StdResult;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub(crate) fn apply_sql(config: Config, opts: ApplyCommandOpt) -> StdResult<()> {
|
pub(crate) fn apply_sql(config: Config, opts: ApplyCommandOpt) -> StdResult<()> {
|
||||||
let database_connection_string = &config.database_connection_string()?;
|
let mut connection = DatabaseConnection::try_from(&config)?;
|
||||||
let mut client = database::connect(database_connection_string)?;
|
|
||||||
|
|
||||||
let file_path = PathBuilder::from(config.directory_path())
|
let file_path = PathBuilder::from(config.directory_path())
|
||||||
.append(opts.file_name)
|
.append(opts.file_name)
|
||||||
|
@ -15,7 +15,7 @@ pub(crate) fn apply_sql(config: Config, opts: ApplyCommandOpt) -> StdResult<()>
|
||||||
|
|
||||||
let content = std::fs::read_to_string(file_path)?;
|
let content = std::fs::read_to_string(file_path)?;
|
||||||
|
|
||||||
match database::apply_sql(&mut client, &content) {
|
match connection.apply_sql(&content) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("File was applied successfully");
|
println!("File was applied successfully");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::database;
|
use crate::database::DatabaseConnection;
|
||||||
use crate::StdResult;
|
use crate::StdResult;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub(crate) fn downgrade_applied_migrations(config: Config) -> StdResult<()> {
|
pub(crate) fn downgrade_applied_migrations(config: Config) -> StdResult<()> {
|
||||||
let database_connection_string = &config.database_connection_string()?;
|
let mut connection = DatabaseConnection::try_from(&config)?;
|
||||||
let mut client = database::connect(database_connection_string)?;
|
|
||||||
|
|
||||||
let applied_migrations = database::applied_migrations(&mut client)?;
|
let applied_migrations = connection.applied_migration_names()?;
|
||||||
let migrations = config.migrations()?;
|
let migrations = config.migrations()?;
|
||||||
|
|
||||||
if let Some(first_applied_migration) = applied_migrations.first() {
|
if let Some(first_applied_migration) = applied_migrations.first() {
|
||||||
|
@ -15,7 +15,7 @@ pub(crate) fn downgrade_applied_migrations(config: Config) -> StdResult<()> {
|
||||||
.find(|m| m.name() == first_applied_migration)
|
.find(|m| m.name() == first_applied_migration)
|
||||||
{
|
{
|
||||||
println!("downgrade {}...", migration.name());
|
println!("downgrade {}...", migration.name());
|
||||||
migration.downgrade(&mut client)?;
|
migration.downgrade(&mut connection)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::database;
|
use crate::database::DatabaseConnection;
|
||||||
use crate::error::{ErrorKind, StdResult};
|
use crate::error::{ErrorKind, StdResult};
|
||||||
|
|
||||||
const EM_DASH: char = '—';
|
const EM_DASH: char = '—';
|
||||||
|
@ -7,8 +7,8 @@ const EM_DASH: char = '—';
|
||||||
pub(crate) fn print_migration_lists(config: Config) -> StdResult<()> {
|
pub(crate) fn print_migration_lists(config: Config) -> StdResult<()> {
|
||||||
let applied_migrations = match config.database_connection_string() {
|
let applied_migrations = match config.database_connection_string() {
|
||||||
Ok(ref database_connection_string) => {
|
Ok(ref database_connection_string) => {
|
||||||
let mut client = database::connect(database_connection_string)?;
|
let mut connection = DatabaseConnection::connect(database_connection_string)?;
|
||||||
let applied_migrations = database::applied_migrations(&mut client)?;
|
let applied_migrations = connection.applied_migration_names()?;
|
||||||
|
|
||||||
println!("Applied migrations:");
|
println!("Applied migrations:");
|
||||||
if applied_migrations.is_empty() {
|
if applied_migrations.is_empty() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::database;
|
use crate::database;
|
||||||
|
use crate::migration::Migration;
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
use crate::StdResult;
|
use crate::StdResult;
|
||||||
|
|
||||||
|
@ -6,21 +7,34 @@ pub(crate) fn upgrade_pending_migrations(config: Config) -> StdResult<()> {
|
||||||
let database_connection_string = &config.database_connection_string()?;
|
let database_connection_string = &config.database_connection_string()?;
|
||||||
let mut client = database::connect(database_connection_string)?;
|
let mut client = database::connect(database_connection_string)?;
|
||||||
|
|
||||||
let applied_migrations = database::applied_migrations(&mut client)?;
|
let applied_migration_names = database::applied_migration_names(&mut client)?;
|
||||||
|
|
||||||
let migrations = config.migrations()?;
|
let migrations = config.migrations()?;
|
||||||
|
|
||||||
if migrations.is_empty() || migrations.last().map(|m| m.name()) == applied_migrations.first() {
|
if is_up_to_date_migrations(&migrations, &applied_migration_names) {
|
||||||
println!("Up to date");
|
println!("Up to date");
|
||||||
} else {
|
} else {
|
||||||
for m in migrations
|
let pending_migrations = filter_pending_migrations(migrations, &applied_migration_names);
|
||||||
.iter()
|
|
||||||
.filter(|m| !applied_migrations.contains(m.name()))
|
for migration in pending_migrations.iter() {
|
||||||
{
|
println!("upgrade {}...", migration.name());
|
||||||
println!("upgrade {}...", m.name());
|
migration.upgrade(&mut client)?;
|
||||||
m.upgrade(&mut client)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_up_to_date_migrations(migrations: &[Migration], applied_migration_names: &[String]) -> bool {
|
||||||
|
migrations.is_empty() || migrations.last().map(|m| m.name()) == applied_migration_names.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_pending_migrations(
|
||||||
|
migrations: Vec<Migration>,
|
||||||
|
applied_migration_names: &[String],
|
||||||
|
) -> Vec<Migration> {
|
||||||
|
migrations
|
||||||
|
.into_iter()
|
||||||
|
.filter(|m| !applied_migration_names.contains(m.name()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::StdResult;
|
||||||
use postgres::{Client, Error, NoTls};
|
use postgres::{Client, Error, NoTls};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub fn connect(connection_string: &str) -> Result<Client, Error> {
|
pub struct DatabaseConnection {
|
||||||
Client::connect(connection_string, NoTls)
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_sql(client: &mut Client, sql_content: &str) -> Result<(), Error> {
|
impl TryFrom<&Config> for DatabaseConnection {
|
||||||
client.batch_execute(sql_content)
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
fn try_from(config: &Config) -> Result<Self, Self::Error> {
|
||||||
|
DatabaseConnection::connect(&config.database_connection_string()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DatabaseConnection {
|
||||||
|
pub fn connect(connection_string: &str) -> StdResult<DatabaseConnection> {
|
||||||
|
let client = Client::connect(connection_string, NoTls)?;
|
||||||
|
Ok(DatabaseConnection { client })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_migrations_table_not_found(e: &Error) -> bool {
|
pub fn is_migrations_table_not_found(e: &Error) -> bool {
|
||||||
|
@ -13,34 +27,42 @@ pub fn is_migrations_table_not_found(e: &Error) -> bool {
|
||||||
.contains(r#"relation "migrations" does not exist"#)
|
.contains(r#"relation "migrations" does not exist"#)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn applied_migrations(client: &mut Client) -> Result<Vec<String>, Error> {
|
impl DatabaseConnection {
|
||||||
let res = client
|
pub fn apply_sql(&mut self, sql_content: &str) -> Result<(), Error> {
|
||||||
.query("SELECT name FROM migrations ORDER BY id DESC", &[])
|
self.client.batch_execute(sql_content)
|
||||||
.or_else(|e| {
|
}
|
||||||
if is_migrations_table_not_found(&e) {
|
|
||||||
Ok(Vec::new())
|
|
||||||
} else {
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(res.into_iter().map(|row| row.get(0)).collect())
|
pub fn applied_migration_names(&mut self) -> Result<Vec<String>, Error> {
|
||||||
}
|
let res = self
|
||||||
|
.client
|
||||||
|
.query("SELECT name FROM migrations ORDER BY id DESC", &[])
|
||||||
|
.or_else(|e| {
|
||||||
|
if is_migrations_table_not_found(&e) {
|
||||||
|
Ok(Vec::new())
|
||||||
|
} else {
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
pub fn create_migrations_table(client: &mut Client) -> Result<(), Error> {
|
Ok(res.into_iter().map(|row| row.get(0)).collect())
|
||||||
apply_sql(
|
}
|
||||||
client,
|
|
||||||
r#"CREATE TABLE IF NOT EXISTS migrations (
|
|
||||||
id serial PRIMARY KEY,
|
|
||||||
name text NOT NULL UNIQUE
|
|
||||||
)"#,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_migration_info(client: &mut Client, name: &str) -> Result<u64, Error> {
|
pub fn create_migrations_table(&mut self) -> Result<(), Error> {
|
||||||
client.execute("INSERT INTO migrations (name) VALUES ($1)", &[&name])
|
self.apply_sql(
|
||||||
}
|
r#"CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
name text NOT NULL UNIQUE
|
||||||
|
)"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete_migration_info(client: &mut Client, name: &str) -> Result<u64, Error> {
|
pub fn insert_migration_info(&mut self, name: &str) -> Result<u64, Error> {
|
||||||
client.execute("DELETE FROM migrations WHERE name = $1", &[&name])
|
self.client
|
||||||
|
.execute("INSERT INTO migrations (name) VALUES ($1)", &[&name])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_migration_info(&mut self, name: &str) -> Result<u64, Error> {
|
||||||
|
self.client
|
||||||
|
.execute("DELETE FROM migrations WHERE name = $1", &[&name])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::database;
|
use crate::database::DatabaseConnection;
|
||||||
use crate::path::PathBuilder;
|
use crate::path::PathBuilder;
|
||||||
use postgres::Client;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -37,27 +36,27 @@ impl Migration {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade(&self, client: &mut Client) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
pub fn upgrade(
|
||||||
|
&self,
|
||||||
|
connection: &mut DatabaseConnection,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||||
let content = fs::read_to_string(&self.upgrade_sql)?;
|
let content = fs::read_to_string(&self.upgrade_sql)?;
|
||||||
|
|
||||||
database::create_migrations_table(client)?;
|
connection.create_migrations_table()?;
|
||||||
|
connection.apply_sql(&content)?;
|
||||||
database::apply_sql(client, &content)?;
|
connection.insert_migration_info(self.name())?;
|
||||||
|
|
||||||
database::insert_migration_info(client, self.name())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downgrade(
|
pub fn downgrade(
|
||||||
&self,
|
&self,
|
||||||
client: &mut Client,
|
connection: &mut DatabaseConnection,
|
||||||
) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||||
let content = fs::read_to_string(&self.downgrade_sql)?;
|
let content = fs::read_to_string(&self.downgrade_sql)?;
|
||||||
|
|
||||||
database::apply_sql(client, &content)?;
|
connection.apply_sql(&content)?;
|
||||||
|
connection.delete_migration_info(self.name())?;
|
||||||
database::delete_migration_info(client, self.name())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue