Merge branch 'master' into ss/rocket-example
# Conflicts: # Cargo.toml
This commit is contained in:
commit
71926cfd4b
18
.github/workflows/rust.yml
vendored
18
.github/workflows/rust.yml
vendored
@ -11,6 +11,24 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
components: clippy
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-targets --all
|
||||||
|
|
||||||
compile-sqlite:
|
compile-sqlite:
|
||||||
name: Compile SQLite
|
name: Compile SQLite
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
13
Cargo.toml
13
Cargo.toml
@ -27,8 +27,8 @@ chrono = { version = "^0", optional = true }
|
|||||||
futures = { version = "^0.3" }
|
futures = { version = "^0.3" }
|
||||||
futures-util = { version = "^0.3" }
|
futures-util = { version = "^0.3" }
|
||||||
rust_decimal = { version = "^1", optional = true }
|
rust_decimal = { version = "^1", optional = true }
|
||||||
sea-orm-macros = { version = "^0.1.1", optional = true }
|
sea-orm-macros = { version = "^0.1.1", path = "sea-orm-macros", optional = true }
|
||||||
sea-query = { version = "^0.15", features = ["thread-safe"] }
|
sea-query = { version = "^0.16", features = ["thread-safe"] }
|
||||||
sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] }
|
sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
sqlx = { version = "^0.5", optional = true }
|
sqlx = { version = "^0.5", optional = true }
|
||||||
@ -36,8 +36,11 @@ sqlx-core = { version = "^0.5", optional = true }
|
|||||||
sqlx-macros = { version = "^0.5", optional = true }
|
sqlx-macros = { version = "^0.5", optional = true }
|
||||||
serde_json = { version = "^1", optional = true }
|
serde_json = { version = "^1", optional = true }
|
||||||
uuid = { version = "0.8", features = ["serde", "v4"], optional = true }
|
uuid = { version = "0.8", features = ["serde", "v4"], optional = true }
|
||||||
rocket_db_pools = { git = "https://github.com/SergioBenitez/Rocket.git", features = ["sqlx_mysql"], optional = true }
|
rocket_db_pools = { git = "https://github.com/SergioBenitez/Rocket.git", features = [
|
||||||
|
"sqlx_mysql",
|
||||||
|
], optional = true }
|
||||||
async-trait = { version = "0.1", optional = true }
|
async-trait = { version = "0.1", optional = true }
|
||||||
|
log = { version = "^0.4", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
smol = { version = "^1.2" }
|
smol = { version = "^1.2" }
|
||||||
@ -47,10 +50,12 @@ tokio = { version = "^1.6", features = ["full"] }
|
|||||||
actix-rt = { version = "2.2.0" }
|
actix-rt = { version = "2.2.0" }
|
||||||
maplit = { version = "^1" }
|
maplit = { version = "^1" }
|
||||||
rust_decimal_macros = { version = "^1" }
|
rust_decimal_macros = { version = "^1" }
|
||||||
|
env_logger = { version = "^0.9" }
|
||||||
sea-orm = { path = ".", features = ["debug-print"] }
|
sea-orm = { path = ".", features = ["debug-print"] }
|
||||||
|
pretty_assertions = { version = "^0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug-print = []
|
debug-print = ["log"]
|
||||||
default = [
|
default = [
|
||||||
"macros",
|
"macros",
|
||||||
"mock",
|
"mock",
|
||||||
|
@ -87,7 +87,7 @@ let pear = fruit::ActiveModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// insert one
|
// insert one
|
||||||
let res: InsertResult = Fruit::insert(pear).exec(db).await?;
|
let res = Fruit::insert(pear).exec(db).await?;
|
||||||
|
|
||||||
println!("InsertResult: {}", res.last_insert_id);
|
println!("InsertResult: {}", res.last_insert_id);
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> {
|
|||||||
name: Set("pear".to_owned()),
|
name: Set("pear".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res: InsertResult = Fruit::insert(pear).exec(db).await?;
|
let res = Fruit::insert(pear).exec(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!("Inserted: last_insert_id = {}\n", res.last_insert_id);
|
println!("Inserted: last_insert_id = {}\n", res.last_insert_id);
|
||||||
|
@ -2,32 +2,32 @@ use sea_orm::sea_query::{ColumnDef, TableCreateStatement};
|
|||||||
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
|
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
|
||||||
|
|
||||||
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
||||||
let builder = db.get_database_backend();
|
let builder = db.get_database_backend();
|
||||||
db.execute(builder.build(stmt)).await
|
db.execute(builder.build(stmt)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = sea_query::Table::create()
|
||||||
.table(super::post::Entity)
|
.table(super::post::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(super::post::Column::Id)
|
ColumnDef::new(super::post::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(super::post::Column::Title)
|
ColumnDef::new(super::post::Column::Title)
|
||||||
.string()
|
.string()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(super::post::Column::Text)
|
ColumnDef::new(super::post::Column::Text)
|
||||||
.string()
|
.string()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).await
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
format_code_in_doc_comments=true
|
@ -117,6 +117,31 @@ impl Entity {
|
|||||||
format_ident!("{}", auto_increment)
|
format_ident!("{}", auto_increment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_primary_key_rs_type(&self) -> TokenStream {
|
||||||
|
let types = self
|
||||||
|
.primary_keys
|
||||||
|
.iter()
|
||||||
|
.map(|primary_key| {
|
||||||
|
self.columns
|
||||||
|
.iter()
|
||||||
|
.find(|col| col.name.eq(&primary_key.name))
|
||||||
|
.unwrap()
|
||||||
|
.get_rs_type()
|
||||||
|
.to_string()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !types.is_empty() {
|
||||||
|
let value_type = if types.len() > 1 {
|
||||||
|
vec!["(".to_owned(), types.join(", "), ")".to_owned()]
|
||||||
|
} else {
|
||||||
|
types
|
||||||
|
};
|
||||||
|
value_type.join("").parse().unwrap()
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_conjunct_relations_via_snake_case(&self) -> Vec<Ident> {
|
pub fn get_conjunct_relations_via_snake_case(&self) -> Vec<Ident> {
|
||||||
self.conjunct_relations
|
self.conjunct_relations
|
||||||
.iter()
|
.iter()
|
||||||
@ -151,7 +176,7 @@ mod tests {
|
|||||||
columns: vec![
|
columns: vec![
|
||||||
Column {
|
Column {
|
||||||
name: "id".to_owned(),
|
name: "id".to_owned(),
|
||||||
col_type: ColumnType::String(None),
|
col_type: ColumnType::Integer(None),
|
||||||
auto_increment: false,
|
auto_increment: false,
|
||||||
not_null: false,
|
not_null: false,
|
||||||
unique: false,
|
unique: false,
|
||||||
@ -373,6 +398,16 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_primary_key_rs_type() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_primary_key_rs_type().to_string(),
|
||||||
|
entity.columns[0].get_rs_type().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_conjunct_relations_via_snake_case() {
|
fn test_get_conjunct_relations_via_snake_case() {
|
||||||
let entity = setup();
|
let entity = setup();
|
||||||
|
@ -119,33 +119,18 @@ impl From<&ColumnDef> for Column {
|
|||||||
Some(ty) => ty.clone(),
|
Some(ty) => ty.clone(),
|
||||||
None => panic!("ColumnType should not be empty"),
|
None => panic!("ColumnType should not be empty"),
|
||||||
};
|
};
|
||||||
let auto_increments: Vec<bool> = col_def
|
let auto_increment = col_def
|
||||||
.get_column_spec()
|
.get_column_spec()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|spec| match spec {
|
.any(|spec| matches!(spec, ColumnSpec::AutoIncrement));
|
||||||
ColumnSpec::AutoIncrement => Some(true),
|
let not_null = col_def
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let auto_increment = !auto_increments.is_empty();
|
|
||||||
let not_nulls: Vec<bool> = col_def
|
|
||||||
.get_column_spec()
|
.get_column_spec()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|spec| match spec {
|
.any(|spec| matches!(spec, ColumnSpec::NotNull));
|
||||||
ColumnSpec::NotNull => Some(true),
|
let unique = col_def
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let not_null = !not_nulls.is_empty();
|
|
||||||
let uniques: Vec<bool> = col_def
|
|
||||||
.get_column_spec()
|
.get_column_spec()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|spec| match spec {
|
.any(|spec| matches!(spec, ColumnSpec::UniqueKey));
|
||||||
ColumnSpec::UniqueKey => Some(true),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let unique = !uniques.is_empty();
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
col_type,
|
col_type,
|
||||||
|
@ -34,11 +34,6 @@ impl EntityTransformer {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|col_def| col_def.into())
|
.map(|col_def| col_def.into())
|
||||||
.collect();
|
.collect();
|
||||||
let unique_columns: Vec<String> = columns
|
|
||||||
.iter()
|
|
||||||
.filter(|col| col.unique)
|
|
||||||
.map(|col| col.name.clone())
|
|
||||||
.collect();
|
|
||||||
let relations = table_create
|
let relations = table_create
|
||||||
.get_foreign_key_create_stmts()
|
.get_foreign_key_create_stmts()
|
||||||
.iter()
|
.iter()
|
||||||
@ -85,8 +80,13 @@ impl EntityTransformer {
|
|||||||
false => {
|
false => {
|
||||||
let ref_table = rel.ref_table;
|
let ref_table = rel.ref_table;
|
||||||
let mut unique = true;
|
let mut unique = true;
|
||||||
for col in rel.columns.iter() {
|
for column in rel.columns.iter() {
|
||||||
if !unique_columns.contains(col) {
|
if !entity
|
||||||
|
.columns
|
||||||
|
.iter()
|
||||||
|
.filter(|col| col.unique)
|
||||||
|
.any(|col| col.name.as_str() == column)
|
||||||
|
{
|
||||||
unique = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -173,8 +173,11 @@ impl EntityWriter {
|
|||||||
|
|
||||||
pub fn gen_impl_primary_key(entity: &Entity) -> TokenStream {
|
pub fn gen_impl_primary_key(entity: &Entity) -> TokenStream {
|
||||||
let primary_key_auto_increment = entity.get_primary_key_auto_increment();
|
let primary_key_auto_increment = entity.get_primary_key_auto_increment();
|
||||||
|
let value_type = entity.get_primary_key_rs_type();
|
||||||
quote! {
|
quote! {
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = #value_type;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
#primary_key_auto_increment
|
#primary_key_auto_increment
|
||||||
}
|
}
|
||||||
@ -305,7 +308,7 @@ mod tests {
|
|||||||
use sea_query::ColumnType;
|
use sea_query::ColumnType;
|
||||||
use std::io::{self, BufRead, BufReader};
|
use std::io::{self, BufRead, BufReader};
|
||||||
|
|
||||||
const ENTITY_FILES: [&'static str; 5] = [
|
const ENTITY_FILES: [&str; 5] = [
|
||||||
include_str!("../../tests/entity/cake.rs"),
|
include_str!("../../tests/entity/cake.rs"),
|
||||||
include_str!("../../tests/entity/cake_filling.rs"),
|
include_str!("../../tests/entity/cake_filling.rs"),
|
||||||
include_str!("../../tests/entity/filling.rs"),
|
include_str!("../../tests/entity/filling.rs"),
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,10 @@ pub fn test(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[test]
|
#[test]
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
fn #name() #ret {
|
fn #name() #ret {
|
||||||
|
let _ = ::env_logger::builder()
|
||||||
|
.filter_level(::log::LevelFilter::Debug)
|
||||||
|
.is_test(true)
|
||||||
|
.try_init();
|
||||||
crate::block_on!(async { #body })
|
crate::block_on!(async { #body })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -75,7 +75,7 @@ impl DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await,
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await,
|
||||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ impl DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await,
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await,
|
||||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ impl DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await,
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await,
|
||||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,19 +2,22 @@ use crate::{
|
|||||||
debug_print, error::*, DatabaseConnection, DbBackend, ExecResult, MockDatabase, QueryResult,
|
debug_print, error::*, DatabaseConnection, DbBackend, ExecResult, MockDatabase, QueryResult,
|
||||||
Statement, Transaction,
|
Statement, Transaction,
|
||||||
};
|
};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
Mutex,
|
Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MockDatabaseConnector;
|
pub struct MockDatabaseConnector;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MockDatabaseConnection {
|
pub struct MockDatabaseConnection {
|
||||||
counter: AtomicUsize,
|
counter: AtomicUsize,
|
||||||
mocker: Mutex<Box<dyn MockDatabaseTrait>>,
|
mocker: Mutex<Box<dyn MockDatabaseTrait>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MockDatabaseTrait: Send {
|
pub trait MockDatabaseTrait: Send + Debug {
|
||||||
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>;
|
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>;
|
||||||
|
|
||||||
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
|
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
|
||||||
|
@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
|
|||||||
|
|
||||||
use super::sqlx_common::*;
|
use super::sqlx_common::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxMySqlConnector;
|
pub struct SqlxMySqlConnector;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxMySqlPoolConnection {
|
pub struct SqlxMySqlPoolConnection {
|
||||||
pool: MySqlPool,
|
pool: MySqlPool,
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
|
|||||||
|
|
||||||
use super::sqlx_common::*;
|
use super::sqlx_common::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxPostgresConnector;
|
pub struct SqlxPostgresConnector;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxPostgresPoolConnection {
|
pub struct SqlxPostgresPoolConnection {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
}
|
}
|
||||||
@ -102,24 +104,11 @@ impl From<PgRow> for QueryResult {
|
|||||||
impl From<PgQueryResult> for ExecResult {
|
impl From<PgQueryResult> for ExecResult {
|
||||||
fn from(result: PgQueryResult) -> ExecResult {
|
fn from(result: PgQueryResult) -> ExecResult {
|
||||||
ExecResult {
|
ExecResult {
|
||||||
result: ExecResultHolder::SqlxPostgres {
|
result: ExecResultHolder::SqlxPostgres(result),
|
||||||
last_insert_id: 0,
|
|
||||||
rows_affected: result.rows_affected(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result<ExecResult, DbErr> {
|
|
||||||
let last_insert_id: i32 = res.try_get("", "last_insert_id")?;
|
|
||||||
Ok(ExecResult {
|
|
||||||
result: ExecResultHolder::SqlxPostgres {
|
|
||||||
last_insert_id: last_insert_id as u64,
|
|
||||||
rows_affected: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, PgArguments> {
|
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, PgArguments> {
|
||||||
let mut query = sqlx::query(&stmt.sql);
|
let mut query = sqlx::query(&stmt.sql);
|
||||||
if let Some(values) = &stmt.values {
|
if let Some(values) = &stmt.values {
|
||||||
|
@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
|
|||||||
|
|
||||||
use super::sqlx_common::*;
|
use super::sqlx_common::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxSqliteConnector;
|
pub struct SqlxSqliteConnector;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SqlxSqlitePoolConnection {
|
pub struct SqlxSqlitePoolConnection {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
}
|
}
|
||||||
|
@ -221,16 +221,18 @@ where
|
|||||||
let exec = E::insert(am).exec(db);
|
let exec = E::insert(am).exec(db);
|
||||||
let res = exec.await?;
|
let res = exec.await?;
|
||||||
// TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value
|
// TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value
|
||||||
if <E::PrimaryKey as PrimaryKeyTrait>::auto_increment() && res.last_insert_id != 0 {
|
// FIXME: Assumed valid last_insert_id is not equals to Default::default()
|
||||||
|
if <E::PrimaryKey as PrimaryKeyTrait>::auto_increment()
|
||||||
|
&& res.last_insert_id != <E::PrimaryKey as PrimaryKeyTrait>::ValueType::default()
|
||||||
|
{
|
||||||
let find = E::find_by_id(res.last_insert_id).one(db);
|
let find = E::find_by_id(res.last_insert_id).one(db);
|
||||||
let found = find.await;
|
let found = find.await;
|
||||||
let model: Option<E::Model> = found?;
|
let model: Option<E::Model> = found?;
|
||||||
match model {
|
match model {
|
||||||
Some(model) => Ok(model.into_active_model()),
|
Some(model) => Ok(model.into_active_model()),
|
||||||
None => Err(DbErr::Exec(format!(
|
None => Err(DbErr::Exec(format!(
|
||||||
"Failed to find inserted item: {} {}",
|
"Failed to find inserted item: {}",
|
||||||
E::default().to_string(),
|
E::default().to_string(),
|
||||||
res.last_insert_id
|
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,21 +55,21 @@ pub trait EntityTrait: EntityName {
|
|||||||
where
|
where
|
||||||
R: EntityTrait,
|
R: EntityTrait,
|
||||||
{
|
{
|
||||||
RelationBuilder::new(RelationType::HasOne, Self::default(), related)
|
RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_one<R>(_: R) -> RelationBuilder<Self, R>
|
fn has_one<R>(_: R) -> RelationBuilder<Self, R>
|
||||||
where
|
where
|
||||||
R: EntityTrait + Related<Self>,
|
R: EntityTrait + Related<Self>,
|
||||||
{
|
{
|
||||||
RelationBuilder::from_rel(RelationType::HasOne, R::to().rev())
|
RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_many<R>(_: R) -> RelationBuilder<Self, R>
|
fn has_many<R>(_: R) -> RelationBuilder<Self, R>
|
||||||
where
|
where
|
||||||
R: EntityTrait + Related<Self>,
|
R: EntityTrait + Related<Self>,
|
||||||
{
|
{
|
||||||
RelationBuilder::from_rel(RelationType::HasMany, R::to().rev())
|
RelationBuilder::from_rel(RelationType::HasMany, R::to().rev(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct select statement to find one / all models
|
/// Construct select statement to find one / all models
|
||||||
@ -137,13 +137,18 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// db.into_transaction_log(),
|
/// db.into_transaction_log(),
|
||||||
/// vec![
|
/// vec![
|
||||||
/// Transaction::from_sql_and_values(
|
/// Transaction::from_sql_and_values(
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, vec![1u64.into()]
|
/// DbBackend::Postgres,
|
||||||
/// ),
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
|
||||||
/// Transaction::from_sql_and_values(
|
/// vec![1u64.into()]
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
/// ),
|
||||||
/// ),
|
/// Transaction::from_sql_and_values(
|
||||||
/// ]);
|
/// DbBackend::Postgres,
|
||||||
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
|
||||||
|
/// vec![]
|
||||||
|
/// ),
|
||||||
|
/// ]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
fn find() -> Select<Self> {
|
fn find() -> Select<Self> {
|
||||||
Select::new()
|
Select::new()
|
||||||
@ -186,8 +191,11 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// db.into_transaction_log(),
|
/// db.into_transaction_log(),
|
||||||
/// vec![Transaction::from_sql_and_values(
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#, vec![11i32.into()]
|
/// DbBackend::Postgres,
|
||||||
/// )]);
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#,
|
||||||
|
/// vec![11i32.into()]
|
||||||
|
/// )]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
/// Find by composite key
|
/// Find by composite key
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -103,7 +103,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
/// .filter(cake::Column::Id.between(2,3))
|
/// .filter(cake::Column::Id.between(2, 3))
|
||||||
/// .build(DbBackend::MySql)
|
/// .build(DbBackend::MySql)
|
||||||
/// .to_string(),
|
/// .to_string(),
|
||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` BETWEEN 2 AND 3"
|
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` BETWEEN 2 AND 3"
|
||||||
@ -121,7 +121,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
/// .filter(cake::Column::Id.not_between(2,3))
|
/// .filter(cake::Column::Id.not_between(2, 3))
|
||||||
/// .build(DbBackend::MySql)
|
/// .build(DbBackend::MySql)
|
||||||
/// .to_string(),
|
/// .to_string(),
|
||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` NOT BETWEEN 2 AND 3"
|
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` NOT BETWEEN 2 AND 3"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{ColumnTrait, EntityTrait, IdenStatic};
|
use crate::{ColumnTrait, EntityTrait, IdenStatic};
|
||||||
use sea_query::{DynIden, IntoIden};
|
use sea_query::{Alias, DynIden, Iden, IntoIden, SeaRc};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Identity {
|
pub enum Identity {
|
||||||
@ -8,6 +9,25 @@ pub enum Identity {
|
|||||||
Ternary(DynIden, DynIden, DynIden),
|
Ternary(DynIden, DynIden, DynIden),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Iden for Identity {
|
||||||
|
fn unquoted(&self, s: &mut dyn fmt::Write) {
|
||||||
|
match self {
|
||||||
|
Identity::Unary(iden) => {
|
||||||
|
write!(s, "{}", iden.to_string()).unwrap();
|
||||||
|
}
|
||||||
|
Identity::Binary(iden1, iden2) => {
|
||||||
|
write!(s, "{}", iden1.to_string()).unwrap();
|
||||||
|
write!(s, "{}", iden2.to_string()).unwrap();
|
||||||
|
}
|
||||||
|
Identity::Ternary(iden1, iden2, iden3) => {
|
||||||
|
write!(s, "{}", iden1.to_string()).unwrap();
|
||||||
|
write!(s, "{}", iden2.to_string()).unwrap();
|
||||||
|
write!(s, "{}", iden3.to_string()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait IntoIdentity {
|
pub trait IntoIdentity {
|
||||||
fn into_identity(self) -> Identity;
|
fn into_identity(self) -> Identity;
|
||||||
}
|
}
|
||||||
@ -19,6 +39,18 @@ where
|
|||||||
fn identity_of(self) -> Identity;
|
fn identity_of(self) -> Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIdentity for String {
|
||||||
|
fn into_identity(self) -> Identity {
|
||||||
|
self.as_str().into_identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIdentity for &str {
|
||||||
|
fn into_identity(self) -> Identity {
|
||||||
|
Identity::Unary(SeaRc::new(Alias::new(self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> IntoIdentity for T
|
impl<T> IntoIdentity for T
|
||||||
where
|
where
|
||||||
T: IdenStatic,
|
T: IdenStatic,
|
||||||
|
@ -6,6 +6,7 @@ mod model;
|
|||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod primary_key;
|
mod primary_key;
|
||||||
mod relation;
|
mod relation;
|
||||||
|
mod schema;
|
||||||
|
|
||||||
pub use active_model::*;
|
pub use active_model::*;
|
||||||
pub use base_entity::*;
|
pub use base_entity::*;
|
||||||
@ -15,3 +16,4 @@ pub use model::*;
|
|||||||
// pub use prelude::*;
|
// pub use prelude::*;
|
||||||
pub use primary_key::*;
|
pub use primary_key::*;
|
||||||
pub use relation::*;
|
pub use relation::*;
|
||||||
|
pub use schema::*;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{DbErr, EntityTrait, QueryFilter, QueryResult, Related, Select};
|
use crate::{DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Select};
|
||||||
pub use sea_query::Value;
|
pub use sea_query::Value;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@ -16,6 +16,13 @@ pub trait ModelTrait: Clone + Debug {
|
|||||||
{
|
{
|
||||||
<Self::Entity as Related<R>>::find_related().belongs_to(self)
|
<Self::Entity as Related<R>>::find_related().belongs_to(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_linked<L>(&self, l: L) -> Select<L::ToEntity>
|
||||||
|
where
|
||||||
|
L: Linked<FromEntity = Self::Entity>,
|
||||||
|
{
|
||||||
|
l.find_linked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromQueryResult {
|
pub trait FromQueryResult {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType,
|
error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType,
|
||||||
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
||||||
DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait,
|
DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, ForeignKeyAction, Iden,
|
||||||
PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef,
|
IdenStatic, Linked, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult,
|
||||||
RelationTrait, Select, Value,
|
Related, RelationDef, RelationTrait, Select, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "with-json")]
|
#[cfg(feature = "with-json")]
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
use super::{ColumnTrait, IdenStatic, Iterable};
|
use super::{ColumnTrait, IdenStatic, Iterable};
|
||||||
|
use crate::{TryFromU64, TryGetableMany};
|
||||||
|
use sea_query::IntoValueTuple;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
//LINT: composite primary key cannot auto increment
|
//LINT: composite primary key cannot auto increment
|
||||||
pub trait PrimaryKeyTrait: IdenStatic + Iterable {
|
pub trait PrimaryKeyTrait: IdenStatic + Iterable {
|
||||||
|
type ValueType: Sized
|
||||||
|
+ Default
|
||||||
|
+ Debug
|
||||||
|
+ PartialEq
|
||||||
|
+ IntoValueTuple
|
||||||
|
+ TryGetableMany
|
||||||
|
+ TryFromU64;
|
||||||
|
|
||||||
fn auto_increment() -> bool;
|
fn auto_increment() -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ pub enum RelationType {
|
|||||||
HasMany,
|
HasMany,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ForeignKeyAction = sea_query::ForeignKeyAction;
|
||||||
|
|
||||||
pub trait RelationTrait: Iterable + Debug + 'static {
|
pub trait RelationTrait: Iterable + Debug + 'static {
|
||||||
fn def(&self) -> RelationDef;
|
fn def(&self) -> RelationDef;
|
||||||
}
|
}
|
||||||
@ -28,14 +30,35 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Linked {
|
||||||
|
type FromEntity: EntityTrait;
|
||||||
|
|
||||||
|
type ToEntity: EntityTrait;
|
||||||
|
|
||||||
|
fn link(&self) -> Vec<RelationDef>;
|
||||||
|
|
||||||
|
fn find_linked(&self) -> Select<Self::ToEntity> {
|
||||||
|
let mut select = Select::new();
|
||||||
|
for rel in self.link().into_iter().rev() {
|
||||||
|
select = select.join_rev(JoinType::InnerJoin, rel);
|
||||||
|
}
|
||||||
|
select
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RelationDef {
|
pub struct RelationDef {
|
||||||
pub rel_type: RelationType,
|
pub rel_type: RelationType,
|
||||||
pub from_tbl: TableRef,
|
pub from_tbl: TableRef,
|
||||||
pub to_tbl: TableRef,
|
pub to_tbl: TableRef,
|
||||||
pub from_col: Identity,
|
pub from_col: Identity,
|
||||||
pub to_col: Identity,
|
pub to_col: Identity,
|
||||||
|
pub is_owner: bool,
|
||||||
|
pub on_delete: Option<ForeignKeyAction>,
|
||||||
|
pub on_update: Option<ForeignKeyAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RelationBuilder<E, R>
|
pub struct RelationBuilder<E, R>
|
||||||
where
|
where
|
||||||
E: EntityTrait,
|
E: EntityTrait,
|
||||||
@ -47,6 +70,9 @@ where
|
|||||||
to_tbl: TableRef,
|
to_tbl: TableRef,
|
||||||
from_col: Option<Identity>,
|
from_col: Option<Identity>,
|
||||||
to_col: Option<Identity>,
|
to_col: Option<Identity>,
|
||||||
|
is_owner: bool,
|
||||||
|
on_delete: Option<ForeignKeyAction>,
|
||||||
|
on_update: Option<ForeignKeyAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelationDef {
|
impl RelationDef {
|
||||||
@ -58,6 +84,9 @@ impl RelationDef {
|
|||||||
to_tbl: self.from_tbl,
|
to_tbl: self.from_tbl,
|
||||||
from_col: self.to_col,
|
from_col: self.to_col,
|
||||||
to_col: self.from_col,
|
to_col: self.from_col,
|
||||||
|
is_owner: !self.is_owner,
|
||||||
|
on_delete: self.on_delete,
|
||||||
|
on_update: self.on_update,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +96,7 @@ where
|
|||||||
E: EntityTrait,
|
E: EntityTrait,
|
||||||
R: EntityTrait,
|
R: EntityTrait,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(rel_type: RelationType, from: E, to: R) -> Self {
|
pub(crate) fn new(rel_type: RelationType, from: E, to: R, is_owner: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entities: PhantomData,
|
entities: PhantomData,
|
||||||
rel_type,
|
rel_type,
|
||||||
@ -75,10 +104,13 @@ where
|
|||||||
to_tbl: to.table_ref(),
|
to_tbl: to.table_ref(),
|
||||||
from_col: None,
|
from_col: None,
|
||||||
to_col: None,
|
to_col: None,
|
||||||
|
is_owner,
|
||||||
|
on_delete: None,
|
||||||
|
on_update: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_rel(rel_type: RelationType, rel: RelationDef) -> Self {
|
pub(crate) fn from_rel(rel_type: RelationType, rel: RelationDef, is_owner: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entities: PhantomData,
|
entities: PhantomData,
|
||||||
rel_type,
|
rel_type,
|
||||||
@ -86,6 +118,9 @@ where
|
|||||||
to_tbl: rel.to_tbl,
|
to_tbl: rel.to_tbl,
|
||||||
from_col: Some(rel.from_col),
|
from_col: Some(rel.from_col),
|
||||||
to_col: Some(rel.to_col),
|
to_col: Some(rel.to_col),
|
||||||
|
is_owner,
|
||||||
|
on_delete: None,
|
||||||
|
on_update: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +139,16 @@ where
|
|||||||
self.to_col = Some(identifier.identity_of());
|
self.to_col = Some(identifier.identity_of());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_delete(mut self, action: ForeignKeyAction) -> Self {
|
||||||
|
self.on_delete = Some(action);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_update(mut self, action: ForeignKeyAction) -> Self {
|
||||||
|
self.on_update = Some(action);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, R> From<RelationBuilder<E, R>> for RelationDef
|
impl<E, R> From<RelationBuilder<E, R>> for RelationDef
|
||||||
@ -118,6 +163,9 @@ where
|
|||||||
to_tbl: b.to_tbl,
|
to_tbl: b.to_tbl,
|
||||||
from_col: b.from_col.unwrap(),
|
from_col: b.from_col.unwrap(),
|
||||||
to_col: b.to_col.unwrap(),
|
to_col: b.to_col.unwrap(),
|
||||||
|
is_owner: b.is_owner,
|
||||||
|
on_delete: b.on_delete,
|
||||||
|
on_update: b.on_update,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
src/entity/schema.rs
Normal file
161
src/entity/schema.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
use crate::{
|
||||||
|
unpack_table_ref, ColumnTrait, EntityTrait, Identity, Iterable, PrimaryKeyToColumn,
|
||||||
|
PrimaryKeyTrait, RelationTrait,
|
||||||
|
};
|
||||||
|
use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement};
|
||||||
|
|
||||||
|
pub fn entity_to_table_create_statement<E>(entity: E) -> TableCreateStatement
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
{
|
||||||
|
let mut stmt = TableCreateStatement::new();
|
||||||
|
|
||||||
|
for column in E::Column::iter() {
|
||||||
|
let orm_column_def = column.def();
|
||||||
|
let types = orm_column_def.col_type.into();
|
||||||
|
let mut column_def = ColumnDef::new_with_type(column, types);
|
||||||
|
if !orm_column_def.null {
|
||||||
|
column_def.not_null();
|
||||||
|
}
|
||||||
|
if orm_column_def.unique {
|
||||||
|
column_def.unique_key();
|
||||||
|
}
|
||||||
|
for primary_key in E::PrimaryKey::iter() {
|
||||||
|
if column.to_string() == primary_key.into_column().to_string() {
|
||||||
|
if E::PrimaryKey::auto_increment() {
|
||||||
|
column_def.auto_increment();
|
||||||
|
}
|
||||||
|
if E::PrimaryKey::iter().count() == 1 {
|
||||||
|
column_def.primary_key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if orm_column_def.indexed {
|
||||||
|
stmt.index(
|
||||||
|
Index::create()
|
||||||
|
.name(&format!(
|
||||||
|
"idx-{}-{}",
|
||||||
|
entity.to_string(),
|
||||||
|
column.to_string()
|
||||||
|
))
|
||||||
|
.table(entity)
|
||||||
|
.col(column),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
stmt.col(&mut column_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
if E::PrimaryKey::iter().count() > 1 {
|
||||||
|
let mut idx_pk = Index::create();
|
||||||
|
for primary_key in E::PrimaryKey::iter() {
|
||||||
|
idx_pk.col(primary_key);
|
||||||
|
}
|
||||||
|
stmt.primary_key(idx_pk.name(&format!("pk-{}", entity.to_string())).primary());
|
||||||
|
}
|
||||||
|
|
||||||
|
for relation in E::Relation::iter() {
|
||||||
|
let relation = relation.def();
|
||||||
|
if relation.is_owner {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut foreign_key_stmt = ForeignKeyCreateStatement::new();
|
||||||
|
let from_tbl = unpack_table_ref(&relation.from_tbl);
|
||||||
|
let to_tbl = unpack_table_ref(&relation.to_tbl);
|
||||||
|
match relation.from_col {
|
||||||
|
Identity::Unary(o1) => {
|
||||||
|
foreign_key_stmt.from_col(o1);
|
||||||
|
}
|
||||||
|
Identity::Binary(o1, o2) => {
|
||||||
|
foreign_key_stmt.from_col(o1);
|
||||||
|
foreign_key_stmt.from_col(o2);
|
||||||
|
}
|
||||||
|
Identity::Ternary(o1, o2, o3) => {
|
||||||
|
foreign_key_stmt.from_col(o1);
|
||||||
|
foreign_key_stmt.from_col(o2);
|
||||||
|
foreign_key_stmt.from_col(o3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match relation.to_col {
|
||||||
|
Identity::Unary(o1) => {
|
||||||
|
foreign_key_stmt.to_col(o1);
|
||||||
|
}
|
||||||
|
Identity::Binary(o1, o2) => {
|
||||||
|
foreign_key_stmt.to_col(o1);
|
||||||
|
foreign_key_stmt.to_col(o2);
|
||||||
|
}
|
||||||
|
crate::Identity::Ternary(o1, o2, o3) => {
|
||||||
|
foreign_key_stmt.to_col(o1);
|
||||||
|
foreign_key_stmt.to_col(o2);
|
||||||
|
foreign_key_stmt.to_col(o3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(action) = relation.on_delete {
|
||||||
|
foreign_key_stmt.on_delete(action);
|
||||||
|
}
|
||||||
|
if let Some(action) = relation.on_update {
|
||||||
|
foreign_key_stmt.on_update(action);
|
||||||
|
}
|
||||||
|
stmt.foreign_key(
|
||||||
|
foreign_key_stmt
|
||||||
|
.name(&format!(
|
||||||
|
"fk-{}-{}",
|
||||||
|
from_tbl.to_string(),
|
||||||
|
to_tbl.to_string()
|
||||||
|
))
|
||||||
|
.from_tbl(from_tbl)
|
||||||
|
.to_tbl(to_tbl),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt.table(entity).if_not_exists().take()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{entity_to_table_create_statement, tests_cfg::*};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sea_query::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entity_to_table_create_statement() {
|
||||||
|
assert_eq!(
|
||||||
|
entity_to_table_create_statement(CakeFillingPrice).to_string(MysqlQueryBuilder),
|
||||||
|
Table::create()
|
||||||
|
.table(CakeFillingPrice)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(cake_filling_price::Column::CakeId)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(cake_filling_price::Column::FillingId)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(cake_filling_price::Column::Price)
|
||||||
|
.decimal()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.primary_key(
|
||||||
|
Index::create()
|
||||||
|
.name("pk-cake_filling_price")
|
||||||
|
.col(cake_filling_price::Column::CakeId)
|
||||||
|
.col(cake_filling_price::Column::FillingId)
|
||||||
|
.primary()
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKeyCreateStatement::new()
|
||||||
|
.name("fk-cake_filling_price-cake_filling")
|
||||||
|
.from_tbl(CakeFillingPrice)
|
||||||
|
.from_col(cake_filling_price::Column::CakeId)
|
||||||
|
.from_col(cake_filling_price::Column::FillingId)
|
||||||
|
.to_tbl(CakeFilling)
|
||||||
|
.to_col(cake_filling::Column::CakeId)
|
||||||
|
.to_col(cake_filling::Column::FillingId)
|
||||||
|
)
|
||||||
|
.to_string(MysqlQueryBuilder)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DbErr {
|
pub enum DbErr {
|
||||||
Conn(String),
|
Conn(String),
|
||||||
Exec(String),
|
Exec(String),
|
||||||
|
@ -8,10 +8,7 @@ pub(crate) enum ExecResultHolder {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
SqlxMySql(sqlx::mysql::MySqlQueryResult),
|
SqlxMySql(sqlx::mysql::MySqlQueryResult),
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
SqlxPostgres {
|
SqlxPostgres(sqlx::postgres::PgQueryResult),
|
||||||
last_insert_id: u64,
|
|
||||||
rows_affected: u64,
|
|
||||||
},
|
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
SqlxSqlite(sqlx::sqlite::SqliteQueryResult),
|
SqlxSqlite(sqlx::sqlite::SqliteQueryResult),
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
@ -26,7 +23,9 @@ impl ExecResult {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
ExecResultHolder::SqlxPostgres { last_insert_id, .. } => last_insert_id.to_owned(),
|
ExecResultHolder::SqlxPostgres(_) => {
|
||||||
|
panic!("Should not retrieve last_insert_id this way")
|
||||||
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
ExecResultHolder::SqlxSqlite(result) => {
|
ExecResultHolder::SqlxSqlite(result) => {
|
||||||
let last_insert_rowid = result.last_insert_rowid();
|
let last_insert_rowid = result.last_insert_rowid();
|
||||||
@ -46,7 +45,7 @@ impl ExecResult {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
ExecResultHolder::SqlxPostgres { rows_affected, .. } => rows_affected.to_owned(),
|
ExecResultHolder::SqlxPostgres(result) => result.rows_affected(),
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
ExecResultHolder::SqlxSqlite(result) => result.rows_affected(),
|
ExecResultHolder::SqlxSqlite(result) => result.rows_affected(),
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
use crate::{error::*, ActiveModelTrait, DatabaseConnection, Insert, Statement};
|
use crate::{
|
||||||
|
error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait,
|
||||||
|
Statement, TryFromU64,
|
||||||
|
};
|
||||||
use sea_query::InsertStatement;
|
use sea_query::InsertStatement;
|
||||||
use std::future::Future;
|
use std::{future::Future, marker::PhantomData};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Inserter {
|
pub struct Inserter<A>
|
||||||
|
where
|
||||||
|
A: ActiveModelTrait,
|
||||||
|
{
|
||||||
query: InsertStatement,
|
query: InsertStatement,
|
||||||
|
model: PhantomData<A>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InsertResult {
|
pub struct InsertResult<A>
|
||||||
pub last_insert_id: u64,
|
where
|
||||||
|
A: ActiveModelTrait,
|
||||||
|
{
|
||||||
|
pub last_insert_id: <<<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> Insert<A>
|
impl<A> Insert<A>
|
||||||
@ -17,54 +27,79 @@ where
|
|||||||
A: ActiveModelTrait,
|
A: ActiveModelTrait,
|
||||||
{
|
{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn exec(
|
pub fn exec<'a>(
|
||||||
self,
|
self,
|
||||||
db: &DatabaseConnection,
|
db: &'a DatabaseConnection,
|
||||||
) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
|
) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + 'a
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
{
|
||||||
// so that self is dropped before entering await
|
// so that self is dropped before entering await
|
||||||
let mut query = self.query;
|
let mut query = self.query;
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db {
|
if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db {
|
||||||
use crate::{EntityTrait, Iterable};
|
use crate::{sea_query::Query, Iterable};
|
||||||
use sea_query::{Alias, Expr, Query};
|
if <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 {
|
||||||
for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
|
|
||||||
query.returning(
|
query.returning(
|
||||||
Query::select()
|
Query::select()
|
||||||
.expr_as(Expr::col(key), Alias::new("last_insert_id"))
|
.columns(<A::Entity as EntityTrait>::PrimaryKey::iter())
|
||||||
.to_owned(),
|
.take(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Inserter::new(query).exec(db)
|
Inserter::<A>::new(query).exec(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inserter {
|
impl<A> Inserter<A>
|
||||||
|
where
|
||||||
|
A: ActiveModelTrait,
|
||||||
|
{
|
||||||
pub fn new(query: InsertStatement) -> Self {
|
pub fn new(query: InsertStatement) -> Self {
|
||||||
Self { query }
|
Self {
|
||||||
|
query,
|
||||||
|
model: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec(
|
pub fn exec<'a>(
|
||||||
self,
|
self,
|
||||||
db: &DatabaseConnection,
|
db: &'a DatabaseConnection,
|
||||||
) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
|
) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + 'a
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
{
|
||||||
let builder = db.get_database_backend();
|
let builder = db.get_database_backend();
|
||||||
exec_insert(builder.build(&self.query), db)
|
exec_insert(builder.build(&self.query), db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only Statement impl Send
|
// Only Statement impl Send
|
||||||
async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result<InsertResult, DbErr> {
|
async fn exec_insert<A>(
|
||||||
// TODO: Postgres instead use query_one + returning clause
|
statement: Statement,
|
||||||
let result = match db {
|
db: &DatabaseConnection,
|
||||||
|
) -> Result<InsertResult<A>, DbErr>
|
||||||
|
where
|
||||||
|
A: ActiveModelTrait,
|
||||||
|
{
|
||||||
|
type PrimaryKey<A> = <<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey;
|
||||||
|
type ValueTypeOf<A> = <PrimaryKey<A> as PrimaryKeyTrait>::ValueType;
|
||||||
|
let last_insert_id = match db {
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => {
|
DatabaseConnection::SqlxPostgresPoolConnection(conn) => {
|
||||||
|
use crate::{sea_query::Iden, Iterable};
|
||||||
|
let cols = PrimaryKey::<A>::iter()
|
||||||
|
.map(|col| col.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let res = conn.query_one(statement).await?.unwrap();
|
let res = conn.query_one(statement).await?.unwrap();
|
||||||
crate::query_result_into_exec_result(res)?
|
res.try_get_many("", cols.as_ref()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let last_insert_id = db.execute(statement).await?.last_insert_id();
|
||||||
|
ValueTypeOf::<A>::try_from_u64(last_insert_id)
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
_ => db.execute(statement).await?,
|
|
||||||
};
|
};
|
||||||
Ok(InsertResult {
|
Ok(InsertResult { last_insert_id })
|
||||||
last_insert_id: result.last_insert_id(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@ pub(crate) enum QueryResultRow {
|
|||||||
Mock(crate::MockRow),
|
Mock(crate::MockRow),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TryGetable: Sized {
|
||||||
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TryGetError {
|
pub enum TryGetError {
|
||||||
DbErr(DbErr),
|
DbErr(DbErr),
|
||||||
Null,
|
Null,
|
||||||
@ -31,12 +36,6 @@ impl From<TryGetError> for DbErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TryGetable {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryResult //
|
// QueryResult //
|
||||||
|
|
||||||
impl QueryResult {
|
impl QueryResult {
|
||||||
@ -46,6 +45,13 @@ impl QueryResult {
|
|||||||
{
|
{
|
||||||
Ok(T::try_get(self, pre, col)?)
|
Ok(T::try_get(self, pre, col)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr>
|
||||||
|
where
|
||||||
|
T: TryGetableMany,
|
||||||
|
{
|
||||||
|
Ok(T::try_get_many(self, pre, cols)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for QueryResultRow {
|
impl fmt::Debug for QueryResultRow {
|
||||||
@ -54,9 +60,9 @@ impl fmt::Debug for QueryResultRow {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
Self::SqlxMySql(row) => write!(f, "{:?}", row),
|
Self::SqlxMySql(row) => write!(f, "{:?}", row),
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
Self::SqlxPostgres(_) => panic!("QueryResultRow::SqlxPostgres cannot be inspected"),
|
Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"),
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
Self::SqlxSqlite(_) => panic!("QueryResultRow::SqlxSqlite cannot be inspected"),
|
Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"),
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
Self::Mock(row) => write!(f, "{:?}", row),
|
Self::Mock(row) => write!(f, "{:?}", row),
|
||||||
}
|
}
|
||||||
@ -288,3 +294,135 @@ impl TryGetable for Decimal {
|
|||||||
|
|
||||||
#[cfg(feature = "with-uuid")]
|
#[cfg(feature = "with-uuid")]
|
||||||
try_getable_all!(uuid::Uuid);
|
try_getable_all!(uuid::Uuid);
|
||||||
|
|
||||||
|
// TryGetableMany //
|
||||||
|
|
||||||
|
pub trait TryGetableMany: Sized {
|
||||||
|
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TryGetableMany for T
|
||||||
|
where
|
||||||
|
T: TryGetable,
|
||||||
|
{
|
||||||
|
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
|
||||||
|
try_get_many_with_slice_len_of(1, cols)?;
|
||||||
|
T::try_get(res, pre, &cols[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TryGetableMany for (T, T)
|
||||||
|
where
|
||||||
|
T: TryGetable,
|
||||||
|
{
|
||||||
|
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
|
||||||
|
try_get_many_with_slice_len_of(2, cols)?;
|
||||||
|
Ok((
|
||||||
|
T::try_get(res, pre, &cols[0])?,
|
||||||
|
T::try_get(res, pre, &cols[1])?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TryGetableMany for (T, T, T)
|
||||||
|
where
|
||||||
|
T: TryGetable,
|
||||||
|
{
|
||||||
|
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
|
||||||
|
try_get_many_with_slice_len_of(3, cols)?;
|
||||||
|
Ok((
|
||||||
|
T::try_get(res, pre, &cols[0])?,
|
||||||
|
T::try_get(res, pre, &cols[1])?,
|
||||||
|
T::try_get(res, pre, &cols[2])?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
|
||||||
|
if cols.len() < len {
|
||||||
|
Err(TryGetError::DbErr(DbErr::Query(format!(
|
||||||
|
"Expect {} column names supplied but got slice of length {}",
|
||||||
|
len,
|
||||||
|
cols.len()
|
||||||
|
))))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryFromU64 //
|
||||||
|
|
||||||
|
pub trait TryFromU64: Sized {
|
||||||
|
fn try_from_u64(n: u64) -> Result<Self, DbErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_from_u64_err {
|
||||||
|
( $type: ty ) => {
|
||||||
|
impl TryFromU64 for $type {
|
||||||
|
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
|
||||||
|
Err(DbErr::Exec(format!(
|
||||||
|
"{} cannot be converted from u64",
|
||||||
|
stringify!($type)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_from_u64_tuple {
|
||||||
|
( $type: ty ) => {
|
||||||
|
try_from_u64_err!(($type, $type));
|
||||||
|
try_from_u64_err!(($type, $type, $type));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_from_u64_numeric {
|
||||||
|
( $type: ty ) => {
|
||||||
|
impl TryFromU64 for $type {
|
||||||
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
|
use std::convert::TryInto;
|
||||||
|
n.try_into().map_err(|_| {
|
||||||
|
DbErr::Exec(format!(
|
||||||
|
"fail to convert '{}' into '{}'",
|
||||||
|
n,
|
||||||
|
stringify!($type)
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try_from_u64_tuple!($type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_u64_numeric!(i8);
|
||||||
|
try_from_u64_numeric!(i16);
|
||||||
|
try_from_u64_numeric!(i32);
|
||||||
|
try_from_u64_numeric!(i64);
|
||||||
|
try_from_u64_numeric!(u8);
|
||||||
|
try_from_u64_numeric!(u16);
|
||||||
|
try_from_u64_numeric!(u32);
|
||||||
|
try_from_u64_numeric!(u64);
|
||||||
|
|
||||||
|
macro_rules! try_from_u64_string {
|
||||||
|
( $type: ty ) => {
|
||||||
|
impl TryFromU64 for $type {
|
||||||
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
|
Ok(n.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try_from_u64_tuple!($type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_u64_string!(String);
|
||||||
|
|
||||||
|
macro_rules! try_from_u64_dummy {
|
||||||
|
( $type: ty ) => {
|
||||||
|
try_from_u64_err!($type);
|
||||||
|
try_from_u64_err!(($type, $type));
|
||||||
|
try_from_u64_err!(($type, $type, $type));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with-uuid")]
|
||||||
|
try_from_u64_dummy!(uuid::Uuid);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::*, query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable,
|
error::*, DatabaseConnection, EntityTrait, FromQueryResult, IdenStatic, Iterable, JsonValue,
|
||||||
JsonValue, ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectTwo,
|
ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo,
|
||||||
SelectTwoMany, Statement,
|
SelectTwoMany, Statement,
|
||||||
};
|
};
|
||||||
use sea_query::SelectStatement;
|
use sea_query::SelectStatement;
|
||||||
@ -30,6 +30,7 @@ pub trait SelectorTrait {
|
|||||||
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
|
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SelectModel<M>
|
pub struct SelectModel<M>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
@ -66,8 +67,8 @@ where
|
|||||||
|
|
||||||
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
|
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
|
||||||
Ok((
|
Ok((
|
||||||
M::from_query_result(&res, combine::SELECT_A)?,
|
M::from_query_result(&res, SelectA.as_str())?,
|
||||||
N::from_query_result_optional(&res, combine::SELECT_B)?,
|
N::from_query_result_optional(&res, SelectB.as_str())?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +129,7 @@ where
|
|||||||
E: EntityTrait,
|
E: EntityTrait,
|
||||||
F: EntityTrait,
|
F: EntityTrait,
|
||||||
{
|
{
|
||||||
fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
|
pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
N: FromQueryResult,
|
N: FromQueryResult,
|
||||||
@ -289,14 +290,15 @@ where
|
|||||||
///
|
///
|
||||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// let res: Vec<SelectResult> = cake::Entity::find().from_raw_sql(
|
/// let res: Vec<SelectResult> = cake::Entity::find()
|
||||||
/// Statement::from_sql_and_values(
|
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, vec![]
|
/// DbBackend::Postgres,
|
||||||
/// )
|
/// r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
|
||||||
/// )
|
/// vec![],
|
||||||
/// .into_model::<SelectResult>()
|
/// ))
|
||||||
/// .all(&db)
|
/// .into_model::<SelectResult>()
|
||||||
/// .await?;
|
/// .all(&db)
|
||||||
|
/// .await?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// res,
|
/// res,
|
||||||
@ -317,11 +319,12 @@ where
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// db.into_transaction_log(),
|
/// db.into_transaction_log(),
|
||||||
/// vec![
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// Transaction::from_sql_and_values(
|
/// DbBackend::Postgres,
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, vec![]
|
/// r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
|
||||||
/// ),
|
/// vec![]
|
||||||
/// ]);
|
/// ),]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
|
pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
|
||||||
where
|
where
|
||||||
@ -406,22 +409,26 @@ where
|
|||||||
///
|
///
|
||||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// let _: Option<cake::Model> = cake::Entity::find().from_raw_sql(
|
/// let _: Option<cake::Model> = cake::Entity::find()
|
||||||
/// Statement::from_sql_and_values(
|
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, vec![1.into()]
|
/// DbBackend::Postgres,
|
||||||
/// )
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#,
|
||||||
/// ).one(&db).await?;
|
/// vec![1.into()],
|
||||||
|
/// ))
|
||||||
|
/// .one(&db)
|
||||||
|
/// .await?;
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// db.into_transaction_log(),
|
/// db.into_transaction_log(),
|
||||||
/// vec![
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// Transaction::from_sql_and_values(
|
/// DbBackend::Postgres,
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, vec![1.into()]
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#,
|
||||||
/// ),
|
/// vec![1.into()]
|
||||||
/// ]);
|
/// ),]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn one(self, db: &DatabaseConnection) -> Result<Option<S::Item>, DbErr> {
|
pub async fn one(self, db: &DatabaseConnection) -> Result<Option<S::Item>, DbErr> {
|
||||||
let row = db.query_one(self.stmt).await?;
|
let row = db.query_one(self.stmt).await?;
|
||||||
@ -441,22 +448,26 @@ where
|
|||||||
///
|
///
|
||||||
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
/// # let _: Result<(), DbErr> = smol::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// let _: Vec<cake::Model> = cake::Entity::find().from_raw_sql(
|
/// let _: Vec<cake::Model> = cake::Entity::find()
|
||||||
/// Statement::from_sql_and_values(
|
/// .from_raw_sql(Statement::from_sql_and_values(
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
/// DbBackend::Postgres,
|
||||||
/// )
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
|
||||||
/// ).all(&db).await?;
|
/// vec![],
|
||||||
|
/// ))
|
||||||
|
/// .all(&db)
|
||||||
|
/// .await?;
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// db.into_transaction_log(),
|
/// db.into_transaction_log(),
|
||||||
/// vec![
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// Transaction::from_sql_and_values(
|
/// DbBackend::Postgres,
|
||||||
/// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
|
||||||
/// ),
|
/// vec![]
|
||||||
/// ]);
|
/// ),]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<S::Item>, DbErr> {
|
pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<S::Item>, DbErr> {
|
||||||
let rows = db.query_all(self.stmt).await?;
|
let rows = db.query_all(self.stmt).await?;
|
||||||
|
15
src/lib.rs
15
src/lib.rs
@ -1,3 +1,10 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![deny(
|
||||||
|
missing_debug_implementations,
|
||||||
|
clippy::print_stderr,
|
||||||
|
clippy::print_stdout
|
||||||
|
)]
|
||||||
|
|
||||||
//! <div align="center">
|
//! <div align="center">
|
||||||
//!
|
//!
|
||||||
//! <img src="https://www.sea-ql.org/SeaORM/img/SeaORM banner.png"/>
|
//! <img src="https://www.sea-ql.org/SeaORM/img/SeaORM banner.png"/>
|
||||||
@ -70,10 +77,8 @@
|
|||||||
//! let fruits: Vec<fruit::Model> = cheese.find_related(Fruit).all(db).await?;
|
//! let fruits: Vec<fruit::Model> = cheese.find_related(Fruit).all(db).await?;
|
||||||
//!
|
//!
|
||||||
//! // find related models (eager)
|
//! // find related models (eager)
|
||||||
//! let cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> = Cake::find()
|
//! let cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> =
|
||||||
//! .find_with_related(Fruit)
|
//! Cake::find().find_with_related(Fruit).all(db).await?;
|
||||||
//! .all(db)
|
|
||||||
//! .await?;
|
|
||||||
//!
|
//!
|
||||||
//! # Ok(())
|
//! # Ok(())
|
||||||
//! # }
|
//! # }
|
||||||
@ -93,7 +98,7 @@
|
|||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! // insert one
|
//! // insert one
|
||||||
//! let res: InsertResult = Fruit::insert(pear).exec(db).await?;
|
//! let res = Fruit::insert(pear).exec(db).await?;
|
||||||
//!
|
//!
|
||||||
//! println!("InsertResult: {}", res.last_insert_id);
|
//! println!("InsertResult: {}", res.last_insert_id);
|
||||||
//! # Ok(())
|
//! # Ok(())
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
use crate::{EntityTrait, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany};
|
use crate::{
|
||||||
|
EntityTrait, IdenStatic, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany,
|
||||||
|
};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
pub use sea_query::JoinType;
|
pub use sea_query::JoinType;
|
||||||
use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr};
|
use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr};
|
||||||
|
|
||||||
pub const SELECT_A: &str = "A_";
|
macro_rules! select_def {
|
||||||
pub const SELECT_B: &str = "B_";
|
( $ident: ident, $str: expr ) => {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct $ident;
|
||||||
|
|
||||||
|
impl Iden for $ident {
|
||||||
|
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
|
||||||
|
write!(s, "{}", self.as_str()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdenStatic for $ident {
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
$str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
select_def!(SelectA, "A_");
|
||||||
|
select_def!(SelectB, "B_");
|
||||||
|
|
||||||
impl<E> Select<E>
|
impl<E> Select<E>
|
||||||
where
|
where
|
||||||
@ -37,7 +58,7 @@ where
|
|||||||
where
|
where
|
||||||
F: EntityTrait,
|
F: EntityTrait,
|
||||||
{
|
{
|
||||||
self = self.apply_alias(SELECT_A);
|
self = self.apply_alias(SelectA.as_str());
|
||||||
SelectTwo::new(self.into_query())
|
SelectTwo::new(self.into_query())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +66,7 @@ where
|
|||||||
where
|
where
|
||||||
F: EntityTrait,
|
F: EntityTrait,
|
||||||
{
|
{
|
||||||
self = self.apply_alias(SELECT_A);
|
self = self.apply_alias(SelectA.as_str());
|
||||||
SelectTwoMany::new(self.into_query())
|
SelectTwoMany::new(self.into_query())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +123,7 @@ where
|
|||||||
S: QueryTrait<QueryStatement = SelectStatement>,
|
S: QueryTrait<QueryStatement = SelectStatement>,
|
||||||
{
|
{
|
||||||
for col in <F::Column as Iterable>::iter() {
|
for col in <F::Column as Iterable>::iter() {
|
||||||
let alias = format!("{}{}", SELECT_B, col.to_string().as_str());
|
let alias = format!("{}{}", SelectB.as_str(), col.as_str());
|
||||||
selector.query().expr(SelectExpr {
|
selector.query().expr(SelectExpr {
|
||||||
expr: col.into_simple_expr(),
|
expr: col.into_simple_expr(),
|
||||||
alias: Some(SeaRc::new(Alias::new(&alias))),
|
alias: Some(SeaRc::new(Alias::new(&alias))),
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ColumnTrait, EntityTrait, Identity, IntoSimpleExpr, Iterable, ModelTrait, PrimaryKeyToColumn,
|
ColumnTrait, EntityTrait, Identity, IntoIdentity, IntoSimpleExpr, Iterable, ModelTrait,
|
||||||
RelationDef,
|
PrimaryKeyToColumn, RelationDef,
|
||||||
};
|
|
||||||
use sea_query::{
|
|
||||||
Alias, Expr, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef,
|
|
||||||
};
|
};
|
||||||
pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement};
|
pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement};
|
||||||
|
use sea_query::{Expr, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef};
|
||||||
|
|
||||||
// LINT: when the column does not appear in tables selected from
|
// LINT: when the column does not appear in tables selected from
|
||||||
// LINT: when there is a group by clause, but some columns don't have aggregate functions
|
// LINT: when there is a group by clause, but some columns don't have aggregate functions
|
||||||
@ -55,13 +53,14 @@ pub trait QuerySelect: Sized {
|
|||||||
/// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""#
|
/// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""#
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
fn column_as<C>(mut self, col: C, alias: &str) -> Self
|
fn column_as<C, I>(mut self, col: C, alias: I) -> Self
|
||||||
where
|
where
|
||||||
C: IntoSimpleExpr,
|
C: IntoSimpleExpr,
|
||||||
|
I: IntoIdentity,
|
||||||
{
|
{
|
||||||
self.query().expr(SelectExpr {
|
self.query().expr(SelectExpr {
|
||||||
expr: col.into_simple_expr(),
|
expr: col.into_simple_expr(),
|
||||||
alias: Some(SeaRc::new(Alias::new(alias))),
|
alias: Some(SeaRc::new(alias.into_identity())),
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -295,7 +294,7 @@ fn join_condition(rel: RelationDef) -> SimpleExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpack_table_ref(table_ref: &TableRef) -> DynIden {
|
pub(crate) fn unpack_table_ref(table_ref: &TableRef) -> DynIden {
|
||||||
match table_ref {
|
match table_ref {
|
||||||
TableRef::Table(tbl) => SeaRc::clone(tbl),
|
TableRef::Table(tbl) => SeaRc::clone(tbl),
|
||||||
TableRef::SchemaTable(_, tbl) => SeaRc::clone(tbl),
|
TableRef::SchemaTable(_, tbl) => SeaRc::clone(tbl),
|
||||||
|
@ -43,11 +43,11 @@ where
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// Insert::one(cake::Model {
|
/// Insert::one(cake::Model {
|
||||||
/// id: 1,
|
/// id: 1,
|
||||||
/// name: "Apple Pie".to_owned(),
|
/// name: "Apple Pie".to_owned(),
|
||||||
/// })
|
/// })
|
||||||
/// .build(DbBackend::Postgres)
|
/// .build(DbBackend::Postgres)
|
||||||
/// .to_string(),
|
/// .to_string(),
|
||||||
/// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
|
/// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
@ -57,11 +57,11 @@ where
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// Insert::one(cake::ActiveModel {
|
/// Insert::one(cake::ActiveModel {
|
||||||
/// id: Unset(None),
|
/// id: Unset(None),
|
||||||
/// name: Set("Apple Pie".to_owned()),
|
/// name: Set("Apple Pie".to_owned()),
|
||||||
/// })
|
/// })
|
||||||
/// .build(DbBackend::Postgres)
|
/// .build(DbBackend::Postgres)
|
||||||
/// .to_string(),
|
/// .to_string(),
|
||||||
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
|
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
@ -79,17 +79,17 @@ where
|
|||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// Insert::many(vec![
|
/// Insert::many(vec![
|
||||||
/// cake::Model {
|
/// cake::Model {
|
||||||
/// id: 1,
|
/// id: 1,
|
||||||
/// name: "Apple Pie".to_owned(),
|
/// name: "Apple Pie".to_owned(),
|
||||||
/// },
|
/// },
|
||||||
/// cake::Model {
|
/// cake::Model {
|
||||||
/// id: 2,
|
/// id: 2,
|
||||||
/// name: "Orange Scone".to_owned(),
|
/// name: "Orange Scone".to_owned(),
|
||||||
/// }
|
/// }
|
||||||
/// ])
|
/// ])
|
||||||
/// .build(DbBackend::Postgres)
|
/// .build(DbBackend::Postgres)
|
||||||
/// .to_string(),
|
/// .to_string(),
|
||||||
/// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
|
/// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{EntityTrait, QuerySelect, Related, Select, SelectTwo, SelectTwoMany};
|
use crate::{EntityTrait, Linked, QuerySelect, Related, Select, SelectTwo, SelectTwoMany};
|
||||||
pub use sea_query::JoinType;
|
pub use sea_query::JoinType;
|
||||||
|
|
||||||
impl<E> Select<E>
|
impl<E> Select<E>
|
||||||
@ -57,6 +57,19 @@ where
|
|||||||
{
|
{
|
||||||
self.left_join(r).select_with(r)
|
self.left_join(r).select_with(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Left Join with a Linked Entity and select both Entity.
|
||||||
|
pub fn find_also_linked<L, T>(self, l: L) -> SelectTwo<E, T>
|
||||||
|
where
|
||||||
|
L: Linked<FromEntity = E, ToEntity = T>,
|
||||||
|
T: EntityTrait,
|
||||||
|
{
|
||||||
|
let mut slf = self;
|
||||||
|
for rel in l.link() {
|
||||||
|
slf = slf.join(JoinType::LeftJoin, rel);
|
||||||
|
}
|
||||||
|
slf.select_also(T::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -220,4 +233,44 @@ mod tests {
|
|||||||
.join(" ")
|
.join(" ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_10() {
|
||||||
|
let cake_model = cake::Model {
|
||||||
|
id: 12,
|
||||||
|
name: "".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cake_model
|
||||||
|
.find_linked(cake::CakeToFilling)
|
||||||
|
.build(DbBackend::MySql)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
r#"SELECT `filling`.`id`, `filling`.`name`"#,
|
||||||
|
r#"FROM `filling`"#,
|
||||||
|
r#"INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#,
|
||||||
|
r#"INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id`"#,
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_11() {
|
||||||
|
assert_eq!(
|
||||||
|
cake::Entity::find()
|
||||||
|
.find_also_linked(cake::CakeToFilling)
|
||||||
|
.build(DbBackend::MySql)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
|
||||||
|
r#"`filling`.`id` AS `B_id`, `filling`.`name` AS `B_name`"#,
|
||||||
|
r#"FROM `cake`"#,
|
||||||
|
r#"LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#,
|
||||||
|
r#"LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#,
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ mod select;
|
|||||||
mod traits;
|
mod traits;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
// pub use combine::*;
|
pub use combine::{SelectA, SelectB};
|
||||||
pub use delete::*;
|
pub use delete::*;
|
||||||
pub use helper::*;
|
pub use helper::*;
|
||||||
pub use insert::*;
|
pub use insert::*;
|
||||||
|
@ -59,7 +59,7 @@ impl Update {
|
|||||||
/// Update many ActiveModel
|
/// Update many ActiveModel
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::Expr, DbBackend};
|
/// use sea_orm::{entity::*, query::*, sea_query::Expr, tests_cfg::fruit, DbBackend};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// Update::many(fruit::Entity)
|
/// Update::many(fruit::Entity)
|
||||||
|
@ -28,6 +28,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -73,4 +75,20 @@ impl Related<super::filling::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CakeToFilling;
|
||||||
|
|
||||||
|
impl Linked for CakeToFilling {
|
||||||
|
type FromEntity = Entity;
|
||||||
|
|
||||||
|
type ToEntity = super::filling::Entity;
|
||||||
|
|
||||||
|
fn link(&self) -> Vec<RelationDef> {
|
||||||
|
vec![
|
||||||
|
super::cake_filling::Relation::Cake.def().rev(),
|
||||||
|
super::cake_filling::Relation::Filling.def(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(feature = "debug-print")]
|
#[cfg(feature = "debug-print")]
|
||||||
macro_rules! debug_print {
|
macro_rules! debug_print {
|
||||||
($( $args:expr ),*) => { println!( $( $args ),* ); }
|
($( $args:expr ),*) => { log::debug!( $( $args ),* ); }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
pub use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn};
|
||||||
use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn};
|
|
||||||
|
|
||||||
// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlit,runtime-async-std --test basic
|
// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlit,runtime-async-std --test basic
|
||||||
#[sea_orm_macros::test]
|
#[sea_orm_macros::test]
|
||||||
|
@ -31,6 +31,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -49,7 +51,7 @@ impl ColumnTrait for Column {
|
|||||||
Self::Id => ColumnType::Integer.def(),
|
Self::Id => ColumnType::Integer.def(),
|
||||||
Self::Name => ColumnType::String(None).def(),
|
Self::Name => ColumnType::String(None).def(),
|
||||||
Self::ContactDetails => ColumnType::Json.def(),
|
Self::ContactDetails => ColumnType::Json.def(),
|
||||||
Self::BakeryId => ColumnType::Integer.def(),
|
Self::BakeryId => ColumnType::Integer.def().null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +62,8 @@ impl RelationTrait for Relation {
|
|||||||
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
||||||
.from(Column::BakeryId)
|
.from(Column::BakeryId)
|
||||||
.to(super::bakery::Column::Id)
|
.to(super::bakery::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,4 +85,22 @@ impl Related<super::cake::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BakedForCustomer;
|
||||||
|
|
||||||
|
impl Linked for BakedForCustomer {
|
||||||
|
type FromEntity = Entity;
|
||||||
|
|
||||||
|
type ToEntity = super::customer::Entity;
|
||||||
|
|
||||||
|
fn link(&self) -> Vec<RelationDef> {
|
||||||
|
vec![
|
||||||
|
super::cakes_bakers::Relation::Baker.def().rev(),
|
||||||
|
super::cakes_bakers::Relation::Cake.def(),
|
||||||
|
super::lineitem::Relation::Cake.def().rev(),
|
||||||
|
super::lineitem::Relation::Order.def(),
|
||||||
|
super::order::Relation::Customer.def(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -48,7 +50,7 @@ impl ColumnTrait for Column {
|
|||||||
match self {
|
match self {
|
||||||
Self::Id => ColumnType::Integer.def(),
|
Self::Id => ColumnType::Integer.def(),
|
||||||
Self::Name => ColumnType::String(None).def(),
|
Self::Name => ColumnType::String(None).def(),
|
||||||
Self::ProfitMargin => ColumnType::Float.def(),
|
Self::ProfitMargin => ColumnType::Double.def(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -54,9 +56,9 @@ impl ColumnTrait for Column {
|
|||||||
Self::Id => ColumnType::Integer.def(),
|
Self::Id => ColumnType::Integer.def(),
|
||||||
Self::Name => ColumnType::String(None).def(),
|
Self::Name => ColumnType::String(None).def(),
|
||||||
Self::Price => ColumnType::Decimal(Some((19, 4))).def(),
|
Self::Price => ColumnType::Decimal(Some((19, 4))).def(),
|
||||||
Self::BakeryId => ColumnType::Integer.def(),
|
Self::BakeryId => ColumnType::Integer.def().null(),
|
||||||
Self::GlutenFree => ColumnType::Boolean.def(),
|
Self::GlutenFree => ColumnType::Boolean.def(),
|
||||||
Self::Serial => ColumnType::String(None).def(),
|
Self::Serial => ColumnType::Uuid.def(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +69,8 @@ impl RelationTrait for Relation {
|
|||||||
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
||||||
.from(Column::BakeryId)
|
.from(Column::BakeryId)
|
||||||
.to(super::bakery::Column::Id)
|
.to(super::bakery::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
|
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -56,10 +58,14 @@ impl RelationTrait for Relation {
|
|||||||
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
.from(Column::CakeId)
|
.from(Column::CakeId)
|
||||||
.to(super::cake::Column::Id)
|
.to(super::cake::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Baker => Entity::belongs_to(super::baker::Entity)
|
Self::Baker => Entity::belongs_to(super::baker::Entity)
|
||||||
.from(Column::BakerId)
|
.from(Column::BakerId)
|
||||||
.to(super::baker::Column::Id)
|
.to(super::baker::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -46,7 +48,7 @@ impl ColumnTrait for Column {
|
|||||||
match self {
|
match self {
|
||||||
Self::Id => ColumnType::Integer.def(),
|
Self::Id => ColumnType::Integer.def(),
|
||||||
Self::Name => ColumnType::String(None).def(),
|
Self::Name => ColumnType::String(None).def(),
|
||||||
Self::Notes => ColumnType::Text.def(),
|
Self::Notes => ColumnType::Text.def().null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -64,10 +66,14 @@ impl RelationTrait for Relation {
|
|||||||
Self::Order => Entity::belongs_to(super::order::Entity)
|
Self::Order => Entity::belongs_to(super::order::Entity)
|
||||||
.from(Column::OrderId)
|
.from(Column::OrderId)
|
||||||
.to(super::order::Column::Id)
|
.to(super::order::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
.from(Column::CakeId)
|
.from(Column::CakeId)
|
||||||
.to(super::cake::Column::Id)
|
.to(super::cake::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
tests/common/bakery_chain/metadata.rs
Normal file
61
tests/common/bakery_chain/metadata.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub uuid: Uuid,
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Uuid,
|
||||||
|
Key,
|
||||||
|
Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = Uuid;
|
||||||
|
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Uuid => ColumnType::Uuid.def(),
|
||||||
|
Self::Key => ColumnType::String(None).def(),
|
||||||
|
Self::Value => ColumnType::String(None).def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -4,6 +4,7 @@ pub mod cake;
|
|||||||
pub mod cakes_bakers;
|
pub mod cakes_bakers;
|
||||||
pub mod customer;
|
pub mod customer;
|
||||||
pub mod lineitem;
|
pub mod lineitem;
|
||||||
|
pub mod metadata;
|
||||||
pub mod order;
|
pub mod order;
|
||||||
|
|
||||||
pub use super::baker::Entity as Baker;
|
pub use super::baker::Entity as Baker;
|
||||||
@ -12,4 +13,5 @@ pub use super::cake::Entity as Cake;
|
|||||||
pub use super::cakes_bakers::Entity as CakesBakers;
|
pub use super::cakes_bakers::Entity as CakesBakers;
|
||||||
pub use super::customer::Entity as Customer;
|
pub use super::customer::Entity as Customer;
|
||||||
pub use super::lineitem::Entity as Lineitem;
|
pub use super::lineitem::Entity as Lineitem;
|
||||||
|
pub use super::metadata::Entity as Metadata;
|
||||||
pub use super::order::Entity as Order;
|
pub use super::order::Entity as Order;
|
||||||
|
@ -33,6 +33,8 @@ pub enum PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrimaryKeyTrait for PrimaryKey {
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
fn auto_increment() -> bool {
|
fn auto_increment() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -65,10 +67,14 @@ impl RelationTrait for Relation {
|
|||||||
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
Self::Bakery => Entity::belongs_to(super::bakery::Entity)
|
||||||
.from(Column::BakeryId)
|
.from(Column::BakeryId)
|
||||||
.to(super::bakery::Column::Id)
|
.to(super::bakery::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Customer => Entity::belongs_to(super::customer::Entity)
|
Self::Customer => Entity::belongs_to(super::customer::Entity)
|
||||||
.from(Column::CustomerId)
|
.from(Column::CustomerId)
|
||||||
.to(super::customer::Column::Id)
|
.to(super::customer::Column::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
|
Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(),
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ impl TestContext {
|
|||||||
let db: DatabaseConnection = setup::setup(&base_url, test_name).await;
|
let db: DatabaseConnection = setup::setup(&base_url, test_name).await;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base_url: base_url,
|
base_url,
|
||||||
db_name: test_name.to_string(),
|
db_name: test_name.to_string(),
|
||||||
db,
|
db,
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,14 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection {
|
|||||||
Database::connect(base_url).await.unwrap()
|
Database::connect(base_url).await.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(schema::create_bakery_table(&db).await.is_ok());
|
schema::create_bakery_table(&db).await.unwrap();
|
||||||
assert!(schema::create_baker_table(&db).await.is_ok());
|
schema::create_baker_table(&db).await.unwrap();
|
||||||
assert!(schema::create_customer_table(&db).await.is_ok());
|
schema::create_customer_table(&db).await.unwrap();
|
||||||
assert!(schema::create_order_table(&db).await.is_ok());
|
schema::create_order_table(&db).await.unwrap();
|
||||||
assert!(schema::create_cake_table(&db).await.is_ok());
|
schema::create_cake_table(&db).await.unwrap();
|
||||||
assert!(schema::create_cakes_bakers_table(&db).await.is_ok());
|
schema::create_cakes_bakers_table(&db).await.unwrap();
|
||||||
assert!(schema::create_lineitem_table(&db).await.is_ok());
|
schema::create_lineitem_table(&db).await.unwrap();
|
||||||
|
schema::create_metadata_table(&db).await.unwrap();
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
|
|
||||||
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement};
|
|
||||||
|
|
||||||
pub use super::super::bakery_chain::*;
|
pub use super::super::bakery_chain::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sea_orm::{
|
||||||
|
entity_to_table_create_statement, error::*, sea_query, DbConn, EntityTrait, ExecResult,
|
||||||
|
};
|
||||||
|
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, Table, TableCreateStatement};
|
||||||
|
|
||||||
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
async fn create_table<E>(
|
||||||
|
db: &DbConn,
|
||||||
|
stmt: &TableCreateStatement,
|
||||||
|
entity: E,
|
||||||
|
) -> Result<ExecResult, DbErr>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
{
|
||||||
let builder = db.get_database_backend();
|
let builder = db.get_database_backend();
|
||||||
db.execute(builder.build(stmt)).await
|
let stmt = builder.build(stmt);
|
||||||
|
assert_eq!(
|
||||||
|
builder.build(&entity_to_table_create_statement(entity)),
|
||||||
|
stmt
|
||||||
|
);
|
||||||
|
db.execute(stmt).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(bakery::Entity)
|
.table(bakery::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -27,16 +41,17 @@ pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Bakery).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(baker::Entity)
|
.table(baker::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(baker::Column::Id)
|
ColumnDef::new(baker::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
@ -49,7 +64,7 @@ pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
.col(ColumnDef::new(baker::Column::BakeryId).integer())
|
.col(ColumnDef::new(baker::Column::BakeryId).integer())
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_baker_bakery")
|
.name("fk-baker-bakery")
|
||||||
.from(baker::Entity, baker::Column::BakeryId)
|
.from(baker::Entity, baker::Column::BakeryId)
|
||||||
.to(bakery::Entity, bakery::Column::Id)
|
.to(bakery::Entity, bakery::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -57,11 +72,11 @@ pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Baker).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(customer::Entity)
|
.table(customer::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -75,11 +90,11 @@ pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
.col(ColumnDef::new(customer::Column::Notes).text())
|
.col(ColumnDef::new(customer::Column::Notes).text())
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Customer).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(order::Entity)
|
.table(order::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -107,7 +122,7 @@ pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_order_bakery")
|
.name("fk-order-bakery")
|
||||||
.from(order::Entity, order::Column::BakeryId)
|
.from(order::Entity, order::Column::BakeryId)
|
||||||
.to(bakery::Entity, bakery::Column::Id)
|
.to(bakery::Entity, bakery::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -115,7 +130,7 @@ pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_order_customer")
|
.name("fk-order-customer")
|
||||||
.from(order::Entity, order::Column::CustomerId)
|
.from(order::Entity, order::Column::CustomerId)
|
||||||
.to(customer::Entity, customer::Column::Id)
|
.to(customer::Entity, customer::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -123,11 +138,11 @@ pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Order).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(lineitem::Entity)
|
.table(lineitem::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -159,7 +174,7 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_lineitem_order")
|
.name("fk-lineitem-order")
|
||||||
.from(lineitem::Entity, lineitem::Column::OrderId)
|
.from(lineitem::Entity, lineitem::Column::OrderId)
|
||||||
.to(order::Entity, order::Column::Id)
|
.to(order::Entity, order::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -167,7 +182,7 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_lineitem_cake")
|
.name("fk-lineitem-cake")
|
||||||
.from(lineitem::Entity, lineitem::Column::CakeId)
|
.from(lineitem::Entity, lineitem::Column::CakeId)
|
||||||
.to(cake::Entity, cake::Column::Id)
|
.to(cake::Entity, cake::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -175,11 +190,11 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Lineitem).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(cakes_bakers::Entity)
|
.table(cakes_bakers::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -194,12 +209,13 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr>
|
|||||||
)
|
)
|
||||||
.primary_key(
|
.primary_key(
|
||||||
Index::create()
|
Index::create()
|
||||||
|
.name("pk-cakes_bakers")
|
||||||
.col(cakes_bakers::Column::CakeId)
|
.col(cakes_bakers::Column::CakeId)
|
||||||
.col(cakes_bakers::Column::BakerId),
|
.col(cakes_bakers::Column::BakerId),
|
||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_cakes_bakers_cake")
|
.name("fk-cakes_bakers-cake")
|
||||||
.from(cakes_bakers::Entity, cakes_bakers::Column::CakeId)
|
.from(cakes_bakers::Entity, cakes_bakers::Column::CakeId)
|
||||||
.to(cake::Entity, cake::Column::Id)
|
.to(cake::Entity, cake::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -207,7 +223,7 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr>
|
|||||||
)
|
)
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_cakes_bakers_baker")
|
.name("fk-cakes_bakers-baker")
|
||||||
.from(cakes_bakers::Entity, cakes_bakers::Column::BakerId)
|
.from(cakes_bakers::Entity, cakes_bakers::Column::BakerId)
|
||||||
.to(baker::Entity, baker::Column::Id)
|
.to(baker::Entity, baker::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -215,11 +231,11 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr>
|
|||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, CakesBakers).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
let stmt = sea_query::Table::create()
|
let stmt = Table::create()
|
||||||
.table(cake::Entity)
|
.table(cake::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
@ -238,7 +254,7 @@ pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
.col(ColumnDef::new(cake::Column::BakeryId).integer())
|
.col(ColumnDef::new(cake::Column::BakeryId).integer())
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.name("FK_cake_bakery")
|
.name("fk-cake-bakery")
|
||||||
.from(cake::Entity, cake::Column::BakeryId)
|
.from(cake::Entity, cake::Column::BakeryId)
|
||||||
.to(bakery::Entity, bakery::Column::Id)
|
.to(bakery::Entity, bakery::Column::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
@ -252,5 +268,22 @@ pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
.col(ColumnDef::new(cake::Column::Serial).uuid().not_null())
|
.col(ColumnDef::new(cake::Column::Serial).uuid().not_null())
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt, Cake).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_metadata_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
|
let stmt = sea_query::Table::create()
|
||||||
|
.table(metadata::Entity)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(metadata::Column::Uuid)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(metadata::Column::Key).string().not_null())
|
||||||
|
.col(ColumnDef::new(metadata::Column::Value).string().not_null())
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
create_table(db, &stmt, Metadata).await
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ pub async fn test_create_baker(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -30,7 +30,7 @@ pub async fn test_create_baker(db: &DbConn) {
|
|||||||
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res: InsertResult = Baker::insert(baker_bob)
|
let res = Baker::insert(baker_bob)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert baker");
|
.expect("could not insert baker");
|
||||||
|
@ -8,7 +8,7 @@ pub async fn test_create_cake(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -23,7 +23,7 @@ pub async fn test_create_cake(db: &DbConn) {
|
|||||||
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
|
let baker_insert_res = Baker::insert(baker_bob)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert baker");
|
.expect("could not insert baker");
|
||||||
@ -38,7 +38,7 @@ pub async fn test_create_cake(db: &DbConn) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
|
let cake_insert_res = Cake::insert(mud_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -51,18 +51,25 @@ pub async fn test_create_cake(db: &DbConn) {
|
|||||||
let cake_baker = cakes_bakers::ActiveModel {
|
let cake_baker = cakes_bakers::ActiveModel {
|
||||||
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
||||||
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
|
let cake_baker_res = CakesBakers::insert(cake_baker.clone())
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake_baker");
|
.expect("could not insert cake_baker");
|
||||||
|
assert_eq!(
|
||||||
|
cake_baker_res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assert!(cake.is_some());
|
assert!(cake.is_some());
|
||||||
let cake_model = cake.unwrap();
|
let cake_model = cake.unwrap();
|
||||||
assert_eq!(cake_model.name, "Mud Cake");
|
assert_eq!(cake_model.name, "Mud Cake");
|
||||||
assert_eq!(cake_model.price, dec!(10.25));
|
assert_eq!(cake_model.price, dec!(10.25));
|
||||||
assert_eq!(cake_model.gluten_free, false);
|
assert!(!cake_model.gluten_free);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cake_model
|
cake_model
|
||||||
.find_related(Bakery)
|
.find_related(Bakery)
|
||||||
|
@ -10,7 +10,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -26,7 +26,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
|
let baker_insert_res = Baker::insert(baker_bob)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert baker");
|
.expect("could not insert baker");
|
||||||
@ -41,7 +41,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
|
let cake_insert_res = Cake::insert(mud_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -50,12 +50,19 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
let cake_baker = cakes_bakers::ActiveModel {
|
let cake_baker = cakes_bakers::ActiveModel {
|
||||||
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
||||||
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
|
let cake_baker_res = CakesBakers::insert(cake_baker.clone())
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake_baker");
|
.expect("could not insert cake_baker");
|
||||||
|
assert_eq!(
|
||||||
|
cake_baker_res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Customer
|
// Customer
|
||||||
let customer_kate = customer::ActiveModel {
|
let customer_kate = customer::ActiveModel {
|
||||||
@ -63,7 +70,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
notes: Set(Some("Loves cheese cake".to_owned())),
|
notes: Set(Some("Loves cheese cake".to_owned())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let customer_insert_res: InsertResult = Customer::insert(customer_kate)
|
let customer_insert_res = Customer::insert(customer_kate)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert customer");
|
.expect("could not insert customer");
|
||||||
@ -76,7 +83,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
placed_at: Set(Utc::now().naive_utc()),
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
let order_insert_res = Order::insert(order_1)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert order");
|
.expect("could not insert order");
|
||||||
@ -89,7 +96,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
quantity: Set(1),
|
quantity: Set(1),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1)
|
let lineitem_insert_res = Lineitem::insert(lineitem_1)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert lineitem");
|
.expect("could not insert lineitem");
|
||||||
@ -105,7 +112,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
|
|
||||||
assert_eq!(lineitem_model.price, dec!(7.55));
|
assert_eq!(lineitem_model.price, dec!(7.55));
|
||||||
|
|
||||||
let cake: Option<cake::Model> = Cake::find_by_id(lineitem_model.cake_id as u64)
|
let cake: Option<cake::Model> = Cake::find_by_id(lineitem_model.cake_id)
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not find cake");
|
.expect("could not find cake");
|
||||||
|
@ -10,7 +10,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -26,7 +26,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let baker_insert_res: InsertResult = Baker::insert(baker_bob)
|
let baker_insert_res = Baker::insert(baker_bob)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert baker");
|
.expect("could not insert baker");
|
||||||
@ -41,7 +41,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
|
let cake_insert_res = Cake::insert(mud_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -50,12 +50,19 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
let cake_baker = cakes_bakers::ActiveModel {
|
let cake_baker = cakes_bakers::ActiveModel {
|
||||||
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
cake_id: Set(cake_insert_res.last_insert_id as i32),
|
||||||
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
baker_id: Set(baker_insert_res.last_insert_id as i32),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
|
let cake_baker_res = CakesBakers::insert(cake_baker.clone())
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake_baker");
|
.expect("could not insert cake_baker");
|
||||||
|
assert_eq!(
|
||||||
|
cake_baker_res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Customer
|
// Customer
|
||||||
let customer_kate = customer::ActiveModel {
|
let customer_kate = customer::ActiveModel {
|
||||||
@ -63,7 +70,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
notes: Set(Some("Loves cheese cake".to_owned())),
|
notes: Set(Some("Loves cheese cake".to_owned())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let customer_insert_res: InsertResult = Customer::insert(customer_kate)
|
let customer_insert_res = Customer::insert(customer_kate)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert customer");
|
.expect("could not insert customer");
|
||||||
@ -76,7 +83,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
placed_at: Set(Utc::now().naive_utc()),
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
let order_insert_res = Order::insert(order_1)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert order");
|
.expect("could not insert order");
|
||||||
@ -89,7 +96,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
quantity: Set(2),
|
quantity: Set(2),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let _lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1)
|
let _lineitem_insert_res = Lineitem::insert(lineitem_1)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert lineitem");
|
.expect("could not insert lineitem");
|
||||||
@ -103,7 +110,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
let order_model = order.unwrap();
|
let order_model = order.unwrap();
|
||||||
assert_eq!(order_model.total, dec!(15.10));
|
assert_eq!(order_model.total, dec!(15.10));
|
||||||
|
|
||||||
let customer: Option<customer::Model> = Customer::find_by_id(order_model.customer_id as u64)
|
let customer: Option<customer::Model> = Customer::find_by_id(order_model.customer_id)
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not find customer");
|
.expect("could not find customer");
|
||||||
@ -111,7 +118,7 @@ pub async fn test_create_order(db: &DbConn) {
|
|||||||
let customer_model = customer.unwrap();
|
let customer_model = customer.unwrap();
|
||||||
assert_eq!(customer_model.name, "Kate");
|
assert_eq!(customer_model.name, "Kate");
|
||||||
|
|
||||||
let bakery: Option<bakery::Model> = Bakery::find_by_id(order_model.bakery_id as i64)
|
let bakery: Option<bakery::Model> = Bakery::find_by_id(order_model.bakery_id)
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not find bakery");
|
.expect("could not find bakery");
|
||||||
|
@ -10,7 +10,7 @@ pub async fn test_delete_cake(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
use sea_orm::{entity::*, DbConn, InsertResult};
|
|
||||||
|
|
||||||
pub use super::common::bakery_chain::*;
|
|
||||||
|
|
||||||
pub mod create_baker;
|
pub mod create_baker;
|
||||||
pub mod create_cake;
|
pub mod create_cake;
|
||||||
pub mod create_lineitem;
|
pub mod create_lineitem;
|
||||||
@ -9,13 +5,23 @@ pub mod create_order;
|
|||||||
pub mod deletes;
|
pub mod deletes;
|
||||||
pub mod updates;
|
pub mod updates;
|
||||||
|
|
||||||
|
pub use create_baker::*;
|
||||||
|
pub use create_cake::*;
|
||||||
|
pub use create_lineitem::*;
|
||||||
|
pub use create_order::*;
|
||||||
|
pub use deletes::*;
|
||||||
|
pub use updates::*;
|
||||||
|
|
||||||
|
pub use super::common::bakery_chain::*;
|
||||||
|
use sea_orm::{entity::*, DbConn};
|
||||||
|
|
||||||
pub async fn test_create_bakery(db: &DbConn) {
|
pub async fn test_create_bakery(db: &DbConn) {
|
||||||
let seaside_bakery = bakery::ActiveModel {
|
let seaside_bakery = bakery::ActiveModel {
|
||||||
name: Set("SeaSide Bakery".to_owned()),
|
name: Set("SeaSide Bakery".to_owned()),
|
||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res: InsertResult = Bakery::insert(seaside_bakery)
|
let res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -28,7 +34,7 @@ pub async fn test_create_bakery(db: &DbConn) {
|
|||||||
assert!(bakery.is_some());
|
assert!(bakery.is_some());
|
||||||
let bakery_model = bakery.unwrap();
|
let bakery_model = bakery.unwrap();
|
||||||
assert_eq!(bakery_model.name, "SeaSide Bakery");
|
assert_eq!(bakery_model.name, "SeaSide Bakery");
|
||||||
assert_eq!(bakery_model.profit_margin, 10.4);
|
assert!((bakery_model.profit_margin - 10.4).abs() < f64::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn test_create_customer(db: &DbConn) {
|
pub async fn test_create_customer(db: &DbConn) {
|
||||||
@ -37,7 +43,7 @@ pub async fn test_create_customer(db: &DbConn) {
|
|||||||
notes: Set(Some("Loves cheese cake".to_owned())),
|
notes: Set(Some("Loves cheese cake".to_owned())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res: InsertResult = Customer::insert(customer_kate)
|
let res = Customer::insert(customer_kate)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert customer");
|
.expect("could not insert customer");
|
||||||
|
@ -8,7 +8,7 @@ pub async fn test_update_cake(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -22,7 +22,7 @@ pub async fn test_update_cake(db: &DbConn) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
|
let cake_insert_res = Cake::insert(mud_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -36,7 +36,7 @@ pub async fn test_update_cake(db: &DbConn) {
|
|||||||
let cake_model = cake.unwrap();
|
let cake_model = cake.unwrap();
|
||||||
assert_eq!(cake_model.name, "Mud Cake");
|
assert_eq!(cake_model.name, "Mud Cake");
|
||||||
assert_eq!(cake_model.price, dec!(10.25));
|
assert_eq!(cake_model.price, dec!(10.25));
|
||||||
assert_eq!(cake_model.gluten_free, false);
|
assert!(!cake_model.gluten_free);
|
||||||
|
|
||||||
let mut cake_am: cake::ActiveModel = cake_model.into();
|
let mut cake_am: cake::ActiveModel = cake_model.into();
|
||||||
cake_am.name = Set("Extra chocolate mud cake".to_owned());
|
cake_am.name = Set("Extra chocolate mud cake".to_owned());
|
||||||
@ -62,7 +62,7 @@ pub async fn test_update_bakery(db: &DbConn) {
|
|||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
|
let bakery_insert_res = Bakery::insert(seaside_bakery)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert bakery");
|
.expect("could not insert bakery");
|
||||||
@ -75,7 +75,7 @@ pub async fn test_update_bakery(db: &DbConn) {
|
|||||||
assert!(bakery.is_some());
|
assert!(bakery.is_some());
|
||||||
let bakery_model = bakery.unwrap();
|
let bakery_model = bakery.unwrap();
|
||||||
assert_eq!(bakery_model.name, "SeaSide Bakery");
|
assert_eq!(bakery_model.name, "SeaSide Bakery");
|
||||||
assert_eq!(bakery_model.profit_margin, 10.4);
|
assert!((bakery_model.profit_margin - 10.40).abs() < f64::EPSILON);
|
||||||
|
|
||||||
let mut bakery_am: bakery::ActiveModel = bakery_model.into();
|
let mut bakery_am: bakery::ActiveModel = bakery_model.into();
|
||||||
bakery_am.name = Set("SeaBreeze Bakery".to_owned());
|
bakery_am.name = Set("SeaBreeze Bakery".to_owned());
|
||||||
@ -92,7 +92,7 @@ pub async fn test_update_bakery(db: &DbConn) {
|
|||||||
.expect("could not find bakery");
|
.expect("could not find bakery");
|
||||||
let bakery_model = bakery.unwrap();
|
let bakery_model = bakery.unwrap();
|
||||||
assert_eq!(bakery_model.name, "SeaBreeze Bakery");
|
assert_eq!(bakery_model.name, "SeaBreeze Bakery");
|
||||||
assert_eq!(bakery_model.profit_margin, 12.00);
|
assert!((bakery_model.profit_margin - 12.00).abs() < f64::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn test_update_deleted_customer(db: &DbConn) {
|
pub async fn test_update_deleted_customer(db: &DbConn) {
|
||||||
@ -130,11 +130,10 @@ pub async fn test_update_deleted_customer(db: &DbConn) {
|
|||||||
|
|
||||||
assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers);
|
assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers);
|
||||||
|
|
||||||
let customer: Option<customer::Model> =
|
let customer: Option<customer::Model> = Customer::find_by_id(customer_id.clone().unwrap())
|
||||||
Customer::find_by_id(customer_id.clone().unwrap() as i64)
|
.one(db)
|
||||||
.one(db)
|
.await
|
||||||
.await
|
.expect("could not find customer");
|
||||||
.expect("could not find customer");
|
|
||||||
|
|
||||||
assert_eq!(customer, None);
|
assert_eq!(customer, None);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use sea_orm::DatabaseConnection;
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
|
||||||
|
|
||||||
mod crud;
|
mod crud;
|
||||||
|
|
||||||
|
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||||
|
pub use crud::*;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
// Run the test locally:
|
// Run the test locally:
|
||||||
// DATABASE_URL="mysql://root:root@localhost" cargo test --features sqlx-mysql,runtime-async-std --test crud_tests
|
// DATABASE_URL="mysql://root:root@localhost" cargo test --features sqlx-mysql,runtime-async-std --test crud_tests
|
||||||
// DATABASE_URL="postgres://root:root@localhost" cargo test --features sqlx-postgres,runtime-async-std --test crud_tests
|
// DATABASE_URL="postgres://root:root@localhost" cargo test --features sqlx-postgres,runtime-async-std --test crud_tests
|
||||||
@ -20,18 +20,18 @@ async fn main() {
|
|||||||
ctx.delete().await;
|
ctx.delete().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_entities(db: &DatabaseConnection) {
|
pub async fn create_entities(db: &DatabaseConnection) {
|
||||||
crud::test_create_bakery(db).await;
|
test_create_bakery(db).await;
|
||||||
crud::create_baker::test_create_baker(db).await;
|
test_create_baker(db).await;
|
||||||
crud::test_create_customer(db).await;
|
test_create_customer(db).await;
|
||||||
crud::create_cake::test_create_cake(db).await;
|
test_create_cake(db).await;
|
||||||
crud::create_lineitem::test_create_lineitem(db).await;
|
test_create_lineitem(db).await;
|
||||||
crud::create_order::test_create_order(db).await;
|
test_create_order(db).await;
|
||||||
|
|
||||||
crud::updates::test_update_cake(db).await;
|
test_update_cake(db).await;
|
||||||
crud::updates::test_update_bakery(db).await;
|
test_update_bakery(db).await;
|
||||||
crud::updates::test_update_deleted_customer(db).await;
|
test_update_deleted_customer(db).await;
|
||||||
|
|
||||||
crud::deletes::test_delete_cake(db).await;
|
test_delete_cake(db).await;
|
||||||
crud::deletes::test_delete_bakery(db).await;
|
test_delete_bakery(db).await;
|
||||||
}
|
}
|
||||||
|
42
tests/primary_key_tests.rs
Normal file
42
tests/primary_key_tests.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
pub mod common;
|
||||||
|
|
||||||
|
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||||
|
use sea_orm::{entity::prelude::*, DatabaseConnection, Set};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[sea_orm_macros::test]
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "sqlx-mysql",
|
||||||
|
feature = "sqlx-sqlite",
|
||||||
|
feature = "sqlx-postgres"
|
||||||
|
))]
|
||||||
|
async fn main() -> Result<(), DbErr> {
|
||||||
|
let ctx = TestContext::new("bakery_chain_schema_primary_key_tests").await;
|
||||||
|
|
||||||
|
create_metadata(&ctx.db).await?;
|
||||||
|
|
||||||
|
ctx.delete().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||||
|
let metadata = metadata::ActiveModel {
|
||||||
|
uuid: Set(Uuid::new_v4()),
|
||||||
|
key: Set("markup".to_owned()),
|
||||||
|
value: Set("1.18".to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = Metadata::insert(metadata.clone()).exec(db).await?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
metadata.uuid.unwrap()
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
use sea_orm::entity::*;
|
|
||||||
use sea_orm::QueryFilter;
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||||
|
pub use sea_orm::entity::*;
|
||||||
|
pub use sea_orm::QueryFilter;
|
||||||
|
|
||||||
// Run the test locally:
|
// Run the test locally:
|
||||||
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test query_tests
|
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test query_tests
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use chrono::offset::Utc;
|
|
||||||
use rust_decimal::prelude::*;
|
|
||||||
use rust_decimal_macros::dec;
|
|
||||||
use sea_orm::{entity::*, query::*, FromQueryResult};
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
|
pub use chrono::offset::Utc;
|
||||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||||
|
pub use rust_decimal::prelude::*;
|
||||||
|
pub use rust_decimal_macros::dec;
|
||||||
|
pub use sea_orm::{entity::*, query::*, DbErr, FromQueryResult};
|
||||||
|
pub use uuid::Uuid;
|
||||||
|
|
||||||
// Run the test locally:
|
// Run the test locally:
|
||||||
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test relational_tests
|
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std-native-tls --test relational_tests
|
||||||
#[sea_orm_macros::test]
|
#[sea_orm_macros::test]
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "sqlx-mysql",
|
feature = "sqlx-mysql",
|
||||||
@ -474,3 +475,240 @@ pub async fn having() {
|
|||||||
|
|
||||||
ctx.delete().await;
|
ctx.delete().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sea_orm_macros::test]
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "sqlx-mysql",
|
||||||
|
feature = "sqlx-sqlite",
|
||||||
|
feature = "sqlx-postgres"
|
||||||
|
))]
|
||||||
|
pub async fn linked() -> Result<(), DbErr> {
|
||||||
|
use common::bakery_chain::Order;
|
||||||
|
use sea_orm::{SelectA, SelectB};
|
||||||
|
|
||||||
|
let ctx = TestContext::new("test_linked").await;
|
||||||
|
|
||||||
|
// SeaSide Bakery
|
||||||
|
let seaside_bakery = bakery::ActiveModel {
|
||||||
|
name: Set("SeaSide Bakery".to_owned()),
|
||||||
|
profit_margin: Set(10.4),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let seaside_bakery_res = Bakery::insert(seaside_bakery).exec(&ctx.db).await?;
|
||||||
|
|
||||||
|
// Bob's Baker, Cake & Cake Baker
|
||||||
|
let baker_bob = baker::ActiveModel {
|
||||||
|
name: Set("Baker Bob".to_owned()),
|
||||||
|
contact_details: Set(serde_json::json!({
|
||||||
|
"mobile": "+61424000000",
|
||||||
|
"home": "0395555555",
|
||||||
|
"address": "12 Test St, Testville, Vic, Australia"
|
||||||
|
})),
|
||||||
|
bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let baker_bob_res = Baker::insert(baker_bob).exec(&ctx.db).await?;
|
||||||
|
let mud_cake = cake::ActiveModel {
|
||||||
|
name: Set("Mud Cake".to_owned()),
|
||||||
|
price: Set(dec!(10.25)),
|
||||||
|
gluten_free: Set(false),
|
||||||
|
serial: Set(Uuid::new_v4()),
|
||||||
|
bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mud_cake_res = Cake::insert(mud_cake).exec(&ctx.db).await?;
|
||||||
|
let bob_cakes_bakers = cakes_bakers::ActiveModel {
|
||||||
|
cake_id: Set(mud_cake_res.last_insert_id as i32),
|
||||||
|
baker_id: Set(baker_bob_res.last_insert_id as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
CakesBakers::insert(bob_cakes_bakers).exec(&ctx.db).await?;
|
||||||
|
|
||||||
|
// Bobby's Baker, Cake & Cake Baker
|
||||||
|
let baker_bobby = baker::ActiveModel {
|
||||||
|
name: Set("Baker Bobby".to_owned()),
|
||||||
|
contact_details: Set(serde_json::json!({
|
||||||
|
"mobile": "+85212345678",
|
||||||
|
})),
|
||||||
|
bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let baker_bobby_res = Baker::insert(baker_bobby).exec(&ctx.db).await?;
|
||||||
|
let cheese_cake = cake::ActiveModel {
|
||||||
|
name: Set("Cheese Cake".to_owned()),
|
||||||
|
price: Set(dec!(20.5)),
|
||||||
|
gluten_free: Set(false),
|
||||||
|
serial: Set(Uuid::new_v4()),
|
||||||
|
bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let cheese_cake_res = Cake::insert(cheese_cake).exec(&ctx.db).await?;
|
||||||
|
let bobby_cakes_bakers = cakes_bakers::ActiveModel {
|
||||||
|
cake_id: Set(cheese_cake_res.last_insert_id as i32),
|
||||||
|
baker_id: Set(baker_bobby_res.last_insert_id as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
CakesBakers::insert(bobby_cakes_bakers)
|
||||||
|
.exec(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
let chocolate_cake = cake::ActiveModel {
|
||||||
|
name: Set("Chocolate Cake".to_owned()),
|
||||||
|
price: Set(dec!(30.15)),
|
||||||
|
gluten_free: Set(false),
|
||||||
|
serial: Set(Uuid::new_v4()),
|
||||||
|
bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let chocolate_cake_res = Cake::insert(chocolate_cake).exec(&ctx.db).await?;
|
||||||
|
let bobby_cakes_bakers = cakes_bakers::ActiveModel {
|
||||||
|
cake_id: Set(chocolate_cake_res.last_insert_id as i32),
|
||||||
|
baker_id: Set(baker_bobby_res.last_insert_id as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
CakesBakers::insert(bobby_cakes_bakers)
|
||||||
|
.exec(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Kate's Customer, Order & Line Item
|
||||||
|
let customer_kate = customer::ActiveModel {
|
||||||
|
name: Set("Kate".to_owned()),
|
||||||
|
notes: Set(Some("Loves cheese cake".to_owned())),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let customer_kate_res = Customer::insert(customer_kate).exec(&ctx.db).await?;
|
||||||
|
let kate_order_1 = order::ActiveModel {
|
||||||
|
bakery_id: Set(seaside_bakery_res.last_insert_id as i32),
|
||||||
|
customer_id: Set(customer_kate_res.last_insert_id as i32),
|
||||||
|
total: Set(dec!(15.10)),
|
||||||
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let kate_order_1_res = Order::insert(kate_order_1).exec(&ctx.db).await?;
|
||||||
|
lineitem::ActiveModel {
|
||||||
|
cake_id: Set(cheese_cake_res.last_insert_id as i32),
|
||||||
|
order_id: Set(kate_order_1_res.last_insert_id as i32),
|
||||||
|
price: Set(dec!(7.55)),
|
||||||
|
quantity: Set(2),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
let kate_order_2 = order::ActiveModel {
|
||||||
|
bakery_id: Set(seaside_bakery_res.last_insert_id as i32),
|
||||||
|
customer_id: Set(customer_kate_res.last_insert_id as i32),
|
||||||
|
total: Set(dec!(29.7)),
|
||||||
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let kate_order_2_res = Order::insert(kate_order_2).exec(&ctx.db).await?;
|
||||||
|
lineitem::ActiveModel {
|
||||||
|
cake_id: Set(chocolate_cake_res.last_insert_id as i32),
|
||||||
|
order_id: Set(kate_order_2_res.last_insert_id as i32),
|
||||||
|
price: Set(dec!(9.9)),
|
||||||
|
quantity: Set(3),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Kara's Customer, Order & Line Item
|
||||||
|
let customer_kara = customer::ActiveModel {
|
||||||
|
name: Set("Kara".to_owned()),
|
||||||
|
notes: Set(Some("Loves all cakes".to_owned())),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let customer_kara_res = Customer::insert(customer_kara).exec(&ctx.db).await?;
|
||||||
|
let kara_order_1 = order::ActiveModel {
|
||||||
|
bakery_id: Set(seaside_bakery_res.last_insert_id as i32),
|
||||||
|
customer_id: Set(customer_kara_res.last_insert_id as i32),
|
||||||
|
total: Set(dec!(15.10)),
|
||||||
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let kara_order_1_res = Order::insert(kara_order_1).exec(&ctx.db).await?;
|
||||||
|
lineitem::ActiveModel {
|
||||||
|
cake_id: Set(mud_cake_res.last_insert_id as i32),
|
||||||
|
order_id: Set(kara_order_1_res.last_insert_id as i32),
|
||||||
|
price: Set(dec!(7.55)),
|
||||||
|
quantity: Set(2),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
let kara_order_2 = order::ActiveModel {
|
||||||
|
bakery_id: Set(seaside_bakery_res.last_insert_id as i32),
|
||||||
|
customer_id: Set(customer_kara_res.last_insert_id as i32),
|
||||||
|
total: Set(dec!(29.7)),
|
||||||
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let kara_order_2_res = Order::insert(kara_order_2).exec(&ctx.db).await?;
|
||||||
|
lineitem::ActiveModel {
|
||||||
|
cake_id: Set(cheese_cake_res.last_insert_id as i32),
|
||||||
|
order_id: Set(kara_order_2_res.last_insert_id as i32),
|
||||||
|
price: Set(dec!(9.9)),
|
||||||
|
quantity: Set(3),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
#[derive(Debug, FromQueryResult, PartialEq)]
|
||||||
|
struct BakerLite {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromQueryResult, PartialEq)]
|
||||||
|
struct CustomerLite {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let baked_for_customers: Vec<(BakerLite, Option<CustomerLite>)> = Baker::find()
|
||||||
|
.find_also_linked(baker::BakedForCustomer)
|
||||||
|
.select_only()
|
||||||
|
.column_as(baker::Column::Name, (SelectA, baker::Column::Name))
|
||||||
|
.column_as(customer::Column::Name, (SelectB, customer::Column::Name))
|
||||||
|
.group_by(baker::Column::Id)
|
||||||
|
.group_by(customer::Column::Id)
|
||||||
|
.group_by(baker::Column::Name)
|
||||||
|
.group_by(customer::Column::Name)
|
||||||
|
.order_by_asc(baker::Column::Id)
|
||||||
|
.order_by_asc(customer::Column::Id)
|
||||||
|
.into_model()
|
||||||
|
.all(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
baked_for_customers,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
BakerLite {
|
||||||
|
name: "Baker Bob".to_owned(),
|
||||||
|
},
|
||||||
|
Some(CustomerLite {
|
||||||
|
name: "Kara".to_owned(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BakerLite {
|
||||||
|
name: "Baker Bobby".to_owned(),
|
||||||
|
},
|
||||||
|
Some(CustomerLite {
|
||||||
|
name: "Kate".to_owned(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BakerLite {
|
||||||
|
name: "Baker Bobby".to_owned(),
|
||||||
|
},
|
||||||
|
Some(CustomerLite {
|
||||||
|
name: "Kara".to_owned(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.delete().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use chrono::offset::Utc;
|
|
||||||
use rust_decimal::prelude::*;
|
|
||||||
use rust_decimal_macros::dec;
|
|
||||||
use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
|
pub use chrono::offset::Utc;
|
||||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||||
|
pub use rust_decimal::prelude::*;
|
||||||
|
pub use rust_decimal_macros::dec;
|
||||||
|
pub use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult};
|
||||||
|
pub use uuid::Uuid;
|
||||||
|
|
||||||
// Run the test locally:
|
// Run the test locally:
|
||||||
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test sequential_op_tests
|
// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test sequential_op_tests
|
||||||
@ -67,7 +67,7 @@ async fn init_setup(db: &DatabaseConnection) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
|
let cake_insert_res = Cake::insert(mud_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -78,10 +78,18 @@ async fn init_setup(db: &DatabaseConnection) {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
|
let cake_baker_res = CakesBakers::insert(cake_baker.clone())
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake_baker");
|
.expect("could not insert cake_baker");
|
||||||
|
assert_eq!(
|
||||||
|
cake_baker_res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let customer_kate = customer::ActiveModel {
|
let customer_kate = customer::ActiveModel {
|
||||||
name: Set("Kate".to_owned()),
|
name: Set("Kate".to_owned()),
|
||||||
@ -183,7 +191,7 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option<baker::Model>
|
|||||||
|
|
||||||
results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold));
|
results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold));
|
||||||
|
|
||||||
Baker::find_by_id(results.last().unwrap().id as i64)
|
Baker::find_by_id(results.last().unwrap().id)
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -200,7 +208,7 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option<cak
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cake_insert_res: InsertResult = Cake::insert(new_cake)
|
let cake_insert_res = Cake::insert(new_cake)
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake");
|
.expect("could not insert cake");
|
||||||
@ -211,10 +219,18 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option<cak
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
|
let cake_baker_res = CakesBakers::insert(cake_baker.clone())
|
||||||
.exec(db)
|
.exec(db)
|
||||||
.await
|
.await
|
||||||
.expect("could not insert cake_baker");
|
.expect("could not insert cake_baker");
|
||||||
|
assert_eq!(
|
||||||
|
cake_baker_res.last_insert_id,
|
||||||
|
if cfg!(feature = "sqlx-postgres") {
|
||||||
|
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Cake::find_by_id(cake_insert_res.last_insert_id)
|
Cake::find_by_id(cake_insert_res.last_insert_id)
|
||||||
.one(db)
|
.one(db)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user