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
This commit is contained in:
parent
e63d463155
commit
8eb095385d
@ -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" }
|
||||
|
@ -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" }
|
||||
|
@ -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"]
|
||||
|
@ -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
|
||||
|
||||
|
27
examples/rocket_example/entity/Cargo.toml
Normal file
27
examples/rocket_example/entity/Cargo.toml
Normal file
@ -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
|
6
examples/rocket_example/entity/src/lib.rs
Normal file
6
examples/rocket_example/entity/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
pub mod post;
|
||||
|
||||
pub use sea_orm;
|
14
examples/rocket_example/migration/Cargo.toml
Normal file
14
examples/rocket_example/migration/Cargo.toml
Normal file
@ -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" }
|
37
examples/rocket_example/migration/README.md
Normal file
37
examples/rocket_example/migration/README.md
Normal file
@ -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
|
||||
```
|
12
examples/rocket_example/migration/src/lib.rs
Normal file
12
examples/rocket_example/migration/src/lib.rs
Normal file
@ -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<Box<dyn MigrationTrait>> {
|
||||
vec![Box::new(m20220120_000001_create_post_table::Migration)]
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
18
examples/rocket_example/migration/src/main.rs
Normal file
18
examples/rocket_example/migration/src/main.rs
Normal file
@ -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;
|
||||
}
|
@ -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<Build>) -> 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)
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<ExecResult, DbErr> {
|
||||
let builder = db.get_database_backend();
|
||||
db.execute(builder.build(stmt)).await
|
||||
}
|
||||
|
||||
pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
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
|
||||
}
|
@ -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"] }
|
||||
|
@ -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",
|
||||
|
@ -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"] }
|
||||
|
@ -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
|
||||
```
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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<dyn Er
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_migrate_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Error>> {
|
||||
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<E>(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)
|
||||
|
12
sea-orm-cli/template/migration/Cargo.toml
Normal file
12
sea-orm-cli/template/migration/Cargo.toml
Normal file
@ -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" ] }
|
37
sea-orm-cli/template/migration/README.md
Normal file
37
sea-orm-cli/template/migration/README.md
Normal file
@ -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
|
||||
```
|
12
sea-orm-cli/template/migration/src/lib.rs
Normal file
12
sea-orm-cli/template/migration/src/lib.rs
Normal file
@ -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<Box<dyn MigrationTrait>> {
|
||||
vec![Box::new(m20220101_000001_create_table::Migration)]
|
||||
}
|
||||
}
|
@ -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!()
|
||||
}
|
||||
}
|
7
sea-orm-cli/template/migration/src/main.rs
Normal file
7
sea-orm-cli/template/migration/src/main.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use migration::Migrator;
|
||||
use sea_schema::migration::*;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
cli::run_cli(Migrator).await;
|
||||
}
|
@ -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",
|
||||
|
@ -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<Item = Result<QueryResult, DbErr>>;
|
||||
|
||||
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<Vec<QueryResult>, 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<Item = Result<QueryResult, DbErr>>;
|
||||
|
||||
/// Execute a [Statement] and return a stream of results
|
||||
fn stream(
|
||||
&'a self,
|
||||
stmt: Statement,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Self::Stream, DbErr>> + '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<DatabaseTransaction, DbErr>;
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<DatabaseTransaction, DbErr> {
|
||||
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")]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -156,6 +156,8 @@ impl QueryStream {
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(c) => c.fetch(stmt),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
.build()
|
||||
|
@ -85,6 +85,8 @@ impl<'a> TransactionStream<'a> {
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(c) => c.fetch(stmt),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -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, DbErr> {
|
||||
DatabaseTransaction::begin(
|
||||
|
@ -276,7 +276,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
where
|
||||
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_save(self, true)?;
|
||||
let model = <Self::Entity as EntityTrait>::insert(am)
|
||||
@ -398,7 +398,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
where
|
||||
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_save(self, false)?;
|
||||
let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
|
||||
@ -411,7 +411,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
where
|
||||
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let mut is_update = true;
|
||||
for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||
@ -475,7 +475,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
async fn delete<'a, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
|
||||
where
|
||||
Self: ActiveModelBehavior + 'a,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let am = ActiveModelBehavior::before_delete(self)?;
|
||||
let am_clone = am.clone();
|
||||
|
@ -41,7 +41,7 @@ pub trait ModelTrait: Clone + Send + Debug {
|
||||
async fn delete<'a, A, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
|
||||
where
|
||||
Self: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: ActiveModelTrait<Entity = Self::Entity> + ActiveModelBehavior + Send + 'a,
|
||||
{
|
||||
self.into_active_model().delete(db).await
|
||||
|
@ -24,7 +24,7 @@ where
|
||||
/// Execute a DELETE operation on one ActiveModel
|
||||
pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
|
||||
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<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
|
||||
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<Output = Result<DeleteResult, DbErr>> + '_
|
||||
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<DeleteResult, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
Deleter::new(query).exec(db).await
|
||||
}
|
||||
|
||||
async fn exec_delete<'a, C>(statement: Statement, db: &'a C) -> Result<DeleteResult, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let result = db.execute(statement).await?;
|
||||
Ok(DeleteResult {
|
||||
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ where
|
||||
#[allow(unused_mut)]
|
||||
pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + '_
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: 'a,
|
||||
{
|
||||
// so that self is dropped before entering await
|
||||
@ -58,7 +58,7 @@ where
|
||||
) -> impl Future<Output = Result<<A::Entity as EntityTrait>::Model, DbErr>> + '_
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: 'a,
|
||||
{
|
||||
Inserter::<A>::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<Output = Result<InsertResult<A>, DbErr>> + '_
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: 'a,
|
||||
{
|
||||
let builder = db.get_database_backend();
|
||||
@ -95,7 +95,7 @@ where
|
||||
) -> impl Future<Output = Result<<A::Entity as EntityTrait>::Model, DbErr>> + '_
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: 'a,
|
||||
{
|
||||
exec_insert_with_returning::<A, _>(self.primary_key, self.query, db)
|
||||
@ -108,7 +108,7 @@ async fn exec_insert<'a, A, C>(
|
||||
db: &'a C,
|
||||
) -> Result<InsertResult<A>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: ActiveModelTrait,
|
||||
{
|
||||
type PrimaryKey<A> = <<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey;
|
||||
@ -143,7 +143,7 @@ async fn exec_insert_with_returning<'a, A, C>(
|
||||
) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
A: ActiveModelTrait,
|
||||
{
|
||||
let db_backend = db.get_database_backend();
|
||||
|
@ -14,7 +14,7 @@ pub type PinBoxStream<'db, Item> = Pin<Box<dyn Stream<Item = Item> + '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<S>
|
||||
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<E>
|
||||
where
|
||||
C: ConnectionTrait<'db>,
|
||||
C: ConnectionTrait,
|
||||
E: EntityTrait<Model = M>,
|
||||
M: FromQueryResult + Sized + Send + Sync + 'db,
|
||||
{
|
||||
@ -234,7 +234,7 @@ where
|
||||
|
||||
impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo<E, F>
|
||||
where
|
||||
C: ConnectionTrait<'db>,
|
||||
C: ConnectionTrait,
|
||||
E: EntityTrait<Model = M>,
|
||||
F: EntityTrait<Model = N>,
|
||||
M: FromQueryResult + Sized + Send + Sync + 'db,
|
||||
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Option<E::Model>, 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<Vec<E::Model>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
self.into_model().all(db).await
|
||||
}
|
||||
@ -275,7 +275,7 @@ where
|
||||
db: &'a C,
|
||||
) -> Result<impl Stream<Item = Result<E::Model, DbErr>> + '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<Option<(E::Model, Option<F::Model>)>, 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<Vec<(E::Model, Option<F::Model>)>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
self.into_model().all(db).await
|
||||
}
|
||||
@ -329,7 +329,7 @@ where
|
||||
db: &'a C,
|
||||
) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), 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<Option<(E::Model, Option<F::Model>)>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
self.into_model().one(db).await
|
||||
}
|
||||
@ -375,7 +375,7 @@ where
|
||||
db: &'a C,
|
||||
) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), 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<Vec<(E::Model, Vec<F::Model>)>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let rows = self.into_model().all(db).await?;
|
||||
Ok(consolidate_query_result::<E, F>(rows))
|
||||
@ -421,7 +421,7 @@ where
|
||||
|
||||
fn into_selector_raw<'a, C>(self, db: &C) -> SelectorRaw<S>
|
||||
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<Option<S::Item>, 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<Vec<S::Item>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
self.into_selector_raw(db).all(db).await
|
||||
}
|
||||
@ -454,7 +454,7 @@ where
|
||||
db: &'a C,
|
||||
) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + '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<Option<S::Item>, 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<Vec<S::Item>, 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<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b>>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait + StreamTrait<'a>,
|
||||
S: 'b,
|
||||
{
|
||||
let stream = db.stream(self.stmt).await?;
|
||||
|
@ -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<<A::Entity as EntityTrait>::Model, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'b>,
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
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<C>(self, db: &'a C) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_
|
||||
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<Output = Result<UpdateResult, DbErr>> + '_
|
||||
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<UpdateResult, DbErr>
|
||||
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<<A::Entity as EntityTrait>::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<UpdateResult, DbErr>
|
||||
where
|
||||
C: ConnectionTrait<'a>,
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let result = db.execute(statement).await?;
|
||||
if check_record_exists && result.rows_affected() == 0 {
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user