Merge branch 'master' into ss/test_suite
# Conflicts: # Cargo.toml
This commit is contained in:
commit
2e0f6df743
1
.github/workflows/rust.yml
vendored
1
.github/workflows/rust.yml
vendored
@ -31,3 +31,4 @@ jobs:
|
|||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
args: --all
|
||||||
|
44
Cargo.toml
44
Cargo.toml
@ -37,23 +37,27 @@ name = "sea_orm"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-stream = { version="^0.3" }
|
async-stream = { version = "^0.3" }
|
||||||
chrono = { version="^0", optional=true }
|
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-query = { version="^0.12" }
|
sea-query = { version = "^0.12" }
|
||||||
sea-orm-macros = { path="sea-orm-macros", optional=true }
|
sea-orm-macros = { path = "sea-orm-macros", optional = true }
|
||||||
serde = { version="^1.0", features=["derive"] }
|
sea-orm-codegen = { path = "sea-orm-codegen", optional = true }
|
||||||
sqlx = { version="^0.5", optional=true }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
strum = { git="https://github.com/SeaQL/strum.git", branch="sea-orm", version="^0.21", features=["derive", "sea-orm"] }
|
sqlx = { version = "^0.5", optional = true }
|
||||||
serde_json = { version="^1", optional=true }
|
strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [
|
||||||
|
"derive",
|
||||||
|
"sea-orm",
|
||||||
|
] }
|
||||||
|
serde_json = { version = "^1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
async-std = { version="^1.9", features=["attributes"] }
|
async-std = { version = "^1.9", features = ["attributes"] }
|
||||||
maplit = { version="^1" }
|
maplit = { version = "^1" }
|
||||||
rust_decimal_macros = { version="^1" }
|
rust_decimal_macros = { version = "^1" }
|
||||||
sea-orm = { path=".", features=[
|
sea-orm = { path = ".", features = [
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"sqlx-json",
|
"sqlx-json",
|
||||||
"sqlx-chrono",
|
"sqlx-chrono",
|
||||||
@ -64,8 +68,16 @@ sea-orm = { path=".", features=[
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug-print = []
|
debug-print = []
|
||||||
default = ["macros", "with-json", "with-chrono", "with-rust_decimal", "mock"]
|
default = [
|
||||||
|
"macros",
|
||||||
|
"codegen",
|
||||||
|
"with-json",
|
||||||
|
"with-chrono",
|
||||||
|
"with-rust_decimal",
|
||||||
|
"mock",
|
||||||
|
]
|
||||||
macros = ["sea-orm-macros"]
|
macros = ["sea-orm-macros"]
|
||||||
|
codegen = ["sea-orm-codegen"]
|
||||||
mock = []
|
mock = []
|
||||||
with-json = ["serde_json", "sea-query/with-json"]
|
with-json = ["serde_json", "sea-query/with-json"]
|
||||||
with-chrono = ["chrono", "sea-query/with-chrono"]
|
with-chrono = ["chrono", "sea-query/with-chrono"]
|
||||||
|
@ -23,3 +23,7 @@ syn = { version = "^1", default-features = false, features = [ "derive", "parsin
|
|||||||
quote = "^1"
|
quote = "^1"
|
||||||
heck = "^0.3"
|
heck = "^0.3"
|
||||||
proc-macro2 = "^1"
|
proc-macro2 = "^1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||||
|
sea-orm = { path = "../", features = ["mock", "sqlx-json", "sqlx-chrono", "runtime-async-std-native-tls"] }
|
||||||
|
@ -22,7 +22,7 @@ impl Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rs_type(&self) -> TokenStream {
|
pub fn get_rs_type(&self) -> TokenStream {
|
||||||
let ident = match self.col_type {
|
let ident: TokenStream = match self.col_type {
|
||||||
ColumnType::Char(_)
|
ColumnType::Char(_)
|
||||||
| ColumnType::String(_)
|
| ColumnType::String(_)
|
||||||
| ColumnType::Text
|
| ColumnType::Text
|
||||||
@ -32,18 +32,18 @@ impl Column {
|
|||||||
| ColumnType::Date
|
| ColumnType::Date
|
||||||
| ColumnType::Json
|
| ColumnType::Json
|
||||||
| ColumnType::JsonBinary
|
| ColumnType::JsonBinary
|
||||||
| ColumnType::Custom(_) => format_ident!("String"),
|
| ColumnType::Custom(_) => "String",
|
||||||
ColumnType::TinyInteger(_) => format_ident!("i8"),
|
ColumnType::TinyInteger(_) => "i8",
|
||||||
ColumnType::SmallInteger(_) => format_ident!("i16"),
|
ColumnType::SmallInteger(_) => "i16",
|
||||||
ColumnType::Integer(_) => format_ident!("i32"),
|
ColumnType::Integer(_) => "i32",
|
||||||
ColumnType::BigInteger(_) => format_ident!("i64"),
|
ColumnType::BigInteger(_) => "i64",
|
||||||
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => {
|
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => "f32",
|
||||||
format_ident!("f32")
|
ColumnType::Double(_) => "f64",
|
||||||
}
|
ColumnType::Binary(_) => "Vec<u8>",
|
||||||
ColumnType::Double(_) => format_ident!("f64"),
|
ColumnType::Boolean => "bool",
|
||||||
ColumnType::Binary(_) => format_ident!("Vec<u8>"),
|
}
|
||||||
ColumnType::Boolean => format_ident!("bool"),
|
.parse()
|
||||||
};
|
.unwrap();
|
||||||
match self.not_null {
|
match self.not_null {
|
||||||
true => quote! { #ident },
|
true => quote! { #ident },
|
||||||
false => quote! { Option<#ident> },
|
false => quote! { Option<#ident> },
|
||||||
@ -102,6 +102,12 @@ impl Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ColumnDef> for Column {
|
||||||
|
fn from(col_def: ColumnDef) -> Self {
|
||||||
|
(&col_def).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&ColumnDef> for Column {
|
impl From<&ColumnDef> for Column {
|
||||||
fn from(col_def: &ColumnDef) -> Self {
|
fn from(col_def: &ColumnDef) -> Self {
|
||||||
let name = col_def.get_column_name();
|
let name = col_def.get_column_name();
|
||||||
@ -145,3 +151,164 @@ impl From<&ColumnDef> for Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::Column;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use sea_query::{Alias, ColumnDef, ColumnType, SeaRc};
|
||||||
|
|
||||||
|
fn setup() -> Vec<Column> {
|
||||||
|
macro_rules! make_col {
|
||||||
|
($name:expr, $col_type:expr) => {
|
||||||
|
Column {
|
||||||
|
name: $name.to_owned(),
|
||||||
|
col_type: $col_type,
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
vec![
|
||||||
|
make_col!("id", ColumnType::String(Some(255))),
|
||||||
|
make_col!(
|
||||||
|
"cake_id",
|
||||||
|
ColumnType::Custom(SeaRc::new(Alias::new("cus_col")))
|
||||||
|
),
|
||||||
|
make_col!("CakeId", ColumnType::TinyInteger(None)),
|
||||||
|
make_col!("CakeId", ColumnType::SmallInteger(None)),
|
||||||
|
make_col!("CakeId", ColumnType::Integer(Some(11))),
|
||||||
|
make_col!("CakeFillingId", ColumnType::BigInteger(None)),
|
||||||
|
make_col!("cake-filling-id", ColumnType::Float(None)),
|
||||||
|
make_col!("CAKE_FILLING_ID", ColumnType::Double(None)),
|
||||||
|
make_col!("CAKE-FILLING-ID", ColumnType::Binary(None)),
|
||||||
|
make_col!("CAKE", ColumnType::Boolean),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_name_snake_case() {
|
||||||
|
let columns = setup();
|
||||||
|
let snack_cases = vec![
|
||||||
|
"id",
|
||||||
|
"cake_id",
|
||||||
|
"cake_id",
|
||||||
|
"cake_id",
|
||||||
|
"cake_id",
|
||||||
|
"cake_filling_id",
|
||||||
|
"cake_filling_id",
|
||||||
|
"cake_filling_id",
|
||||||
|
"cake_filling_id",
|
||||||
|
"cake",
|
||||||
|
];
|
||||||
|
for (col, snack_case) in columns.into_iter().zip(snack_cases) {
|
||||||
|
assert_eq!(col.get_name_snake_case().to_string(), snack_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_name_camel_case() {
|
||||||
|
let columns = setup();
|
||||||
|
let camel_cases = vec![
|
||||||
|
"Id",
|
||||||
|
"CakeId",
|
||||||
|
"CakeId",
|
||||||
|
"CakeId",
|
||||||
|
"CakeId",
|
||||||
|
"CakeFillingId",
|
||||||
|
"CakeFillingId",
|
||||||
|
"CakeFillingId",
|
||||||
|
"CakeFillingId",
|
||||||
|
"Cake",
|
||||||
|
];
|
||||||
|
for (col, camel_case) in columns.into_iter().zip(camel_cases) {
|
||||||
|
assert_eq!(col.get_name_camel_case().to_string(), camel_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rs_type() {
|
||||||
|
let columns = setup();
|
||||||
|
let rs_types = vec![
|
||||||
|
"String", "String", "i8", "i16", "i32", "i64", "f32", "f64", "Vec<u8>", "bool",
|
||||||
|
];
|
||||||
|
for (mut col, rs_type) in columns.into_iter().zip(rs_types) {
|
||||||
|
let rs_type: TokenStream = rs_type.parse().unwrap();
|
||||||
|
|
||||||
|
col.not_null = true;
|
||||||
|
assert_eq!(col.get_rs_type().to_string(), quote!(#rs_type).to_string());
|
||||||
|
|
||||||
|
col.not_null = false;
|
||||||
|
assert_eq!(
|
||||||
|
col.get_rs_type().to_string(),
|
||||||
|
quote!(Option<#rs_type>).to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_def() {
|
||||||
|
let columns = setup();
|
||||||
|
let col_defs = vec![
|
||||||
|
"ColumnType::String(Some(255u32)).def()",
|
||||||
|
"ColumnType::Custom(\"cus_col\".to_owned()).def()",
|
||||||
|
"ColumnType::TinyInteger.def()",
|
||||||
|
"ColumnType::SmallInteger.def()",
|
||||||
|
"ColumnType::Integer.def()",
|
||||||
|
"ColumnType::BigInteger.def()",
|
||||||
|
"ColumnType::Float.def()",
|
||||||
|
"ColumnType::Double.def()",
|
||||||
|
"ColumnType::Binary.def()",
|
||||||
|
"ColumnType::Boolean.def()",
|
||||||
|
];
|
||||||
|
for (mut col, col_def) in columns.into_iter().zip(col_defs) {
|
||||||
|
let mut col_def: TokenStream = col_def.parse().unwrap();
|
||||||
|
|
||||||
|
col.not_null = true;
|
||||||
|
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||||
|
|
||||||
|
col.not_null = false;
|
||||||
|
col_def.extend(quote!(.null()));
|
||||||
|
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||||
|
|
||||||
|
col.unique = true;
|
||||||
|
col_def.extend(quote!(.unique()));
|
||||||
|
assert_eq!(col.get_def().to_string(), col_def.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_column_def() {
|
||||||
|
let column: Column = ColumnDef::new(Alias::new("id")).string().into();
|
||||||
|
assert_eq!(
|
||||||
|
column.get_def().to_string(),
|
||||||
|
quote! {
|
||||||
|
ColumnType::String(None).def().null()
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
let column: Column = ColumnDef::new(Alias::new("id")).string().not_null().into();
|
||||||
|
assert!(column.not_null);
|
||||||
|
|
||||||
|
let column: Column = ColumnDef::new(Alias::new("id"))
|
||||||
|
.string()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
.into();
|
||||||
|
assert!(column.unique);
|
||||||
|
assert!(column.not_null);
|
||||||
|
|
||||||
|
let column: Column = ColumnDef::new(Alias::new("id"))
|
||||||
|
.string()
|
||||||
|
.auto_increment()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
.into();
|
||||||
|
assert!(column.auto_increment);
|
||||||
|
assert!(column.unique);
|
||||||
|
assert!(column.not_null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -111,15 +111,242 @@ impl Entity {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_relation_rel_find_helpers(&self) -> Vec<Ident> {
|
|
||||||
self.relations
|
|
||||||
.iter()
|
|
||||||
.map(|rel| rel.get_rel_find_helper())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_primary_key_auto_increment(&self) -> Ident {
|
pub fn get_primary_key_auto_increment(&self) -> Ident {
|
||||||
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
|
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
|
||||||
format_ident!("{}", auto_increment)
|
format_ident!("{}", auto_increment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{Column, Entity, PrimaryKey, Relation, RelationType};
|
||||||
|
use quote::format_ident;
|
||||||
|
use sea_query::ColumnType;
|
||||||
|
|
||||||
|
fn setup() -> Entity {
|
||||||
|
Entity {
|
||||||
|
table_name: "special_cake".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::String(None),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::String(None),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "fruit".to_owned(),
|
||||||
|
columns: vec!["id".to_owned()],
|
||||||
|
ref_columns: vec!["cake_id".to_owned()],
|
||||||
|
rel_type: RelationType::HasOne,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "filling".to_owned(),
|
||||||
|
columns: vec!["id".to_owned()],
|
||||||
|
ref_columns: vec!["cake_id".to_owned()],
|
||||||
|
rel_type: RelationType::HasOne,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_table_name_snake_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_table_name_snake_case(),
|
||||||
|
"special_cake".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_table_name_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(entity.get_table_name_camel_case(), "SpecialCake".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_table_name_snake_case_ident() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_table_name_snake_case_ident(),
|
||||||
|
format_ident!("{}", "special_cake")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_table_name_camel_case_ident() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_table_name_camel_case_ident(),
|
||||||
|
format_ident!("{}", "SpecialCake")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_column_names_snake_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_column_names_snake_case().into_iter().enumerate() {
|
||||||
|
assert_eq!(elem, entity.columns[i].get_name_snake_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_column_names_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_column_names_camel_case().into_iter().enumerate() {
|
||||||
|
assert_eq!(elem, entity.columns[i].get_name_camel_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_column_rs_types() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_column_rs_types().into_iter().enumerate() {
|
||||||
|
assert_eq!(
|
||||||
|
elem.to_string(),
|
||||||
|
entity.columns[i].get_rs_type().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_column_defs() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_column_defs().into_iter().enumerate() {
|
||||||
|
assert_eq!(elem.to_string(), entity.columns[i].get_def().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_primary_key_names_snake_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_primary_key_names_snake_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.primary_keys[i].get_name_snake_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_primary_key_names_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_primary_key_names_camel_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.primary_keys[i].get_name_camel_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_ref_tables_snake_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_relation_ref_tables_snake_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.relations[i].get_ref_table_snake_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_ref_tables_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_relation_ref_tables_camel_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.relations[i].get_ref_table_camel_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_defs() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_relation_defs().into_iter().enumerate() {
|
||||||
|
assert_eq!(elem.to_string(), entity.relations[i].get_def().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_rel_types() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity.get_relation_rel_types().into_iter().enumerate() {
|
||||||
|
assert_eq!(elem, entity.relations[i].get_rel_type());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_columns_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_relation_columns_camel_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.relations[i].get_column_camel_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_relation_ref_columns_camel_case() {
|
||||||
|
let entity = setup();
|
||||||
|
|
||||||
|
for (i, elem) in entity
|
||||||
|
.get_relation_ref_columns_camel_case()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(elem, entity.relations[i].get_ref_column_camel_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_primary_key_auto_increment() {
|
||||||
|
let mut entity = setup();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_primary_key_auto_increment(),
|
||||||
|
format_ident!("{}", false)
|
||||||
|
);
|
||||||
|
|
||||||
|
entity.columns[0].auto_increment = true;
|
||||||
|
assert_eq!(
|
||||||
|
entity.get_primary_key_auto_increment(),
|
||||||
|
format_ident!("{}", true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,3 +16,28 @@ impl PrimaryKey {
|
|||||||
format_ident!("{}", self.name.to_camel_case())
|
format_ident!("{}", self.name.to_camel_case())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::PrimaryKey;
|
||||||
|
|
||||||
|
fn setup() -> PrimaryKey {
|
||||||
|
PrimaryKey {
|
||||||
|
name: "cake_id".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_name_snake_case() {
|
||||||
|
let primary_key = setup();
|
||||||
|
|
||||||
|
assert_eq!(primary_key.get_name_snake_case(), "cake_id".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_name_camel_case() {
|
||||||
|
let primary_key = setup();
|
||||||
|
|
||||||
|
assert_eq!(primary_key.get_name_camel_case(), "CakeId".to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,10 +64,6 @@ impl Relation {
|
|||||||
pub fn get_ref_column_camel_case(&self) -> Ident {
|
pub fn get_ref_column_camel_case(&self) -> Ident {
|
||||||
format_ident!("{}", self.ref_columns[0].to_camel_case())
|
format_ident!("{}", self.ref_columns[0].to_camel_case())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rel_find_helper(&self) -> Ident {
|
|
||||||
format_ident!("find_{}", self.ref_table.to_snake_case())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TableForeignKey> for Relation {
|
impl From<&TableForeignKey> for Relation {
|
||||||
@ -87,3 +83,95 @@ impl From<&TableForeignKey> for Relation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{Relation, RelationType};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
|
||||||
|
fn setup() -> Vec<Relation> {
|
||||||
|
vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "fruit".to_owned(),
|
||||||
|
columns: vec!["id".to_owned()],
|
||||||
|
ref_columns: vec!["cake_id".to_owned()],
|
||||||
|
rel_type: RelationType::HasOne,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "filling".to_owned(),
|
||||||
|
columns: vec!["filling_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::BelongsTo,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "filling".to_owned(),
|
||||||
|
columns: vec!["filling_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_ref_table_snake_case() {
|
||||||
|
let relations = setup();
|
||||||
|
let snake_cases = vec!["fruit", "filling", "filling"];
|
||||||
|
for (rel, snake_case) in relations.into_iter().zip(snake_cases) {
|
||||||
|
assert_eq!(rel.get_ref_table_snake_case().to_string(), snake_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_ref_table_camel_case() {
|
||||||
|
let relations = setup();
|
||||||
|
let camel_cases = vec!["Fruit", "Filling", "Filling"];
|
||||||
|
for (rel, camel_case) in relations.into_iter().zip(camel_cases) {
|
||||||
|
assert_eq!(rel.get_ref_table_camel_case().to_string(), camel_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_def() {
|
||||||
|
let relations = setup();
|
||||||
|
let rel_defs = vec![
|
||||||
|
"Entity::has_one(super::fruit::Entity).into()",
|
||||||
|
"Entity::belongs_to(super::filling::Entity) \
|
||||||
|
.from(Column::FillingId) \
|
||||||
|
.to(super::filling::Column::Id) \
|
||||||
|
.into()",
|
||||||
|
"Entity::has_many(super::filling::Entity).into()",
|
||||||
|
];
|
||||||
|
for (rel, rel_def) in relations.into_iter().zip(rel_defs) {
|
||||||
|
let rel_def: TokenStream = rel_def.parse().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(rel.get_def().to_string(), rel_def.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rel_type() {
|
||||||
|
let relations = setup();
|
||||||
|
let rel_types = vec!["has_one", "belongs_to", "has_many"];
|
||||||
|
for (rel, rel_type) in relations.into_iter().zip(rel_types) {
|
||||||
|
assert_eq!(rel.get_rel_type(), rel_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_column_camel_case() {
|
||||||
|
let relations = setup();
|
||||||
|
let cols = vec!["Id", "FillingId", "FillingId"];
|
||||||
|
for (rel, col) in relations.into_iter().zip(cols) {
|
||||||
|
assert_eq!(rel.get_column_camel_case(), col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_ref_column_camel_case() {
|
||||||
|
let relations = setup();
|
||||||
|
let ref_cols = vec!["CakeId", "Id", "Id"];
|
||||||
|
for (rel, ref_col) in relations.into_iter().zip(ref_cols) {
|
||||||
|
assert_eq!(rel.get_ref_column_camel_case(), ref_col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -98,6 +98,7 @@ impl EntityTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!("{:#?}", entities);
|
||||||
Ok(EntityWriter { entities })
|
Ok(EntityWriter { entities })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,9 +116,7 @@ impl EntityWriter {
|
|||||||
Self::gen_impl_relation_trait(entity),
|
Self::gen_impl_relation_trait(entity),
|
||||||
];
|
];
|
||||||
code_blocks.extend(Self::gen_impl_related(entity));
|
code_blocks.extend(Self::gen_impl_related(entity));
|
||||||
code_blocks.extend(vec![
|
code_blocks.extend(vec![Self::gen_impl_active_model_behavior()]);
|
||||||
Self::gen_impl_active_model_behavior(),
|
|
||||||
]);
|
|
||||||
code_blocks
|
code_blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +150,7 @@ impl EntityWriter {
|
|||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#(pub #column_names_snake_case: #column_rs_types),*
|
#(pub #column_names_snake_case: #column_rs_types,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +160,7 @@ impl EntityWriter {
|
|||||||
quote! {
|
quote! {
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
pub enum Column {
|
pub enum Column {
|
||||||
#(#column_names_camel_case),*
|
#(#column_names_camel_case,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,7 +170,7 @@ impl EntityWriter {
|
|||||||
quote! {
|
quote! {
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
pub enum PrimaryKey {
|
pub enum PrimaryKey {
|
||||||
#(#primary_key_names_camel_case),*
|
#(#primary_key_names_camel_case,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +191,7 @@ impl EntityWriter {
|
|||||||
quote! {
|
quote! {
|
||||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
#(#relation_ref_tables_camel_case),*
|
#(#relation_ref_tables_camel_case,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,7 +205,7 @@ impl EntityWriter {
|
|||||||
|
|
||||||
fn def(&self) -> ColumnDef {
|
fn def(&self) -> ColumnDef {
|
||||||
match self {
|
match self {
|
||||||
#(Self::#column_names_camel_case => #column_defs),*
|
#(Self::#column_names_camel_case => #column_defs,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +221,7 @@ impl EntityWriter {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
#(Self::#relation_ref_tables_camel_case => #relation_defs),*
|
#(Self::#relation_ref_tables_camel_case => #relation_defs,)*
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
quote! {
|
quote! {
|
||||||
@ -276,3 +274,239 @@ impl EntityWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{Column, Entity, EntityWriter, PrimaryKey, Relation, RelationType};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use sea_query::ColumnType;
|
||||||
|
use std::io::{self, BufRead, BufReader};
|
||||||
|
|
||||||
|
const ENTITY_FILES: [&'static str; 5] = [
|
||||||
|
include_str!("../../tests/entity/cake.rs"),
|
||||||
|
include_str!("../../tests/entity/cake_filling.rs"),
|
||||||
|
include_str!("../../tests/entity/filling.rs"),
|
||||||
|
include_str!("../../tests/entity/fruit.rs"),
|
||||||
|
include_str!("../../tests/entity/vendor.rs"),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn setup() -> Vec<Entity> {
|
||||||
|
vec![
|
||||||
|
Entity {
|
||||||
|
table_name: "cake".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: true,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::String(Some(255)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "cake_filling".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "fruit".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
table_name: "cake_filling".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "cake_id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "filling_id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "cake".to_owned(),
|
||||||
|
columns: vec!["cake_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::BelongsTo,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "filling".to_owned(),
|
||||||
|
columns: vec!["filling_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::BelongsTo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_keys: vec![
|
||||||
|
PrimaryKey {
|
||||||
|
name: "cake_id".to_owned(),
|
||||||
|
},
|
||||||
|
PrimaryKey {
|
||||||
|
name: "filling_id".to_owned(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
table_name: "filling".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: true,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::String(Some(255)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![Relation {
|
||||||
|
ref_table: "cake_filling".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
}],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
table_name: "fruit".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: true,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::String(Some(255)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "cake_id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "cake".to_owned(),
|
||||||
|
columns: vec!["cake_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::BelongsTo,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "vendor".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
table_name: "vendor".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: true,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::String(Some(255)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "fruit_id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer(Some(11)),
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![Relation {
|
||||||
|
ref_table: "fruit".to_owned(),
|
||||||
|
columns: vec!["fruit_id".to_owned()],
|
||||||
|
ref_columns: vec!["id".to_owned()],
|
||||||
|
rel_type: RelationType::BelongsTo,
|
||||||
|
}],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gen_code_blocks() -> io::Result<()> {
|
||||||
|
let entities = setup();
|
||||||
|
|
||||||
|
assert_eq!(entities.len(), ENTITY_FILES.len());
|
||||||
|
|
||||||
|
for (i, entity) in entities.iter().enumerate() {
|
||||||
|
let mut reader = BufReader::new(ENTITY_FILES[i].as_bytes());
|
||||||
|
let mut lines: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
reader.read_until(b';', &mut Vec::new())?;
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
while reader.read_line(&mut line)? > 0 {
|
||||||
|
lines.push(line.to_owned());
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
let content = lines.join("");
|
||||||
|
let expected: TokenStream = content.parse().unwrap();
|
||||||
|
let generated = EntityWriter::gen_code_blocks(entity)
|
||||||
|
.into_iter()
|
||||||
|
.skip(1)
|
||||||
|
.fold(TokenStream::new(), |mut acc, tok| {
|
||||||
|
acc.extend(tok);
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
assert_eq!(expected.to_string(), generated.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
74
sea-orm-codegen/tests/entity/cake.rs
Normal file
74
sea-orm-codegen/tests/entity/cake.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
CakeFilling,
|
||||||
|
Fruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||||
|
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake_filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::CakeFilling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
81
sea-orm-codegen/tests/entity/cake_filling.rs
Normal file
81
sea-orm-codegen/tests/entity/cake_filling.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake_filling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub cake_id: i32,
|
||||||
|
pub filling_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
Filling,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeId => ColumnType::Integer.def(),
|
||||||
|
Self::FillingId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
Self::Filling => Entity::belongs_to(super::filling::Entity)
|
||||||
|
.from(Column::FillingId)
|
||||||
|
.to(super::filling::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Filling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
66
sea-orm-codegen/tests/entity/filling.rs
Normal file
66
sea-orm-codegen/tests/entity/filling.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"filling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
CakeFilling,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake_filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::CakeFilling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
80
sea-orm-codegen/tests/entity/fruit.rs
Normal file
80
sea-orm-codegen/tests/entity/fruit.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"fruit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub cake_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
CakeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
Vendor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
Self::CakeId => ColumnType::Integer.def().null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
Self::Vendor => Entity::has_many(super::vendor::Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::vendor::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Vendor.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
7
sea-orm-codegen/tests/entity/mod.rs
Normal file
7
sea-orm-codegen/tests/entity/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
pub mod cake;
|
||||||
|
pub mod cake_filling;
|
||||||
|
pub mod filling;
|
||||||
|
pub mod fruit;
|
||||||
|
pub mod vendor;
|
7
sea-orm-codegen/tests/entity/prelude.rs
Normal file
7
sea-orm-codegen/tests/entity/prelude.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
pub use super::cake::Entity as Cake;
|
||||||
|
pub use super::cake_filling::Entity as CakeFilling;
|
||||||
|
pub use super::filling::Entity as Filling;
|
||||||
|
pub use super::fruit::Entity as Fruit;
|
||||||
|
pub use super::vendor::Entity as Vendor;
|
72
sea-orm-codegen/tests/entity/vendor.rs
Normal file
72
sea-orm-codegen/tests/entity/vendor.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"vendor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub fruit_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
FruitId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Fruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
Self::FruitId => ColumnType::Integer.def().null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Fruit => Entity::belongs_to(super::fruit::Entity)
|
||||||
|
.from(Column::FruitId)
|
||||||
|
.to(super::fruit::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
127
sea-orm-codegen/tests/mod.rs
Normal file
127
sea-orm-codegen/tests/mod.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
mod entity;
|
||||||
|
|
||||||
|
use entity::*;
|
||||||
|
|
||||||
|
use sea_orm::{entity::*, error::*, MockDatabase, MockExecResult, Transaction};
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_insert() -> Result<(), DbErr> {
|
||||||
|
let exec_result = MockExecResult {
|
||||||
|
last_insert_id: 1,
|
||||||
|
rows_affected: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = MockDatabase::new()
|
||||||
|
.append_exec_results(vec![exec_result.clone()])
|
||||||
|
.into_connection();
|
||||||
|
|
||||||
|
let apple = cake::ActiveModel {
|
||||||
|
name: Set("Apple Pie".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||||
|
|
||||||
|
assert_eq!(insert_result.last_insert_id, exec_result.last_insert_id);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.into_transaction_log(),
|
||||||
|
vec![Transaction::from_sql_and_values(
|
||||||
|
r#"INSERT INTO "cake" ("name") VALUES ($1)"#,
|
||||||
|
vec!["Apple Pie".into()]
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_select() -> Result<(), DbErr> {
|
||||||
|
let query_results = vec![cake_filling::Model {
|
||||||
|
cake_id: 2,
|
||||||
|
filling_id: 3,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let db = MockDatabase::new()
|
||||||
|
.append_query_results(vec![query_results.clone()])
|
||||||
|
.into_connection();
|
||||||
|
|
||||||
|
let selected_models = cake_filling::Entity::find_by_id((2, 3)).all(&db).await?;
|
||||||
|
|
||||||
|
assert_eq!(selected_models, query_results);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.into_transaction_log(),
|
||||||
|
vec![Transaction::from_sql_and_values([
|
||||||
|
r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
|
||||||
|
r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
|
||||||
|
].join(" ").as_str(),
|
||||||
|
vec![2i32.into(), 3i32.into()]
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_update() -> Result<(), DbErr> {
|
||||||
|
let exec_result = MockExecResult {
|
||||||
|
last_insert_id: 1,
|
||||||
|
rows_affected: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = MockDatabase::new()
|
||||||
|
.append_exec_results(vec![exec_result.clone()])
|
||||||
|
.into_connection();
|
||||||
|
|
||||||
|
let orange = fruit::ActiveModel {
|
||||||
|
id: Set(1),
|
||||||
|
name: Set("Orange".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let updated_model = fruit::Entity::update(orange.clone()).exec(&db).await?;
|
||||||
|
|
||||||
|
assert_eq!(updated_model, orange);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.into_transaction_log(),
|
||||||
|
vec![Transaction::from_sql_and_values(
|
||||||
|
r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2"#,
|
||||||
|
vec!["Orange".into(), 1i32.into()]
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_delete() -> Result<(), DbErr> {
|
||||||
|
let exec_result = MockExecResult {
|
||||||
|
last_insert_id: 1,
|
||||||
|
rows_affected: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = MockDatabase::new()
|
||||||
|
.append_exec_results(vec![exec_result.clone()])
|
||||||
|
.into_connection();
|
||||||
|
|
||||||
|
let orange = fruit::ActiveModel {
|
||||||
|
id: Set(3),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
|
||||||
|
|
||||||
|
assert_eq!(delete_result.rows_affected, exec_result.rows_affected);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
db.into_transaction_log(),
|
||||||
|
vec![Transaction::from_sql_and_values(
|
||||||
|
r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
|
||||||
|
vec![3i32.into()]
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -71,7 +71,11 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
|
|||||||
|
|
||||||
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> {
|
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> {
|
||||||
match c {
|
match c {
|
||||||
#(<Self::Entity as EntityTrait>::Column::#name => std::mem::take(&mut self.#field).into_wrapped_value(),)*
|
#(<Self::Entity as EntityTrait>::Column::#name => {
|
||||||
|
let mut value = sea_orm::ActiveValue::unset();
|
||||||
|
std::mem::swap(&mut value, &mut self.#field);
|
||||||
|
value.into_wrapped_value()
|
||||||
|
},)*
|
||||||
_ => sea_orm::ActiveValue::unset(),
|
_ => sea_orm::ActiveValue::unset(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{error::*, ExecResult, QueryResult, Statement};
|
use crate::{error::*, ExecResult, QueryResult, Statement, Syntax};
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder,
|
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SchemaStatementBuilder,
|
||||||
SqliteQueryBuilder,
|
SqliteQueryBuilder,
|
||||||
@ -134,29 +134,49 @@ impl DatabaseConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QueryBuilderBackend {
|
impl QueryBuilderBackend {
|
||||||
|
pub fn syntax(&self) -> Syntax {
|
||||||
|
match self {
|
||||||
|
Self::MySql => Syntax::MySql,
|
||||||
|
Self::Postgres => Syntax::Postgres,
|
||||||
|
Self::Sqlite => Syntax::Sqlite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build<S>(&self, statement: &S) -> Statement
|
pub fn build<S>(&self, statement: &S) -> Statement
|
||||||
where
|
where
|
||||||
S: QueryStatementBuilder,
|
S: QueryStatementBuilder,
|
||||||
{
|
{
|
||||||
match self {
|
Statement::from_string_values_tuple(
|
||||||
Self::MySql => statement.build(MysqlQueryBuilder),
|
self.syntax(),
|
||||||
Self::Postgres => statement.build(PostgresQueryBuilder),
|
match self {
|
||||||
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
}
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
.into()
|
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchemaBuilderBackend {
|
impl SchemaBuilderBackend {
|
||||||
|
pub fn syntax(&self) -> Syntax {
|
||||||
|
match self {
|
||||||
|
Self::MySql => Syntax::MySql,
|
||||||
|
Self::Postgres => Syntax::Postgres,
|
||||||
|
Self::Sqlite => Syntax::Sqlite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build<S>(&self, statement: &S) -> Statement
|
pub fn build<S>(&self, statement: &S) -> Statement
|
||||||
where
|
where
|
||||||
S: SchemaStatementBuilder,
|
S: SchemaStatementBuilder,
|
||||||
{
|
{
|
||||||
match self {
|
Statement::from_string(
|
||||||
Self::MySql => statement.build(MysqlQueryBuilder),
|
self.syntax(),
|
||||||
Self::Postgres => statement.build(PostgresQueryBuilder),
|
match self {
|
||||||
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
}
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
.into()
|
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,11 +79,7 @@ impl MockDatabaseTrait for MockDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query(
|
fn query(&mut self, counter: usize, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||||
&mut self,
|
|
||||||
counter: usize,
|
|
||||||
statement: Statement,
|
|
||||||
) -> Result<Vec<QueryResult>, DbErr> {
|
|
||||||
self.transaction_log.push(Transaction::one(statement));
|
self.transaction_log.push(Transaction::one(statement));
|
||||||
if counter < self.query_results.len() {
|
if counter < self.query_results.len() {
|
||||||
Ok(std::mem::take(&mut self.query_results[counter])
|
Ok(std::mem::take(&mut self.query_results[counter])
|
||||||
|
@ -29,6 +29,9 @@ impl Database {
|
|||||||
if crate::MockDatabaseConnector::accepts(string) {
|
if crate::MockDatabaseConnector::accepts(string) {
|
||||||
return crate::MockDatabaseConnector::connect(string).await;
|
return crate::MockDatabaseConnector::connect(string).await;
|
||||||
}
|
}
|
||||||
Err(DbErr::Conn(format!("The connection string '{}' has no supporting driver.", string)))
|
Err(DbErr::Conn(format!(
|
||||||
|
"The connection string '{}' has no supporting driver.",
|
||||||
|
string
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,38 @@
|
|||||||
use sea_query::{inject_parameters, MySqlQueryBuilder, Values};
|
use crate::QueryBuilderWithSyntax;
|
||||||
|
use sea_query::{
|
||||||
|
inject_parameters, MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder,
|
||||||
|
Values,
|
||||||
|
};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum Syntax {
|
||||||
|
MySql,
|
||||||
|
Postgres,
|
||||||
|
Sqlite,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
pub sql: String,
|
pub sql: String,
|
||||||
pub values: Option<Values>,
|
pub values: Option<Values>,
|
||||||
|
pub syntax: Syntax,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for Statement {
|
impl Statement {
|
||||||
fn from(stmt: String) -> Statement {
|
pub fn from_string(syntax: Syntax, stmt: String) -> Statement {
|
||||||
Statement {
|
Statement {
|
||||||
sql: stmt,
|
sql: stmt,
|
||||||
values: None,
|
values: None,
|
||||||
|
syntax,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(String, Values)> for Statement {
|
pub fn from_string_values_tuple(syntax: Syntax, stmt: (String, Values)) -> Statement {
|
||||||
fn from(stmt: (String, Values)) -> Statement {
|
|
||||||
Statement {
|
Statement {
|
||||||
sql: stmt.0,
|
sql: stmt.0,
|
||||||
values: Some(stmt.1),
|
values: Some(stmt.1),
|
||||||
|
syntax,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,8 +41,11 @@ impl fmt::Display for Statement {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match &self.values {
|
match &self.values {
|
||||||
Some(values) => {
|
Some(values) => {
|
||||||
let string =
|
let string = inject_parameters(
|
||||||
inject_parameters(&self.sql, values.0.clone(), &MySqlQueryBuilder::default());
|
&self.sql,
|
||||||
|
values.0.clone(),
|
||||||
|
self.syntax.get_query_builder().as_ref(),
|
||||||
|
);
|
||||||
write!(f, "{}", &string)
|
write!(f, "{}", &string)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -39,3 +54,31 @@ impl fmt::Display for Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Syntax {
|
||||||
|
pub fn get_query_builder(&self) -> Box<dyn QueryBuilder> {
|
||||||
|
match self {
|
||||||
|
Self::MySql => Box::new(MysqlQueryBuilder),
|
||||||
|
Self::Postgres => Box::new(PostgresQueryBuilder),
|
||||||
|
Self::Sqlite => Box::new(SqliteQueryBuilder),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryBuilderWithSyntax for MysqlQueryBuilder {
|
||||||
|
fn syntax(&self) -> Syntax {
|
||||||
|
Syntax::MySql
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryBuilderWithSyntax for PostgresQueryBuilder {
|
||||||
|
fn syntax(&self) -> Syntax {
|
||||||
|
Syntax::Postgres
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryBuilderWithSyntax for SqliteQueryBuilder {
|
||||||
|
fn syntax(&self) -> Syntax {
|
||||||
|
Syntax::Sqlite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::Statement;
|
use crate::{Statement, Syntax};
|
||||||
use sea_query::{Value, Values};
|
use sea_query::{Value, Values};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -11,10 +11,10 @@ impl Transaction {
|
|||||||
where
|
where
|
||||||
I: IntoIterator<Item = Value>,
|
I: IntoIterator<Item = Value>,
|
||||||
{
|
{
|
||||||
Self::one(Statement {
|
Self::one(Statement::from_string_values_tuple(
|
||||||
sql: sql.to_owned(),
|
Syntax::Postgres,
|
||||||
values: Some(Values(values.into_iter().collect())),
|
(sql.to_string(), Values(values.into_iter().collect())),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Transaction with one statement
|
/// Create a Transaction with one statement
|
||||||
|
@ -49,7 +49,9 @@ impl SqlxMySqlPoolConnection {
|
|||||||
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Exec(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +68,9 @@ impl SqlxMySqlPoolConnection {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Query(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +84,9 @@ impl SqlxMySqlPoolConnection {
|
|||||||
Err(err) => Err(sqlx_error_to_query_err(err)),
|
Err(err) => Err(sqlx_error_to_query_err(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Query(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,9 @@ impl SqlxSqlitePoolConnection {
|
|||||||
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Exec(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +68,9 @@ impl SqlxSqlitePoolConnection {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Query(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +84,9 @@ impl SqlxSqlitePoolConnection {
|
|||||||
Err(err) => Err(sqlx_error_to_query_err(err)),
|
Err(err) => Err(sqlx_error_to_query_err(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query("Failed to acquire connection from pool.".to_owned()))
|
Err(DbErr::Query(
|
||||||
|
"Failed to acquire connection from pool.".to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,11 @@ where
|
|||||||
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!("Failed to find inserted item: {} {}", E::default().to_string(), res.last_insert_id))),
|
None => Err(DbErr::Exec(format!(
|
||||||
|
"Failed to find inserted item: {} {}",
|
||||||
|
E::default().to_string(),
|
||||||
|
res.last_insert_id
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(A::default())
|
Ok(A::default())
|
||||||
@ -243,6 +247,7 @@ where
|
|||||||
exec.await
|
exec.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete an active model by its primary key
|
||||||
pub async fn delete_active_model<A, E>(
|
pub async fn delete_active_model<A, E>(
|
||||||
mut am: A,
|
mut am: A,
|
||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
|
@ -18,7 +18,18 @@ pub trait EntityName: IdenStatic + Default {
|
|||||||
Self::table_name(self)
|
Self::table_name(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Each table in database correspond to a Entity implemented [`EntityTrait`].
|
||||||
|
///
|
||||||
|
/// This trait provides an API for you to inspect it's properties
|
||||||
|
/// - Column (implemented [`ColumnTrait`])
|
||||||
|
/// - Relation (implemented [`RelationTrait`])
|
||||||
|
/// - Primary Key (implemented [`PrimaryKeyTrait`] and [`PrimaryKeyToColumn`])
|
||||||
|
///
|
||||||
|
/// This trait also provides an API for CRUD actions
|
||||||
|
/// - Select: `find`, `find_*`
|
||||||
|
/// - Insert: `insert`, `insert_*`
|
||||||
|
/// - Update: `update`, `update_*`
|
||||||
|
/// - Delete: `delete`, `delete_*`
|
||||||
pub trait EntityTrait: EntityName {
|
pub trait EntityTrait: EntityName {
|
||||||
type Model: ModelTrait<Entity = Self> + FromQueryResult;
|
type Model: ModelTrait<Entity = Self> + FromQueryResult;
|
||||||
|
|
||||||
@ -49,16 +60,66 @@ pub trait EntityTrait: EntityName {
|
|||||||
RelationBuilder::from_rel(RelationType::HasMany, R::to().rev())
|
RelationBuilder::from_rel(RelationType::HasMany, R::to().rev())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct select statement to find one / all models
|
||||||
|
///
|
||||||
|
/// - To select columns, join tables and group by expressions, see [`QuerySelect`](crate::query::QuerySelect)
|
||||||
|
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||||
|
/// - To apply order by expressions, see [`QueryOrder`](crate::query::QueryOrder)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_query_results(vec![
|
||||||
|
/// # vec![
|
||||||
|
/// # cake::Model {
|
||||||
|
/// # id: 1,
|
||||||
|
/// # name: "New York Cheese".to_owned(),
|
||||||
|
/// # },
|
||||||
|
/// # ],
|
||||||
|
/// # vec![
|
||||||
|
/// # cake::Model {
|
||||||
|
/// # id: 1,
|
||||||
|
/// # name: "New York Cheese".to_owned(),
|
||||||
|
/// # },
|
||||||
|
/// # cake::Model {
|
||||||
|
/// # id: 2,
|
||||||
|
/// # name: "Chocolate Forest".to_owned(),
|
||||||
|
/// # },
|
||||||
|
/// # ],
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// cake::Entity::find().one(&db).await;
|
/// #
|
||||||
/// cake::Entity::find().all(&db).await;
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find().one(&db).await?,
|
||||||
|
/// Some(cake::Model {
|
||||||
|
/// id: 1,
|
||||||
|
/// name: "New York Cheese".to_owned(),
|
||||||
|
/// })
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find().all(&db).await?,
|
||||||
|
/// vec![
|
||||||
|
/// cake::Model {
|
||||||
|
/// id: 1,
|
||||||
|
/// name: "New York Cheese".to_owned(),
|
||||||
|
/// },
|
||||||
|
/// cake::Model {
|
||||||
|
/// id: 2,
|
||||||
|
/// name: "Chocolate Forest".to_owned(),
|
||||||
|
/// },
|
||||||
|
/// ]
|
||||||
|
/// );
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -77,15 +138,37 @@ pub trait EntityTrait: EntityName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find a model by primary key
|
/// Find a model by primary key
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_query_results(vec![
|
||||||
|
/// # vec![
|
||||||
|
/// # cake::Model {
|
||||||
|
/// # id: 11,
|
||||||
|
/// # name: "Sponge Cake".to_owned(),
|
||||||
|
/// # },
|
||||||
|
/// # ],
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// cake::Entity::find_by_id(11).all(&db).await;
|
/// #
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find_by_id(11).all(&db).await?,
|
||||||
|
/// vec![cake::Model {
|
||||||
|
/// id: 11,
|
||||||
|
/// name: "Sponge Cake".to_owned(),
|
||||||
|
/// }]
|
||||||
|
/// );
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -97,13 +180,32 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// Find by composite key
|
/// Find by composite key
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_query_results(vec![
|
||||||
|
/// # vec![
|
||||||
|
/// # cake_filling::Model {
|
||||||
|
/// # cake_id: 2,
|
||||||
|
/// # filling_id: 3,
|
||||||
|
/// # },
|
||||||
|
/// # ],
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await;
|
/// #
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await?,
|
||||||
|
/// vec![cake_filling::Model {
|
||||||
|
/// cake_id: 2,
|
||||||
|
/// filling_id: 3,
|
||||||
|
/// }]
|
||||||
|
/// );
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -135,10 +237,22 @@ pub trait EntityTrait: EntityName {
|
|||||||
select
|
select
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert an model into database
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 15,
|
||||||
|
/// # rows_affected: 1,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
@ -147,8 +261,14 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// cake::Entity::insert(apple).exec(&db).await;
|
/// #
|
||||||
|
/// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(insert_result.last_insert_id, 15);
|
||||||
|
/// // assert_eq!(insert_result.rows_affected, 1);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -164,10 +284,22 @@ pub trait EntityTrait: EntityName {
|
|||||||
Insert::one(model)
|
Insert::one(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert many models into database
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 28,
|
||||||
|
/// # rows_affected: 2,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
@ -180,8 +312,14 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// cake::Entity::insert_many(vec![apple, orange]).exec(&db).await;
|
/// #
|
||||||
|
/// let insert_result = cake::Entity::insert_many(vec![apple, orange]).exec(&db).await?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(insert_result.last_insert_id, 28);
|
||||||
|
/// // assert_eq!(insert_result.rows_affected, 2);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -199,10 +337,24 @@ pub trait EntityTrait: EntityName {
|
|||||||
Insert::many(models)
|
Insert::many(models)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update an model in database
|
||||||
|
///
|
||||||
|
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 0,
|
||||||
|
/// # rows_affected: 1,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
///
|
///
|
||||||
@ -212,8 +364,14 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// fruit::Entity::update(orange).exec(&db).await;
|
/// #
|
||||||
|
/// assert_eq!(
|
||||||
|
/// fruit::Entity::update(orange.clone()).exec(&db).await?, // Clone here because we need to assert_eq
|
||||||
|
/// orange
|
||||||
|
/// );
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -229,19 +387,38 @@ pub trait EntityTrait: EntityName {
|
|||||||
Update::one(model)
|
Update::one(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update many models in database
|
||||||
|
///
|
||||||
|
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 0,
|
||||||
|
/// # rows_affected: 5,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::{Expr, Value}};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::{Expr, Value}};
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// fruit::Entity::update_many()
|
/// #
|
||||||
|
/// let update_result = fruit::Entity::update_many()
|
||||||
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
|
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
|
||||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||||
/// .exec(&db)
|
/// .exec(&db)
|
||||||
/// .await;
|
/// .await?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(update_result.rows_affected, 5);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -254,10 +431,24 @@ pub trait EntityTrait: EntityName {
|
|||||||
Update::many(Self::default())
|
Update::many(Self::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete an model from database
|
||||||
|
///
|
||||||
|
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 0,
|
||||||
|
/// # rows_affected: 1,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
///
|
///
|
||||||
@ -266,8 +457,13 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// fruit::Entity::delete(orange).exec(&db).await;
|
/// #
|
||||||
|
/// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(delete_result.rows_affected, 1);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
@ -283,18 +479,37 @@ pub trait EntityTrait: EntityName {
|
|||||||
Delete::one(model)
|
Delete::one(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete many models from database
|
||||||
|
///
|
||||||
|
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "mock")]
|
/// # #[cfg(feature = "mock")]
|
||||||
/// # use sea_orm::{MockDatabase, Transaction};
|
/// # use sea_orm::{error::*, MockDatabase, MockExecResult, Transaction, tests_cfg::*};
|
||||||
/// # let db = MockDatabase::new().into_connection();
|
/// #
|
||||||
|
/// # let db = MockDatabase::new()
|
||||||
|
/// # .append_exec_results(vec![
|
||||||
|
/// # MockExecResult {
|
||||||
|
/// # last_insert_id: 0,
|
||||||
|
/// # rows_affected: 5,
|
||||||
|
/// # },
|
||||||
|
/// # ])
|
||||||
|
/// # .into_connection();
|
||||||
/// #
|
/// #
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
///
|
///
|
||||||
/// # async_std::task::block_on(async {
|
/// # let _: Result<(), DbErr> = async_std::task::block_on(async {
|
||||||
/// fruit::Entity::delete_many()
|
/// #
|
||||||
|
/// let delete_result = fruit::Entity::delete_many()
|
||||||
/// .filter(fruit::Column::Name.contains("Apple"))
|
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||||
/// .exec(&db)
|
/// .exec(&db)
|
||||||
/// .await;
|
/// .await?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(delete_result.rows_affected, 5);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
/// # });
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
|
@ -66,6 +66,7 @@ macro_rules! bind_vec_func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LINT: when the operand value does not match column type
|
// LINT: when the operand value does not match column type
|
||||||
|
/// Wrapper of the identically named method in [`sea_query::Expr`]
|
||||||
pub trait ColumnTrait: IdenStatic + Iterable {
|
pub trait ColumnTrait: IdenStatic + Iterable {
|
||||||
type EntityName: EntityName;
|
type EntityName: EntityName;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{EntityTrait, DbErr, QueryFilter, QueryResult, Related, Select};
|
use crate::{DbErr, EntityTrait, QueryFilter, QueryResult, Related, Select};
|
||||||
pub use sea_query::Value;
|
pub use sea_query::Value;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
@ -62,10 +62,7 @@ async fn exec_delete_only(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only Statement impl Send
|
// Only Statement impl Send
|
||||||
async fn exec_delete(
|
async fn exec_delete(statement: Statement, db: &DatabaseConnection) -> Result<DeleteResult, DbErr> {
|
||||||
statement: Statement,
|
|
||||||
db: &DatabaseConnection,
|
|
||||||
) -> Result<DeleteResult, DbErr> {
|
|
||||||
let result = db.execute(statement).await?;
|
let result = db.execute(statement).await?;
|
||||||
Ok(DeleteResult {
|
Ok(DeleteResult {
|
||||||
rows_affected: result.rows_affected(),
|
rows_affected: result.rows_affected(),
|
||||||
|
@ -40,10 +40,7 @@ impl Inserter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only Statement impl Send
|
// Only Statement impl Send
|
||||||
async fn exec_insert(
|
async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result<InsertResult, DbErr> {
|
||||||
statement: Statement,
|
|
||||||
db: &DatabaseConnection,
|
|
||||||
) -> Result<InsertResult, DbErr> {
|
|
||||||
let result = db.execute(statement).await?;
|
let result = db.execute(statement).await?;
|
||||||
// TODO: Postgres instead use query_one + returning clause
|
// TODO: Postgres instead use query_one + returning clause
|
||||||
Ok(InsertResult {
|
Ok(InsertResult {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::DbErr;
|
use crate::DbErr;
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -56,12 +57,14 @@ macro_rules! try_getable_all {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
|
row.try_get(column.as_str())
|
||||||
|
.map_err(crate::sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
QueryResultRow::SqlxSqlite(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
|
row.try_get(column.as_str())
|
||||||
|
.map_err(crate::sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||||
@ -109,7 +112,8 @@ macro_rules! try_getable_mysql {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
|
row.try_get(column.as_str())
|
||||||
|
.map_err(crate::sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(_) => {
|
QueryResultRow::SqlxSqlite(_) => {
|
||||||
@ -160,6 +164,7 @@ try_getable_mysql!(u64);
|
|||||||
try_getable_all!(f32);
|
try_getable_all!(f32);
|
||||||
try_getable_all!(f64);
|
try_getable_all!(f64);
|
||||||
try_getable_all!(String);
|
try_getable_all!(String);
|
||||||
|
try_getable_all!(NaiveDateTime);
|
||||||
|
|
||||||
#[cfg(feature = "with-rust_decimal")]
|
#[cfg(feature = "with-rust_decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
@ -172,14 +177,18 @@ impl TryGetable for Decimal {
|
|||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)
|
row.try_get(column.as_str())
|
||||||
|
.map_err(crate::sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
QueryResultRow::SqlxSqlite(row) => {
|
QueryResultRow::SqlxSqlite(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
let val: f64 = row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err)?;
|
let val: f64 = row
|
||||||
|
.try_get(column.as_str())
|
||||||
|
.map_err(crate::sqlx_error_to_query_err)?;
|
||||||
use rust_decimal::prelude::FromPrimitive;
|
use rust_decimal::prelude::FromPrimitive;
|
||||||
Decimal::from_f64(val).ok_or_else(|| DbErr::Query("Failed to convert f64 into Decimal".to_owned()))
|
Decimal::from_f64(val)
|
||||||
|
.ok_or_else(|| DbErr::Query("Failed to convert f64 into Decimal".to_owned()))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||||
|
@ -18,10 +18,7 @@ impl<'a, A: 'a> UpdateOne<A>
|
|||||||
where
|
where
|
||||||
A: ActiveModelTrait,
|
A: ActiveModelTrait,
|
||||||
{
|
{
|
||||||
pub fn exec(
|
pub fn exec(self, db: &'a DatabaseConnection) -> impl Future<Output = Result<A, DbErr>> + 'a {
|
||||||
self,
|
|
||||||
db: &'a DatabaseConnection,
|
|
||||||
) -> impl Future<Output = Result<A, DbErr>> + 'a {
|
|
||||||
// so that self is dropped before entering await
|
// so that self is dropped before entering await
|
||||||
exec_update_and_return_original(self.query, self.model, db)
|
exec_update_and_return_original(self.query, self.model, db)
|
||||||
}
|
}
|
||||||
@ -74,10 +71,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only Statement impl Send
|
// Only Statement impl Send
|
||||||
async fn exec_update(
|
async fn exec_update(statement: Statement, db: &DatabaseConnection) -> Result<UpdateResult, DbErr> {
|
||||||
statement: Statement,
|
|
||||||
db: &DatabaseConnection,
|
|
||||||
) -> Result<UpdateResult, DbErr> {
|
|
||||||
let result = db.execute(statement).await?;
|
let result = db.execute(statement).await?;
|
||||||
Ok(UpdateResult {
|
Ok(UpdateResult {
|
||||||
rows_affected: result.rows_affected(),
|
rows_affected: result.rows_affected(),
|
||||||
|
@ -177,9 +177,9 @@
|
|||||||
//! Licensed under either of
|
//! Licensed under either of
|
||||||
//!
|
//!
|
||||||
//! - Apache License, Version 2.0
|
//! - Apache License, Version 2.0
|
||||||
//! ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
//! ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
//! - MIT license
|
//! - MIT license
|
||||||
//! ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
//! ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
//!
|
//!
|
||||||
//! at your option.
|
//! at your option.
|
||||||
//!
|
//!
|
||||||
@ -215,5 +215,5 @@ pub use sea_orm_macros::{
|
|||||||
};
|
};
|
||||||
pub use sea_query;
|
pub use sea_query;
|
||||||
pub use sea_query::Iden;
|
pub use sea_query::Iden;
|
||||||
pub use strum::EnumIter;
|
|
||||||
pub use strum;
|
pub use strum;
|
||||||
|
pub use strum::EnumIter;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{FromQueryResult, DbErr, QueryResult, QueryResultRow};
|
use crate::{DbErr, FromQueryResult, QueryResult, QueryResultRow};
|
||||||
use serde_json::Map;
|
use serde_json::Map;
|
||||||
pub use serde_json::Value as JsonValue;
|
pub use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::Statement;
|
use crate::{Statement, Syntax};
|
||||||
use sea_query::{QueryBuilder, QueryStatementBuilder};
|
use sea_query::{QueryBuilder, QueryStatementBuilder};
|
||||||
|
|
||||||
pub trait QueryTrait {
|
pub trait QueryTrait {
|
||||||
@ -16,8 +16,12 @@ pub trait QueryTrait {
|
|||||||
/// Build the query as [`Statement`]
|
/// Build the query as [`Statement`]
|
||||||
fn build<B>(&self, builder: B) -> Statement
|
fn build<B>(&self, builder: B) -> Statement
|
||||||
where
|
where
|
||||||
B: QueryBuilder,
|
B: QueryBuilderWithSyntax,
|
||||||
{
|
{
|
||||||
self.as_query().build(builder).into()
|
Statement::from_string_values_tuple(builder.syntax(), self.as_query().build(builder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait QueryBuilderWithSyntax: QueryBuilder {
|
||||||
|
fn syntax(&self) -> Syntax;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use chrono::NaiveDateTime;
|
||||||
use rust_decimal::prelude::*;
|
use rust_decimal::prelude::*;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ pub struct Model {
|
|||||||
pub total: Decimal,
|
pub total: Decimal,
|
||||||
pub bakery_id: Option<i32>,
|
pub bakery_id: Option<i32>,
|
||||||
pub customer_id: Option<i32>,
|
pub customer_id: Option<i32>,
|
||||||
pub placed_at: String,
|
pub placed_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbConn};
|
use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbConn, Statement, Syntax};
|
||||||
|
|
||||||
mod setup;
|
mod setup;
|
||||||
|
|
||||||
@ -27,7 +27,9 @@ async fn setup_schema(db: &DbConn) {
|
|||||||
.col(ColumnDef::new(cake::Column::Name).string())
|
.col(ColumnDef::new(cake::Column::Name).string())
|
||||||
.build(SqliteQueryBuilder);
|
.build(SqliteQueryBuilder);
|
||||||
|
|
||||||
let result = db.execute(stmt.into()).await;
|
let result = db
|
||||||
|
.execute(Statement::from_string(Syntax::Sqlite, stmt))
|
||||||
|
.await;
|
||||||
println!("Create table cake: {:?}", result);
|
println!("Create table cake: {:?}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub use super::*;
|
pub use super::*;
|
||||||
|
use chrono::offset::Utc;
|
||||||
use rust_decimal_macros::dec;
|
use rust_decimal_macros::dec;
|
||||||
|
|
||||||
pub async fn test_create_lineitem(db: &DbConn) {
|
pub async fn test_create_lineitem(db: &DbConn) {
|
||||||
@ -64,7 +65,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
|
|||||||
let order_1 = order::ActiveModel {
|
let order_1 = order::ActiveModel {
|
||||||
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
|
||||||
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
|
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
|
||||||
placed_at: Set("placeholder".to_string()),
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
let order_insert_res: InsertResult = Order::insert(order_1)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub use super::*;
|
pub use super::*;
|
||||||
|
use chrono::offset::Utc;
|
||||||
use rust_decimal_macros::dec;
|
use rust_decimal_macros::dec;
|
||||||
|
|
||||||
pub async fn test_create_order(db: &DbConn) {
|
pub async fn test_create_order(db: &DbConn) {
|
||||||
@ -65,7 +66,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)),
|
||||||
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
|
customer_id: Set(Some(customer_insert_res.last_insert_id as i32)),
|
||||||
total: Set(dec!(15.10)),
|
total: Set(dec!(15.10)),
|
||||||
placed_at: Set("placeholder".to_string()),
|
placed_at: Set(Utc::now().naive_utc()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let order_insert_res: InsertResult = Order::insert(order_1)
|
let order_insert_res: InsertResult = Order::insert(order_1)
|
||||||
|
@ -4,208 +4,208 @@ use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateState
|
|||||||
pub use super::bakery_chain::*;
|
pub use super::bakery_chain::*;
|
||||||
|
|
||||||
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_schema_builder_backend();
|
let builder = db.get_schema_builder_backend();
|
||||||
db.execute(builder.build(stmt)).await
|
db.execute(builder.build(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 = sea_query::Table::create()
|
||||||
.table(bakery::Entity)
|
.table(bakery::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(bakery::Column::Id)
|
ColumnDef::new(bakery::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(bakery::Column::Name).string())
|
.col(ColumnDef::new(bakery::Column::Name).string())
|
||||||
.col(ColumnDef::new(bakery::Column::ProfitMargin).float())
|
.col(ColumnDef::new(bakery::Column::ProfitMargin).float())
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).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 = sea_query::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()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(baker::Column::Name).string())
|
.col(ColumnDef::new(baker::Column::Name).string())
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).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 = sea_query::Table::create()
|
||||||
.table(customer::Entity)
|
.table(customer::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(customer::Column::Id)
|
ColumnDef::new(customer::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(customer::Column::Name).string())
|
.col(ColumnDef::new(customer::Column::Name).string())
|
||||||
.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).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 = sea_query::Table::create()
|
||||||
.table(order::Entity)
|
.table(order::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(order::Column::Id)
|
ColumnDef::new(order::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(order::Column::Total).float())
|
.col(ColumnDef::new(order::Column::Total).float())
|
||||||
.col(ColumnDef::new(order::Column::BakeryId).integer().not_null())
|
.col(ColumnDef::new(order::Column::BakeryId).integer().not_null())
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(order::Column::CustomerId)
|
ColumnDef::new(order::Column::CustomerId)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(order::Column::PlacedAt)
|
ColumnDef::new(order::Column::PlacedAt)
|
||||||
.date_time()
|
.date_time()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).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 = sea_query::Table::create()
|
||||||
.table(lineitem::Entity)
|
.table(lineitem::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(lineitem::Column::Id)
|
ColumnDef::new(lineitem::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(lineitem::Column::Price).decimal())
|
.col(ColumnDef::new(lineitem::Column::Price).decimal())
|
||||||
.col(ColumnDef::new(lineitem::Column::Quantity).integer())
|
.col(ColumnDef::new(lineitem::Column::Quantity).integer())
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(lineitem::Column::OrderId)
|
ColumnDef::new(lineitem::Column::OrderId)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(lineitem::Column::CakeId)
|
ColumnDef::new(lineitem::Column::CakeId)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).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 = sea_query::Table::create()
|
||||||
.table(cakes_bakers::Entity)
|
.table(cakes_bakers::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(cakes_bakers::Column::CakeId)
|
ColumnDef::new(cakes_bakers::Column::CakeId)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(cakes_bakers::Column::BakerId)
|
ColumnDef::new(cakes_bakers::Column::BakerId)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.primary_key(
|
.primary_key(
|
||||||
Index::create()
|
Index::create()
|
||||||
.col(cakes_bakers::Column::CakeId)
|
.col(cakes_bakers::Column::CakeId)
|
||||||
.col(cakes_bakers::Column::BakerId),
|
.col(cakes_bakers::Column::BakerId),
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).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 = sea_query::Table::create()
|
||||||
.table(cake::Entity)
|
.table(cake::Entity)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(cake::Column::Id)
|
ColumnDef::new(cake::Column::Id)
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.auto_increment()
|
.auto_increment()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(cake::Column::Name).string())
|
.col(ColumnDef::new(cake::Column::Name).string())
|
||||||
.col(ColumnDef::new(cake::Column::Price).float())
|
.col(ColumnDef::new(cake::Column::Price).float())
|
||||||
.col(ColumnDef::new(cake::Column::BakeryId).integer().not_null())
|
.col(ColumnDef::new(cake::Column::BakeryId).integer().not_null())
|
||||||
.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)
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(cake::Column::GlutenFree).boolean())
|
.col(ColumnDef::new(cake::Column::GlutenFree).boolean())
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
create_table(db, &stmt).await
|
create_table(db, &stmt).await
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user