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 alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::error::Error;
use core::fmt::Display;
pub type Result<T> = core::result::Result<T, Box<dyn Error + Send + Sync>>;
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, FromSqlRow, AsExpression)]
#[diesel(sql_type = Text)]
pub struct MigrationVersion<'a>(Cow<'a, str>);
impl MigrationVersion<'_> {
pub fn as_owned(&self) -> MigrationVersion<'static> {
MigrationVersion(Cow::Owned(self.0.as_ref().to_owned()))
}
}
impl<DB> FromSql<Text, DB> for MigrationVersion<'_>
where
String: FromSql<Text, DB>,
DB: Backend,
{
fn from_sql(bytes: DB::RawValue<'_>) -> 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<'b>(
&'b self,
out: &mut crate::serialize::Output<'b, '_, DB>,
) -> crate::serialize::Result {
self.0.to_sql(out)
}
}
impl From<String> for MigrationVersion<'_> {
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 Display for MigrationVersion<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.0.as_ref())
}
}
pub trait MigrationName: Display {
fn version(&self) -> MigrationVersion<'_>;
}
pub trait Migration<DB: Backend> {
fn run(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()>;
fn revert(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()>;
fn metadata(&self) -> &dyn MigrationMetadata;
fn name(&self) -> &dyn MigrationName;
}
pub trait MigrationMetadata {
fn run_in_transaction(&self) -> bool {
true
}
}
pub trait MigrationSource<DB: Backend> {
fn migrations(&self) -> Result<Vec<Box<dyn Migration<DB>>>>;
}
impl<DB: Backend> Migration<DB> for Box<dyn Migration<DB> + '_> {
fn run(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).run(conn)
}
fn revert(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).revert(conn)
}
fn metadata(&self) -> &dyn MigrationMetadata {
(**self).metadata()
}
fn name(&self) -> &dyn MigrationName {
(**self).name()
}
}
impl<DB: Backend> Migration<DB> for alloc::rc::Rc<dyn Migration<DB> + '_> {
fn run(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).run(conn)
}
fn revert(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).revert(conn)
}
fn metadata(&self) -> &dyn MigrationMetadata {
(**self).metadata()
}
fn name(&self) -> &dyn MigrationName {
(**self).name()
}
}
impl<DB: Backend> Migration<DB> for alloc::sync::Arc<dyn Migration<DB> + '_> {
fn run(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).run(conn)
}
fn revert(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).revert(conn)
}
fn metadata(&self) -> &dyn MigrationMetadata {
(**self).metadata()
}
fn name(&self) -> &dyn MigrationName {
(**self).name()
}
}
impl<DB: Backend> Migration<DB> for &dyn Migration<DB> {
fn run(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).run(conn)
}
fn revert(&self, conn: &mut dyn BoxableConnection<DB>) -> Result<()> {
(**self).revert(conn)
}
fn metadata(&self) -> &dyn MigrationMetadata {
(**self).metadata()
}
fn name(&self) -> &dyn MigrationName {
(**self).name()
}
}
pub const CREATE_MIGRATIONS_TABLE: &str = include_str!("setup_migration_table.sql");
pub trait MigrationConnection: Connection {
fn setup(&mut self) -> QueryResult<usize>;
#[doc(hidden)]
fn read_search_path(&mut self) -> QueryResult<Option<String>> {
Ok(None)
}
#[doc(hidden)]
fn set_search_path(&mut self, _search_path: &str) -> QueryResult<()> {
Ok(())
}
}
#[cfg(feature = "postgres")]
impl MigrationConnection for crate::pg::PgConnection {
fn setup(&mut self) -> QueryResult<usize> {
use crate::RunQueryDsl;
crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self)
}
fn read_search_path(&mut self) -> QueryResult<Option<String>> {
use crate::RunQueryDsl;
use crate::dsl::{select, sql};
let search_path =
select(sql::<Text>("current_setting('search_path')")).get_result::<String>(self)?;
Ok(Some(search_path))
}
fn set_search_path(&mut self, search_path: &str) -> QueryResult<()> {
use crate::RunQueryDsl;
crate::sql_query("SELECT set_config('search_path', $1, false)")
.bind::<Text, _>(search_path)
.execute(self)?;
Ok(())
}
}
#[cfg(feature = "mysql")]
impl MigrationConnection for crate::mysql::MysqlConnection {
fn setup(&mut self) -> QueryResult<usize> {
use crate::RunQueryDsl;
crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self)
}
}
#[cfg(feature = "mariadb")]
impl MigrationConnection for crate::mariadb::MariadbConnection {
fn setup(&mut self) -> QueryResult<usize> {
use crate::RunQueryDsl;
crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self)
}
}
#[cfg(feature = "__sqlite-shared")]
impl MigrationConnection for crate::sqlite::SqliteConnection {
fn setup(&mut self) -> QueryResult<usize> {
use crate::RunQueryDsl;
crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self)
}
}
#[cfg(test)]
mod tests {
use super::Migration;
use crate::backend::Backend;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
fn assert_migration<DB: Backend, M: Migration<DB>>() {}
#[allow(dead_code)]
fn migration_for_box_rc_arc_dyn_compiles<DB: Backend>() {
assert_migration::<DB, Box<dyn Migration<DB>>>();
assert_migration::<DB, Rc<dyn Migration<DB>>>();
assert_migration::<DB, Arc<dyn Migration<DB>>>();
}
}