From 8eb095385d76fe24c5f9c70a4cc16d9edb2e1248 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 5 Feb 2022 20:32:30 +0800 Subject: [PATCH] Migration (#335) * Refactor `ConnectionTrait` * Refactoring * Build index & foreign key statements * Fix imports * Fixup * Rocket example with migration * async-std compatible with the tokio 1.0 runtime * Use reexported dependency * Compile without selecting any db backend * Updating sea-orm-cli dep * sea-orm-cli migrate commands * cargo fmt * Test [cli] * Refactoring * Clap app name should be "sea-orm-cli" * Correctly capture MIGRATION_DIR * Rename README * Add `sea-orm-cli migrate init` command * Update README * Try restructured sea-query dependency (SeaQL/sea-schema#41) * Set `DATABASE_URL` environment variable --- Cargo.toml | 2 +- examples/basic/Cargo.toml | 2 +- examples/rocket_example/Cargo.toml | 14 +-- examples/rocket_example/README.md | 2 +- examples/rocket_example/entity/Cargo.toml | 27 ++++++ examples/rocket_example/entity/src/lib.rs | 6 ++ .../rocket_example/{ => entity}/src/post.rs | 0 examples/rocket_example/migration/Cargo.toml | 14 +++ examples/rocket_example/migration/README.md | 37 ++++++++ examples/rocket_example/migration/src/lib.rs | 12 +++ .../src/m20220120_000001_create_post_table.rs | 42 +++++++++ examples/rocket_example/migration/src/main.rs | 18 ++++ examples/rocket_example/src/main.rs | 16 ++-- examples/rocket_example/src/pool.rs | 1 + examples/rocket_example/src/setup.rs | 33 ------- issues/262/Cargo.toml | 2 +- issues/319/Cargo.toml | 2 +- sea-orm-cli/Cargo.toml | 5 +- sea-orm-cli/README.md | 45 +++++++++- sea-orm-cli/src/cli.rs | 22 ++++- sea-orm-cli/src/main.rs | 87 ++++++++++++++++++- sea-orm-cli/template/migration/Cargo.toml | 12 +++ sea-orm-cli/template/migration/README.md | 37 ++++++++ sea-orm-cli/template/migration/src/lib.rs | 12 +++ .../src/m20220101_000001_create_table.rs | 23 +++++ sea-orm-cli/template/migration/src/main.rs | 7 ++ sea-orm-codegen/Cargo.toml | 2 +- src/database/connection.rs | 38 ++++---- src/database/db_connection.rs | 24 +++-- src/database/mock.rs | 4 +- src/database/statement.rs | 4 + src/database/stream/query.rs | 2 + src/database/stream/transaction.rs | 2 + src/database/transaction.rs | 22 ++++- src/entity/active_model.rs | 8 +- src/entity/model.rs | 2 +- src/executor/delete.rs | 10 +-- src/executor/execute.rs | 4 + src/executor/insert.rs | 12 +-- src/executor/paginator.rs | 12 +-- src/executor/query.rs | 12 +++ src/executor/select.rs | 34 ++++---- src/executor/update.rs | 17 ++-- src/query/mod.rs | 3 +- 44 files changed, 548 insertions(+), 144 deletions(-) create mode 100644 examples/rocket_example/entity/Cargo.toml create mode 100644 examples/rocket_example/entity/src/lib.rs rename examples/rocket_example/{ => entity}/src/post.rs (100%) create mode 100644 examples/rocket_example/migration/Cargo.toml create mode 100644 examples/rocket_example/migration/README.md create mode 100644 examples/rocket_example/migration/src/lib.rs create mode 100644 examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs create mode 100644 examples/rocket_example/migration/src/main.rs delete mode 100644 examples/rocket_example/src/setup.rs create mode 100644 sea-orm-cli/template/migration/Cargo.toml create mode 100644 sea-orm-cli/template/migration/README.md create mode 100644 sea-orm-cli/template/migration/src/lib.rs create mode 100644 sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs create mode 100644 sea-orm-cli/template/migration/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index ea187ef8..45cc5140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ once_cell = "1.8" [dev-dependencies] smol = { version = "^1.2" } smol-potat = { version = "^1.1" } -async-std = { version = "^1.9", features = ["attributes"] } +async-std = { version = "^1.9", features = ["attributes", "tokio1"] } tokio = { version = "^1.6", features = ["full"] } actix-rt = { version = "2.2.0" } maplit = { version = "^1" } diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index bd9f0a08..9c53df39 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" publish = false [dependencies] -async-std = { version = "^1.9", features = [ "attributes" ] } +async-std = { version = "^1.9", features = [ "attributes", "tokio1" ] } sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-async-std-native-tls" ] } serde_json = { version = "^1" } futures = { version = "^0.3" } diff --git a/examples/rocket_example/Cargo.toml b/examples/rocket_example/Cargo.toml index 36eac643..7badb27c 100644 --- a/examples/rocket_example/Cargo.toml +++ b/examples/rocket_example/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" publish = false [workspace] +members = [".", "entity", "migration"] [dependencies] async-stream = { version = "^0.3" } @@ -19,18 +20,9 @@ rocket_dyn_templates = { version = "0.1.0-rc.1", features = [ "tera", ] } serde_json = { version = "^1" } - -[dependencies.sea-orm] -path = "../../" # remove this line in your own project -version = "^0.5.0" -features = ["macros", "runtime-tokio-native-tls"] -default-features = false +entity = { path = "entity" } +migration = { path = "migration" } [dependencies.sea-orm-rocket] path = "../../sea-orm-rocket/lib" # remove this line in your own project and use the git line # git = "https://github.com/SeaQL/sea-orm" - -[features] -default = ["sqlx-postgres"] -sqlx-mysql = ["sea-orm/sqlx-mysql"] -sqlx-postgres = ["sea-orm/sqlx-postgres"] diff --git a/examples/rocket_example/README.md b/examples/rocket_example/README.md index 596a763a..b3a50db6 100644 --- a/examples/rocket_example/README.md +++ b/examples/rocket_example/README.md @@ -4,7 +4,7 @@ 1. Modify the `url` var in `Rocket.toml` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `Cargo.toml` (the `default = ["sqlx-postgres"]` line) +1. Turn on the appropriate database feature for your chosen db in `entity/Cargo.toml` (the `"sqlx-postgres",` line) 1. `cargo run` to start the server diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml new file mode 100644 index 00000000..b2a52d33 --- /dev/null +++ b/examples/rocket_example/entity/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "entity" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "entity" +path = "src/lib.rs" + +[dependencies] +rocket = { version = "0.5.0-rc.1", features = [ + "json", +] } + +[dependencies.sea-orm] +# path = "../../../" # remove this line in your own project +git = "https://github.com/SeaQL/sea-orm" +branch = "migration" +version = "^0.5.0" +features = [ + "macros", + "runtime-tokio-native-tls", + "sqlx-postgres", + # "sqlx-mysql", +] +default-features = false \ No newline at end of file diff --git a/examples/rocket_example/entity/src/lib.rs b/examples/rocket_example/entity/src/lib.rs new file mode 100644 index 00000000..e15a5ab1 --- /dev/null +++ b/examples/rocket_example/entity/src/lib.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate rocket; + +pub mod post; + +pub use sea_orm; diff --git a/examples/rocket_example/src/post.rs b/examples/rocket_example/entity/src/post.rs similarity index 100% rename from examples/rocket_example/src/post.rs rename to examples/rocket_example/entity/src/post.rs diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml new file mode 100644 index 00000000..75ee4322 --- /dev/null +++ b/examples/rocket_example/migration/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +sea-schema = { git = "https://github.com/SeaQL/sea-schema.git", branch = "restructure-sea-query-dep", default-features = false, features = [ "migration", "debug-print" ] } +entity = { path = "../entity" } +rocket = { version = "0.5.0-rc.1" } diff --git a/examples/rocket_example/migration/README.md b/examples/rocket_example/migration/README.md new file mode 100644 index 00000000..963caaeb --- /dev/null +++ b/examples/rocket_example/migration/README.md @@ -0,0 +1,37 @@ +# Running Migrator CLI + +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/examples/rocket_example/migration/src/lib.rs b/examples/rocket_example/migration/src/lib.rs new file mode 100644 index 00000000..8a6746ab --- /dev/null +++ b/examples/rocket_example/migration/src/lib.rs @@ -0,0 +1,12 @@ +pub use sea_schema::migration::*; + +mod m20220120_000001_create_post_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![Box::new(m20220120_000001_create_post_table::Migration)] + } +} 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 new file mode 100644 index 00000000..f22dcdd6 --- /dev/null +++ b/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs @@ -0,0 +1,42 @@ +use entity::post::*; +use sea_schema::migration::{ + sea_query::{self, *}, + *, +}; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220120_000001_create_post_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Entity) + .if_not_exists() + .col( + ColumnDef::new(Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Column::Title).string().not_null()) + .col(ColumnDef::new(Column::Text).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Entity).to_owned()) + .await + } +} diff --git a/examples/rocket_example/migration/src/main.rs b/examples/rocket_example/migration/src/main.rs new file mode 100644 index 00000000..41cba673 --- /dev/null +++ b/examples/rocket_example/migration/src/main.rs @@ -0,0 +1,18 @@ +use migration::Migrator; +use sea_schema::migration::*; + +#[async_std::main] +async fn main() { + // Setting `DATABASE_URL` environment variable + let key = "DATABASE_URL"; + if std::env::var(key).is_err() { + // Getting the database URL from Rocket.toml if it's not set + let figment = rocket::Config::figment(); + let database_url: String = figment + .extract_inner("databases.sea_orm.url") + .expect("Cannot find Database URL in Rocket.toml"); + std::env::set_var(key, database_url); + } + + cli::run_cli(Migrator).await; +} diff --git a/examples/rocket_example/src/main.rs b/examples/rocket_example/src/main.rs index 5ec1e52f..13944e26 100644 --- a/examples/rocket_example/src/main.rs +++ b/examples/rocket_example/src/main.rs @@ -10,16 +10,16 @@ use rocket::{Build, Request, Rocket}; use rocket_dyn_templates::Template; use serde_json::json; +use entity::sea_orm; +use migration::MigratorTrait; use sea_orm::{entity::*, query::*}; use sea_orm_rocket::{Connection, Database}; mod pool; use pool::Db; -mod setup; - -mod post; -pub use post::Entity as Post; +pub use entity::post; +pub use entity::post::Entity as Post; const DEFAULT_POSTS_PER_PAGE: usize = 5; @@ -114,7 +114,7 @@ async fn list( "num_pages": num_pages, "posts": posts, "flash": flash.map(FlashMessage::into_inner), - }) + }), ) } @@ -131,7 +131,7 @@ async fn edit(conn: Connection<'_, Db>, id: i32) -> Template { "edit", json! ({ "post": post, - }) + }), ) } @@ -160,13 +160,13 @@ pub fn not_found(req: &Request<'_>) -> Template { "error/404", json! ({ "uri": req.uri() - }) + }), ) } async fn run_migrations(rocket: Rocket) -> fairing::Result { let conn = &Db::fetch(&rocket).unwrap().conn; - let _ = setup::create_post_table(conn).await; + let _ = migration::Migrator::up(conn, None).await; Ok(rocket) } diff --git a/examples/rocket_example/src/pool.rs b/examples/rocket_example/src/pool.rs index fe5f8c6e..adf5afb7 100644 --- a/examples/rocket_example/src/pool.rs +++ b/examples/rocket_example/src/pool.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use entity::sea_orm; use sea_orm::ConnectOptions; use sea_orm_rocket::{rocket::figment::Figment, Config, Database}; use std::time::Duration; diff --git a/examples/rocket_example/src/setup.rs b/examples/rocket_example/src/setup.rs deleted file mode 100644 index f5b5a99e..00000000 --- a/examples/rocket_example/src/setup.rs +++ /dev/null @@ -1,33 +0,0 @@ -use sea_orm::sea_query::{ColumnDef, TableCreateStatement}; -use sea_orm::{error::*, query::*, sea_query, DbConn, ExecResult}; - -async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result { - let builder = db.get_database_backend(); - db.execute(builder.build(stmt)).await -} - -pub async fn create_post_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() - .table(super::post::Entity) - .if_not_exists() - .col( - ColumnDef::new(super::post::Column::Id) - .integer() - .not_null() - .auto_increment() - .primary_key(), - ) - .col( - ColumnDef::new(super::post::Column::Title) - .string() - .not_null(), - ) - .col( - ColumnDef::new(super::post::Column::Text) - .string() - .not_null(), - ) - .to_owned(); - - create_table(db, &stmt).await -} diff --git a/issues/262/Cargo.toml b/issues/262/Cargo.toml index f6fad508..b397fb8a 100644 --- a/issues/262/Cargo.toml +++ b/issues/262/Cargo.toml @@ -9,4 +9,4 @@ publish = false [dependencies] sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-async-std-native-tls", "debug-print" ] } -async-std = { version = "^1", features = ["attributes"] } +async-std = { version = "^1", features = ["attributes", "tokio1"] } diff --git a/issues/319/Cargo.toml b/issues/319/Cargo.toml index 3eb4adaa..b4e054c8 100644 --- a/issues/319/Cargo.toml +++ b/issues/319/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" publish = false [dependencies] -async-std = { version = "^1", features = ["attributes"] } +async-std = { version = "^1", features = ["attributes", "tokio1"] } serde = { version = "^1", features = ["derive"] } sea-orm = { path = "../../", features = [ "sqlx-mysql", diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 4c1dbb9c..2e2a6158 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -20,15 +20,16 @@ path = "src/main.rs" [dependencies] clap = { version = "^2.33.3" } dotenv = { version = "^0.15" } -async-std = { version = "^1.9", features = [ "attributes" ] } +async-std = { version = "^1.9", features = [ "attributes", "tokio1" ] } sea-orm-codegen = { version = "^0.5.0", path = "../sea-orm-codegen" } -sea-schema = { version = "0.4.0", default-features = false, features = [ +sea-schema = { git = "https://github.com/SeaQL/sea-schema.git", branch = "restructure-sea-query-dep", 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" ] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/sea-orm-cli/README.md b/sea-orm-cli/README.md index bcf49100..af7f1dd3 100644 --- a/sea-orm-cli/README.md +++ b/sea-orm-cli/README.md @@ -6,7 +6,7 @@ Getting Help: cargo run -- -h ``` -Running Entity Generator: +## Running Entity Generator: ```sh # MySQL (`--database-schema` option is ignored) @@ -18,3 +18,46 @@ 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-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index f0d12b7b..ee81f7b5 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -74,10 +74,30 @@ pub fn build_cli() -> App<'static, 'static> { ) .setting(AppSettings::SubcommandRequiredElseHelp); - App::new("sea-orm") + let arg_migration_dir = Arg::with_name("MIGRATION_DIR") + .long("migration-dir") + .short("d") + .help("Migration script directory") + .takes_value(true) + .default_value("./migration"); + let mut migrate_subcommands = SubCommand::with_name("migrate") + .about("Migration related commands") + .subcommand( + SubCommand::with_name("init") + .about("Initialize migration directory") + .arg(arg_migration_dir.clone()), + ) + .arg(arg_migration_dir.clone()); + for subcommand in sea_schema::migration::get_subcommands() { + migrate_subcommands = + migrate_subcommands.subcommand(subcommand.arg(arg_migration_dir.clone())); + } + + App::new("sea-orm-cli") .version(env!("CARGO_PKG_VERSION")) .setting(AppSettings::VersionlessSubcommands) .subcommand(entity_subcommand) + .subcommand(migrate_subcommands) .arg( Arg::with_name("VERBOSE") .long("verbose") diff --git a/sea-orm-cli/src/main.rs b/sea-orm-cli/src/main.rs index 8cf9134a..d35375fb 100644 --- a/sea-orm-cli/src/main.rs +++ b/sea-orm-cli/src/main.rs @@ -16,6 +16,7 @@ async fn main() { ("generate", Some(matches)) => run_generate_command(matches) .await .unwrap_or_else(handle_error), + ("migrate", Some(matches)) => run_migrate_command(matches).unwrap_or_else(handle_error), _ => unreachable!("You should never see this message"), } } @@ -187,6 +188,78 @@ async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box) -> Result<(), Box> { + let migrate_subcommand = matches.subcommand(); + // If it's `migrate init` + if let ("init", Some(args)) = migrate_subcommand { + let migration_dir = args.value_of("MIGRATION_DIR").unwrap(); + let migration_dir = match migration_dir.ends_with('/') { + true => migration_dir.to_string(), + false => format!("{}/", migration_dir), + }; + println!("Initializing migration directory..."); + macro_rules! write_file { + ($filename: literal) => { + let filepath = [&migration_dir, $filename].join(""); + println!("Creating file `{}`", filepath); + let path = Path::new(&filepath); + let prefix = path.parent().unwrap(); + fs::create_dir_all(prefix).unwrap(); + let mut file = fs::File::create(path)?; + let content = include_str!(concat!("../template/migration/", $filename)); + file.write_all(content.as_bytes())?; + }; + } + write_file!("src/lib.rs"); + write_file!("src/m20220101_000001_create_table.rs"); + write_file!("src/main.rs"); + write_file!("Cargo.toml"); + write_file!("README.md"); + println!("Done!"); + // Early exit! + return Ok(()); + } + let (subcommand, migration_dir, steps, verbose) = match migrate_subcommand { + // Catch all command with pattern `migrate xxx` + (subcommand, Some(args)) => { + let migration_dir = args.value_of("MIGRATION_DIR").unwrap(); + let steps = args.value_of("NUM_MIGRATION"); + let verbose = args.is_present("VERBOSE"); + (subcommand, migration_dir, steps, verbose) + } + // Catch command `migrate`, this will be treated as `migrate up` + _ => { + let migration_dir = matches.value_of("MIGRATION_DIR").unwrap(); + let verbose = matches.is_present("VERBOSE"); + ("up", migration_dir, None, verbose) + } + }; + // Construct the `--manifest-path` + let manifest_path = if migration_dir.ends_with('/') { + format!("{}Cargo.toml", migration_dir) + } else { + format!("{}/Cargo.toml", migration_dir) + }; + // Construct the arguments that will be supplied to `cargo` command + let mut args = vec![ + "run", + "--manifest-path", + manifest_path.as_str(), + "--", + subcommand, + ]; + if let Some(steps) = steps { + args.extend(["-n", steps]); + } + if verbose { + args.push("-v"); + } + // Run migrator CLI on user's behalf + println!("Running `cargo {}`", args.join(" ")); + Command::new("cargo").args(args).spawn()?.wait()?; + Ok(()) +} + fn handle_error(error: E) where E: Display, @@ -197,11 +270,13 @@ where #[cfg(test)] mod tests { - use clap::AppSettings; use super::*; + use clap::AppSettings; #[test] - #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: RelativeUrlWithoutBase")] + #[should_panic( + expected = "called `Result::unwrap()` on an `Err` value: RelativeUrlWithoutBase" + )] fn test_generate_entity_no_protocol() { let matches = cli::build_cli() .setting(AppSettings::NoBinaryName) @@ -216,7 +291,9 @@ mod tests { } #[test] - #[should_panic(expected = "There is no database name as part of the url path: postgresql://root:root@localhost:3306")] + #[should_panic( + expected = "There is no database name as part of the url path: postgresql://root:root@localhost:3306" + )] fn test_generate_entity_no_database_section() { let matches = cli::build_cli() .setting(AppSettings::NoBinaryName) @@ -231,7 +308,9 @@ mod tests { } #[test] - #[should_panic(expected = "There is no database name as part of the url path: mysql://root:root@localhost:3306/")] + #[should_panic( + expected = "There is no database name as part of the url path: mysql://root:root@localhost:3306/" + )] fn test_generate_entity_no_database_path() { let matches = cli::build_cli() .setting(AppSettings::NoBinaryName) diff --git a/sea-orm-cli/template/migration/Cargo.toml b/sea-orm-cli/template/migration/Cargo.toml new file mode 100644 index 00000000..c55187a2 --- /dev/null +++ b/sea-orm-cli/template/migration/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +sea-schema = { git = "https://github.com/SeaQL/sea-schema.git", branch = "restructure-sea-query-dep", default-features = false, features = [ "migration", "debug-print" ] } diff --git a/sea-orm-cli/template/migration/README.md b/sea-orm-cli/template/migration/README.md new file mode 100644 index 00000000..963caaeb --- /dev/null +++ b/sea-orm-cli/template/migration/README.md @@ -0,0 +1,37 @@ +# Running Migrator CLI + +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/sea-orm-cli/template/migration/src/lib.rs b/sea-orm-cli/template/migration/src/lib.rs new file mode 100644 index 00000000..339d693b --- /dev/null +++ b/sea-orm-cli/template/migration/src/lib.rs @@ -0,0 +1,12 @@ +pub use sea_schema::migration::*; + +mod m20220101_000001_create_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![Box::new(m20220101_000001_create_table::Migration)] + } +} 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 new file mode 100644 index 00000000..df6dcf70 --- /dev/null +++ b/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs @@ -0,0 +1,23 @@ +use sea_schema::migration::{ + sea_query::{self, *}, + *, +}; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220101_000001_create_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + todo!() + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + todo!() + } +} diff --git a/sea-orm-cli/template/migration/src/main.rs b/sea-orm-cli/template/migration/src/main.rs new file mode 100644 index 00000000..7e5e996d --- /dev/null +++ b/sea-orm-cli/template/migration/src/main.rs @@ -0,0 +1,7 @@ +use migration::Migrator; +use sea_schema::migration::*; + +#[async_std::main] +async fn main() { + cli::run_cli(Migrator).await; +} diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 22ada992..086a0e17 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.20.0" } +sea-query = { version = "0.21.0" } syn = { version = "^1", default-features = false, features = [ "derive", "parsing", diff --git a/src/database/connection.rs b/src/database/connection.rs index 8c409999..b6d74fd6 100644 --- a/src/database/connection.rs +++ b/src/database/connection.rs @@ -7,10 +7,7 @@ use std::{future::Future, pin::Pin}; /// Creates constraints for any structure that can create a database connection /// and execute SQL statements #[async_trait::async_trait] -pub trait ConnectionTrait<'a>: Sync { - /// Create a stream for the [QueryResult] - type Stream: Stream>; - +pub trait ConnectionTrait: Sync { /// Fetch the database backend as specified in [DbBackend]. /// This depends on feature flags enabled. fn get_database_backend(&self) -> DbBackend; @@ -24,12 +21,34 @@ pub trait ConnectionTrait<'a>: Sync { /// Execute a [Statement] and return a collection Vec<[QueryResult]> on success async fn query_all(&self, stmt: Statement) -> Result, DbErr>; + /// Check if the connection supports `RETURNING` syntax on insert and update + fn support_returning(&self) -> bool { + let db_backend = self.get_database_backend(); + db_backend.support_returning() + } + + /// Check if the connection is a test connection for the Mock database + fn is_mock_connection(&self) -> bool { + false + } +} + +/// Stream query results +#[async_trait::async_trait] +pub trait StreamTrait<'a>: Sync { + /// Create a stream for the [QueryResult] + type Stream: Stream>; + /// Execute a [Statement] and return a stream of results fn stream( &'a self, stmt: Statement, ) -> Pin> + 'a>>; +} +/// Spawn database transaction +#[async_trait::async_trait] +pub trait TransactionTrait { /// Execute SQL `BEGIN` transaction. /// Returns a Transaction that can be committed or rolled back async fn begin(&self) -> Result; @@ -44,15 +63,4 @@ pub trait ConnectionTrait<'a>: Sync { + Send, T: Send, E: std::error::Error + Send; - - /// Check if the connection supports `RETURNING` syntax on insert and update - fn support_returning(&self) -> bool { - let db_backend = self.get_database_backend(); - db_backend.support_returning() - } - - /// Check if the connection is a test connection for the Mock database - fn is_mock_connection(&self) -> bool { - false - } } diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 90016737..5031dc7a 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -1,6 +1,6 @@ use crate::{ error::*, ConnectionTrait, DatabaseTransaction, ExecResult, QueryResult, Statement, - StatementBuilder, TransactionError, + StatementBuilder, StreamTrait, TransactionError, TransactionTrait, }; use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder}; use std::{future::Future, pin::Pin}; @@ -89,9 +89,7 @@ impl std::fmt::Debug for DatabaseConnection { } #[async_trait::async_trait] -impl<'a> ConnectionTrait<'a> for DatabaseConnection { - type Stream = crate::QueryStream; - +impl ConnectionTrait for DatabaseConnection { fn get_database_backend(&self) -> DbBackend { match self { #[cfg(feature = "sqlx-mysql")] @@ -151,6 +149,16 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection { } } + #[cfg(feature = "mock")] + fn is_mock_connection(&self) -> bool { + matches!(self, DatabaseConnection::MockDatabaseConnection(_)) + } +} + +#[async_trait::async_trait] +impl<'a> StreamTrait<'a> for DatabaseConnection { + type Stream = crate::QueryStream; + #[instrument(level = "trace")] fn stream( &'a self, @@ -172,7 +180,10 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection { }) }) } +} +#[async_trait::async_trait] +impl TransactionTrait for DatabaseConnection { #[instrument(level = "trace")] async fn begin(&self) -> Result { match self { @@ -221,11 +232,6 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection { DatabaseConnection::Disconnected => panic!("Disconnected"), } } - - #[cfg(feature = "mock")] - fn is_mock_connection(&self) -> bool { - matches!(self, DatabaseConnection::MockDatabaseConnection(_)) - } } #[cfg(feature = "mock")] diff --git a/src/database/mock.rs b/src/database/mock.rs index 844fcbd9..20371393 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -311,8 +311,8 @@ impl OpenTransaction { #[cfg(feature = "mock")] mod tests { use crate::{ - entity::*, tests_cfg::*, ConnectionTrait, DbBackend, DbErr, MockDatabase, Statement, - Transaction, TransactionError, + entity::*, tests_cfg::*, DbBackend, DbErr, MockDatabase, Statement, Transaction, + TransactionError, TransactionTrait, }; use pretty_assertions::assert_eq; diff --git a/src/database/statement.rs b/src/database/statement.rs index a1ba8126..94420c38 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -124,6 +124,10 @@ build_schema_stmt!(sea_query::TableDropStatement); build_schema_stmt!(sea_query::TableAlterStatement); build_schema_stmt!(sea_query::TableRenameStatement); build_schema_stmt!(sea_query::TableTruncateStatement); +build_schema_stmt!(sea_query::IndexCreateStatement); +build_schema_stmt!(sea_query::IndexDropStatement); +build_schema_stmt!(sea_query::ForeignKeyCreateStatement); +build_schema_stmt!(sea_query::ForeignKeyDropStatement); macro_rules! build_type_stmt { ($stmt: ty) => { diff --git a/src/database/stream/query.rs b/src/database/stream/query.rs index 98f1142a..bfa66ba8 100644 --- a/src/database/stream/query.rs +++ b/src/database/stream/query.rs @@ -156,6 +156,8 @@ impl QueryStream { } #[cfg(feature = "mock")] InnerConnection::Mock(c) => c.fetch(stmt), + #[allow(unreachable_patterns)] + _ => unreachable!(), }, } .build() diff --git a/src/database/stream/transaction.rs b/src/database/stream/transaction.rs index daa912ef..4f584274 100644 --- a/src/database/stream/transaction.rs +++ b/src/database/stream/transaction.rs @@ -85,6 +85,8 @@ impl<'a> TransactionStream<'a> { } #[cfg(feature = "mock")] InnerConnection::Mock(c) => c.fetch(stmt), + #[allow(unreachable_patterns)] + _ => unreachable!(), } }) }, diff --git a/src/database/transaction.rs b/src/database/transaction.rs index e1f85536..6e2df54a 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -1,6 +1,6 @@ use crate::{ debug_print, ConnectionTrait, DbBackend, DbErr, ExecResult, InnerConnection, QueryResult, - Statement, TransactionStream, + Statement, StreamTrait, TransactionStream, TransactionTrait, }; #[cfg(feature = "sqlx-dep")] use crate::{sqlx_error_to_exec_err, sqlx_error_to_query_err}; @@ -226,6 +226,8 @@ impl DatabaseTransaction { InnerConnection::Mock(c) => { c.rollback(); } + #[allow(unreachable_patterns)] + _ => unreachable!(), } } else { //this should never happen @@ -242,9 +244,7 @@ impl Drop for DatabaseTransaction { } #[async_trait::async_trait] -impl<'a> ConnectionTrait<'a> for DatabaseTransaction { - type Stream = TransactionStream<'a>; - +impl ConnectionTrait for DatabaseTransaction { fn get_database_backend(&self) -> DbBackend { // this way we don't need to lock self.backend @@ -278,6 +278,8 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction { } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.execute(stmt), + #[allow(unreachable_patterns)] + _ => unreachable!(), }; #[cfg(feature = "sqlx-dep")] _res.map_err(sqlx_error_to_exec_err) @@ -305,6 +307,8 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction { } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_one(stmt), + #[allow(unreachable_patterns)] + _ => unreachable!(), }; #[cfg(feature = "sqlx-dep")] if let Err(sqlx::Error::RowNotFound) = _res { @@ -345,10 +349,17 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction { } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_all(stmt), + #[allow(unreachable_patterns)] + _ => unreachable!(), }; #[cfg(feature = "sqlx-dep")] _res.map_err(sqlx_error_to_query_err) } +} + +#[async_trait::async_trait] +impl<'a> StreamTrait<'a> for DatabaseTransaction { + type Stream = TransactionStream<'a>; #[instrument(level = "trace")] fn stream( @@ -364,7 +375,10 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction { .await) }) } +} +#[async_trait::async_trait] +impl TransactionTrait for DatabaseTransaction { #[instrument(level = "trace")] async fn begin(&self) -> Result { DatabaseTransaction::begin( diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 514bf435..06e30cde 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -276,7 +276,7 @@ pub trait ActiveModelTrait: Clone + Debug { where ::Model: IntoActiveModel, Self: ActiveModelBehavior + 'a, - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let am = ActiveModelBehavior::before_save(self, true)?; let model = ::insert(am) @@ -398,7 +398,7 @@ pub trait ActiveModelTrait: Clone + Debug { where ::Model: IntoActiveModel, Self: ActiveModelBehavior + 'a, - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let am = ActiveModelBehavior::before_save(self, false)?; let model: ::Model = Self::Entity::update(am).exec(db).await?; @@ -411,7 +411,7 @@ pub trait ActiveModelTrait: Clone + Debug { where ::Model: IntoActiveModel, Self: ActiveModelBehavior + 'a, - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let mut is_update = true; for key in ::PrimaryKey::iter() { @@ -475,7 +475,7 @@ pub trait ActiveModelTrait: Clone + Debug { async fn delete<'a, C>(self, db: &'a C) -> Result where Self: ActiveModelBehavior + 'a, - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let am = ActiveModelBehavior::before_delete(self)?; let am_clone = am.clone(); diff --git a/src/entity/model.rs b/src/entity/model.rs index 271493ef..34520dc3 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -41,7 +41,7 @@ pub trait ModelTrait: Clone + Send + Debug { async fn delete<'a, A, C>(self, db: &'a C) -> Result where Self: IntoActiveModel, - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: ActiveModelTrait + ActiveModelBehavior + Send + 'a, { self.into_active_model().delete(db).await diff --git a/src/executor/delete.rs b/src/executor/delete.rs index 7577b976..25a1060e 100644 --- a/src/executor/delete.rs +++ b/src/executor/delete.rs @@ -24,7 +24,7 @@ where /// Execute a DELETE operation on one ActiveModel pub fn exec(self, db: &'a C) -> impl Future> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { // so that self is dropped before entering await exec_delete_only(self.query, db) @@ -38,7 +38,7 @@ where /// Execute a DELETE operation on many ActiveModels pub fn exec(self, db: &'a C) -> impl Future> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { // so that self is dropped before entering await exec_delete_only(self.query, db) @@ -54,7 +54,7 @@ impl Deleter { /// Execute a DELETE operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let builder = db.get_database_backend(); exec_delete(builder.build(&self.query), db) @@ -63,14 +63,14 @@ impl Deleter { async fn exec_delete_only<'a, C>(query: DeleteStatement, db: &'a C) -> Result where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { Deleter::new(query).exec(db).await } async fn exec_delete<'a, C>(statement: Statement, db: &'a C) -> Result where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let result = db.execute(statement).await?; Ok(DeleteResult { diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f3168180..821f7bed 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -46,6 +46,8 @@ impl ExecResult { } #[cfg(feature = "mock")] ExecResultHolder::Mock(result) => result.last_insert_id, + #[allow(unreachable_patterns)] + _ => unreachable!(), } } @@ -60,6 +62,8 @@ impl ExecResult { ExecResultHolder::SqlxSqlite(result) => result.rows_affected(), #[cfg(feature = "mock")] ExecResultHolder::Mock(result) => result.rows_affected, + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 1d9f7442..ef5c11ea 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -36,7 +36,7 @@ where #[allow(unused_mut)] pub fn exec<'a, C>(self, db: &'a C) -> impl Future, DbErr>> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: 'a, { // so that self is dropped before entering await @@ -58,7 +58,7 @@ where ) -> impl Future::Model, DbErr>> + '_ where ::Model: IntoActiveModel, - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: 'a, { Inserter::::new(self.primary_key, self.query).exec_with_returning(db) @@ -81,7 +81,7 @@ where /// Execute an insert operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future, DbErr>> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: 'a, { let builder = db.get_database_backend(); @@ -95,7 +95,7 @@ where ) -> impl Future::Model, DbErr>> + '_ where ::Model: IntoActiveModel, - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: 'a, { exec_insert_with_returning::(self.primary_key, self.query, db) @@ -108,7 +108,7 @@ async fn exec_insert<'a, A, C>( db: &'a C, ) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: ActiveModelTrait, { type PrimaryKey = <::Entity as EntityTrait>::PrimaryKey; @@ -143,7 +143,7 @@ async fn exec_insert_with_returning<'a, A, C>( ) -> Result<::Model, DbErr> where ::Model: IntoActiveModel, - C: ConnectionTrait<'a>, + C: ConnectionTrait, A: ActiveModelTrait, { let db_backend = db.get_database_backend(); diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index e8f37bea..59453ff1 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -14,7 +14,7 @@ pub type PinBoxStream<'db, Item> = Pin + 'db>>; #[derive(Clone, Debug)] pub struct Paginator<'db, C, S> where - C: ConnectionTrait<'db>, + C: ConnectionTrait, S: SelectorTrait + 'db, { pub(crate) query: SelectStatement, @@ -28,7 +28,7 @@ where impl<'db, C, S> Paginator<'db, C, S> where - C: ConnectionTrait<'db>, + C: ConnectionTrait, S: SelectorTrait + 'db, { /// Fetch a specific page; page index starts from zero @@ -184,7 +184,7 @@ where /// A Trait for any type that can paginate results pub trait PaginatorTrait<'db, C> where - C: ConnectionTrait<'db>, + C: ConnectionTrait, { /// Select operation type Selector: SelectorTrait + Send + Sync + 'db; @@ -203,7 +203,7 @@ where impl<'db, C, S> PaginatorTrait<'db, C> for Selector where - C: ConnectionTrait<'db>, + C: ConnectionTrait, S: SelectorTrait + Send + Sync + 'db, { type Selector = S; @@ -221,7 +221,7 @@ where impl<'db, C, M, E> PaginatorTrait<'db, C> for Select where - C: ConnectionTrait<'db>, + C: ConnectionTrait, E: EntityTrait, M: FromQueryResult + Sized + Send + Sync + 'db, { @@ -234,7 +234,7 @@ where impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo where - C: ConnectionTrait<'db>, + C: ConnectionTrait, E: EntityTrait, F: EntityTrait, M: FromQueryResult + Sized + Send + Sync + 'db, diff --git a/src/executor/query.rs b/src/executor/query.rs index e15b3a88..4b03e312 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -75,6 +75,8 @@ impl fmt::Debug for QueryResultRow { Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"), #[cfg(feature = "mock")] Self::Mock(row) => write!(f, "{:?}", row), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } @@ -124,6 +126,8 @@ macro_rules! try_getable_all { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } @@ -160,6 +164,8 @@ macro_rules! try_getable_unsigned { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } @@ -193,6 +199,8 @@ macro_rules! try_getable_mysql { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } @@ -236,6 +244,8 @@ macro_rules! try_getable_date_time { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } @@ -321,6 +331,8 @@ impl TryGetable for Decimal { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } } diff --git a/src/executor/select.rs b/src/executor/select.rs index 20067ace..aa0b86de 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1,7 +1,7 @@ use crate::{ error::*, ConnectionTrait, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement, - TryGetableMany, + StreamTrait, TryGetableMany, }; use futures::{Stream, TryStreamExt}; use sea_query::SelectStatement; @@ -256,7 +256,7 @@ where /// Get one Model from the SELECT query pub async fn one<'a, C>(self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_model().one(db).await } @@ -264,7 +264,7 @@ where /// Get all Models from the SELECT query pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_model().all(db).await } @@ -275,7 +275,7 @@ where db: &'a C, ) -> Result> + 'b, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait + StreamTrait<'a>, { self.into_model().stream(db).await } @@ -310,7 +310,7 @@ where /// Get one Model from the Select query pub async fn one<'a, C>(self, db: &C) -> Result)>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_model().one(db).await } @@ -318,7 +318,7 @@ where /// Get all Models from the Select query pub async fn all<'a, C>(self, db: &C) -> Result)>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_model().all(db).await } @@ -329,7 +329,7 @@ where db: &'a C, ) -> Result), DbErr>> + 'b, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait + StreamTrait<'a>, { self.into_model().stream(db).await } @@ -364,7 +364,7 @@ where /// Select one Model pub async fn one<'a, C>(self, db: &C) -> Result)>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_model().one(db).await } @@ -375,7 +375,7 @@ where db: &'a C, ) -> Result), DbErr>> + 'b, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait + StreamTrait<'a>, { self.into_model().stream(db).await } @@ -383,7 +383,7 @@ where /// Get all Models from the select operation pub async fn all<'a, C>(self, db: &C) -> Result)>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let rows = self.into_model().all(db).await?; Ok(consolidate_query_result::(rows)) @@ -421,7 +421,7 @@ where fn into_selector_raw<'a, C>(self, db: &C) -> SelectorRaw where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let builder = db.get_database_backend(); let stmt = builder.build(&self.query); @@ -434,7 +434,7 @@ where /// Get an item from the Select query pub async fn one<'a, C>(mut self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.query.limit(1); self.into_selector_raw(db).one(db).await @@ -443,7 +443,7 @@ where /// Get all items from the Select query pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { self.into_selector_raw(db).all(db).await } @@ -454,7 +454,7 @@ where db: &'a C, ) -> Result> + 'b>>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait + StreamTrait<'a>, S: 'b, { self.into_selector_raw(db).stream(db).await @@ -672,7 +672,7 @@ where /// ``` pub async fn one<'a, C>(self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let row = db.query_one(self.stmt).await?; match row { @@ -723,7 +723,7 @@ where /// ``` pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let rows = db.query_all(self.stmt).await?; let mut models = Vec::new(); @@ -739,7 +739,7 @@ where db: &'a C, ) -> Result> + 'b>>, DbErr> where - C: ConnectionTrait<'a>, + C: ConnectionTrait + StreamTrait<'a>, S: 'b, { let stream = db.stream(self.stmt).await?; diff --git a/src/executor/update.rs b/src/executor/update.rs index eb997f13..70e971a8 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -1,6 +1,6 @@ use crate::{ - error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Iterable, SelectModel, - SelectorRaw, Statement, UpdateMany, UpdateOne, + error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel, + Iterable, SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne, }; use sea_query::{Alias, Expr, FromValueTuple, Query, UpdateStatement}; use std::future::Future; @@ -26,7 +26,8 @@ where /// Execute an update operation on an ActiveModel pub async fn exec<'b, C>(self, db: &'b C) -> Result<::Model, DbErr> where - C: ConnectionTrait<'b>, + ::Model: IntoActiveModel, + C: ConnectionTrait, { // so that self is dropped before entering await exec_update_and_return_updated(self.query, self.model, db).await @@ -40,7 +41,7 @@ where /// Execute an update operation on multiple ActiveModels pub fn exec(self, db: &'a C) -> impl Future> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { // so that self is dropped before entering await exec_update_only(self.query, db) @@ -65,7 +66,7 @@ impl Updater { /// Execute an update operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future> + '_ where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let builder = db.get_database_backend(); exec_update(builder.build(&self.query), db, self.check_record_exists) @@ -74,7 +75,7 @@ impl Updater { async fn exec_update_only<'a, C>(query: UpdateStatement, db: &'a C) -> Result where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { Updater::new(query).exec(db).await } @@ -86,7 +87,7 @@ async fn exec_update_and_return_updated<'a, A, C>( ) -> Result<::Model, DbErr> where A: ActiveModelTrait, - C: ConnectionTrait<'a>, + C: ConnectionTrait, { match db.support_returning() { true => { @@ -141,7 +142,7 @@ async fn exec_update<'a, C>( check_record_exists: bool, ) -> Result where - C: ConnectionTrait<'a>, + C: ConnectionTrait, { let result = db.execute(statement).await?; if check_record_exists && result.rows_affected() == 0 { diff --git a/src/query/mod.rs b/src/query/mod.rs index 3168e81e..b7e304ac 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -23,5 +23,6 @@ pub use update::*; pub use util::*; pub use crate::{ - ConnectionTrait, InsertResult, PaginatorTrait, Statement, UpdateResult, Value, Values, + ConnectionTrait, InsertResult, PaginatorTrait, Statement, StreamTrait, TransactionTrait, + UpdateResult, Value, Values, };