Implement migration

This commit is contained in:
fluo10 2025-05-10 09:11:46 +09:00
parent 94d30ae4f9
commit 4e3572ac0e
6 changed files with 379 additions and 92 deletions

View file

@ -11,9 +11,6 @@ progress-pile-migration-core = {workspace = true, features = ["client"]}
[dependencies.sea-orm-migration] [dependencies.sea-orm-migration]
version = "1.1.0" version = "1.1.0"
features = [ features = [
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. "runtime-tokio-rustls",
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. "sqlx-sqlite",
# e.g. ]
# "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature
# "sqlx-postgres", # `DATABASE_DRIVER` feature
]

View file

@ -10,3 +10,16 @@ impl MigratorTrait for Migrator {
vec![Box::new(m20220101_000001_create_table::Migration)] vec![Box::new(m20220101_000001_create_table::Migration)]
} }
} }
#[cfg(test)]
mod tests {
use sea_orm_migration::sea_orm::{ConnectOptions, Database};
use super::*;
#[async_std::test]
async fn test_migration_server() {
let db = Database::connect(ConnectOptions::new("sqlite::memory:")).await.unwrap();
Migrator::up(&db, None).await.unwrap();
Migrator::down(&db, None).await.unwrap();
}
}

View file

@ -8,15 +8,15 @@ use progress_pile_migration_core::m20220101_000001_create_table::*;
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(ProgressCategory::client_table_create_statement()).await?; ProgressCategory::up_client(manager).await?;
manager.create_table(ProgressEntry::client_table_create_statement()).await?; ProgressEntry::up_client(manager).await?;
Ok(()) Ok(())
} }
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(ProgressCategory::table_drop_statement()).await?; ProgressEntry::down_client(manager).await?;
manager.drop_table(ProgressEntry::table_drop_statement()).await?; ProgressCategory::down_client(manager).await?;
Ok(()) Ok(())
} }
} }

View file

@ -1,5 +1,71 @@
use sea_orm_migration::{prelude::*, schema::*}; use sea_orm_migration::{prelude::*, schema::*};
pub trait TableMigrator {
#[cfg(feature="server")]
async fn up_server<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.create_table(Self::table_create_statement_server()).await?;
for statement in Self::index_create_statements_server().into_iter() {
manager.create_index(statement).await?
}
Ok(())
}
#[cfg(feature="server")]
async fn down_server<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.drop_table(Self::table_drop_statement_server()).await?;
Ok(())
}
#[cfg(feature="client")]
async fn up_client<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.create_table(Self::table_create_statement_client()).await?;
for statement in Self::index_create_statements_client().into_iter() {
manager.create_index(statement).await?
}
Ok(())
}
#[cfg(feature="client")]
async fn down_client<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.drop_table(Self::table_drop_statement_client()).await?;
Ok(())
}
fn table_create_statement_default() -> TableCreateStatement;
#[cfg(feature="client")]
fn table_create_statement_client() -> TableCreateStatement {
Self::table_create_statement_default()
}
#[cfg(feature="server")]
fn table_create_statement_server() -> TableCreateStatement {
Self::table_create_statement_default()
}
fn index_create_statements_default() -> Vec<IndexCreateStatement>;
#[cfg(feature="client")]
fn index_create_statements_client() -> Vec<IndexCreateStatement> {
Self::index_create_statements_default()
}
#[cfg(feature="server")]
fn index_create_statements_server() -> Vec<IndexCreateStatement>{
Self::index_create_statements_default()
}
fn table_drop_statement_default() -> TableDropStatement;
#[cfg(feature="client")]
fn table_drop_statement_client() -> TableDropStatement {
Self::table_drop_statement_default()
}
#[cfg(feature="server")]
fn table_drop_statement_server() -> TableDropStatement {
Self::table_drop_statement_default()
}
}
#[cfg(feature="server")] #[cfg(feature="server")]
#[derive(DeriveIden)] #[derive(DeriveIden)]
@ -12,9 +78,15 @@ pub enum User {
LoginName, LoginName,
PasswordHash, PasswordHash,
} }
static IDX_USER_LOGIN_NAME: &str = "idx_user_login_name";
static IDX_USER_CREATED_AT: &str = "idx_user_created_at";
static IDX_USER_UPDATED_AT: &str = "idx_user_updated_at";
static IDX_USER_DELETED_AT: &str = "idx_user_deleted_at";
#[cfg(feature="server")] #[cfg(feature="server")]
impl User { impl TableMigrator for User {
pub fn server_table_create_statement() -> TableCreateStatement{
fn table_create_statement_default() -> TableCreateStatement{
Table::create() Table::create()
.table(Self::Table) .table(Self::Table)
.if_not_exists() .if_not_exists()
@ -24,10 +96,32 @@ impl User {
.col(timestamp_with_time_zone_null(Self::DeletedAt)) .col(timestamp_with_time_zone_null(Self::DeletedAt))
.col(string_uniq(Self::LoginName)) .col(string_uniq(Self::LoginName))
.col(string(Self::PasswordHash)) .col(string(Self::PasswordHash))
.to_owned() .to_owned()
} }
pub fn table_drop_statement() -> TableDropStatement { fn index_create_statements_default() -> Vec<IndexCreateStatement> {
vec![
Index::create().name(IDX_USER_LOGIN_NAME)
.table(Self::Table)
.col(Self::LoginName)
.to_owned(),
Index::create().name(IDX_USER_CREATED_AT)
.table(Self::Table)
.col((Self::CreatedAt, IndexOrder::Desc))
.to_owned(),
Index::create().name(IDX_USER_UPDATED_AT)
.table(Self::Table)
.col((Self::UpdatedAt, IndexOrder::Desc))
.to_owned(),
Index::create().name(IDX_USER_DELETED_AT)
.table(Self::Table)
.col((Self::DeletedAt, IndexOrder::Desc))
.to_owned(),
]
}
fn table_drop_statement_default() -> TableDropStatement {
Table::drop().table(Self::Table).to_owned() Table::drop().table(Self::Table).to_owned()
} }
} }
@ -41,26 +135,77 @@ pub enum AccessToken{
CreatedAt, CreatedAt,
UpdatedAt, UpdatedAt,
ExpiredAt, ExpiredAt,
AccessToken, TokenValue,
Note, Note,
} }
#[cfg(feature="server")] static IDX_ACCESS_TOKEN_TOKEN_VALUE: &str = "idx_access_token_token_value";
impl AccessToken { static IDX_ACCESS_TOKEN_CREATED_AT: &str = "idx_access_token_created_at";
pub fn server_table_create_statement() -> TableCreateStatement { static IDX_ACCESS_TOKEN_UPDATED_AT: &str = "idx_access_token_updated_at";
Table::create() static IDX_ACCESS_TOKEN_EXPIRED_AT: &str = "idx_access_token_expired_at";
.table(Self::Table) static IDX_ACCESS_TOKEN_USER_ID_CREATED_AT: &str = "idx_access_token_user_id_created_at";
.if_not_exists() static IDX_ACCESS_TOKEN_USER_ID_UPDATED_AT: &str = "idx_access_token_user_id_updated_at";
.col(pk_auto(Self::Id)) static IDX_ACCESS_TOKEN_USER_ID_EXPIRED_AT: &str = "idx_access_token_user_id_expired_at";
.col(timestamp_with_time_zone(Self::CreatedAt)) static FK_ACCESS_TOKEN_USER: &str = "fk_access_token_user";
.col(timestamp_with_time_zone(Self::UpdatedAt))
.col(timestamp_with_time_zone_null(Self::ExpiredAt))
.col(string(Self::AccessToken))
.col(string(Self::Note))
.to_owned()
}
pub fn table_drop_statement() -> TableDropStatement { #[cfg(feature="server")]
impl TableMigrator for AccessToken {
fn table_create_statement_default() -> TableCreateStatement {
Table::create()
.table(Self::Table)
.if_not_exists()
.col(pk_auto(Self::Id))
.col(timestamp_with_time_zone(Self::CreatedAt))
.col(timestamp_with_time_zone(Self::UpdatedAt))
.col(timestamp_with_time_zone_null(Self::ExpiredAt))
.col(string(Self::TokenValue))
.col(string(Self::Note))
.foreign_key(ForeignKey::create()
.name(FK_ACCESS_TOKEN_USER)
.from(Self::Table, Self::UserId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade)
)
.to_owned()
}
fn index_create_statements_default() -> Vec<IndexCreateStatement> {
vec![
Index::create().name(IDX_ACCESS_TOKEN_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_EXPIRED_AT)
.table(Self::Table)
.col(Self::ExpiredAt)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_TOKEN_VALUE)
.table(Self::Table)
.col(Self::TokenValue)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_UPDATED_AT)
.table(Self::Table)
.col(Self::UpdatedAt)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_USER_ID_CREATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_USER_ID_EXPIRED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::ExpiredAt)
.to_owned(),
Index::create().name(IDX_ACCESS_TOKEN_USER_ID_UPDATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::UpdatedAt)
.to_owned(),
]
}
fn table_drop_statement_default() -> TableDropStatement {
Table::drop().table(Self::Table).to_owned() Table::drop().table(Self::Table).to_owned()
} }
} }
@ -69,40 +214,103 @@ impl AccessToken {
pub enum ProgressCategory { pub enum ProgressCategory {
Table, Table,
#[cfg(feature="server")] #[cfg(feature="server")]
Id,
#[cfg(feature="server")]
UserId, UserId,
Uuid, Id,
CreatedAt, CreatedAt,
UpdatedAt, UpdatedAt,
DeletedAt, DeletedAt,
Name, Name,
} }
impl ProgressCategory { static IDX_PROGRESS_CATEGORY_NAME: &str = "idx_progress_category_name";
fn default_table_create_statement() -> TableCreateStatement { static IDX_PROGRESS_CATEGORY_CREATED_AT: &str = "idx_progress_category_created_at";
static IDX_PROGRESS_CATEGORY_UPDATED_AT: &str = "idx_progress_category_updated_at";
static IDX_PROGRESS_CATEGORY_DELETED_AT: &str = "idx_progress_category_deleted_at";
static IDX_PROGRESS_CATEGORY_USER_ID_NAME: &str = "idx_progress_category_user_id_name";
static IDX_PROGRESS_CATEGORY_USER_ID_CREATED_AT: &str = "idx_progress_category_user_id_created_at";
static IDX_PROGRESS_CATEGORY_USER_ID_UPDATED_AT: &str = "idx_progress_category_user_id_updated_at";
static IDX_PROGRESS_CATEGORY_USER_ID_DELETED_AT: &str = "idx_progress_category_user_id_deleted_at";
static FK_PROGRESS_CATEGORY_USER: &str = "fk_progress_category_user";
static PK_PROGRESS_CATEGORY: &str = "pk_progress_category";
impl TableMigrator for ProgressCategory {
fn table_create_statement_default() -> TableCreateStatement {
Table::create() Table::create()
.table(Self::Table) .table(Self::Table)
.if_not_exists() .if_not_exists()
.col(timestamp_with_time_zone(Self::CreatedAt)) .col(uuid(Self::Id))
.col(timestamp_with_time_zone(Self::UpdatedAt)) .col(string(Self::Name))
.col(timestamp_with_time_zone_null(Self::DeletedAt)) .col(timestamp_with_time_zone(Self::CreatedAt))
.to_owned() .col(timestamp_with_time_zone(Self::UpdatedAt))
.col(timestamp_with_time_zone_null(Self::DeletedAt))
.to_owned()
} }
#[cfg(feature="client")] #[cfg(feature="client")]
pub fn client_table_create_statement() -> TableCreateStatement{ fn table_create_statement_client() -> TableCreateStatement{
let mut tcs = Self::default_table_create_statement(); let mut tcs = Self::table_create_statement_default();
tcs.col(pk_uuid(Self::Uuid)) tcs.primary_key(Index::create().name(PK_PROGRESS_CATEGORY).col(Self::Id));
.col(string_uniq(Self::Name));
tcs tcs
} }
#[cfg(feature="server")] #[cfg(feature="server")]
pub fn server_table_create_statement() -> TableCreateStatement{ fn table_create_statement_server() -> TableCreateStatement{
todo!() let mut tcs = Self::table_create_statement_default();
tcs.col(integer(Self::UserId));
tcs.foreign_key(ForeignKey::create().name(FK_PROGRESS_CATEGORY_USER).from(Self::Table, Self::UserId)
.to(User::Table, User::Id));
tcs.primary_key(Index::create().name(PK_PROGRESS_CATEGORY).col(Self::UserId).col(Self::Id));
tcs
}
fn index_create_statements_default() -> Vec<IndexCreateStatement> {
vec![
Index::create().name(IDX_PROGRESS_CATEGORY_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_DELETED_AT)
.table(Self::Table)
.col(Self::DeletedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_NAME)
.table(Self::Table)
.col(Self::Name)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_UPDATED_AT)
.table(Self::Table)
.col(Self::UpdatedAt)
.to_owned(),
]
}
#[cfg(feature="server")]
fn index_create_statements_server() -> Vec<IndexCreateStatement> {
[Self::index_create_statements_default(), vec![
Index::create().name(IDX_PROGRESS_CATEGORY_USER_ID_CREATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_USER_ID_DELETED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::DeletedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_USER_ID_NAME)
.table(Self::Table)
.col(Self::UserId)
.col(Self::Name)
.to_owned(),
Index::create().name(IDX_PROGRESS_CATEGORY_USER_ID_UPDATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::UpdatedAt)
.to_owned(),
]].concat()
} }
pub fn table_drop_statement() -> TableDropStatement { fn table_drop_statement_default() -> TableDropStatement {
Table::drop().table(Self::Table).to_owned() Table::drop().table(Self::Table).to_owned()
} }
} }
@ -111,56 +319,124 @@ impl ProgressCategory {
pub enum ProgressEntry { pub enum ProgressEntry {
Table, Table,
#[cfg(feature="server")] #[cfg(feature="server")]
Id,
#[cfg(feature="server")]
UserId, UserId,
Uuid, Id,
ProgressCategoryUuid, ProgressCategoryId,
CreatedAt, CreatedAt,
UpdatedAt, UpdatedAt,
DeletedAt, DeletedAt,
EnteredAt, ProgressedAt,
Quantity, Quantity,
Note, Note,
} }
static PROGRESS_ENTRY_PROGRESS_CATEGORY_FOREIGN_KEY_NAME: &str = "fk__progress_entry__progress__category"; static IDX_PROGRESS_ENTITY_CREATED_AT: &str = "idx_progress_entity_created_at";
static IDX_PROGRESS_ENTITY_UPDATED_AT: &str = "idx_progress_entity_updated_at";
static IDX_PROGRESS_ENTITY_DELETED_AT: &str = "idx_progress_entity_deleted_at";
static IDX_PROGRESS_ENTITY_PROGRESSED_AT: &str = "idx_progress_entity_progressed_at";
static IDX_PROGRESS_ENTITY_USER_ID_CREATED_AT: &str = "idx_progress_entity_user_id_created_at";
static IDX_PROGRESS_ENTITY_USER_ID_UPDATED_AT: &str = "idx_progress_entity_user_id_updated_at";
static IDX_PROGRESS_ENTITY_USER_ID_DELETED_AT: &str = "idx_progress_entity_user_id_deleted_at";
static IDX_PROGRESS_ENTITY_USER_ID_PROGRESSED_AT: &str = "idx_progress_entity_user_id_progressed_at";
static FK_PROGRESS_ENTITY_PROGRESS_CATEGORY: &str = "fk_progress_entity_progress_category";
static FK_PROGRESS_ENTITY_USER: &str = "fk_progress_entity_user";
static PK_PROGRESS_ENTITY: &str = "pk_progress_entity";
static PROGRESS_ENTRY_PROGRESS_ENTITY_FOREIGN_KEY_NAME: &str = "fk__progress_entry__progress__category";
impl ProgressEntry { impl TableMigrator for ProgressEntry {
fn default_table_create_statement() -> TableCreateStatement {
fn table_create_statement_default() -> TableCreateStatement {
Table::create() Table::create()
.table(Self::Table) .table(Self::Table)
.if_not_exists() .if_not_exists()
.col(uuid(Self::ProgressCategoryUuid)) .col(uuid(Self::Id))
.col(timestamp_with_time_zone(Self::CreatedAt)) .col(uuid(Self::ProgressCategoryId))
.col(timestamp_with_time_zone(Self::UpdatedAt)) .col(timestamp_with_time_zone(Self::CreatedAt))
.col(timestamp_with_time_zone_null(Self::DeletedAt)) .col(timestamp_with_time_zone(Self::UpdatedAt))
.col(timestamp_with_time_zone(Self::EnteredAt)) .col(timestamp_with_time_zone_null(Self::DeletedAt))
.col(integer(Self::Quantity)) .col(timestamp_with_time_zone(Self::ProgressedAt))
.col(string(Self::Note)) .col(integer(Self::Quantity))
.col(string(Self::Note))
.to_owned()
}
#[cfg(feature="client")]
pub fn client_table_create_statement() -> TableCreateStatement{
let mut tcs = Self::default_table_create_statement();
tcs.col(pk_uuid(Self::Uuid))
.foreign_key(ForeignKey::create() .foreign_key(ForeignKey::create()
.name(PROGRESS_ENTRY_PROGRESS_CATEGORY_FOREIGN_KEY_NAME) .name(FK_PROGRESS_ENTITY_PROGRESS_CATEGORY)
.from(Self::Table, Self::ProgressCategoryUuid) .from(Self::Table, Self::ProgressCategoryId)
.to(ProgressCategory::Table, ProgressCategory::Uuid) .to(ProgressCategory::Table, ProgressCategory::Id)
.on_delete(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade)
); )
.to_owned()
}
#[cfg(feature="client")]
fn table_create_statement_client() -> TableCreateStatement{
let mut tcs: TableCreateStatement = Self::table_create_statement_default();
tcs.primary_key(Index::create().name(PK_PROGRESS_ENTITY).col(Self::Id));
tcs tcs
} }
#[cfg(feature="server")] #[cfg(feature="server")]
pub fn server_table_create_statement() -> TableCreateStatement{ fn table_create_statement_server() -> TableCreateStatement{
todo!() let mut tcs: TableCreateStatement = Self::table_create_statement_default();
tcs.primary_key(Index::create().name(PK_PROGRESS_ENTITY).col(Self::UserId).col(Self::Id));
tcs.foreign_key(ForeignKey::create()
.name(FK_PROGRESS_ENTITY_USER)
.from(Self::Table, Self::UserId)
.to(User::Table, User::Id)
);
tcs
} }
pub fn table_drop_statement() -> TableDropStatement {
fn index_create_statements_default() -> Vec<IndexCreateStatement> {
vec![
Index::create().name(IDX_PROGRESS_ENTITY_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_DELETED_AT)
.table(Self::Table)
.col(Self::DeletedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_PROGRESSED_AT)
.table(Self::Table)
.col(Self::ProgressedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_UPDATED_AT)
.table(Self::Table)
.col(Self::UpdatedAt)
.to_owned(),
]
}
#[cfg(feature="server")]
fn index_create_statements_server() -> Vec<IndexCreateStatement> {
let mut default = Self::index_create_statements_default();
default.append(&mut vec![
Index::create().name(IDX_PROGRESS_ENTITY_USER_ID_CREATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::CreatedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_USER_ID_DELETED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::DeletedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_USER_ID_PROGRESSED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::ProgressedAt)
.to_owned(),
Index::create().name(IDX_PROGRESS_ENTITY_USER_ID_UPDATED_AT)
.table(Self::Table)
.col(Self::UserId)
.col(Self::UpdatedAt)
.to_owned(),
]);
default
}
fn table_drop_statement_default() -> TableDropStatement {
Table::drop().table(Self::Table).to_owned() Table::drop().table(Self::Table).to_owned()
} }
} }

View file

@ -13,3 +13,16 @@ impl MigratorTrait for Migrator {
} }
} }
#[cfg(test)]
mod tests {
use sea_orm_migration::sea_orm::{ConnectOptions, Database};
use super::*;
#[async_std::test]
async fn test_migration_server() {
let db = Database::connect(ConnectOptions::new("sqlite::memory:")).await.unwrap();
Migrator::up(&db, None).await.unwrap();
Migrator::down(&db, None).await.unwrap();
}
}

View file

@ -7,22 +7,10 @@ use progress_pile_migration_core::m20220101_000001_create_table::*;
#[async_trait::async_trait] #[async_trait::async_trait]
impl MigrationTrait for Migration { impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
User::up_server(manager).await
manager.create_table(User::server_table_create_statement()).await?;
manager.create_table(AccessToken::server_table_create_statement()).await?;
Ok(())
} }
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
User::down_server(manager).await
manager.drop_table(User::table_drop_statement()).await?;
manager.drop_table(AccessToken::table_drop_statement()).await?;
manager.drop_table(ProgressCategory::table_drop_statement()).await?;
manager.drop_table(ProgressEntry::table_drop_statement()).await?;
Ok(())
} }
} }