Restructure sea-orm-cli & sea-orm-migration
This commit is contained in:
parent
498c0154ca
commit
75d5c0f5ea
@ -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",
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
49
sea-orm-cli/src/migration.rs
Normal file
49
sea-orm-cli/src/migration.rs
Normal file
@ -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<App<'static, 'static>> {
|
||||
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"),
|
||||
),
|
||||
]
|
||||
}
|
@ -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",
|
||||
|
@ -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"]
|
||||
|
@ -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<M>(migrator: M)
|
||||
where
|
||||
M: MigratorTrait,
|
||||
@ -21,20 +19,20 @@ where
|
||||
get_matches(migrator, db, app).await;
|
||||
}
|
||||
|
||||
async fn get_matches<M>(_: M, db: &DbConn, app: App<'static, 'static>)
|
||||
pub async fn get_matches<M>(_: M, db: &DbConn, app: App<'static, 'static>)
|
||||
where
|
||||
M: MigratorTrait,
|
||||
{
|
||||
let matches = app.get_matches();
|
||||
let mut verbose = false;
|
||||
let filter = match matches.subcommand() {
|
||||
(_, None) => "sea_orm::migration=info",
|
||||
(_, 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<E>(error: E)
|
||||
where
|
||||
E: Display,
|
||||
|
@ -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<Option<Box<dyn QueryResultTrait>>, MigrationErr> {
|
||||
ConnectionTrait::query_one(self.conn, into_orm_stmt(stmt))
|
||||
.await
|
||||
.map(|res| res.map(|res| Box::new(QueryResult { res }) as Box<dyn QueryResultTrait>))
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
|
||||
async fn query_all(
|
||||
&self,
|
||||
stmt: migration::Statement,
|
||||
) -> Result<Vec<Box<dyn QueryResultTrait>>, MigrationErr> {
|
||||
ConnectionTrait::query_all(self.conn, into_orm_stmt(stmt))
|
||||
.await
|
||||
.map(|rows| {
|
||||
rows.into_iter()
|
||||
.map(|res| Box::new(QueryResult { res }) as Box<dyn QueryResultTrait>)
|
||||
.collect()
|
||||
})
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
@ -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
|
||||
|
@ -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<S>(&self, stmt: S) -> Result<(), DbErr>
|
||||
where
|
||||
S: StatementBuilder,
|
||||
{
|
||||
let builder = self.conn.conn.get_database_backend();
|
||||
self.conn
|
||||
.conn
|
||||
.execute(builder.build(&stmt))
|
||||
.await
|
||||
.map(|_| ())
|
||||
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<T>(&self, table: T) -> Result<bool, DbErr>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
migration::SchemaManager::has_table(table, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
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<T, C>(&self, table: T, column: C) -> Result<bool, DbErr>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
C: AsRef<str>,
|
||||
{
|
||||
migration::SchemaManager::has_column(table, column, &self.conn)
|
||||
.await
|
||||
.map_err(into_orm_db_err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn MigrationTrait>,
|
||||
status: MigrationStatus,
|
||||
|
@ -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;
|
||||
|
@ -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<String, MigrationErr> {
|
||||
self.res
|
||||
.try_get::<String>("", col)
|
||||
.map_err(into_migration_err)
|
||||
}
|
||||
|
||||
fn try_get_i64(&self, col: &str) -> Result<i64, MigrationErr> {
|
||||
self.res.try_get::<i64>("", col).map_err(into_migration_err)
|
||||
}
|
||||
}
|
@ -1,7 +1,3 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! Migration entity
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
89
sea-orm-migration/tests/main.rs
Normal file
89
sea-orm-migration/tests/main.rs
Normal file
@ -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(())
|
||||
}
|
@ -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,
|
||||
}
|
@ -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,
|
||||
}
|
@ -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 {}
|
||||
}
|
18
sea-orm-migration/tests/migrator/mod.rs
Normal file
18
sea-orm-migration/tests/migrator/mod.rs
Normal file
@ -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<Box<dyn MigrationTrait>> {
|
||||
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),
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user