Skip to content

Commit

Permalink
Rewrite the migration harness
Browse files Browse the repository at this point in the history
  • Loading branch information
weiznich committed Feb 26, 2021
1 parent 113b523 commit 35d68ba
Show file tree
Hide file tree
Showing 40 changed files with 1,162 additions and 1,133 deletions.
3 changes: 2 additions & 1 deletion diesel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ ipnetwork = ">=0.12.2, <0.18.0"
quickcheck = "0.9"

[features]
default = ["with-deprecated", "32-column-tables"]
#default = ["with-deprecated", "32-column-tables"]
default = ["r2d2"]
extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"]
unstable = ["diesel_derives/nightly"]
large-tables = ["32-column-tables"]
Expand Down
69 changes: 63 additions & 6 deletions diesel/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ pub trait SimpleConnection {
}

/// A connection to a database
pub trait Connection: SimpleConnection + Sized + Send {
pub trait Connection: SimpleConnection + Send {
/// The backend this type connects to
type Backend: Backend;
#[doc(hidden)]
type TransactionManager: TransactionManager<Self>;
// #[doc(hidden)]
// type TransactionManager: TransactionManager<Self>;

/// Establishes a new connection to the database
///
/// The argument to this method varies by backend.
/// See the documentation for that backend's connection class
/// for details about what it accepts.
fn establish(database_url: &str) -> ConnectionResult<Self>;
fn establish(database_url: &str) -> ConnectionResult<Self>
where
Self: Sized;

/// Executes the given function inside of a database transaction
///
Expand Down Expand Up @@ -100,6 +102,7 @@ pub trait Connection: SimpleConnection + Sized + Send {
/// ```
fn transaction<T, E, F>(&self, f: F) -> Result<T, E>
where
Self: Sized,
F: FnOnce() -> Result<T, E>,
E: From<Error>,
{
Expand All @@ -119,7 +122,10 @@ pub trait Connection: SimpleConnection + Sized + Send {

/// Creates a transaction that will never be committed. This is useful for
/// tests. Panics if called while inside of a transaction.
fn begin_test_transaction(&self) -> QueryResult<()> {
fn begin_test_transaction(&self) -> QueryResult<()>
where
Self: Sized,
{
let transaction_manager = self.transaction_manager();
assert_eq!(transaction_manager.get_transaction_depth(), 0);
transaction_manager.begin_transaction(self)
Expand Down Expand Up @@ -162,6 +168,7 @@ pub trait Connection: SimpleConnection + Sized + Send {
where
F: FnOnce() -> Result<T, E>,
E: Debug,
Self: Sized,
{
let mut user_result = None;
let _ = self.transaction::<(), _, _>(|| {
Expand All @@ -177,6 +184,7 @@ pub trait Connection: SimpleConnection + Sized + Send {
#[doc(hidden)]
fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
where
Self: Sized,
T: AsQuery,
T::Query: QueryFragment<Self::Backend> + QueryId,
U: FromSqlRow<T::SqlType, Self::Backend>,
Expand All @@ -185,8 +193,57 @@ pub trait Connection: SimpleConnection + Sized + Send {
#[doc(hidden)]
fn execute_returning_count<T>(&self, source: &T) -> QueryResult<usize>
where
Self: Sized,
T: QueryFragment<Self::Backend> + QueryId;

#[doc(hidden)]
fn transaction_manager(&self) -> &Self::TransactionManager;
fn transaction_manager(&self) -> &dyn TransactionManager<Self>
where
Self: Sized;
}

/// A variant of the [`Connection`](trait.Connection.html) that is
/// usable with dynamic dispatch
///
/// If you are looking for a way to use pass database connections
/// for different database backends around in your application
/// this trait won't help you much. Normally you should only
/// need to use this trait if you are interacting with a connection
/// passed to a [`Migration`](../migration/trait.Migration.html)
pub trait BoxableConnection<DB: Backend>: Connection<Backend = DB> + std::any::Any {
#[doc(hidden)]
fn as_any(&self) -> &dyn std::any::Any;
}

impl<C> BoxableConnection<C::Backend> for C
where
C: Connection + std::any::Any,
{
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

impl<DB: Backend> dyn BoxableConnection<DB> {
/// Downcast the current connection to a specific connection
/// type.
///
/// This will return `None` if the underlying
/// connection does not match the corresponding
/// type, otherwise a reference to the underlying connection is returned
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: Connection<Backend = DB> + 'static,
{
self.as_any().downcast_ref::<T>()
}

/// Check if the current connection is
/// a specific connection type
pub fn is<T>(&self) -> bool
where
T: Connection<Backend = DB> + 'static,
{
self.as_any().is::<T>()
}
}
2 changes: 1 addition & 1 deletion diesel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
// For the `specialization` feature.
#![cfg_attr(feature = "unstable", allow(incomplete_features))]
// Built-in Lints
#![deny(warnings)]
//#![deny(warnings)]
#![warn(
missing_debug_implementations,
missing_copy_implementations,
Expand Down
152 changes: 129 additions & 23 deletions diesel/src/migration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,163 @@
#![allow(unused_imports, missing_docs)]
//! Representation of migrations
mod errors;
pub use self::errors::{MigrationError, RunMigrationsError};

use crate::connection::{Connection, SimpleConnection};
use crate::backend::Backend;
use crate::connection::{BoxableConnection, Connection};
use crate::deserialize::{FromSql, FromSqlRow};
use crate::expression::AsExpression;
use crate::result::QueryResult;
use crate::serialize::ToSql;
use crate::sql_types::Text;
use std::borrow::Cow;
use std::error::Error;
use std::fmt::Display;
use std::path::Path;

#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, FromSqlRow, AsExpression)]
#[sql_type = "Text"]
pub struct MigrationVersion<'a>(Cow<'a, str>);

impl<'a> MigrationVersion<'a> {
pub fn into_owned(&self) -> MigrationVersion<'static> {
MigrationVersion(Cow::Owned(self.0.as_ref().to_owned()))
}
}

impl<'a, DB> FromSql<Text, DB> for MigrationVersion<'a>
where
String: FromSql<Text, DB>,
DB: Backend,
{
fn from_sql(bytes: crate::backend::RawValue<DB>) -> crate::deserialize::Result<Self> {
let s = String::from_sql(bytes)?;
Ok(Self(Cow::Owned(s)))
}
}

impl<'a, DB> ToSql<Text, DB> for MigrationVersion<'a>
where
Cow<'a, str>: ToSql<Text, DB>,
DB: Backend,
{
fn to_sql<W: std::io::Write>(
&self,
out: &mut crate::serialize::Output<W, DB>,
) -> crate::serialize::Result {
self.0.to_sql(out)
}
}

impl<'a> From<String> for MigrationVersion<'a> {
fn from(s: String) -> Self {
MigrationVersion(Cow::Owned(s))
}
}

impl<'a> From<&'a str> for MigrationVersion<'a> {
fn from(s: &'a str) -> Self {
MigrationVersion(Cow::Borrowed(s))
}
}

impl<'a> From<&'a String> for MigrationVersion<'a> {
fn from(s: &'a String) -> Self {
MigrationVersion(Cow::Borrowed(s))
}
}

impl<'a> Display for MigrationVersion<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.0.as_ref())
}
}

/// Represents a migration that interacts with diesel
pub trait Migration {
pub trait Migration<DB: Backend> {
/// Get the migration version
fn version(&self) -> &str;
fn version<'a>(&'a self) -> MigrationVersion<'a>;
/// Apply this migration
fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>;
fn run(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
/// Revert this migration
fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>;
/// Get the migration file path
fn file_path(&self) -> Option<&Path> {
None
fn revert(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
/// Get a the attached metadata for this migration
fn metadata(&self) -> &dyn MigrationMetadata;
}

/// This trait is designed to customize the behaviour
/// of the default migration harness of diesel
///
/// Any new customization option will be added
/// as new function here. Each new function
/// will have a default implementation
/// returning the old a value corresponding
/// to the old uncustomized behaviour
pub trait MigrationMetadata {
/// Should the current migration be executed in a migration
/// or not?
///
/// By default this function returns true
fn run_in_transaction(&self) -> bool {
true
}
}

impl<'a> Migration for Box<dyn Migration + 'a> {
fn version(&self) -> &str {
pub trait MigrationSource<DB: Backend> {
fn migrations(
&self,
) -> Result<Vec<Box<dyn Migration<DB>>>, Box<dyn Error + Send + Sync + 'static>>;
}

impl<'a, DB: Backend> Migration<DB> for Box<dyn Migration<DB> + 'a> {
fn version<'b>(&'b self) -> MigrationVersion<'b> {
(&**self).version()
}

fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> {
fn run(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
(&**self).run(conn)
}

fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> {
fn revert(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
(&**self).revert(conn)
}
fn file_path(&self) -> Option<&Path> {
(&**self).file_path()

fn metadata(&self) -> &dyn MigrationMetadata {
(&**self).metadata()
}
}

impl<'a> Migration for &'a dyn Migration {
fn version(&self) -> &str {
impl<'a, DB: Backend> Migration<DB> for &'a dyn Migration<DB> {
fn version<'b>(&'b self) -> MigrationVersion<'b> {
(&**self).version()
}

fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> {
fn run(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
(&**self).run(conn)
}

fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> {
fn revert(
&self,
conn: &dyn BoxableConnection<DB>,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
(&**self).revert(conn)
}
fn file_path(&self) -> Option<&Path> {
(&**self).file_path()

fn metadata(&self) -> &dyn MigrationMetadata {
(&**self).metadata()
}
}

Expand Down
3 changes: 1 addition & 2 deletions diesel/src/mysql/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ impl SimpleConnection for MysqlConnection {

impl Connection for MysqlConnection {
type Backend = Mysql;
type TransactionManager = AnsiTransactionManager;

fn establish(database_url: &str) -> ConnectionResult<Self> {
use crate::result::ConnectionError::CouldntSetupConfiguration;
Expand Down Expand Up @@ -89,7 +88,7 @@ impl Connection for MysqlConnection {
}

#[doc(hidden)]
fn transaction_manager(&self) -> &Self::TransactionManager {
fn transaction_manager(&self) -> &dyn TransactionManager<Self> {
&self.transaction_manager
}
}
Expand Down
3 changes: 1 addition & 2 deletions diesel/src/pg/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ impl SimpleConnection for PgConnection {

impl Connection for PgConnection {
type Backend = Pg;
type TransactionManager = AnsiTransactionManager;

fn establish(database_url: &str) -> ConnectionResult<PgConnection> {
RawConnection::establish(database_url).and_then(|raw_conn| {
Expand Down Expand Up @@ -95,7 +94,7 @@ impl Connection for PgConnection {
}

#[doc(hidden)]
fn transaction_manager(&self) -> &Self::TransactionManager {
fn transaction_manager(&self) -> &dyn TransactionManager<Self> {
&self.transaction_manager
}
}
Expand Down
2 changes: 1 addition & 1 deletion diesel/src/pg/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ impl<'a> TransactionBuilder<'a> {
let mut query_builder = <Pg as Backend>::QueryBuilder::default();
self.to_sql(&mut query_builder)?;
let sql = query_builder.finish();
let transaction_manager = self.connection.transaction_manager();
let transaction_manager = &self.connection.transaction_manager;

transaction_manager.begin_transaction_sql(self.connection, &sql)?;
match f() {
Expand Down
Loading

0 comments on commit 35d68ba

Please sign in to comment.