Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,44 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table("cake")
.col(pk_auto("id"))
.col(string("name"))
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("cake_name_index")
.table("cake")
.col("name")
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table("cake").to_owned())
.await?;
if std::env::var_os("ABORT_MIGRATION").eq(&Some("YES".into())) {
return Err(DbErr::Migration(
"Abort migration and rollback changes".into(),
));
}
Ok(())
}
}
@@ -0,0 +1,43 @@
use sea_orm_migration::sea_orm::DbBackend;
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table("fruit")
.col(pk_auto("id"))
.col(string("name"))
.col(integer("cake_id"))
.foreign_key(
ForeignKey::create()
.name("fk-fruit-cake_id")
.from("fruit", "cake_id")
.to("cake", "id"),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
if manager.get_database_backend() != DbBackend::Sqlite {
manager
.drop_foreign_key(
ForeignKey::drop()
.table("fruit")
.name("fk-fruit-cake_id")
.to_owned(),
)
.await?;
}
manager
.drop_table(Table::drop().table("fruit").to_owned())
.await
}
}
@@ -0,0 +1,49 @@
use sea_orm_migration::prelude::*;
use sea_orm_migration::sea_orm::{entity::*, query::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
cake::ActiveModel {
name: Set("Cheesecake".to_owned()),
..Default::default()
}
.insert(db)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
cake::Entity::delete_many()
.filter(cake::Column::Name.eq("Cheesecake"))
.exec(db)
.await?;
Ok(())
}
}
mod cake {
use sea_orm_migration::sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
@@ -0,0 +1,47 @@
use sea_orm_migration::prelude::{sea_query::extension::postgres::Type, *};
use sea_orm_migration::sea_orm::{ConnectionTrait, DbBackend};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
if db.get_database_backend() == DbBackend::Postgres {
manager
.create_type(
Type::create()
.as_enum(Tea::Enum)
.values([Tea::EverydayTea, Tea::BreakfastTea])
.to_owned(),
)
.await?;
}
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
if db.get_database_backend() == DbBackend::Postgres {
manager
.drop_type(Type::drop().name(Tea::Enum).to_owned())
.await?;
}
Ok(())
}
}
#[derive(DeriveIden)]
pub enum Tea {
#[sea_orm(iden = "tea")]
Enum,
#[sea_orm(iden = "EverydayTea")]
EverydayTea,
#[sea_orm(iden = "BreakfastTea")]
BreakfastTea,
}
@@ -0,0 +1,30 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let insert = Query::insert()
.into_table("cake")
.columns(["name"])
.values_panic(["Tiramisu".into()])
.to_owned();
manager.execute(insert).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let delete = Query::delete()
.from_table("cake")
.and_where(Expr::col("name").eq("Tiramisu"))
.to_owned();
manager.execute(delete).await?;
Ok(())
}
}
@@ -0,0 +1,63 @@
use sea_orm_migration::prelude::*;
use sea_orm_migration::sea_orm::{entity::*, query::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
let transaction = db.begin().await?;
cake::ActiveModel {
name: Set("Cheesecake".to_owned()),
..Default::default()
}
.insert(&transaction)
.await?;
if std::env::var_os("ABORT_MIGRATION").eq(&Some("YES".into())) {
return Err(DbErr::Migration(
"Abort migration and rollback changes".into(),
));
}
transaction.commit().await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
let transaction = db.begin().await?;
cake::Entity::delete_many()
.filter(cake::Column::Name.eq("Cheesecake"))
.exec(&transaction)
.await?;
transaction.commit().await?;
Ok(())
}
}
mod cake {
use sea_orm_migration::sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
@@ -0,0 +1,63 @@
use sea_orm_migration::prelude::*;
use sea_orm_migration::schema::*;
use sea_orm_migration::sea_orm::DbBackend;
pub struct Migration {
pub use_transaction: Option<bool>,
pub should_fail: bool,
}
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20250101_000001_create_test_table"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
fn use_transaction(&self) -> Option<bool> {
self.use_transaction
}
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let expect_txn = self
.use_transaction
.unwrap_or(manager.get_database_backend() == DbBackend::Postgres);
assert_eq!(
manager.get_connection().is_transaction(),
expect_txn,
"up: expected is_transaction() = {expect_txn}"
);
manager
.create_table(
Table::create()
.table("test_table")
.col(pk_auto("id"))
.col(string("name"))
.to_owned(),
)
.await?;
if self.should_fail {
return Err(DbErr::Migration("intentional failure".into()));
}
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let expect_txn = self
.use_transaction
.unwrap_or(manager.get_database_backend() == DbBackend::Postgres);
assert_eq!(
manager.get_connection().is_transaction(),
expect_txn,
"down: expected is_transaction() = {expect_txn}"
);
manager
.drop_table(Table::drop().table("test_table").to_owned())
.await
}
}
@@ -0,0 +1,50 @@
use sea_orm_migration::prelude::*;
use sea_orm_migration::schema::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20250101_000002_manual_transaction"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
fn use_transaction(&self) -> Option<bool> {
Some(false)
}
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
assert!(
!manager.get_connection().is_transaction(),
"outer manager should not be in a transaction"
);
let m = manager.begin().await?;
assert!(
m.get_connection().is_transaction(),
"inner manager should be in a transaction"
);
m.create_table(
Table::create()
.table("manual_txn_table")
.col(pk_auto("id"))
.col(string("name"))
.to_owned(),
)
.await?;
m.commit().await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let m = manager.begin().await?;
m.drop_table(Table::drop().table("manual_txn_table").to_owned())
.await?;
m.commit().await?;
Ok(())
}
}
@@ -0,0 +1,8 @@
pub mod m20220118_000001_create_cake_table;
pub mod m20220118_000002_create_fruit_table;
pub mod m20220118_000003_seed_cake_table;
pub mod m20220118_000004_create_tea_enum;
pub mod m20220923_000001_seed_cake_table;
pub mod m20230109_000001_seed_cake_table;
pub mod m20250101_000001_create_test_table;
pub mod m20250101_000002_manual_transaction;
@@ -0,0 +1,18 @@
use crate::common::migration::*;
use sea_orm_migration::prelude::*;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220118_000001_create_cake_table::Migration),
Box::new(m20220118_000002_create_fruit_table::Migration),
Box::new(m20220118_000003_seed_cake_table::Migration),
Box::new(m20220118_000004_create_tea_enum::Migration),
Box::new(m20220923_000001_seed_cake_table::Migration),
Box::new(m20230109_000001_seed_cake_table::Migration),
]
}
}
@@ -0,0 +1,4 @@
pub mod default;
pub mod override_migration_table_name;
pub mod transaction_test;
pub mod with_self;
@@ -0,0 +1,22 @@
use crate::common::migration::*;
use sea_orm_migration::prelude::*;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220118_000001_create_cake_table::Migration),
Box::new(m20220118_000002_create_fruit_table::Migration),
Box::new(m20220118_000003_seed_cake_table::Migration),
Box::new(m20220118_000004_create_tea_enum::Migration),
Box::new(m20220923_000001_seed_cake_table::Migration),
Box::new(m20230109_000001_seed_cake_table::Migration),
]
}
fn migration_table_name() -> sea_orm::DynIden {
"override_migration_table_name".into_iden()
}
}
@@ -0,0 +1,26 @@
use crate::common::migration::*;
use sea_orm_migration::{MigratorTraitSelf, prelude::*};
pub struct Migrator {
pub use_transaction: Option<bool>,
pub should_fail: bool,
}
#[async_trait::async_trait]
impl MigratorTraitSelf for Migrator {
fn migrations(&self) -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(m20250101_000001_create_test_table::Migration {
use_transaction: self.use_transaction,
should_fail: self.should_fail,
})]
}
}
pub struct ManualTxnMigrator;
#[async_trait::async_trait]
impl MigratorTrait for ManualTxnMigrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(m20250101_000002_manual_transaction::Migration)]
}
}
@@ -0,0 +1,20 @@
use crate::common::migration::*;
use sea_orm_migration::{MigratorTraitSelf, prelude::*};
pub struct Migrator {
pub i: i32,
}
#[async_trait::async_trait]
impl MigratorTraitSelf for Migrator {
fn migrations(&self) -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220118_000001_create_cake_table::Migration),
Box::new(m20220118_000002_create_fruit_table::Migration),
Box::new(m20220118_000003_seed_cake_table::Migration),
Box::new(m20220118_000004_create_tea_enum::Migration),
Box::new(m20220923_000001_seed_cake_table::Migration),
Box::new(m20230109_000001_seed_cake_table::Migration),
]
}
}
@@ -0,0 +1,2 @@
pub mod migration;
pub mod migrator;
+375
View File
@@ -0,0 +1,375 @@
mod common;
use common::migrator::*;
use sea_orm::{ConnectOptions, ConnectionTrait, Database, DbBackend, DbErr, Statement};
use sea_orm_migration::{MigratorTraitSelf, migrator::MigrationStatus, prelude::*};
#[tokio::test]
async fn main() -> Result<(), DbErr> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_test_writer()
.init();
let url = &std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set");
run_migration(url, default::Migrator, "sea_orm_migration", "public").await?;
run_migration(
url,
default::Migrator,
"sea_orm_migration_schema",
"my_schema",
)
.await?;
run_migration(
url,
with_self::Migrator { i: 12 },
"sea_orm_migration_self",
"public",
)
.await?;
run_migration(
url,
override_migration_table_name::Migrator,
"sea_orm_migration_table_name",
"public",
)
.await?;
run_migration(
url,
override_migration_table_name::Migrator,
"sea_orm_migration_table_name_schema",
"my_schema",
)
.await?;
run_transaction_test(url, "sea_orm_migration_txn", "public").await?;
Ok(())
}
async fn create_db(
url: &str,
db_name: &str,
schema: &str,
) -> Result<sea_orm::DatabaseConnection, DbErr> {
let db_connect = |url: String| async {
let connect_options = ConnectOptions::new(url)
.set_schema_search_path(format!("{schema},public"))
.to_owned();
Database::connect(connect_options).await
};
let db = db_connect(url.to_owned()).await?;
match db.get_database_backend() {
DbBackend::MySql => {
db.execute_raw(Statement::from_string(
db.get_database_backend(),
format!("CREATE DATABASE IF NOT EXISTS `{db_name}`;"),
))
.await?;
let url = format!("{url}/{db_name}");
db_connect(url).await
}
DbBackend::Postgres => {
db.execute_raw(Statement::from_string(
db.get_database_backend(),
format!("DROP DATABASE IF EXISTS \"{db_name}\";"),
))
.await?;
db.execute_raw(Statement::from_string(
db.get_database_backend(),
format!("CREATE DATABASE \"{db_name}\";"),
))
.await?;
let url = format!("{url}/{db_name}");
let db = db_connect(url).await?;
db.execute_raw(Statement::from_string(
db.get_database_backend(),
format!("CREATE SCHEMA IF NOT EXISTS \"{schema}\";"),
))
.await?;
Ok(db)
}
DbBackend::Sqlite => Ok(db),
db => Err(DbErr::BackendNotSupported {
db: db.as_str(),
ctx: "create_db",
}),
}
}
async fn run_migration<M>(url: &str, migrator: M, db_name: &str, schema: &str) -> Result<(), DbErr>
where
M: MigratorTraitSelf,
{
let db = &create_db(url, db_name, schema).await?;
let manager = SchemaManager::new(db);
println!("\nMigrator::status");
migrator.status(db).await?;
println!("\nMigrator::install");
migrator.install(db).await?;
let migration_table_name = migrator.migration_table_name().to_string();
let migration_table_name = migration_table_name.as_str();
assert!(manager.has_table(migration_table_name).await?);
if migration_table_name != "seaql_migrations" {
assert!(!manager.has_table("seaql_migrations").await?);
}
println!("\nMigrator::reset");
migrator.reset(db).await?;
assert!(!manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::up");
migrator.up(db, Some(0)).await?;
assert!(!manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::up");
migrator.up(db, Some(1)).await?;
println!("\nMigrator::get_pending_migrations");
let migrations = migrator.get_pending_migrations(db).await?;
assert_eq!(migrations.len(), 5);
let migration = migrations.get(0).unwrap();
assert_eq!(migration.name(), "m20220118_000002_create_fruit_table");
assert_eq!(migration.status(), MigrationStatus::Pending);
assert!(manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::down");
migrator.down(db, Some(0)).await?;
assert!(manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::down");
migrator.down(db, Some(1)).await?;
assert!(!manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
// Tests rolling back a failing migration on Postgres.
// With per-migration transactions, only the failing migration is rolled back;
// earlier migrations that committed successfully are preserved.
if matches!(db.get_database_backend(), DbBackend::Postgres) {
println!("\nRoll back changes when encounter errors");
// Set a flag to throw error inside `m20230109_000001_seed_cake_table.rs`
unsafe {
std::env::set_var("ABORT_MIGRATION", "YES");
}
// Should throw an error
println!("\nMigrator::up");
assert_eq!(
migrator.up(db, None).await,
Err(DbErr::Migration(
"Abort migration and rollback changes".into()
))
);
println!("\nMigrator::status");
migrator.status(db).await?;
// Only the failing migration (m20230109) is rolled back;
// earlier migrations (cake, fruit, etc.) committed successfully
assert!(manager.has_table("cake").await?);
assert!(manager.has_table("fruit").await?);
// Unset the flag
unsafe {
std::env::remove_var("ABORT_MIGRATION");
}
}
println!("\nMigrator::up");
migrator.up(db, None).await?;
println!("\nMigrator::get_applied_migrations");
let migrations = migrator.get_applied_migrations(db).await?;
assert_eq!(migrations.len(), 6);
assert!(!manager.has_index("cake", "non_existent_index").await?);
assert!(manager.has_index("cake", "cake_name_index").await?);
let migration = migrations.get(0).unwrap();
assert_eq!(migration.name(), "m20220118_000001_create_cake_table");
assert_eq!(migration.status(), MigrationStatus::Applied);
println!("\nMigrator::status");
migrator.status(db).await?;
assert!(manager.has_table("cake").await?);
assert!(manager.has_table("fruit").await?);
assert!(manager.has_column("cake", "name").await?);
assert!(manager.has_column("fruit", "cake_id").await?);
// Tests rolling back a failing migration-down on Postgres.
// With per-migration transactions, rollbacks happen one at a time in reverse.
// Migrations 6-2 roll back and commit successfully. Migration 1 (drops cake
// then ABORTs) fails, so its DROP is restored. But migration 2's DROP of
// the fruit table already committed.
if matches!(db.get_database_backend(), DbBackend::Postgres) {
println!("\nRoll back changes when encounter errors");
// Set a flag to throw error inside `m20220118_000001_create_cake_table.rs`
unsafe {
std::env::set_var("ABORT_MIGRATION", "YES");
}
// Should throw an error
println!("\nMigrator::down");
assert_eq!(
migrator.down(db, None).await,
Err(DbErr::Migration(
"Abort migration and rollback changes".into()
))
);
println!("\nMigrator::status");
migrator.status(db).await?;
// Only migration 1's down was rolled back (cake table restored).
// Migrations 2-6 were rolled back successfully (fruit table dropped).
assert!(manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
// Unset the flag
unsafe {
std::env::remove_var("ABORT_MIGRATION");
}
}
println!("\nMigrator::down");
migrator.down(db, None).await?;
assert!(manager.has_table(migration_table_name).await?);
if migration_table_name != "seaql_migrations" {
assert!(!manager.has_table("seaql_migrations").await?);
}
assert!(!manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::fresh");
migrator.fresh(db).await?;
assert!(manager.has_table("cake").await?);
assert!(manager.has_table("fruit").await?);
println!("\nMigrator::refresh");
migrator.refresh(db).await?;
assert!(manager.has_table("cake").await?);
assert!(manager.has_table("fruit").await?);
println!("\nMigrator::reset");
migrator.reset(db).await?;
assert!(!manager.has_table("cake").await?);
assert!(!manager.has_table("fruit").await?);
println!("\nMigrator::status");
migrator.status(db).await?;
Ok(())
}
async fn run_transaction_test(url: &str, db_name: &str, schema: &str) -> Result<(), DbErr> {
let db = &create_db(url, db_name, schema).await?;
let backend = db.get_database_backend();
let manager = SchemaManager::new(db);
// use_transaction = None: Postgres wraps by default, others don't.
// The assertion happens inside the migration's up()/down() body.
println!("\nTransaction test: use_transaction = None");
let m = transaction_test::Migrator {
use_transaction: None,
should_fail: false,
};
m.up(db, None).await?;
assert!(manager.has_table("test_table").await?);
m.down(db, None).await?;
assert!(!manager.has_table("test_table").await?);
m.reset(db).await.ok();
// use_transaction = Some(true): forces transaction on every backend.
println!("\nTransaction test: use_transaction = Some(true)");
let m = transaction_test::Migrator {
use_transaction: Some(true),
should_fail: false,
};
m.up(db, None).await?;
assert!(manager.has_table("test_table").await?);
m.down(db, None).await?;
assert!(!manager.has_table("test_table").await?);
m.reset(db).await.ok();
// use_transaction = Some(false): disables transaction, including on Postgres.
println!("\nTransaction test: use_transaction = Some(false)");
let m = transaction_test::Migrator {
use_transaction: Some(false),
should_fail: false,
};
m.up(db, None).await?;
assert!(manager.has_table("test_table").await?);
m.down(db, None).await?;
assert!(!manager.has_table("test_table").await?);
m.reset(db).await.ok();
// Failure with transaction: DDL rolled back (except MySQL which auto-commits DDL).
println!("\nTransaction test: failure with transaction");
let m = transaction_test::Migrator {
use_transaction: Some(true),
should_fail: true,
};
assert!(m.up(db, None).await.is_err());
if backend != DbBackend::MySql {
assert!(
!manager.has_table("test_table").await?,
"DDL should be rolled back"
);
}
m.reset(db).await.ok();
// Failure without transaction: DDL persists.
println!("\nTransaction test: failure without transaction");
let m = transaction_test::Migrator {
use_transaction: Some(false),
should_fail: true,
};
assert!(m.up(db, None).await.is_err());
assert!(manager.has_table("test_table").await?, "DDL should persist");
db.execute_unprepared("DROP TABLE IF EXISTS test_table")
.await?;
m.reset(db).await.ok();
// Manual transaction via manager.begin() / commit().
println!("\nTransaction test: manual begin/commit");
let m = transaction_test::ManualTxnMigrator;
m.up(db, None).await?;
assert!(manager.has_table("manual_txn_table").await?);
m.down(db, None).await?;
assert!(!manager.has_table("manual_txn_table").await?);
m.reset(db).await.ok();
Ok(())
}
+102
View File
@@ -0,0 +1,102 @@
mod common;
#[cfg(all(test, feature = "sqlx-postgres"))]
mod inner {
use crate::common::migrator::default::*;
use sea_orm::{ConnectOptions, ConnectionTrait, Database, DbBackend, Statement, error::DbErr};
use sea_orm_migration::prelude::*;
#[tokio::test]
async fn test_fresh_with_extension() -> Result<(), DbErr> {
let url =
&std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set");
let db_name = "test_fresh_with_extension";
let db_connect = |url: String| async {
let connect_options = ConnectOptions::new(url).to_owned();
Database::connect(connect_options).await
};
let db = db_connect(url.to_owned()).await?;
if !matches!(db.get_database_backend(), DbBackend::Postgres) {
return Ok(());
}
db.execute_unprepared(&format!(r#"DROP DATABASE IF EXISTS "{db_name}""#))
.await?;
db.execute_unprepared(&format!(r#"CREATE DATABASE "{db_name}""#))
.await?;
let url = format!("{url}/{db_name}");
let db = db_connect(url).await?;
// Create the extension, a custom type, and dependent objects.
db.execute_unprepared("CREATE EXTENSION IF NOT EXISTS citext")
.await?;
db.execute_unprepared("CREATE TYPE \"UserFruit\" AS ENUM ('Apple', 'Banana')")
.await?;
db.execute_unprepared(r#"CREATE DOMAIN "UserFruitDomain" AS "UserFruit""#)
.await?;
db.execute_unprepared(
r#"CREATE FUNCTION format_user_fruit("UserFruit") RETURNS text LANGUAGE SQL AS $$ SELECT $1::text $$"#,
)
.await?;
// Run the fresh migration
Migrator::fresh(&db).await?;
// Check that the custom type and its dependent objects were dropped,
// and the extension's type was not.
let citext_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_type WHERE typname = 'citext'"#.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());
assert_eq!(citext_exists, Some(1), "the citext type should still exist");
let user_fruit_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_type WHERE typname = 'UserFruit'"#.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());
assert_eq!(
user_fruit_exists, None,
"the UserFruit type should have been dropped"
);
let user_fruit_domain_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_type WHERE typname = 'UserFruitDomain'"#.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());
assert_eq!(
user_fruit_domain_exists, None,
"the dependent UserFruitDomain type should have been dropped"
);
let format_user_fruit_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_proc WHERE proname = 'format_user_fruit'"#
.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());
assert_eq!(
format_user_fruit_exists, None,
"the dependent format_user_fruit function should have been dropped"
);
Ok(())
}
}