diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 21e2bd3e..4ab12836 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -383,6 +383,14 @@ jobs: --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path sea-orm-migration/Cargo.toml + --test '*' + --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + mysql: name: MySQL needs: @@ -444,6 +452,14 @@ jobs: --test '*' --features default,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path sea-orm-migration/Cargo.toml + --test '*' + --features sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + mariadb: name: MariaDB needs: @@ -562,3 +578,11 @@ jobs: args: > --test '*' --features default,sqlx-postgres,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path sea-orm-migration/Cargo.toml + --test '*' + --features sqlx-postgres,runtime-${{ matrix.runtime }}-${{ matrix.tls }} diff --git a/Cargo.toml b/Cargo.toml index c3ea1ce8..6dc978d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index b0daaeda..67879a0a 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -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", diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -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" diff --git a/examples/actix3_example/migration/src/lib.rs b/examples/actix3_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/actix3_example/migration/src/lib.rs +++ b/examples/actix3_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs b/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/actix3_example/migration/src/main.rs b/examples/actix3_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/actix3_example/migration/src/main.rs +++ b/examples/actix3_example/migration/src/main.rs @@ -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; } diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 9db2a583..c0d21f5c 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -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", diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -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" diff --git a/examples/actix_example/migration/src/lib.rs b/examples/actix_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/actix_example/migration/src/lib.rs +++ b/examples/actix_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs b/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/actix_example/migration/src/main.rs b/examples/actix_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/actix_example/migration/src/main.rs +++ b/examples/actix_example/migration/src/main.rs @@ -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; } diff --git a/examples/actix_example/src/main.rs b/examples/actix_example/src/main.rs index 5855bbed..dd100fef 100644 --- a/examples/actix_example/src/main.rs +++ b/examples/actix_example/src/main.rs @@ -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; diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 407a6942..961eb33c 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -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", diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -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" diff --git a/examples/axum_example/migration/src/lib.rs b/examples/axum_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/axum_example/migration/src/lib.rs +++ b/examples/axum_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs b/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/axum_example/migration/src/main.rs b/examples/axum_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/axum_example/migration/src/main.rs +++ b/examples/axum_example/migration/src/main.rs @@ -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; } diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index c7e1da0a..5623ad04 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -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", diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index af696384..9c5242f8 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -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" } \ No newline at end of file +entity = { path = "../entity" } + +[dependencies.sea-orm-migration] +path = "../../../sea-orm-migration" # remove this line in your own project +version = "^0.7.0" diff --git a/examples/graphql_example/migration/src/lib.rs b/examples/graphql_example/migration/src/lib.rs index 339d693b..2c605afb 100644 --- a/examples/graphql_example/migration/src/lib.rs +++ b/examples/graphql_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::*; +pub use sea_orm_migration::prelude::*; mod m20220101_000001_create_table; diff --git a/examples/graphql_example/migration/src/m20220101_000001_create_table.rs b/examples/graphql_example/migration/src/m20220101_000001_create_table.rs index 0c754f6c..42278d2c 100644 --- a/examples/graphql_example/migration/src/m20220101_000001_create_table.rs +++ b/examples/graphql_example/migration/src/m20220101_000001_create_table.rs @@ -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; diff --git a/examples/graphql_example/migration/src/main.rs b/examples/graphql_example/migration/src/main.rs index 5a5548f8..3e249b20 100644 --- a/examples/graphql_example/migration/src/main.rs +++ b/examples/graphql_example/migration/src/main.rs @@ -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; } diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index e0ff8062..d24020b8 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -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", diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -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" diff --git a/examples/jsonrpsee_example/migration/src/lib.rs b/examples/jsonrpsee_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/jsonrpsee_example/migration/src/lib.rs +++ b/examples/jsonrpsee_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/jsonrpsee_example/migration/src/main.rs b/examples/jsonrpsee_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/jsonrpsee_example/migration/src/main.rs +++ b/examples/jsonrpsee_example/migration/src/main.rs @@ -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; } diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index e0ff8062..d24020b8 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -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", diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -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" diff --git a/examples/poem_example/migration/src/lib.rs b/examples/poem_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/poem_example/migration/src/lib.rs +++ b/examples/poem_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs b/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/poem_example/migration/src/main.rs b/examples/poem_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/poem_example/migration/src/main.rs +++ b/examples/poem_example/migration/src/main.rs @@ -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; } diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 645dbf3f..b29afd4e 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -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", diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index a5e565b7..01f5b9b7 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -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" } diff --git a/examples/rocket_example/migration/src/lib.rs b/examples/rocket_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/rocket_example/migration/src/lib.rs +++ b/examples/rocket_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs b/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/rocket_example/migration/src/main.rs b/examples/rocket_example/migration/src/main.rs index e47018eb..4626e82f 100644 --- a/examples/rocket_example/migration/src/main.rs +++ b/examples/rocket_example/migration/src/main.rs @@ -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; } diff --git a/examples/tonic_example/Cargo.toml b/examples/tonic_example/Cargo.toml index cded186c..64620a1c 100644 --- a/examples/tonic_example/Cargo.toml +++ b/examples/tonic_example/Cargo.toml @@ -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" diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 178dcbec..7b430b07 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -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", diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 89174a73..4ffcdd4e 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -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" diff --git a/examples/tonic_example/migration/src/lib.rs b/examples/tonic_example/migration/src/lib.rs index 3679d81f..af8d9b2a 100644 --- a/examples/tonic_example/migration/src/lib.rs +++ b/examples/tonic_example/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220120_000001_create_post_table; diff --git a/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs b/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs index 0fe872c4..097c2267 100644 --- a/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs @@ -1,5 +1,5 @@ use entity::post::*; -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/examples/tonic_example/migration/src/main.rs b/examples/tonic_example/migration/src/main.rs index 7e5e996d..c6b6e48d 100644 --- a/examples/tonic_example/migration/src/main.rs +++ b/examples/tonic_example/migration/src/main.rs @@ -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; } diff --git a/examples/tonic_example/src/server.rs b/examples/tonic_example/src/server.rs index 6e3d2632..c51ebb10 100644 --- a/examples/tonic_example/src/server.rs +++ b/examples/tonic_example/src/server.rs @@ -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}; diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 0efea548..6bfb65a9 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -14,7 +14,6 @@ categories = [ "database" ] keywords = ["async", "orm", "mysql", "postgres", "sqlite"] default-run = "sea-orm-cli" - [lib] name = "sea_orm_cli" path = "src/lib.rs" @@ -22,26 +21,20 @@ path = "src/lib.rs" [[bin]] name = "sea-orm-cli" path = "src/bin/main.rs" +required-features = ["codegen"] [[bin]] name = "sea" path = "src/bin/sea.rs" +required-features = ["codegen"] [dependencies] 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 = [ - "debug-print", - "sqlx-mysql", - "sqlx-sqlite", - "sqlx-postgres", - "discovery", - "writer", - "migration", -] } -sqlx = { version = "^0.5", default-features = false, features = [ "mysql", "postgres" ] } +sea-orm-codegen = { version = "^0.7.0", path = "../sea-orm-codegen", optional = true } +sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "master" } +sqlx = { version = "^0.5", default-features = false, features = [ "mysql", "postgres" ], optional = true } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing = { version = "0.1" } url = "^2.2" @@ -50,28 +43,11 @@ url = "^2.2" smol = "1.2.5" [features] -default = [ "runtime-async-std-native-tls" ] -runtime-actix-native-tls = [ - "sqlx/runtime-actix-native-tls", - "sea-schema/runtime-actix-native-tls", -] -runtime-async-std-native-tls = [ - "sqlx/runtime-async-std-native-tls", - "sea-schema/runtime-async-std-native-tls", -] -runtime-tokio-native-tls = [ - "sqlx/runtime-tokio-native-tls", - "sea-schema/runtime-tokio-native-tls", -] -runtime-actix-rustls = [ - "sqlx/runtime-actix-rustls", - "sea-schema/runtime-actix-rustls", -] -runtime-async-std-rustls = [ - "sqlx/runtime-async-std-rustls", - "sea-schema/runtime-async-std-rustls", -] -runtime-tokio-rustls = [ - "sqlx/runtime-tokio-rustls", - "sea-schema/runtime-tokio-rustls", -] +default = [ "codegen", "runtime-async-std-native-tls" ] +codegen = [ "sea-schema/sqlx-all", "sea-orm-codegen" ] +runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls", "sea-schema/runtime-actix-native-tls" ] +runtime-async-std-native-tls = [ "sqlx/runtime-async-std-native-tls", "sea-schema/runtime-async-std-native-tls" ] +runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls", "sea-schema/runtime-tokio-native-tls" ] +runtime-actix-rustls = [ "sqlx/runtime-actix-rustls", "sea-schema/runtime-actix-rustls" ] +runtime-async-std-rustls = [ "sqlx/runtime-async-std-rustls", "sea-schema/runtime-async-std-rustls" ] +runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls", "sea-schema/runtime-tokio-rustls" ] diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index e8e22a1f..362e4914 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -1,3 +1,4 @@ +use crate::migration::get_subcommands; use clap::{App, AppSettings, Arg, SubCommand}; pub fn build_cli() -> App<'static, 'static> { @@ -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_subcommands() { migrate_subcommands = migrate_subcommands.subcommand(subcommand.arg(arg_migration_dir.clone())); } diff --git a/sea-orm-cli/src/commands.rs b/sea-orm-cli/src/commands.rs index 6a071fc2..c9a1ef00 100644 --- a/sea-orm-cli/src/commands.rs +++ b/sea-orm-cli/src/commands.rs @@ -117,7 +117,7 @@ pub async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box { - use sea_schema::sqlite::SchemaDiscovery; + use sea_schema::sqlite::discovery::SchemaDiscovery; use sqlx::Sqlite; let connection = connect::(max_connections, url.as_str()).await?; diff --git a/sea-orm-cli/src/lib.rs b/sea-orm-cli/src/lib.rs index 366b8fa6..d334c9d6 100644 --- a/sea-orm-cli/src/lib.rs +++ b/sea-orm-cli/src/lib.rs @@ -1,5 +1,8 @@ pub mod cli; +#[cfg(feature = "codegen")] pub mod commands; +pub mod migration; pub use cli::*; +#[cfg(feature = "codegen")] pub use commands::*; diff --git a/sea-orm-cli/src/migration.rs b/sea-orm-cli/src/migration.rs new file mode 100644 index 00000000..41cd507b --- /dev/null +++ b/sea-orm-cli/src/migration.rs @@ -0,0 +1,49 @@ +use clap::{App, AppSettings, Arg, SubCommand}; + +pub 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_subcommands() { + app = app.subcommand(subcommand); + } + app +} + +pub fn get_subcommands() -> Vec> { + vec![ + SubCommand::with_name("fresh") + .about("Drop all tables from the database, then reapply all migrations"), + SubCommand::with_name("refresh") + .about("Rollback all applied migrations, then reapply all migrations"), + SubCommand::with_name("reset").about("Rollback all applied migrations"), + SubCommand::with_name("status").about("Check the status of all migrations"), + SubCommand::with_name("up") + .about("Apply pending migrations") + .arg( + Arg::with_name("NUM_MIGRATION") + .long("num") + .short("n") + .help("Number of pending migrations to be applied") + .takes_value(true), + ), + SubCommand::with_name("down") + .about("Rollback applied migrations") + .arg( + Arg::with_name("NUM_MIGRATION") + .long("num") + .short("n") + .help("Number of pending migrations to be rolled back") + .takes_value(true) + .default_value("1"), + ), + ] +} diff --git a/sea-orm-cli/template/migration/_Cargo.toml b/sea-orm-cli/template/migration/_Cargo.toml index 73d34bfa..4ffcdd4e 100644 --- a/sea-orm-cli/template/migration/_Cargo.toml +++ b/sea-orm-cli/template/migration/_Cargo.toml @@ -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" diff --git a/sea-orm-cli/template/migration/src/lib.rs b/sea-orm-cli/template/migration/src/lib.rs index 3cabe22c..2c605afb 100644 --- a/sea-orm-cli/template/migration/src/lib.rs +++ b/sea-orm-cli/template/migration/src/lib.rs @@ -1,4 +1,4 @@ -pub use sea_schema::migration::prelude::*; +pub use sea_orm_migration::prelude::*; mod m20220101_000001_create_table; diff --git a/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs b/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs index a5907a28..b4dc891e 100644 --- a/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs +++ b/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs @@ -1,4 +1,4 @@ -use sea_schema::migration::prelude::*; +use sea_orm_migration::prelude::*; pub struct Migration; diff --git a/sea-orm-cli/template/migration/src/main.rs b/sea-orm-cli/template/migration/src/main.rs index 89b349c9..c6b6e48d 100644 --- a/sea-orm-cli/template/migration/src/main.rs +++ b/sea-orm-cli/template/migration/src/main.rs @@ -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; } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index e1d408db..88b2ed86 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -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", diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml new file mode 100644 index 00000000..5b4ed006 --- /dev/null +++ b/sea-orm-migration/Cargo.toml @@ -0,0 +1,40 @@ +[workspace] +# A separate workspace + +[package] +name = "sea-orm-migration" +version = "0.7.2" +authors = [ "Billy Chan " ] +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] +async-std = { version = "^1", features = ["attributes", "tokio1"] } +async-trait = { version = "^0.1" } +clap = { version = "^2.33" } +dotenv = { version = "^0.15" } +sea-orm = { path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { path = "../sea-orm-cli", default-features = false } +sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "master" } +tracing = { version = "0.1", features = ["log"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[features] +sqlx-mysql = ["sea-orm/sqlx-mysql"] +sqlx-postgres = ["sea-orm/sqlx-postgres"] +sqlx-sqlite = ["sea-orm/sqlx-sqlite"] +runtime-actix-native-tls = [ "sea-orm/runtime-actix-native-tls" ] +runtime-async-std-native-tls = [ "sea-orm/runtime-async-std-native-tls" ] +runtime-tokio-native-tls = [ "sea-orm/runtime-tokio-native-tls" ] +runtime-actix-rustls = [ "sea-orm/runtime-actix-rustls" ] +runtime-async-std-rustls = [ "sea-orm/runtime-async-std-rustls" ] +runtime-tokio-rustls = [ "sea-orm/runtime-tokio-rustls" ] \ No newline at end of file diff --git a/sea-orm-migration/README.md b/sea-orm-migration/README.md new file mode 100644 index 00000000..87a41232 --- /dev/null +++ b/sea-orm-migration/README.md @@ -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 + ``` + diff --git a/sea-orm-migration/src/cli.rs b/sea-orm-migration/src/cli.rs new file mode 100644 index 00000000..c00e4824 --- /dev/null +++ b/sea-orm-migration/src/cli.rs @@ -0,0 +1,83 @@ +use clap::App; +use dotenv::dotenv; +use std::{fmt::Display, process::exit}; +use tracing_subscriber::{prelude::*, EnvFilter}; + +use sea_orm::{Database, DbConn}; +use sea_orm_cli::build_cli; + +use super::MigratorTrait; + +pub async fn run_cli(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; +} + +pub async fn get_matches(_: 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_schema::migration=info", + (_, Some(args)) => match args.is_present("VERBOSE") { + true => { + verbose = true; + "debug" + } + false => "sea_schema::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 handle_error(error: E) +where + E: Display, +{ + eprintln!("{}", error); + exit(1); +} diff --git a/sea-orm-migration/src/lib.rs b/sea-orm-migration/src/lib.rs new file mode 100644 index 00000000..a77f6d2f --- /dev/null +++ b/sea-orm-migration/src/lib.rs @@ -0,0 +1,28 @@ +pub mod cli; +pub mod manager; +pub mod migrator; +pub mod prelude; +pub mod seaql_migrations; + +pub use manager::*; +pub use migrator::*; + +pub use async_std; +pub use async_trait; +pub use sea_orm; +pub use sea_orm::sea_query; +pub use sea_orm::DbErr; + +pub trait MigrationName { + fn name(&self) -> &str; +} + +/// The migration definition +#[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>; +} diff --git a/sea-orm-migration/src/manager.rs b/sea-orm-migration/src/manager.rs new file mode 100644 index 00000000..ef0f5f38 --- /dev/null +++ b/sea-orm-migration/src/manager.rs @@ -0,0 +1,133 @@ +use sea_orm::sea_query::{ + extension::postgres::{TypeAlterStatement, TypeCreateStatement, TypeDropStatement}, + Alias, Expr, ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, + IndexDropStatement, Query, TableAlterStatement, TableCreateStatement, TableDropStatement, + TableRenameStatement, TableTruncateStatement, +}; +use sea_orm::{Condition, ConnectionTrait, DbBackend, DbConn, DbErr, Statement, StatementBuilder}; +use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite}; + +/// Helper struct for writing migration scripts in migration file +pub struct SchemaManager<'c> { + conn: &'c DbConn, +} + +impl<'c> SchemaManager<'c> { + pub fn new(conn: &'c DbConn) -> Self { + Self { conn } + } + + pub async fn exec_stmt(&self, stmt: S) -> Result<(), DbErr> + where + S: StatementBuilder, + { + let builder = self.conn.get_database_backend(); + self.conn.execute(builder.build(&stmt)).await.map(|_| ()) + } + + pub fn get_database_backend(&self) -> DbBackend { + self.conn.get_database_backend() + } + + pub fn get_connection(&self) -> &'c DbConn { + self.conn + } +} + +/// Schema Creation +impl<'c> SchemaManager<'c> { + pub async fn create_table(&self, stmt: TableCreateStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn create_index(&self, stmt: IndexCreateStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn create_foreign_key(&self, stmt: ForeignKeyCreateStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn create_type(&self, stmt: TypeCreateStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } +} + +/// Schema Mutation +impl<'c> SchemaManager<'c> { + pub async fn alter_table(&self, stmt: TableAlterStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn drop_table(&self, stmt: TableDropStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn rename_table(&self, stmt: TableRenameStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn truncate_table(&self, stmt: TableTruncateStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn drop_index(&self, stmt: IndexDropStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn drop_foreign_key(&self, stmt: ForeignKeyDropStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn alter_type(&self, stmt: TypeAlterStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } + + pub async fn drop_type(&self, stmt: TypeDropStatement) -> Result<(), DbErr> { + self.exec_stmt(stmt).await + } +} + +/// Schema Inspection +impl<'c> SchemaManager<'c> { + pub async fn has_table(&self, table: T) -> Result + where + T: AsRef, + { + let stmt = match self.conn.get_database_backend() { + DbBackend::MySql => MySql::has_table(table), + DbBackend::Postgres => Postgres::has_table(table), + DbBackend::Sqlite => Sqlite::has_table(table), + }; + + let builder = self.conn.get_database_backend(); + let res = self + .conn + .query_one(builder.build(&stmt)) + .await? + .ok_or_else(|| DbErr::Custom("Failed to check table exists".to_owned()))?; + + Ok(res.try_get("", "has_table")?) + } + + pub async fn has_column(&self, table: T, column: C) -> Result + where + T: AsRef, + C: AsRef, + { + let stmt = match self.conn.get_database_backend() { + DbBackend::MySql => MySql::has_column(table, column), + DbBackend::Postgres => Postgres::has_column(table, column), + DbBackend::Sqlite => Sqlite::has_column(table, column), + }; + + let builder = self.conn.get_database_backend(); + let res = self + .conn + .query_one(builder.build(&stmt)) + .await? + .ok_or_else(|| DbErr::Custom("Failed to check column exists".to_owned()))?; + + Ok(res.try_get("", "has_column")?) + } +} diff --git a/sea-orm-migration/src/migrator.rs b/sea-orm-migration/src/migrator.rs new file mode 100644 index 00000000..f5e12b05 --- /dev/null +++ b/sea-orm-migration/src/migrator.rs @@ -0,0 +1,308 @@ +use std::fmt::Display; +use std::time::SystemTime; +use tracing::info; + +use sea_orm::sea_query::{Alias, Expr, ForeignKey, Query, SelectStatement, SimpleExpr, Table}; +use sea_orm::{ + ActiveModelTrait, ActiveValue, ColumnTrait, Condition, ConnectionTrait, DbBackend, DbConn, + DbErr, EntityTrait, QueryFilter, QueryOrder, Schema, Statement, +}; +use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite}; + +use super::{seaql_migrations, MigrationTrait, SchemaManager}; + +#[derive(Debug, PartialEq)] +/// Status of migration +pub enum MigrationStatus { + /// Not yet applied + Pending, + /// Applied + Applied, +} + +pub struct Migration { + migration: Box, + 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>; + + /// Get list of migrations wrapped in `Migration` struct + fn get_migration_files() -> Vec { + 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, 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, 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, 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, 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) -> 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) -> 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 { + match db.get_database_backend() { + DbBackend::MySql => MySql::query_tables(), + DbBackend::Postgres => Postgres::query_tables(), + DbBackend::Sqlite => Sqlite::query_tables(), + } +} + +pub(crate) fn get_current_schema(db: &DbConn) -> SimpleExpr { + match db.get_database_backend() { + DbBackend::MySql => MySql::get_current_schema(), + DbBackend::Postgres => Postgres::get_current_schema(), + DbBackend::Sqlite => unimplemented!(), + } +} diff --git a/sea-orm-migration/src/prelude.rs b/sea-orm-migration/src/prelude.rs new file mode 100644 index 00000000..21122408 --- /dev/null +++ b/sea-orm-migration/src/prelude.rs @@ -0,0 +1,11 @@ +pub use sea_orm_cli::migration as cli; + +pub use super::manager::SchemaManager; +pub use super::migrator::MigratorTrait; +pub use super::{MigrationName, MigrationTrait}; +pub use async_std; +pub use async_trait; +pub use sea_orm; +pub use sea_orm::sea_query; +pub use sea_orm::sea_query::*; +pub use sea_orm::DbErr; diff --git a/sea-orm-migration/src/seaql_migrations.rs b/sea-orm-migration/src/seaql_migrations.rs new file mode 100644 index 00000000..43f044ec --- /dev/null +++ b/sea-orm-migration/src/seaql_migrations.rs @@ -0,0 +1,14 @@ +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 {} diff --git a/sea-orm-migration/tests/main.rs b/sea-orm-migration/tests/main.rs new file mode 100644 index 00000000..13f0b404 --- /dev/null +++ b/sea-orm-migration/tests/main.rs @@ -0,0 +1,121 @@ +mod migrator; +use migrator::Migrator; + +use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement}; +use sea_orm_migration::prelude::*; + +#[async_std::test] +async fn main() -> Result<(), DbErr> { + let url = std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set"); + let db_name = "sea_orm_migration"; + let db = Database::connect(&url).await?; + let db = &match db.get_database_backend() { + DbBackend::MySql => { + db.execute(Statement::from_string( + db.get_database_backend(), + format!("CREATE DATABASE IF NOT EXISTS `{}`;", db_name), + )) + .await?; + + let url = format!("{}/{}", url, db_name); + Database::connect(&url).await? + } + DbBackend::Postgres => { + db.execute(Statement::from_string( + db.get_database_backend(), + format!("DROP DATABASE IF EXISTS \"{}\";", db_name), + )) + .await?; + db.execute(Statement::from_string( + db.get_database_backend(), + format!("CREATE DATABASE \"{}\";", db_name), + )) + .await?; + + let url = format!("{}/{}", url, db_name); + Database::connect(&url).await? + } + DbBackend::Sqlite => db, + }; + let manager = SchemaManager::new(db); + + println!("\nMigrator::status"); + Migrator::status(db).await?; + + println!("\nMigrator::install"); + Migrator::install(db).await?; + + 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?; + + 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?); + + println!("\nMigrator::up"); + Migrator::up(db, None).await?; + + 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?); + + println!("\nMigrator::down"); + Migrator::down(db, None).await?; + + 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(()) +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs b/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs new file mode 100644 index 00000000..bc70ba3b --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs @@ -0,0 +1,43 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000001_create_cake_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Cake::Table) + .col( + ColumnDef::new(Cake::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Cake::Name).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Cake::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum Cake { + Table, + Id, + Name, +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs b/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs new file mode 100644 index 00000000..238e4af3 --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs @@ -0,0 +1,63 @@ +use super::m20220118_000001_create_cake_table::Cake; +use sea_orm::DbBackend; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000002_create_fruit_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Fruit::Table) + .col( + ColumnDef::new(Fruit::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Fruit::Name).string().not_null()) + .col(ColumnDef::new(Fruit::CakeId).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-fruit-cake_id") + .from(Fruit::Table, Fruit::CakeId) + .to(Cake::Table, 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::Table) + .name("fk-fruit-cake_id") + .to_owned(), + ) + .await?; + } + manager + .drop_table(Table::drop().table(Fruit::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum Fruit { + Table, + Id, + Name, + CakeId, +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs b/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs new file mode 100644 index 00000000..03ba594c --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs @@ -0,0 +1,52 @@ +use sea_orm::{entity::prelude::*, Set}; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000003_seed_cake_table" + } +} + +#[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 + .map(|_| ()) + } + + 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 + .map(|_| ()) + } +} + +mod cake { + use sea_orm::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, 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 {} +} diff --git a/sea-orm-migration/tests/migrator/mod.rs b/sea-orm-migration/tests/migrator/mod.rs new file mode 100644 index 00000000..fdb92d9e --- /dev/null +++ b/sea-orm-migration/tests/migrator/mod.rs @@ -0,0 +1,18 @@ +use sea_orm_migration::prelude::*; + +mod m20220118_000001_create_cake_table; +mod m20220118_000002_create_fruit_table; +mod m20220118_000003_seed_cake_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + 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), + ] + } +} diff --git a/src/error.rs b/src/error.rs index 273b9c9d..3e6753c6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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), } } }