Restructure SeaORM migration
Bump codegen's sea-query version [cli] Update CLI subcommand method Move migration utility into sea-orm-migration
This commit is contained in:
parent
86e7e808b3
commit
498c0154ca
@ -44,7 +44,7 @@ once_cell = "1.8"
|
||||
[dev-dependencies]
|
||||
smol = { version = "^1.2" }
|
||||
smol-potat = { version = "^1.1" }
|
||||
async-std = { version = "^1.9", features = ["attributes", "tokio1"] }
|
||||
async-std = { version = "^1", features = ["attributes", "tokio1"] }
|
||||
tokio = { version = "^1.6", features = ["full"] }
|
||||
actix-rt = { version = "2.2.0" }
|
||||
maplit = { version = "^1" }
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use actix_files::Files as Fs;
|
||||
use actix_web::{
|
||||
error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer, Result
|
||||
error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer, Result,
|
||||
};
|
||||
|
||||
use entity::post;
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ serde = { version = "1", features = ["derive"] }
|
||||
version = "^3.0.38"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,6 +9,9 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
dotenv = "0.15.0"
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220101_000001_create_table;
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
use entity::{
|
||||
note,
|
||||
sea_orm::{DbBackend, EntityTrait, Schema},
|
||||
};
|
||||
use sea_schema::migration::{
|
||||
sea_query::*,
|
||||
*,
|
||||
};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@ -22,5 +21,5 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ rocket = { version = "0.5.0-rc.1", features = [
|
||||
] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,6 +9,9 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
rocket = { version = "0.5.0-rc.1" }
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
@ -14,5 +13,5 @@ async fn main() {
|
||||
std::env::set_var(key, database_url);
|
||||
}
|
||||
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ tonic = "0.7"
|
||||
tokio = { version = "1.17", features = ["macros", "rt-multi-thread", "full"] }
|
||||
entity = { path = "entity" }
|
||||
migration = { path = "migration" }
|
||||
sea-orm = { version = "0.7.1", features = [ "sqlx-postgres", "runtime-tokio-rustls", "macros" ] }
|
||||
prost = "0.10.0"
|
||||
serde = "1.0"
|
||||
|
||||
|
@ -12,7 +12,7 @@ path = "src/lib.rs"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dependencies.sea-orm]
|
||||
# path = "../../../" # remove this line in your own project
|
||||
path = "../../../" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
features = [
|
||||
"macros",
|
||||
|
@ -9,5 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220120_000001_create_post_table;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use entity::post::*;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use sea_orm_tonic_example::post::{
|
||||
|
||||
use entity::{
|
||||
post::{self, Entity as PostEntity},
|
||||
sea_orm::{entity::*, query::*, DatabaseConnection},
|
||||
sea_orm::{self, entity::*, query::*, DatabaseConnection},
|
||||
};
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
|
||||
|
@ -32,7 +32,7 @@ clap = { version = "^2.33.3" }
|
||||
dotenv = { version = "^0.15" }
|
||||
async-std = { version = "^1.9", features = [ "attributes", "tokio1" ] }
|
||||
sea-orm-codegen = { version = "^0.7.0", path = "../sea-orm-codegen" }
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [
|
||||
sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "dump-sea-orm-dep", default-features = false, features = [
|
||||
"debug-print",
|
||||
"sqlx-mysql",
|
||||
"sqlx-sqlite",
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clap::{App, AppSettings, Arg, SubCommand};
|
||||
use sea_schema::get_cli_subcommands;
|
||||
|
||||
pub fn build_cli() -> App<'static, 'static> {
|
||||
let entity_subcommand = SubCommand::with_name("generate")
|
||||
@ -95,7 +96,7 @@ pub fn build_cli() -> App<'static, 'static> {
|
||||
.arg(arg_migration_dir.clone()),
|
||||
)
|
||||
.arg(arg_migration_dir.clone());
|
||||
for subcommand in sea_schema::migration::get_subcommands() {
|
||||
for subcommand in get_cli_subcommands!() {
|
||||
migrate_subcommands =
|
||||
migrate_subcommands.subcommand(subcommand.arg(arg_migration_dir.clone()));
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ name = "migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-schema = { version = "^0.7.0", default-features = false, features = [ "migration", "debug-print" ] }
|
||||
entity = { path = "../entity" }
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||
version = "^0.7.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use sea_schema::migration::prelude::*;
|
||||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220101_000001_create_table;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::prelude::*;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
cli::run_cli(migration::Migrator).await;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ name = "sea_orm_codegen"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-query = { version = "0.22.0" }
|
||||
sea-query = { version = "0.24.0" }
|
||||
syn = { version = "^1", default-features = false, features = [
|
||||
"derive",
|
||||
"parsing",
|
||||
|
28
sea-orm-migration/Cargo.toml
Normal file
28
sea-orm-migration/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[workspace]
|
||||
# A separate workspace
|
||||
|
||||
[package]
|
||||
name = "sea-orm-migration"
|
||||
version = "0.7.2"
|
||||
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
|
||||
edition = "2021"
|
||||
description = "Migration utility for SeaORM"
|
||||
license = "MIT OR Apache-2.0"
|
||||
documentation = "https://docs.rs/sea-orm"
|
||||
repository = "https://github.com/SeaQL/sea-orm"
|
||||
categories = [ "database" ]
|
||||
keywords = ["async", "orm", "mysql", "postgres", "sqlite"]
|
||||
|
||||
[lib]
|
||||
name = "sea_orm_migration"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
sea-orm = { path = "../", default-features = false, features = ["macros"] }
|
||||
sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "dump-sea-orm-dep", features = ["migration"], default-features = false }
|
||||
async-std = { version = "^1", features = ["attributes", "tokio1"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
async-trait = { version = "^0.1" }
|
||||
dotenv = { version = "^0.15" }
|
||||
clap = { version = "^2.33" }
|
77
sea-orm-migration/README.md
Normal file
77
sea-orm-migration/README.md
Normal file
@ -0,0 +1,77 @@
|
||||
# SeaORM CLI
|
||||
|
||||
Install and Usage:
|
||||
|
||||
```sh
|
||||
> cargo install sea-orm-cli
|
||||
> sea-orm-cli help
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```sh
|
||||
> cargo install --bin sea
|
||||
> sea help
|
||||
```
|
||||
|
||||
Getting Help:
|
||||
|
||||
```sh
|
||||
cargo run -- -h
|
||||
```
|
||||
|
||||
## Running Entity Generator:
|
||||
|
||||
```sh
|
||||
# MySQL (`--database-schema` option is ignored)
|
||||
cargo run -- generate entity -u mysql://sea:sea@localhost/bakery -o out
|
||||
|
||||
# SQLite (`--database-schema` option is ignored)
|
||||
cargo run -- generate entity -u sqlite://bakery.db -o out
|
||||
|
||||
# PostgreSQL
|
||||
cargo run -- generate entity -u postgres://sea:sea@localhost/bakery -s public -o out
|
||||
```
|
||||
|
||||
## Running Migration:
|
||||
|
||||
- Initialize migration directory
|
||||
```sh
|
||||
cargo run -- migrate init
|
||||
```
|
||||
- Apply all pending migrations
|
||||
```sh
|
||||
cargo run -- migrate
|
||||
```
|
||||
```sh
|
||||
cargo run -- migrate up
|
||||
```
|
||||
- Apply first 10 pending migrations
|
||||
```sh
|
||||
cargo run -- migrate up -n 10
|
||||
```
|
||||
- Rollback last applied migrations
|
||||
```sh
|
||||
cargo run -- migrate down
|
||||
```
|
||||
- Rollback last 10 applied migrations
|
||||
```sh
|
||||
cargo run -- migrate down -n 10
|
||||
```
|
||||
- Drop all tables from the database, then reapply all migrations
|
||||
```sh
|
||||
cargo run -- migrate fresh
|
||||
```
|
||||
- Rollback all applied migrations, then reapply all migrations
|
||||
```sh
|
||||
cargo run -- migrate refresh
|
||||
```
|
||||
- Rollback all applied migrations
|
||||
```sh
|
||||
cargo run -- migrate reset
|
||||
```
|
||||
- Check the status of all migrations
|
||||
```sh
|
||||
cargo run -- migrate status
|
||||
```
|
||||
|
103
sea-orm-migration/src/cli.rs
Normal file
103
sea-orm-migration/src/cli.rs
Normal file
@ -0,0 +1,103 @@
|
||||
//! Run migrator CLI
|
||||
|
||||
use crate::MigratorTrait;
|
||||
use clap::{App, AppSettings, Arg};
|
||||
use dotenv::dotenv;
|
||||
use sea_orm::{Database, DbConn};
|
||||
use sea_schema::get_cli_subcommands;
|
||||
use std::{fmt::Display, process::exit};
|
||||
use tracing_subscriber::{prelude::*, EnvFilter};
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Migrator CLI application
|
||||
pub async fn run_cli<M>(migrator: M)
|
||||
where
|
||||
M: MigratorTrait,
|
||||
{
|
||||
dotenv().ok();
|
||||
let url = std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set");
|
||||
let db = &Database::connect(&url).await.unwrap();
|
||||
let app = build_cli();
|
||||
get_matches(migrator, db, app).await;
|
||||
}
|
||||
|
||||
async fn get_matches<M>(_: M, db: &DbConn, app: App<'static, 'static>)
|
||||
where
|
||||
M: MigratorTrait,
|
||||
{
|
||||
let matches = app.get_matches();
|
||||
let mut verbose = false;
|
||||
let filter = match matches.subcommand() {
|
||||
(_, None) => "sea_orm::migration=info",
|
||||
(_, Some(args)) => match args.is_present("VERBOSE") {
|
||||
true => {
|
||||
verbose = true;
|
||||
"debug"
|
||||
}
|
||||
false => "sea_orm::migration=info",
|
||||
},
|
||||
};
|
||||
let filter_layer = EnvFilter::try_new(filter).unwrap();
|
||||
if verbose {
|
||||
let fmt_layer = tracing_subscriber::fmt::layer();
|
||||
tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(fmt_layer)
|
||||
.init()
|
||||
} else {
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
.with_target(false)
|
||||
.with_level(false)
|
||||
.without_time();
|
||||
tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(fmt_layer)
|
||||
.init()
|
||||
};
|
||||
match matches.subcommand() {
|
||||
("fresh", _) => M::fresh(db).await,
|
||||
("refresh", _) => M::refresh(db).await,
|
||||
("reset", _) => M::reset(db).await,
|
||||
("status", _) => M::status(db).await,
|
||||
("up", None) => M::up(db, None).await,
|
||||
("down", None) => M::down(db, Some(1)).await,
|
||||
("up", Some(args)) => {
|
||||
let str = args.value_of("NUM_MIGRATION").unwrap_or_default();
|
||||
let steps = str.parse().ok();
|
||||
M::up(db, steps).await
|
||||
}
|
||||
("down", Some(args)) => {
|
||||
let str = args.value_of("NUM_MIGRATION").unwrap();
|
||||
let steps = str.parse().ok().unwrap_or(1);
|
||||
M::down(db, Some(steps)).await
|
||||
}
|
||||
_ => M::up(db, None).await,
|
||||
}
|
||||
.unwrap_or_else(handle_error);
|
||||
}
|
||||
|
||||
fn build_cli() -> App<'static, 'static> {
|
||||
let mut app = App::new("sea-schema-migration")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.setting(AppSettings::VersionlessSubcommands)
|
||||
.arg(
|
||||
Arg::with_name("VERBOSE")
|
||||
.long("verbose")
|
||||
.short("v")
|
||||
.help("Show debug messages")
|
||||
.takes_value(false)
|
||||
.global(true),
|
||||
);
|
||||
for subcommand in get_cli_subcommands!() {
|
||||
app = app.subcommand(subcommand);
|
||||
}
|
||||
app
|
||||
}
|
||||
|
||||
fn handle_error<E>(error: E)
|
||||
where
|
||||
E: Display,
|
||||
{
|
||||
eprintln!("{}", error);
|
||||
exit(1);
|
||||
}
|
48
sea-orm-migration/src/connection.rs
Normal file
48
sea-orm-migration/src/connection.rs
Normal file
@ -0,0 +1,48 @@
|
||||
//! Manage migration connection
|
||||
|
||||
use crate::{into_migration_db_backend, into_migration_err, into_orm_stmt, QueryResult};
|
||||
use sea_orm::ConnectionTrait;
|
||||
use sea_schema::migration::{self, MigrationErr, QueryResultTrait};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DatabaseConnection<'c> {
|
||||
pub(crate) conn: &'c sea_orm::DatabaseConnection,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl migration::ConnectionTrait for DatabaseConnection<'_> {
|
||||
fn get_database_backend(&self) -> migration::DbBackend {
|
||||
into_migration_db_backend(ConnectionTrait::get_database_backend(self.conn))
|
||||
}
|
||||
|
||||
async fn execute(&self, stmt: migration::Statement) -> Result<(), MigrationErr> {
|
||||
ConnectionTrait::execute(self.conn, into_orm_stmt(stmt))
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| MigrationErr(e.to_string()))
|
||||
}
|
||||
|
||||
async fn query_one(
|
||||
&self,
|
||||
stmt: migration::Statement,
|
||||
) -> Result<Option<Box<dyn QueryResultTrait>>, MigrationErr> {
|
||||
ConnectionTrait::query_one(self.conn, into_orm_stmt(stmt))
|
||||
.await
|
||||
.map(|res| res.map(|res| Box::new(QueryResult { res }) as Box<dyn QueryResultTrait>))
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
|
||||
async fn query_all(
|
||||
&self,
|
||||
stmt: migration::Statement,
|
||||
) -> Result<Vec<Box<dyn QueryResultTrait>>, MigrationErr> {
|
||||
ConnectionTrait::query_all(self.conn, into_orm_stmt(stmt))
|
||||
.await
|
||||
.map(|rows| {
|
||||
rows.into_iter()
|
||||
.map(|res| Box::new(QueryResult { res }) as Box<dyn QueryResultTrait>)
|
||||
.collect()
|
||||
})
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
}
|
19
sea-orm-migration/src/database.rs
Normal file
19
sea-orm-migration/src/database.rs
Normal file
@ -0,0 +1,19 @@
|
||||
//! Convert database backend
|
||||
|
||||
use sea_schema::migration;
|
||||
|
||||
pub(crate) fn into_orm_db_backend(db_backend: migration::DbBackend) -> sea_orm::DbBackend {
|
||||
match db_backend {
|
||||
migration::DbBackend::MySql => sea_orm::DbBackend::MySql,
|
||||
migration::DbBackend::Postgres => sea_orm::DbBackend::Postgres,
|
||||
migration::DbBackend::Sqlite => sea_orm::DbBackend::Sqlite,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_migration_db_backend(db_backend: sea_orm::DbBackend) -> migration::DbBackend {
|
||||
match db_backend {
|
||||
sea_orm::DbBackend::MySql => migration::DbBackend::MySql,
|
||||
sea_orm::DbBackend::Postgres => migration::DbBackend::Postgres,
|
||||
sea_orm::DbBackend::Sqlite => migration::DbBackend::Sqlite,
|
||||
}
|
||||
}
|
11
sea-orm-migration/src/error.rs
Normal file
11
sea-orm-migration/src/error.rs
Normal file
@ -0,0 +1,11 @@
|
||||
//! Convert migration error
|
||||
|
||||
use sea_schema::migration;
|
||||
|
||||
pub(crate) fn into_orm_db_err(err: migration::MigrationErr) -> sea_orm::DbErr {
|
||||
sea_orm::DbErr::Migration(err.to_string())
|
||||
}
|
||||
|
||||
pub(crate) fn into_migration_err(err: sea_orm::DbErr) -> migration::MigrationErr {
|
||||
migration::MigrationErr(err.to_string())
|
||||
}
|
36
sea-orm-migration/src/lib.rs
Normal file
36
sea-orm-migration/src/lib.rs
Normal file
@ -0,0 +1,36 @@
|
||||
pub mod cli;
|
||||
pub mod connection;
|
||||
pub mod database;
|
||||
pub mod error;
|
||||
pub mod manager;
|
||||
pub mod migrator;
|
||||
pub mod prelude;
|
||||
pub mod query;
|
||||
pub mod seaql_migrations;
|
||||
pub mod statement;
|
||||
|
||||
pub use manager::*;
|
||||
pub use migrator::*;
|
||||
|
||||
use connection::*;
|
||||
use database::*;
|
||||
use error::*;
|
||||
use query::*;
|
||||
use statement::*;
|
||||
use sea_orm::DbErr;
|
||||
|
||||
/// Define the name of a migration
|
||||
pub trait MigrationName {
|
||||
/// Get migration name
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// Define the actions of a migration
|
||||
#[async_trait::async_trait]
|
||||
pub trait MigrationTrait: MigrationName + Send + Sync {
|
||||
/// Define actions to perform when applying the migration
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr>;
|
||||
|
||||
/// Define actions to perform when rolling back the migration
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr>;
|
||||
}
|
163
sea-orm-migration/src/manager.rs
Normal file
163
sea-orm-migration/src/manager.rs
Normal file
@ -0,0 +1,163 @@
|
||||
//! Schema manager
|
||||
|
||||
use crate::{into_orm_db_err, DatabaseConnection};
|
||||
use sea_orm::sea_query::{
|
||||
extension::postgres::{TypeAlterStatement, TypeCreateStatement, TypeDropStatement},
|
||||
ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, IndexDropStatement,
|
||||
TableAlterStatement, TableCreateStatement, TableDropStatement, TableRenameStatement,
|
||||
TableTruncateStatement,
|
||||
};
|
||||
use sea_orm::{ConnectionTrait, DbBackend, DbConn, DbErr, StatementBuilder};
|
||||
use sea_schema::migration;
|
||||
|
||||
/// Helper struct for writing migration scripts in migration file
|
||||
#[derive(Debug)]
|
||||
pub struct SchemaManager<'c> {
|
||||
conn: DatabaseConnection<'c>,
|
||||
}
|
||||
|
||||
impl<'c> SchemaManager<'c> {
|
||||
/// Initialize [`SchemaManager`]
|
||||
pub fn new(conn: &'c DbConn) -> Self {
|
||||
Self {
|
||||
conn: DatabaseConnection { conn },
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute any statement that implemented [`StatementBuilder`]
|
||||
pub async fn exec_stmt<S>(&self, stmt: S) -> Result<(), DbErr>
|
||||
where
|
||||
S: StatementBuilder,
|
||||
{
|
||||
let builder = self.conn.conn.get_database_backend();
|
||||
self.conn
|
||||
.conn
|
||||
.execute(builder.build(&stmt))
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Get database backend
|
||||
pub fn get_database_backend(&self) -> DbBackend {
|
||||
self.conn.conn.get_database_backend()
|
||||
}
|
||||
|
||||
/// Borrow database connection
|
||||
pub fn get_connection(&self) -> &'c DbConn {
|
||||
self.conn.conn
|
||||
}
|
||||
}
|
||||
|
||||
/// Schema Creation
|
||||
impl<'c> SchemaManager<'c> {
|
||||
/// Create table
|
||||
pub async fn create_table(&self, stmt: TableCreateStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::create_table(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Create index
|
||||
pub async fn create_index(&self, stmt: IndexCreateStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::create_index(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Create foreign key
|
||||
pub async fn create_foreign_key(&self, stmt: ForeignKeyCreateStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::create_foreign_key(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Create type
|
||||
pub async fn create_type(&self, stmt: TypeCreateStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::create_type(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Schema Mutation
|
||||
impl<'c> SchemaManager<'c> {
|
||||
/// Alter table
|
||||
pub async fn alter_table(&self, stmt: TableAlterStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::alter_table(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Drop table
|
||||
pub async fn drop_table(&self, stmt: TableDropStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::drop_table(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Rename table
|
||||
pub async fn rename_table(&self, stmt: TableRenameStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::rename_table(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Truncate table
|
||||
pub async fn truncate_table(&self, stmt: TableTruncateStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::truncate_table(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Drop index
|
||||
pub async fn drop_index(&self, stmt: IndexDropStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::drop_index(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Drop foreign key
|
||||
pub async fn drop_foreign_key(&self, stmt: ForeignKeyDropStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::drop_foreign_key(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Alter type
|
||||
pub async fn alter_type(&self, stmt: TypeAlterStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::alter_type(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Drop type
|
||||
pub async fn drop_type(&self, stmt: TypeDropStatement) -> Result<(), DbErr> {
|
||||
migration::SchemaManager::drop_type(stmt, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Schema Inspection
|
||||
impl<'c> SchemaManager<'c> {
|
||||
/// Check if a table exists in the database
|
||||
pub async fn has_table<T>(&self, table: T) -> Result<bool, DbErr>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
migration::SchemaManager::has_table(table, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
|
||||
/// Check if a column exists in a specific database table
|
||||
pub async fn has_column<T, C>(&self, table: T, column: C) -> Result<bool, DbErr>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
C: AsRef<str>,
|
||||
{
|
||||
migration::SchemaManager::has_column(table, column, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
}
|
||||
}
|
338
sea-orm-migration/src/migrator.rs
Normal file
338
sea-orm-migration/src/migrator.rs
Normal file
@ -0,0 +1,338 @@
|
||||
//! Migration executor
|
||||
|
||||
use crate::{seaql_migrations, MigrationTrait, SchemaManager};
|
||||
use sea_orm::sea_query::{
|
||||
Alias, Expr, ForeignKey, IntoTableRef, Query, SelectStatement, SimpleExpr, Table,
|
||||
};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ActiveValue, ColumnTrait, Condition, ConnectionTrait, DbBackend, DbConn,
|
||||
DbErr, EntityTrait, QueryFilter, QueryOrder, Schema, Statement,
|
||||
};
|
||||
use std::fmt::Display;
|
||||
use std::time::SystemTime;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
/// Status of migration
|
||||
pub enum MigrationStatus {
|
||||
/// Not yet applied
|
||||
Pending,
|
||||
/// Applied
|
||||
Applied,
|
||||
}
|
||||
|
||||
/// Wrapper of [`MigrationTrait`] with migration status
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Migration {
|
||||
migration: Box<dyn MigrationTrait>,
|
||||
status: MigrationStatus,
|
||||
}
|
||||
|
||||
impl Display for MigrationStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let status = match self {
|
||||
MigrationStatus::Pending => "Pending",
|
||||
MigrationStatus::Applied => "Applied",
|
||||
};
|
||||
write!(f, "{}", status)
|
||||
}
|
||||
}
|
||||
|
||||
/// Performing migrations on a database
|
||||
#[async_trait::async_trait]
|
||||
pub trait MigratorTrait: Send {
|
||||
/// Vector of migrations in time sequence
|
||||
fn migrations() -> Vec<Box<dyn MigrationTrait>>;
|
||||
|
||||
/// Get list of migrations wrapped in `Migration` struct
|
||||
fn get_migration_files() -> Vec<Migration> {
|
||||
Self::migrations()
|
||||
.into_iter()
|
||||
.map(|migration| Migration {
|
||||
migration,
|
||||
status: MigrationStatus::Pending,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get list of applied migrations from database
|
||||
async fn get_migration_models(db: &DbConn) -> Result<Vec<seaql_migrations::Model>, DbErr> {
|
||||
Self::install(db).await?;
|
||||
seaql_migrations::Entity::find()
|
||||
.order_by_asc(seaql_migrations::Column::Version)
|
||||
.all(db)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Get list of migrations with status
|
||||
async fn get_migration_with_status(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
|
||||
Self::install(db).await?;
|
||||
let mut migration_files = Self::get_migration_files();
|
||||
let migration_models = Self::get_migration_models(db).await?;
|
||||
for (i, migration_model) in migration_models.into_iter().enumerate() {
|
||||
if let Some(migration_file) = migration_files.get_mut(i) {
|
||||
if migration_file.migration.name() == migration_model.version.as_str() {
|
||||
migration_file.status = MigrationStatus::Applied;
|
||||
} else {
|
||||
return Err(DbErr::Custom(format!("Migration mismatch: applied migration != migration file, '{0}' != '{1}'\nMigration '{0}' has been applied but its corresponding migration file is missing.", migration_file.migration.name(), migration_model.version)));
|
||||
}
|
||||
} else {
|
||||
return Err(DbErr::Custom(format!("Migration file of version '{}' is missing, this migration has been applied but its file is missing", migration_model.version)));
|
||||
}
|
||||
}
|
||||
Ok(migration_files)
|
||||
}
|
||||
|
||||
/// Get list of pending migrations
|
||||
async fn get_pending_migrations(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
|
||||
Self::install(db).await?;
|
||||
Ok(Self::get_migration_with_status(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|file| file.status == MigrationStatus::Pending)
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Get list of applied migrations
|
||||
async fn get_applied_migrations(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
|
||||
Self::install(db).await?;
|
||||
Ok(Self::get_migration_with_status(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|file| file.status == MigrationStatus::Applied)
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Create migration table `seaql_migrations` in the database
|
||||
async fn install(db: &DbConn) -> Result<(), DbErr> {
|
||||
let builder = db.get_database_backend();
|
||||
let schema = Schema::new(builder);
|
||||
let mut stmt = schema.create_table_from_entity(seaql_migrations::Entity);
|
||||
stmt.if_not_exists();
|
||||
db.execute(builder.build(&stmt)).await.map(|_| ())
|
||||
}
|
||||
|
||||
/// Drop all tables from the database, then reapply all migrations
|
||||
async fn fresh(db: &DbConn) -> Result<(), DbErr> {
|
||||
Self::install(db).await?;
|
||||
let db_backend = db.get_database_backend();
|
||||
|
||||
// Temporarily disable the foreign key check
|
||||
if db_backend == DbBackend::Sqlite {
|
||||
info!("Disabling foreign key check");
|
||||
db.execute(Statement::from_string(
|
||||
db_backend,
|
||||
"PRAGMA foreign_keys = OFF".to_owned(),
|
||||
))
|
||||
.await?;
|
||||
info!("Foreign key check disabled");
|
||||
}
|
||||
|
||||
// Drop all foreign keys
|
||||
if db_backend == DbBackend::MySql {
|
||||
info!("Dropping all foreign keys");
|
||||
let mut stmt = Query::select();
|
||||
stmt.columns([Alias::new("TABLE_NAME"), Alias::new("CONSTRAINT_NAME")])
|
||||
.from((
|
||||
Alias::new("information_schema"),
|
||||
Alias::new("table_constraints"),
|
||||
))
|
||||
.cond_where(
|
||||
Condition::all()
|
||||
.add(
|
||||
Expr::expr(get_current_schema(db)).equals(
|
||||
Alias::new("table_constraints"),
|
||||
Alias::new("table_schema"),
|
||||
),
|
||||
)
|
||||
.add(Expr::expr(Expr::value("FOREIGN KEY")).equals(
|
||||
Alias::new("table_constraints"),
|
||||
Alias::new("constraint_type"),
|
||||
)),
|
||||
);
|
||||
let rows = db.query_all(db_backend.build(&stmt)).await?;
|
||||
for row in rows.into_iter() {
|
||||
let constraint_name: String = row.try_get("", "CONSTRAINT_NAME")?;
|
||||
let table_name: String = row.try_get("", "TABLE_NAME")?;
|
||||
info!(
|
||||
"Dropping foreign key '{}' from table '{}'",
|
||||
constraint_name, table_name
|
||||
);
|
||||
let mut stmt = ForeignKey::drop();
|
||||
stmt.table(Alias::new(table_name.as_str()))
|
||||
.name(constraint_name.as_str());
|
||||
db.execute(db_backend.build(&stmt)).await?;
|
||||
info!("Foreign key '{}' has been dropped", constraint_name);
|
||||
}
|
||||
info!("All foreign keys dropped");
|
||||
}
|
||||
|
||||
// Drop all tables
|
||||
let stmt = query_tables(db);
|
||||
let rows = db.query_all(db_backend.build(&stmt)).await?;
|
||||
for row in rows.into_iter() {
|
||||
let table_name: String = row.try_get("", "table_name")?;
|
||||
info!("Dropping table '{}'", table_name);
|
||||
let mut stmt = Table::drop();
|
||||
stmt.table(Alias::new(table_name.as_str()))
|
||||
.if_exists()
|
||||
.cascade();
|
||||
db.execute(db_backend.build(&stmt)).await?;
|
||||
info!("Table '{}' has been dropped", table_name);
|
||||
}
|
||||
|
||||
// Restore the foreign key check
|
||||
if db_backend == DbBackend::Sqlite {
|
||||
info!("Restoring foreign key check");
|
||||
db.execute(Statement::from_string(
|
||||
db_backend,
|
||||
"PRAGMA foreign_keys = ON".to_owned(),
|
||||
))
|
||||
.await?;
|
||||
info!("Foreign key check restored");
|
||||
}
|
||||
|
||||
// Reapply all migrations
|
||||
Self::up(db, None).await
|
||||
}
|
||||
|
||||
/// Rollback all applied migrations, then reapply all migrations
|
||||
async fn refresh(db: &DbConn) -> Result<(), DbErr> {
|
||||
Self::down(db, None).await?;
|
||||
Self::up(db, None).await
|
||||
}
|
||||
|
||||
/// Rollback all applied migrations
|
||||
async fn reset(db: &DbConn) -> Result<(), DbErr> {
|
||||
Self::down(db, None).await
|
||||
}
|
||||
|
||||
/// Check the status of all migrations
|
||||
async fn status(db: &DbConn) -> Result<(), DbErr> {
|
||||
Self::install(db).await?;
|
||||
|
||||
info!("Checking migration status");
|
||||
|
||||
for Migration { migration, status } in Self::get_migration_with_status(db).await? {
|
||||
info!("Migration '{}'... {}", migration.name(), status);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply pending migrations
|
||||
async fn up(db: &DbConn, mut steps: Option<u32>) -> Result<(), DbErr> {
|
||||
Self::install(db).await?;
|
||||
let manager = SchemaManager::new(db);
|
||||
|
||||
if let Some(steps) = steps {
|
||||
info!("Applying {} pending migrations", steps);
|
||||
} else {
|
||||
info!("Applying all pending migrations");
|
||||
}
|
||||
|
||||
let migrations = Self::get_pending_migrations(db).await?.into_iter();
|
||||
if migrations.len() == 0 {
|
||||
info!("No pending migrations");
|
||||
}
|
||||
for Migration { migration, .. } in migrations {
|
||||
if let Some(steps) = steps.as_mut() {
|
||||
if steps == &0 {
|
||||
break;
|
||||
}
|
||||
*steps -= 1;
|
||||
}
|
||||
info!("Applying migration '{}'", migration.name());
|
||||
migration.up(&manager).await?;
|
||||
info!("Migration '{}' has been applied", migration.name());
|
||||
let now = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("SystemTime before UNIX EPOCH!");
|
||||
seaql_migrations::ActiveModel {
|
||||
version: ActiveValue::Set(migration.name().to_owned()),
|
||||
applied_at: ActiveValue::Set(now.as_secs() as i64),
|
||||
}
|
||||
.insert(db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rollback applied migrations
|
||||
async fn down(db: &DbConn, mut steps: Option<u32>) -> Result<(), DbErr> {
|
||||
Self::install(db).await?;
|
||||
let manager = SchemaManager::new(db);
|
||||
|
||||
if let Some(steps) = steps {
|
||||
info!("Rolling back {} applied migrations", steps);
|
||||
} else {
|
||||
info!("Rolling back all applied migrations");
|
||||
}
|
||||
|
||||
let migrations = Self::get_applied_migrations(db).await?.into_iter().rev();
|
||||
if migrations.len() == 0 {
|
||||
info!("No applied migrations");
|
||||
}
|
||||
for Migration { migration, .. } in migrations {
|
||||
if let Some(steps) = steps.as_mut() {
|
||||
if steps == &0 {
|
||||
break;
|
||||
}
|
||||
*steps -= 1;
|
||||
}
|
||||
info!("Rolling back migration '{}'", migration.name());
|
||||
migration.down(&manager).await?;
|
||||
info!("Migration '{}' has been rollbacked", migration.name());
|
||||
seaql_migrations::Entity::delete_many()
|
||||
.filter(seaql_migrations::Column::Version.eq(migration.name()))
|
||||
.exec(db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn query_tables(db: &DbConn) -> SelectStatement {
|
||||
let mut stmt = Query::select();
|
||||
let (expr, tbl_ref, condition) = match db.get_database_backend() {
|
||||
DbBackend::MySql => (
|
||||
Expr::col(Alias::new("table_name")),
|
||||
(Alias::new("information_schema"), Alias::new("tables")).into_table_ref(),
|
||||
Condition::all().add(
|
||||
Expr::expr(get_current_schema(db))
|
||||
.equals(Alias::new("tables"), Alias::new("table_schema")),
|
||||
),
|
||||
),
|
||||
DbBackend::Postgres => (
|
||||
Expr::col(Alias::new("table_name")),
|
||||
(Alias::new("information_schema"), Alias::new("tables")).into_table_ref(),
|
||||
Condition::all()
|
||||
.add(
|
||||
Expr::expr(get_current_schema(db))
|
||||
.equals(Alias::new("tables"), Alias::new("table_schema")),
|
||||
)
|
||||
.add(Expr::col(Alias::new("table_type")).eq("BASE TABLE")),
|
||||
),
|
||||
DbBackend::Sqlite => (
|
||||
Expr::col(Alias::new("name")),
|
||||
Alias::new("sqlite_master").into_table_ref(),
|
||||
Condition::all()
|
||||
.add(Expr::col(Alias::new("type")).eq("table"))
|
||||
.add(Expr::col(Alias::new("name")).ne("sqlite_sequence")),
|
||||
),
|
||||
};
|
||||
stmt.expr_as(expr, Alias::new("table_name"))
|
||||
.from(tbl_ref)
|
||||
.cond_where(condition);
|
||||
stmt
|
||||
}
|
||||
|
||||
pub(crate) fn get_current_schema(db: &DbConn) -> SimpleExpr {
|
||||
match db.get_database_backend() {
|
||||
DbBackend::MySql => Expr::cust("DATABASE()"),
|
||||
DbBackend::Postgres => Expr::cust("CURRENT_SCHEMA()"),
|
||||
DbBackend::Sqlite => unimplemented!(),
|
||||
}
|
||||
}
|
8
sea-orm-migration/src/prelude.rs
Normal file
8
sea-orm-migration/src/prelude.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//! Import migration utility
|
||||
|
||||
pub use crate::*;
|
||||
pub use async_std;
|
||||
pub use async_trait;
|
||||
pub use sea_orm::sea_query;
|
||||
pub use sea_orm::sea_query::*;
|
||||
pub use sea_orm::DbErr;
|
20
sea-orm-migration/src/query.rs
Normal file
20
sea-orm-migration/src/query.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Get query result from db
|
||||
|
||||
use crate::into_migration_err;
|
||||
use sea_schema::migration::{self, MigrationErr};
|
||||
|
||||
pub(crate) struct QueryResult {
|
||||
pub(crate) res: sea_orm::QueryResult,
|
||||
}
|
||||
|
||||
impl migration::QueryResultTrait for QueryResult {
|
||||
fn try_get_string(&self, col: &str) -> Result<String, MigrationErr> {
|
||||
self.res
|
||||
.try_get::<String>("", col)
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
|
||||
fn try_get_i64(&self, col: &str) -> Result<i64, MigrationErr> {
|
||||
self.res.try_get::<i64>("", col).map_err(into_migration_err)
|
||||
}
|
||||
}
|
18
sea-orm-migration/src/seaql_migrations.rs
Normal file
18
sea-orm-migration/src/seaql_migrations.rs
Normal file
@ -0,0 +1,18 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! Migration entity
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "seaql_migrations")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub version: String,
|
||||
pub applied_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
12
sea-orm-migration/src/statement.rs
Normal file
12
sea-orm-migration/src/statement.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//! Convert SQL statement
|
||||
|
||||
use crate::into_orm_db_backend;
|
||||
use sea_schema::migration;
|
||||
|
||||
pub(crate) fn into_orm_stmt(stmt: migration::Statement) -> sea_orm::Statement {
|
||||
sea_orm::Statement {
|
||||
sql: stmt.sql,
|
||||
values: stmt.values,
|
||||
db_backend: into_orm_db_backend(stmt.db_backend),
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ pub enum DbErr {
|
||||
Type(String),
|
||||
/// Error occurred while parsing json value as target type
|
||||
Json(String),
|
||||
/// A migration error
|
||||
Migration(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for DbErr {}
|
||||
@ -29,6 +31,7 @@ impl std::fmt::Display for DbErr {
|
||||
Self::Custom(s) => write!(f, "Custom Error: {}", s),
|
||||
Self::Type(s) => write!(f, "Type Error: {}", s),
|
||||
Self::Json(s) => write!(f, "Json Error: {}", s),
|
||||
Self::Migration(s) => write!(f, "Migration Error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user