diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 33cc67bf..1b5290bb 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -14,7 +14,6 @@ categories = [ "database" ] keywords = ["async", "orm", "mysql", "postgres", "sqlite"] default-run = "sea-orm-cli" - [lib] name = "sea_orm_cli" path = "src/lib.rs" @@ -22,26 +21,27 @@ path = "src/lib.rs" [[bin]] name = "sea-orm-cli" path = "src/bin/main.rs" +required-features = ["codegen"] [[bin]] name = "sea" path = "src/bin/sea.rs" +required-features = ["codegen"] [dependencies] clap = { version = "^2.33.3" } dotenv = { version = "^0.15" } async-std = { version = "^1.9", features = [ "attributes", "tokio1" ] } -sea-orm-codegen = { version = "^0.7.0", path = "../sea-orm-codegen" } -sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "dump-sea-orm-dep", default-features = false, features = [ +sea-orm-codegen = { version = "^0.7.0", path = "../sea-orm-codegen", optional = true } +sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "remove-migration", 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" ] } +], optional = true } +sqlx = { version = "^0.5", default-features = false, features = [ "mysql", "postgres" ], optional = true } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing = { version = "0.1" } url = "^2.2" @@ -50,7 +50,8 @@ url = "^2.2" smol = "1.2.5" [features] -default = [ "runtime-async-std-native-tls" ] +default = [ "codegen", "runtime-async-std-native-tls" ] +codegen = [ "sea-schema", "sea-orm-codegen" ] runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls", "sea-schema/runtime-actix-native-tls", diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 3b1efab7..362e4914 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -1,5 +1,5 @@ +use crate::migration::get_subcommands; use clap::{App, AppSettings, Arg, SubCommand}; -use sea_schema::get_cli_subcommands; pub fn build_cli() -> App<'static, 'static> { let entity_subcommand = SubCommand::with_name("generate") @@ -96,7 +96,7 @@ pub fn build_cli() -> App<'static, 'static> { .arg(arg_migration_dir.clone()), ) .arg(arg_migration_dir.clone()); - for subcommand in get_cli_subcommands!() { + for subcommand in get_subcommands() { migrate_subcommands = migrate_subcommands.subcommand(subcommand.arg(arg_migration_dir.clone())); } diff --git a/sea-orm-cli/src/lib.rs b/sea-orm-cli/src/lib.rs index 366b8fa6..d334c9d6 100644 --- a/sea-orm-cli/src/lib.rs +++ b/sea-orm-cli/src/lib.rs @@ -1,5 +1,8 @@ pub mod cli; +#[cfg(feature = "codegen")] pub mod commands; +pub mod migration; pub use cli::*; +#[cfg(feature = "codegen")] pub use commands::*; diff --git a/sea-orm-cli/src/migration.rs b/sea-orm-cli/src/migration.rs new file mode 100644 index 00000000..41cd507b --- /dev/null +++ b/sea-orm-cli/src/migration.rs @@ -0,0 +1,49 @@ +use clap::{App, AppSettings, Arg, SubCommand}; + +pub fn build_cli() -> App<'static, 'static> { + let mut app = App::new("sea-schema-migration") + .version(env!("CARGO_PKG_VERSION")) + .setting(AppSettings::VersionlessSubcommands) + .arg( + Arg::with_name("VERBOSE") + .long("verbose") + .short("v") + .help("Show debug messages") + .takes_value(false) + .global(true), + ); + for subcommand in get_subcommands() { + app = app.subcommand(subcommand); + } + app +} + +pub fn get_subcommands() -> Vec> { + vec![ + SubCommand::with_name("fresh") + .about("Drop all tables from the database, then reapply all migrations"), + SubCommand::with_name("refresh") + .about("Rollback all applied migrations, then reapply all migrations"), + SubCommand::with_name("reset").about("Rollback all applied migrations"), + SubCommand::with_name("status").about("Check the status of all migrations"), + SubCommand::with_name("up") + .about("Apply pending migrations") + .arg( + Arg::with_name("NUM_MIGRATION") + .long("num") + .short("n") + .help("Number of pending migrations to be applied") + .takes_value(true), + ), + SubCommand::with_name("down") + .about("Rollback applied migrations") + .arg( + Arg::with_name("NUM_MIGRATION") + .long("num") + .short("n") + .help("Number of pending migrations to be rolled back") + .takes_value(true) + .default_value("1"), + ), + ] +} diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 6af01e87..88b2ed86 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -15,7 +15,7 @@ name = "sea_orm_codegen" path = "src/lib.rs" [dependencies] -sea-query = { version = "0.24.0" } +sea-query = { version = "^0.24.0" } syn = { version = "^1", default-features = false, features = [ "derive", "parsing", diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index c5e2e1d2..91495e9b 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -18,11 +18,16 @@ name = "sea_orm_migration" path = "src/lib.rs" [dependencies] -sea-orm = { path = "../", default-features = false, features = ["macros"] } -sea-schema = { git = "https://github.com/SeaQL/sea-schema", branch = "dump-sea-orm-dep", features = ["migration"], default-features = false } async-std = { version = "^1", features = ["attributes", "tokio1"] } -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -tracing = { version = "0.1", features = ["log"] } async-trait = { version = "^0.1" } -dotenv = { version = "^0.15" } clap = { version = "^2.33" } +dotenv = { version = "^0.15" } +sea-orm = { path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { path = "../sea-orm-cli", default-features = false } +tracing = { version = "0.1", features = ["log"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[features] +sqlx-mysql = ["sea-orm/sqlx-mysql", "sea-orm/runtime-async-std-native-tls"] +sqlx-postgres = ["sea-orm/sqlx-postgres", "sea-orm/runtime-async-std-native-tls"] +sqlx-sqlite = ["sea-orm/sqlx-sqlite", "sea-orm/runtime-async-std-native-tls"] diff --git a/sea-orm-migration/src/cli.rs b/sea-orm-migration/src/cli.rs index bc211f29..c00e4824 100644 --- a/sea-orm-migration/src/cli.rs +++ b/sea-orm-migration/src/cli.rs @@ -1,15 +1,13 @@ -//! Run migrator CLI - -use crate::MigratorTrait; -use clap::{App, AppSettings, Arg}; +use clap::App; use dotenv::dotenv; -use sea_orm::{Database, DbConn}; -use sea_schema::get_cli_subcommands; use std::{fmt::Display, process::exit}; use tracing_subscriber::{prelude::*, EnvFilter}; -#[allow(dead_code)] -/// Migrator CLI application +use sea_orm::{Database, DbConn}; +use sea_orm_cli::build_cli; + +use super::MigratorTrait; + pub async fn run_cli(migrator: M) where M: MigratorTrait, @@ -21,20 +19,20 @@ where get_matches(migrator, db, app).await; } -async fn get_matches(_: M, db: &DbConn, app: App<'static, 'static>) +pub async fn get_matches(_: M, db: &DbConn, app: App<'static, 'static>) where M: MigratorTrait, { let matches = app.get_matches(); let mut verbose = false; let filter = match matches.subcommand() { - (_, None) => "sea_orm::migration=info", + (_, None) => "sea_schema::migration=info", (_, Some(args)) => match args.is_present("VERBOSE") { true => { verbose = true; "debug" } - false => "sea_orm::migration=info", + false => "sea_schema::migration=info", }, }; let filter_layer = EnvFilter::try_new(filter).unwrap(); @@ -76,24 +74,6 @@ where .unwrap_or_else(handle_error); } -fn build_cli() -> App<'static, 'static> { - let mut app = App::new("sea-schema-migration") - .version(env!("CARGO_PKG_VERSION")) - .setting(AppSettings::VersionlessSubcommands) - .arg( - Arg::with_name("VERBOSE") - .long("verbose") - .short("v") - .help("Show debug messages") - .takes_value(false) - .global(true), - ); - for subcommand in get_cli_subcommands!() { - app = app.subcommand(subcommand); - } - app -} - fn handle_error(error: E) where E: Display, diff --git a/sea-orm-migration/src/connection.rs b/sea-orm-migration/src/connection.rs deleted file mode 100644 index 9d49b97a..00000000 --- a/sea-orm-migration/src/connection.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Manage migration connection - -use crate::{into_migration_db_backend, into_migration_err, into_orm_stmt, QueryResult}; -use sea_orm::ConnectionTrait; -use sea_schema::migration::{self, MigrationErr, QueryResultTrait}; - -#[derive(Debug)] -pub(crate) struct DatabaseConnection<'c> { - pub(crate) conn: &'c sea_orm::DatabaseConnection, -} - -#[async_trait::async_trait] -impl migration::ConnectionTrait for DatabaseConnection<'_> { - fn get_database_backend(&self) -> migration::DbBackend { - into_migration_db_backend(ConnectionTrait::get_database_backend(self.conn)) - } - - async fn execute(&self, stmt: migration::Statement) -> Result<(), MigrationErr> { - ConnectionTrait::execute(self.conn, into_orm_stmt(stmt)) - .await - .map(|_| ()) - .map_err(|e| MigrationErr(e.to_string())) - } - - async fn query_one( - &self, - stmt: migration::Statement, - ) -> Result>, MigrationErr> { - ConnectionTrait::query_one(self.conn, into_orm_stmt(stmt)) - .await - .map(|res| res.map(|res| Box::new(QueryResult { res }) as Box)) - .map_err(into_migration_err) - } - - async fn query_all( - &self, - stmt: migration::Statement, - ) -> Result>, MigrationErr> { - ConnectionTrait::query_all(self.conn, into_orm_stmt(stmt)) - .await - .map(|rows| { - rows.into_iter() - .map(|res| Box::new(QueryResult { res }) as Box) - .collect() - }) - .map_err(into_migration_err) - } -} diff --git a/sea-orm-migration/src/database.rs b/sea-orm-migration/src/database.rs deleted file mode 100644 index 82142840..00000000 --- a/sea-orm-migration/src/database.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Convert database backend - -use sea_schema::migration; - -pub(crate) fn into_orm_db_backend(db_backend: migration::DbBackend) -> sea_orm::DbBackend { - match db_backend { - migration::DbBackend::MySql => sea_orm::DbBackend::MySql, - migration::DbBackend::Postgres => sea_orm::DbBackend::Postgres, - migration::DbBackend::Sqlite => sea_orm::DbBackend::Sqlite, - } -} - -pub(crate) fn into_migration_db_backend(db_backend: sea_orm::DbBackend) -> migration::DbBackend { - match db_backend { - sea_orm::DbBackend::MySql => migration::DbBackend::MySql, - sea_orm::DbBackend::Postgres => migration::DbBackend::Postgres, - sea_orm::DbBackend::Sqlite => migration::DbBackend::Sqlite, - } -} diff --git a/sea-orm-migration/src/error.rs b/sea-orm-migration/src/error.rs deleted file mode 100644 index 43bd4dd4..00000000 --- a/sea-orm-migration/src/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Convert migration error - -use sea_schema::migration; - -pub(crate) fn into_orm_db_err(err: migration::MigrationErr) -> sea_orm::DbErr { - sea_orm::DbErr::Migration(err.to_string()) -} - -pub(crate) fn into_migration_err(err: sea_orm::DbErr) -> migration::MigrationErr { - migration::MigrationErr(err.to_string()) -} diff --git a/sea-orm-migration/src/lib.rs b/sea-orm-migration/src/lib.rs index cb2712f3..a77f6d2f 100644 --- a/sea-orm-migration/src/lib.rs +++ b/sea-orm-migration/src/lib.rs @@ -1,31 +1,23 @@ pub mod cli; -pub mod connection; -pub mod database; -pub mod error; pub mod manager; pub mod migrator; pub mod prelude; -pub mod query; pub mod seaql_migrations; -pub mod statement; pub use manager::*; pub use migrator::*; -use connection::*; -use database::*; -use error::*; -use query::*; -use statement::*; -use sea_orm::DbErr; +pub use async_std; +pub use async_trait; +pub use sea_orm; +pub use sea_orm::sea_query; +pub use sea_orm::DbErr; -/// Define the name of a migration pub trait MigrationName { - /// Get migration name fn name(&self) -> &str; } -/// Define the actions of a migration +/// The migration definition #[async_trait::async_trait] pub trait MigrationTrait: MigrationName + Send + Sync { /// Define actions to perform when applying the migration diff --git a/sea-orm-migration/src/manager.rs b/sea-orm-migration/src/manager.rs index 4bd0c16a..fec6a22d 100644 --- a/sea-orm-migration/src/manager.rs +++ b/sea-orm-migration/src/manager.rs @@ -1,163 +1,167 @@ -//! Schema manager - -use crate::{into_orm_db_err, DatabaseConnection}; use sea_orm::sea_query::{ extension::postgres::{TypeAlterStatement, TypeCreateStatement, TypeDropStatement}, - ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, IndexDropStatement, - TableAlterStatement, TableCreateStatement, TableDropStatement, TableRenameStatement, - TableTruncateStatement, + Alias, Expr, ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, + IndexDropStatement, Query, TableAlterStatement, TableCreateStatement, TableDropStatement, + TableRenameStatement, TableTruncateStatement, }; -use sea_orm::{ConnectionTrait, DbBackend, DbConn, DbErr, StatementBuilder}; -use sea_schema::migration; +use sea_orm::{Condition, ConnectionTrait, DbBackend, DbConn, DbErr, Statement, StatementBuilder}; + +use super::query_tables; /// Helper struct for writing migration scripts in migration file -#[derive(Debug)] pub struct SchemaManager<'c> { - conn: DatabaseConnection<'c>, + conn: &'c DbConn, } impl<'c> SchemaManager<'c> { - /// Initialize [`SchemaManager`] pub fn new(conn: &'c DbConn) -> Self { - Self { - conn: DatabaseConnection { conn }, - } + Self { conn } } - /// Execute any statement that implemented [`StatementBuilder`] pub async fn exec_stmt(&self, stmt: S) -> Result<(), DbErr> where S: StatementBuilder, { - let builder = self.conn.conn.get_database_backend(); - self.conn - .conn - .execute(builder.build(&stmt)) - .await - .map(|_| ()) + let builder = self.conn.get_database_backend(); + self.conn.execute(builder.build(&stmt)).await.map(|_| ()) } - /// Get database backend pub fn get_database_backend(&self) -> DbBackend { - self.conn.conn.get_database_backend() + self.conn.get_database_backend() } - /// Borrow database connection pub fn get_connection(&self) -> &'c DbConn { - self.conn.conn + self.conn } } /// Schema Creation impl<'c> SchemaManager<'c> { - /// Create table pub async fn create_table(&self, stmt: TableCreateStatement) -> Result<(), DbErr> { - migration::SchemaManager::create_table(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Create index pub async fn create_index(&self, stmt: IndexCreateStatement) -> Result<(), DbErr> { - migration::SchemaManager::create_index(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Create foreign key pub async fn create_foreign_key(&self, stmt: ForeignKeyCreateStatement) -> Result<(), DbErr> { - migration::SchemaManager::create_foreign_key(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Create type pub async fn create_type(&self, stmt: TypeCreateStatement) -> Result<(), DbErr> { - migration::SchemaManager::create_type(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } } /// Schema Mutation impl<'c> SchemaManager<'c> { - /// Alter table pub async fn alter_table(&self, stmt: TableAlterStatement) -> Result<(), DbErr> { - migration::SchemaManager::alter_table(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Drop table pub async fn drop_table(&self, stmt: TableDropStatement) -> Result<(), DbErr> { - migration::SchemaManager::drop_table(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Rename table pub async fn rename_table(&self, stmt: TableRenameStatement) -> Result<(), DbErr> { - migration::SchemaManager::rename_table(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Truncate table pub async fn truncate_table(&self, stmt: TableTruncateStatement) -> Result<(), DbErr> { - migration::SchemaManager::truncate_table(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Drop index pub async fn drop_index(&self, stmt: IndexDropStatement) -> Result<(), DbErr> { - migration::SchemaManager::drop_index(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Drop foreign key pub async fn drop_foreign_key(&self, stmt: ForeignKeyDropStatement) -> Result<(), DbErr> { - migration::SchemaManager::drop_foreign_key(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Alter type pub async fn alter_type(&self, stmt: TypeAlterStatement) -> Result<(), DbErr> { - migration::SchemaManager::alter_type(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } - /// Drop type pub async fn drop_type(&self, stmt: TypeDropStatement) -> Result<(), DbErr> { - migration::SchemaManager::drop_type(stmt, &self.conn) - .await - .map_err(into_orm_db_err) + self.exec_stmt(stmt).await } } /// Schema Inspection impl<'c> SchemaManager<'c> { - /// Check if a table exists in the database pub async fn has_table(&self, table: T) -> Result where T: AsRef, { - migration::SchemaManager::has_table(table, &self.conn) - .await - .map_err(into_orm_db_err) + let mut stmt = Query::select(); + let mut subquery = query_tables(self.conn); + subquery.cond_where(Expr::col(Alias::new("table_name")).eq(table.as_ref())); + stmt.expr_as(Expr::cust("COUNT(*)"), Alias::new("rows")) + .from_subquery(subquery, Alias::new("subquery")); + + let builder = self.conn.get_database_backend(); + let res = self + .conn + .query_one(builder.build(&stmt)) + .await? + .ok_or_else(|| DbErr::Custom("Fail to check table exists".to_owned()))?; + let rows: i64 = res.try_get("", "rows")?; + + Ok(rows > 0) } - /// Check if a column exists in a specific database table pub async fn has_column(&self, table: T, column: C) -> Result where T: AsRef, C: AsRef, { - migration::SchemaManager::has_column(table, column, &self.conn) - .await - .map_err(into_orm_db_err) + let db_backend = self.conn.get_database_backend(); + let found = match db_backend { + DbBackend::MySql | DbBackend::Postgres => { + let schema_name = match db_backend { + DbBackend::MySql => "DATABASE()", + DbBackend::Postgres => "CURRENT_SCHEMA()", + DbBackend::Sqlite => unreachable!(), + }; + let mut stmt = Query::select(); + stmt.expr_as(Expr::cust("COUNT(*)"), Alias::new("rows")) + .from((Alias::new("information_schema"), Alias::new("columns"))) + .cond_where( + Condition::all() + .add( + Expr::expr(Expr::cust(schema_name)) + .equals(Alias::new("columns"), Alias::new("table_schema")), + ) + .add(Expr::col(Alias::new("table_name")).eq(table.as_ref())) + .add(Expr::col(Alias::new("column_name")).eq(column.as_ref())), + ); + + let res = self + .conn + .query_one(db_backend.build(&stmt)) + .await? + .ok_or_else(|| DbErr::Custom("Fail to check column exists".to_owned()))?; + let rows: i64 = res.try_get("", "rows")?; + rows > 0 + } + DbBackend::Sqlite => { + let stmt = Statement::from_string( + db_backend, + format!("PRAGMA table_info({})", table.as_ref()), + ); + let results = self.conn.query_all(stmt).await?; + let mut found = false; + for res in results { + let name: String = res.try_get("", "name")?; + if name.as_str() == column.as_ref() { + found = true; + } + } + found + } + }; + Ok(found) } } diff --git a/sea-orm-migration/src/migrator.rs b/sea-orm-migration/src/migrator.rs index a846f123..6c3d5386 100644 --- a/sea-orm-migration/src/migrator.rs +++ b/sea-orm-migration/src/migrator.rs @@ -1,6 +1,4 @@ -//! Migration executor - -use crate::{seaql_migrations, MigrationTrait, SchemaManager}; +use super::{seaql_migrations, MigrationTrait, SchemaManager}; use sea_orm::sea_query::{ Alias, Expr, ForeignKey, IntoTableRef, Query, SelectStatement, SimpleExpr, Table, }; @@ -21,8 +19,6 @@ pub enum MigrationStatus { Applied, } -/// Wrapper of [`MigrationTrait`] with migration status -#[allow(missing_debug_implementations)] pub struct Migration { migration: Box, status: MigrationStatus, diff --git a/sea-orm-migration/src/prelude.rs b/sea-orm-migration/src/prelude.rs index c116a49c..21122408 100644 --- a/sea-orm-migration/src/prelude.rs +++ b/sea-orm-migration/src/prelude.rs @@ -1,8 +1,11 @@ -//! Import migration utility +pub use sea_orm_cli::migration as cli; -pub use crate::*; +pub use super::manager::SchemaManager; +pub use super::migrator::MigratorTrait; +pub use super::{MigrationName, MigrationTrait}; pub use async_std; pub use async_trait; +pub use sea_orm; pub use sea_orm::sea_query; pub use sea_orm::sea_query::*; pub use sea_orm::DbErr; diff --git a/sea-orm-migration/src/query.rs b/sea-orm-migration/src/query.rs deleted file mode 100644 index 0b23d0d5..00000000 --- a/sea-orm-migration/src/query.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Get query result from db - -use crate::into_migration_err; -use sea_schema::migration::{self, MigrationErr}; - -pub(crate) struct QueryResult { - pub(crate) res: sea_orm::QueryResult, -} - -impl migration::QueryResultTrait for QueryResult { - fn try_get_string(&self, col: &str) -> Result { - self.res - .try_get::("", col) - .map_err(into_migration_err) - } - - fn try_get_i64(&self, col: &str) -> Result { - self.res.try_get::("", col).map_err(into_migration_err) - } -} diff --git a/sea-orm-migration/src/seaql_migrations.rs b/sea-orm-migration/src/seaql_migrations.rs index b387b57c..43f044ec 100644 --- a/sea-orm-migration/src/seaql_migrations.rs +++ b/sea-orm-migration/src/seaql_migrations.rs @@ -1,7 +1,3 @@ -#![allow(missing_docs)] - -//! Migration entity - use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/sea-orm-migration/src/statement.rs b/sea-orm-migration/src/statement.rs deleted file mode 100644 index c897aee8..00000000 --- a/sea-orm-migration/src/statement.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Convert SQL statement - -use crate::into_orm_db_backend; -use sea_schema::migration; - -pub(crate) fn into_orm_stmt(stmt: migration::Statement) -> sea_orm::Statement { - sea_orm::Statement { - sql: stmt.sql, - values: stmt.values, - db_backend: into_orm_db_backend(stmt.db_backend), - } -} diff --git a/sea-orm-migration/tests/main.rs b/sea-orm-migration/tests/main.rs new file mode 100644 index 00000000..b7e7a97b --- /dev/null +++ b/sea-orm-migration/tests/main.rs @@ -0,0 +1,89 @@ +mod migrator; +use migrator::Migrator; + +use sea_orm::{Database, DbErr}; +use sea_orm_migration::prelude::*; + +#[async_std::test] +async fn main() -> Result<(), DbErr> { + let url = std::env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set"); + let db = &Database::connect(&url).await?; + let manager = SchemaManager::new(db); + + println!("\nMigrator::status"); + Migrator::status(db).await?; + + println!("\nMigrator::install"); + Migrator::install(db).await?; + + assert!(manager.has_table("seaql_migrations").await?); + + println!("\nMigrator::reset"); + Migrator::reset(db).await?; + + assert!(!manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::up"); + Migrator::up(db, Some(0)).await?; + + assert!(!manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::up"); + Migrator::up(db, Some(1)).await?; + + assert!(manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::down"); + Migrator::down(db, Some(0)).await?; + + assert!(manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::down"); + Migrator::down(db, Some(1)).await?; + + assert!(!manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::up"); + Migrator::up(db, None).await?; + + println!("\nMigrator::status"); + Migrator::status(db).await?; + + assert!(manager.has_table("cake").await?); + assert!(manager.has_table("fruit").await?); + + println!("\nMigrator::down"); + Migrator::down(db, None).await?; + + assert!(manager.has_table("seaql_migrations").await?); + assert!(!manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::fresh"); + Migrator::fresh(db).await?; + + assert!(manager.has_table("cake").await?); + assert!(manager.has_table("fruit").await?); + + println!("\nMigrator::refresh"); + Migrator::refresh(db).await?; + + assert!(manager.has_table("cake").await?); + assert!(manager.has_table("fruit").await?); + + println!("\nMigrator::reset"); + Migrator::reset(db).await?; + + assert!(!manager.has_table("cake").await?); + assert!(!manager.has_table("fruit").await?); + + println!("\nMigrator::status"); + Migrator::status(db).await?; + + Ok(()) +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs b/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs new file mode 100644 index 00000000..bc70ba3b --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000001_create_cake_table.rs @@ -0,0 +1,43 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000001_create_cake_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Cake::Table) + .col( + ColumnDef::new(Cake::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Cake::Name).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Cake::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum Cake { + Table, + Id, + Name, +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs b/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs new file mode 100644 index 00000000..238e4af3 --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000002_create_fruit_table.rs @@ -0,0 +1,63 @@ +use super::m20220118_000001_create_cake_table::Cake; +use sea_orm::DbBackend; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000002_create_fruit_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Fruit::Table) + .col( + ColumnDef::new(Fruit::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Fruit::Name).string().not_null()) + .col(ColumnDef::new(Fruit::CakeId).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-fruit-cake_id") + .from(Fruit::Table, Fruit::CakeId) + .to(Cake::Table, Cake::Id), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + if manager.get_database_backend() != DbBackend::Sqlite { + manager + .drop_foreign_key( + ForeignKey::drop() + .table(Fruit::Table) + .name("fk-fruit-cake_id") + .to_owned(), + ) + .await?; + } + manager + .drop_table(Table::drop().table(Fruit::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum Fruit { + Table, + Id, + Name, + CakeId, +} diff --git a/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs b/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs new file mode 100644 index 00000000..03ba594c --- /dev/null +++ b/sea-orm-migration/tests/migrator/m20220118_000003_seed_cake_table.rs @@ -0,0 +1,52 @@ +use sea_orm::{entity::prelude::*, Set}; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220118_000003_seed_cake_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + + cake::ActiveModel { + name: Set("Cheesecake".to_owned()), + ..Default::default() + } + .insert(db) + .await + .map(|_| ()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + + cake::Entity::delete_many() + .filter(cake::Column::Name.eq("Cheesecake")) + .exec(db) + .await + .map(|_| ()) + } +} + +mod cake { + use sea_orm::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] + #[sea_orm(table_name = "cake")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation {} + + impl ActiveModelBehavior for ActiveModel {} +} diff --git a/sea-orm-migration/tests/migrator/mod.rs b/sea-orm-migration/tests/migrator/mod.rs new file mode 100644 index 00000000..fdb92d9e --- /dev/null +++ b/sea-orm-migration/tests/migrator/mod.rs @@ -0,0 +1,18 @@ +use sea_orm_migration::prelude::*; + +mod m20220118_000001_create_cake_table; +mod m20220118_000002_create_fruit_table; +mod m20220118_000003_seed_cake_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20220118_000001_create_cake_table::Migration), + Box::new(m20220118_000002_create_fruit_table::Migration), + Box::new(m20220118_000003_seed_cake_table::Migration), + ] + } +}