feat(migration): configurable migration table name (#1511)

This commit is contained in:
Billy Chan 2023-04-13 11:12:01 +08:00 committed by GitHub
parent 473b769386
commit a806898f74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 151 additions and 25 deletions

View File

@ -6,12 +6,12 @@ use std::time::SystemTime;
use tracing::info; use tracing::info;
use sea_orm::sea_query::{ use sea_orm::sea_query::{
self, extension::postgres::Type, Alias, Expr, ForeignKey, Iden, JoinType, Query, self, extension::postgres::Type, Alias, Expr, ForeignKey, Iden, IntoIden, JoinType, Order,
SelectStatement, SimpleExpr, Table, Query, SelectStatement, SimpleExpr, Table,
}; };
use sea_orm::{ use sea_orm::{
ActiveModelTrait, ActiveValue, ColumnTrait, Condition, ConnectionTrait, DbBackend, DbErr, ActiveModelTrait, ActiveValue, Condition, ConnectionTrait, DbBackend, DbErr, DynIden,
EntityTrait, QueryFilter, QueryOrder, Schema, Statement, TransactionTrait, EntityTrait, FromQueryResult, Iterable, QueryFilter, Schema, Statement, TransactionTrait,
}; };
use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite}; use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite};
@ -59,6 +59,11 @@ pub trait MigratorTrait: Send {
/// Vector of migrations in time sequence /// Vector of migrations in time sequence
fn migrations() -> Vec<Box<dyn MigrationTrait>>; fn migrations() -> Vec<Box<dyn MigrationTrait>>;
/// Name of the migration table, it is `seaql_migrations` by default
fn migration_table_name() -> DynIden {
seaql_migrations::Entity.into_iden()
}
/// Get list of migrations wrapped in `Migration` struct /// Get list of migrations wrapped in `Migration` struct
fn get_migration_files() -> Vec<Migration> { fn get_migration_files() -> Vec<Migration> {
Self::migrations() Self::migrations()
@ -76,8 +81,13 @@ pub trait MigratorTrait: Send {
C: ConnectionTrait, C: ConnectionTrait,
{ {
Self::install(db).await?; Self::install(db).await?;
seaql_migrations::Entity::find() let stmt = Query::select()
.order_by_asc(seaql_migrations::Column::Version) .table_name(Self::migration_table_name())
.columns(seaql_migrations::Column::iter().map(IntoIden::into_iden))
.order_by(seaql_migrations::Column::Version, Order::Asc)
.to_owned();
let builder = db.get_database_backend();
seaql_migrations::Model::find_by_statement(builder.build(&stmt))
.all(db) .all(db)
.await .await
} }
@ -154,7 +164,9 @@ pub trait MigratorTrait: Send {
{ {
let builder = db.get_database_backend(); let builder = db.get_database_backend();
let schema = Schema::new(builder); let schema = Schema::new(builder);
let mut stmt = schema.create_table_from_entity(seaql_migrations::Entity); let mut stmt = schema
.create_table_from_entity(seaql_migrations::Entity)
.table_name(Self::migration_table_name());
stmt.if_not_exists(); stmt.if_not_exists();
db.execute(builder.build(&stmt)).await.map(|_| ()) db.execute(builder.build(&stmt)).await.map(|_| ())
} }
@ -374,11 +386,12 @@ where
let now = SystemTime::now() let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!"); .expect("SystemTime before UNIX EPOCH!");
seaql_migrations::ActiveModel { seaql_migrations::Entity::insert(seaql_migrations::ActiveModel {
version: ActiveValue::Set(migration.name().to_owned()), version: ActiveValue::Set(migration.name().to_owned()),
applied_at: ActiveValue::Set(now.as_secs() as i64), applied_at: ActiveValue::Set(now.as_secs() as i64),
} })
.insert(db) .table_name(M::migration_table_name())
.exec(db)
.await?; .await?;
} }
@ -414,7 +427,8 @@ where
migration.down(manager).await?; migration.down(manager).await?;
info!("Migration '{}' has been rollbacked", migration.name()); info!("Migration '{}' has been rollbacked", migration.name());
seaql_migrations::Entity::delete_many() seaql_migrations::Entity::delete_many()
.filter(seaql_migrations::Column::Version.eq(migration.name())) .filter(Expr::col(seaql_migrations::Column::Version).eq(migration.name()))
.table_name(M::migration_table_name())
.exec(db) .exec(db)
.await?; .await?;
} }
@ -525,3 +539,51 @@ where
); );
stmt stmt
} }
trait QueryTable {
type Statement;
fn table_name(self, table_name: DynIden) -> Self::Statement;
}
impl QueryTable for SelectStatement {
type Statement = SelectStatement;
fn table_name(mut self, table_name: DynIden) -> SelectStatement {
self.from(table_name);
self
}
}
impl QueryTable for sea_query::TableCreateStatement {
type Statement = sea_query::TableCreateStatement;
fn table_name(mut self, table_name: DynIden) -> sea_query::TableCreateStatement {
self.table(table_name);
self
}
}
impl<A> QueryTable for sea_orm::Insert<A>
where
A: ActiveModelTrait,
{
type Statement = sea_orm::Insert<A>;
fn table_name(mut self, table_name: DynIden) -> sea_orm::Insert<A> {
sea_orm::QueryTrait::query(&mut self).into_table(table_name);
self
}
}
impl<E> QueryTable for sea_orm::DeleteMany<E>
where
E: EntityTrait,
{
type Statement = sea_orm::DeleteMany<E>;
fn table_name(mut self, table_name: DynIden) -> sea_orm::DeleteMany<E> {
sea_orm::QueryTrait::query(&mut self).from_table(table_name);
self
}
}

View File

@ -1,6 +1,7 @@
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
// One should override the name of migration table via `MigratorTrait::migration_table_name` method
#[sea_orm(table_name = "seaql_migrations")] #[sea_orm(table_name = "seaql_migrations")]
pub struct Model { pub struct Model {
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]

View File

@ -0,0 +1,6 @@
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;

View File

@ -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),
]
}
}

View File

@ -0,0 +1,2 @@
pub mod default;
pub mod override_migration_table_name;

View File

@ -1,12 +1,6 @@
use crate::common::migration::*;
use sea_orm_migration::prelude::*; use sea_orm_migration::prelude::*;
mod m20220118_000001_create_cake_table;
mod m20220118_000002_create_fruit_table;
mod m20220118_000003_seed_cake_table;
mod m20220118_000004_create_tea_enum;
mod m20220923_000001_seed_cake_table;
mod m20230109_000001_seed_cake_table;
pub struct Migrator; pub struct Migrator;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -21,4 +15,8 @@ impl MigratorTrait for Migrator {
Box::new(m20230109_000001_seed_cake_table::Migration), Box::new(m20230109_000001_seed_cake_table::Migration),
] ]
} }
fn migration_table_name() -> sea_orm::DynIden {
Alias::new("override_migration_table_name").into_iden()
}
} }

View File

@ -0,0 +1,2 @@
pub mod migration;
pub mod migrator;

View File

@ -1,6 +1,6 @@
mod migrator; mod common;
use migrator::Migrator;
use common::migrator::*;
use sea_orm::{ConnectOptions, ConnectionTrait, Database, DbBackend, DbErr, Statement}; use sea_orm::{ConnectOptions, ConnectionTrait, Database, DbBackend, DbErr, Statement};
use sea_orm_migration::{migrator::MigrationStatus, prelude::*}; use sea_orm_migration::{migrator::MigrationStatus, prelude::*};
@ -13,14 +13,42 @@ async fn main() -> Result<(), DbErr> {
let url = &std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set"); let url = &std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set");
run_migration(url, "sea_orm_migration", "public").await?; 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, "sea_orm_migration_schema", "my_schema").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?;
Ok(()) Ok(())
} }
async fn run_migration(url: &str, db_name: &str, schema: &str) -> Result<(), DbErr> { async fn run_migration<Migrator>(
url: &str,
_: Migrator,
db_name: &str,
schema: &str,
) -> Result<(), DbErr>
where
Migrator: MigratorTrait,
{
let db_connect = |url: String| async { let db_connect = |url: String| async {
let connect_options = ConnectOptions::new(url) let connect_options = ConnectOptions::new(url)
.set_schema_search_path(schema.to_owned()) .set_schema_search_path(schema.to_owned())
@ -75,7 +103,12 @@ async fn run_migration(url: &str, db_name: &str, schema: &str) -> Result<(), DbE
println!("\nMigrator::install"); println!("\nMigrator::install");
Migrator::install(db).await?; Migrator::install(db).await?;
assert!(manager.has_table("seaql_migrations").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"); println!("\nMigrator::reset");
Migrator::reset(db).await?; Migrator::reset(db).await?;
@ -192,7 +225,11 @@ async fn run_migration(url: &str, db_name: &str, schema: &str) -> Result<(), DbE
println!("\nMigrator::down"); println!("\nMigrator::down");
Migrator::down(db, None).await?; Migrator::down(db, None).await?;
assert!(manager.has_table("seaql_migrations").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("cake").await?);
assert!(!manager.has_table("fruit").await?); assert!(!manager.has_table("fruit").await?);