Archived
1
0
Fork 0

refac: add struct for db connection

This commit is contained in:
Dmitriy Pleshevskiy 2021-02-13 23:44:41 +03:00
parent 40c0a43dab
commit 8f834264d8
6 changed files with 97 additions and 62 deletions

View file

@ -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");
} }

View file

@ -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)?;
} }
} }

View file

@ -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() {

View file

@ -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()
}

View file

@ -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,8 +27,14 @@ 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> {
self.client.batch_execute(sql_content)
}
pub fn applied_migration_names(&mut self) -> Result<Vec<String>, Error> {
let res = self
.client
.query("SELECT name FROM migrations ORDER BY id DESC", &[]) .query("SELECT name FROM migrations ORDER BY id DESC", &[])
.or_else(|e| { .or_else(|e| {
if is_migrations_table_not_found(&e) { if is_migrations_table_not_found(&e) {
@ -27,9 +47,8 @@ pub fn applied_migrations(client: &mut Client) -> Result<Vec<String>, Error> {
Ok(res.into_iter().map(|row| row.get(0)).collect()) Ok(res.into_iter().map(|row| row.get(0)).collect())
} }
pub fn create_migrations_table(client: &mut Client) -> Result<(), Error> { pub fn create_migrations_table(&mut self) -> Result<(), Error> {
apply_sql( self.apply_sql(
client,
r#"CREATE TABLE IF NOT EXISTS migrations ( r#"CREATE TABLE IF NOT EXISTS migrations (
id serial PRIMARY KEY, id serial PRIMARY KEY,
name text NOT NULL UNIQUE name text NOT NULL UNIQUE
@ -37,10 +56,13 @@ pub fn create_migrations_table(client: &mut Client) -> Result<(), Error> {
) )
} }
pub fn insert_migration_info(client: &mut Client, name: &str) -> Result<u64, Error> { pub fn insert_migration_info(&mut self, name: &str) -> Result<u64, Error> {
client.execute("INSERT INTO migrations (name) VALUES ($1)", &[&name]) self.client
.execute("INSERT INTO migrations (name) VALUES ($1)", &[&name])
} }
pub fn delete_migration_info(client: &mut Client, name: &str) -> Result<u64, Error> { pub fn delete_migration_info(&mut self, name: &str) -> Result<u64, Error> {
client.execute("DELETE FROM migrations WHERE name = $1", &[&name]) self.client
.execute("DELETE FROM migrations WHERE name = $1", &[&name])
}
} }

View file

@ -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(())
} }