From 484da8f6b61abf124be8ace694be95e731664f01 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 28 Aug 2021 20:00:59 +0800 Subject: [PATCH 01/14] WIP --- examples/async-std/src/example_cake.rs | 2 ++ .../async-std/src/example_cake_filling.rs | 2 ++ examples/async-std/src/example_filling.rs | 2 ++ examples/async-std/src/example_fruit.rs | 2 ++ examples/tokio/src/cake.rs | 2 ++ sea-orm-codegen/src/entity/base_entity.rs | 24 ++++++++++++++++++- sea-orm-codegen/src/entity/writer.rs | 3 +++ sea-orm-codegen/tests/entity/cake.rs | 2 ++ sea-orm-codegen/tests/entity/cake_filling.rs | 2 ++ sea-orm-codegen/tests/entity/filling.rs | 2 ++ sea-orm-codegen/tests/entity/fruit.rs | 2 ++ sea-orm-codegen/tests/entity/vendor.rs | 2 ++ src/entity/column.rs | 2 +- src/entity/primary_key.rs | 2 ++ src/tests_cfg/cake.rs | 2 ++ src/tests_cfg/cake_filling.rs | 2 ++ src/tests_cfg/cake_filling_price.rs | 2 ++ src/tests_cfg/filling.rs | 2 ++ src/tests_cfg/fruit.rs | 2 ++ tests/common/bakery_chain/baker.rs | 2 ++ tests/common/bakery_chain/bakery.rs | 2 ++ tests/common/bakery_chain/cake.rs | 2 ++ tests/common/bakery_chain/cakes_bakers.rs | 2 ++ tests/common/bakery_chain/customer.rs | 2 ++ tests/common/bakery_chain/lineitem.rs | 2 ++ tests/common/bakery_chain/order.rs | 2 ++ 26 files changed, 73 insertions(+), 2 deletions(-) diff --git a/examples/async-std/src/example_cake.rs b/examples/async-std/src/example_cake.rs index 475315e8..114347ee 100644 --- a/examples/async-std/src/example_cake.rs +++ b/examples/async-std/src/example_cake.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/async-std/src/example_cake_filling.rs b/examples/async-std/src/example_cake_filling.rs index 19de83e4..c24e7296 100644 --- a/examples/async-std/src/example_cake_filling.rs +++ b/examples/async-std/src/example_cake_filling.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/examples/async-std/src/example_filling.rs b/examples/async-std/src/example_filling.rs index 925b92fc..2a39a7de 100644 --- a/examples/async-std/src/example_filling.rs +++ b/examples/async-std/src/example_filling.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/async-std/src/example_fruit.rs b/examples/async-std/src/example_fruit.rs index b875da24..8802e707 100644 --- a/examples/async-std/src/example_fruit.rs +++ b/examples/async-std/src/example_fruit.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/tokio/src/cake.rs b/examples/tokio/src/cake.rs index 0b1a4439..21b83331 100644 --- a/examples/tokio/src/cake.rs +++ b/examples/tokio/src/cake.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index f1e03b21..2cc6dbb4 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -117,6 +117,18 @@ impl Entity { format_ident!("{}", auto_increment) } + pub fn get_primary_key_rs_type(&self) -> TokenStream { + if let Some(primary_key) = self.primary_keys.first() { + self.columns + .iter() + .find(|col| col.name.eq(&primary_key.name)) + .unwrap() + .get_rs_type() + } else { + TokenStream::new() + } + } + pub fn get_conjunct_relations_via_snake_case(&self) -> Vec { self.conjunct_relations .iter() @@ -151,7 +163,7 @@ mod tests { columns: vec![ Column { name: "id".to_owned(), - col_type: ColumnType::String(None), + col_type: ColumnType::Integer(None), auto_increment: false, not_null: false, unique: false, @@ -373,6 +385,16 @@ mod tests { ); } + #[test] + fn test_get_primary_key_rs_type() { + let entity = setup(); + + assert_eq!( + entity.get_primary_key_rs_type().to_string(), + entity.columns[0].get_rs_type().to_string() + ); + } + #[test] fn test_get_conjunct_relations_via_snake_case() { let entity = setup(); diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 207864ba..7accac2d 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -173,8 +173,11 @@ impl EntityWriter { pub fn gen_impl_primary_key(entity: &Entity) -> TokenStream { let primary_key_auto_increment = entity.get_primary_key_auto_increment(); + let value_type = entity.get_primary_key_rs_type(); quote! { impl PrimaryKeyTrait for PrimaryKey { + type ValueType = #value_type; + fn auto_increment() -> bool { #primary_key_auto_increment } diff --git a/sea-orm-codegen/tests/entity/cake.rs b/sea-orm-codegen/tests/entity/cake.rs index 29f55ac6..55fa279f 100644 --- a/sea-orm-codegen/tests/entity/cake.rs +++ b/sea-orm-codegen/tests/entity/cake.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/cake_filling.rs b/sea-orm-codegen/tests/entity/cake_filling.rs index d0f00560..227740c1 100644 --- a/sea-orm-codegen/tests/entity/cake_filling.rs +++ b/sea-orm-codegen/tests/entity/cake_filling.rs @@ -30,6 +30,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/sea-orm-codegen/tests/entity/filling.rs b/sea-orm-codegen/tests/entity/filling.rs index bedb1ab4..73d58152 100644 --- a/sea-orm-codegen/tests/entity/filling.rs +++ b/sea-orm-codegen/tests/entity/filling.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/fruit.rs b/sea-orm-codegen/tests/entity/fruit.rs index 72c37c1b..12919021 100644 --- a/sea-orm-codegen/tests/entity/fruit.rs +++ b/sea-orm-codegen/tests/entity/fruit.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/vendor.rs b/sea-orm-codegen/tests/entity/vendor.rs index 320400f4..ffc211a8 100644 --- a/sea-orm-codegen/tests/entity/vendor.rs +++ b/sea-orm-codegen/tests/entity/vendor.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/src/entity/column.rs b/src/entity/column.rs index 16546057..611950f5 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,6 +1,6 @@ -use std::str::FromStr; use crate::{EntityName, IdenStatic, Iterable}; use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct ColumnDef { diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 81a28915..a36b04e0 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -2,6 +2,8 @@ use super::{ColumnTrait, IdenStatic, Iterable}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { + type ValueType: Sized; + fn auto_increment() -> bool; } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index f8a35d6c..b4306313 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/src/tests_cfg/cake_filling.rs b/src/tests_cfg/cake_filling.rs index b1151ee4..4336d2fa 100644 --- a/src/tests_cfg/cake_filling.rs +++ b/src/tests_cfg/cake_filling.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/src/tests_cfg/cake_filling_price.rs b/src/tests_cfg/cake_filling_price.rs index c0bcbea1..bd2bc8eb 100644 --- a/src/tests_cfg/cake_filling_price.rs +++ b/src/tests_cfg/cake_filling_price.rs @@ -35,6 +35,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/src/tests_cfg/filling.rs b/src/tests_cfg/filling.rs index b439af7b..5e691980 100644 --- a/src/tests_cfg/filling.rs +++ b/src/tests_cfg/filling.rs @@ -41,6 +41,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/src/tests_cfg/fruit.rs b/src/tests_cfg/fruit.rs index 0511ae58..b0b8bc79 100644 --- a/src/tests_cfg/fruit.rs +++ b/src/tests_cfg/fruit.rs @@ -30,6 +30,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 0c63e721..071a1fbc 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/bakery.rs b/tests/common/bakery_chain/bakery.rs index 61803329..2baa7b6b 100644 --- a/tests/common/bakery_chain/bakery.rs +++ b/tests/common/bakery_chain/bakery.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 72e649ce..f699773c 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -35,6 +35,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index 8106bbdf..853cf6da 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/tests/common/bakery_chain/customer.rs b/tests/common/bakery_chain/customer.rs index ce4319ff..51668131 100644 --- a/tests/common/bakery_chain/customer.rs +++ b/tests/common/bakery_chain/customer.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs index 45a6037f..1d338ed1 100644 --- a/tests/common/bakery_chain/lineitem.rs +++ b/tests/common/bakery_chain/lineitem.rs @@ -33,6 +33,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs index 82b02dee..f2d48971 100644 --- a/tests/common/bakery_chain/order.rs +++ b/tests/common/bakery_chain/order.rs @@ -33,6 +33,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } From ccfda51a5e5f1e50265d7607af193753579e4c89 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 28 Aug 2021 23:48:13 +0800 Subject: [PATCH 02/14] WIP --- src/driver/sqlx_postgres.rs | 7 +++-- src/entity/active_model.rs | 8 +++-- src/entity/primary_key.rs | 5 +++- src/executor/insert.rs | 59 +++++++++++++++++++++++++++---------- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index a283ddf3..9c45413f 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -110,8 +110,11 @@ impl From for ExecResult { } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { - let last_insert_id: i32 = res.try_get("", "last_insert_id")?; +pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result +where + T: TryGetable, +{ + let last_insert_id: T = res.try_get("", "last_insert_id")?; Ok(ExecResult { result: ExecResultHolder::SqlxPostgres { last_insert_id: last_insert_id as u64, diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 54bf037b..612e285d 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -221,7 +221,11 @@ where let exec = E::insert(am).exec(db); let res = exec.await?; // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value - if ::auto_increment() && res.last_insert_id != 0 { + // FIXME: Assumed valid last_insert_id is not equals to Default::default() + if ::auto_increment() + && res.last_insert_id != ::ValueType::default() + { + let last_insert_id = res.last_insert_id.to_string(); let find = E::find_by_id(res.last_insert_id).one(db); let found = find.await; let model: Option = found?; @@ -230,7 +234,7 @@ where None => Err(DbErr::Exec(format!( "Failed to find inserted item: {} {}", E::default().to_string(), - res.last_insert_id + last_insert_id ))), } } else { diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index a36b04e0..1d434c13 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,8 +1,11 @@ use super::{ColumnTrait, IdenStatic, Iterable}; +use crate::TryGetable; +use sea_query::IntoValueTuple; +use std::fmt::{Debug, Display}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { - type ValueType: Sized; + type ValueType: Sized + Default + Debug + Display + PartialEq + IntoValueTuple + TryGetable; fn auto_increment() -> bool; } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 9414fea6..a644d6e9 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,15 +1,24 @@ -use crate::{error::*, ActiveModelTrait, DatabaseConnection, Insert, Statement}; +use crate::{ + error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, Statement, +}; use sea_query::InsertStatement; -use std::future::Future; +use std::{future::Future, marker::PhantomData}; #[derive(Clone, Debug)] -pub struct Inserter { +pub struct Inserter +where + A: ActiveModelTrait, +{ query: InsertStatement, + model: PhantomData, } -#[derive(Clone, Debug)] -pub struct InsertResult { - pub last_insert_id: u64, +#[derive(Debug)] +pub struct InsertResult +where + A: ActiveModelTrait, +{ + pub last_insert_id: <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType, } impl Insert @@ -17,10 +26,13 @@ where A: ActiveModelTrait, { #[allow(unused_mut)] - pub fn exec( + pub fn exec<'a>( self, - db: &DatabaseConnection, - ) -> impl Future> + '_ { + db: &'a DatabaseConnection, + ) -> impl Future, DbErr>> + 'a + where + A: 'a, + { // so that self is dropped before entering await let mut query = self.query; #[cfg(feature = "sqlx-postgres")] @@ -35,26 +47,41 @@ where ); } } - Inserter::new(query).exec(db) + Inserter::::new(query).exec(db) } } -impl Inserter { +impl Inserter +where + A: ActiveModelTrait, +{ pub fn new(query: InsertStatement) -> Self { - Self { query } + Self { + query, + model: PhantomData, + } } - pub fn exec( + pub fn exec<'a>( self, - db: &DatabaseConnection, - ) -> impl Future> + '_ { + db: &'a DatabaseConnection, + ) -> impl Future, DbErr>> + 'a + where + A: 'a, + { let builder = db.get_database_backend(); exec_insert(builder.build(&self.query), db) } } // Only Statement impl Send -async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result { +async fn exec_insert( + statement: Statement, + db: &DatabaseConnection, +) -> Result, DbErr> +where + A: ActiveModelTrait, +{ // TODO: Postgres instead use query_one + returning clause let result = match db { #[cfg(feature = "sqlx-postgres")] From 3bada7fbc3befd5f3316611dff889c87fa7ff2e7 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 12:05:55 +0800 Subject: [PATCH 03/14] WIP --- README.md | 2 +- examples/async-std/src/operation.rs | 2 +- src/driver/sqlx_postgres.rs | 20 +++++++------------ src/executor/execute.rs | 31 +++++++++++++++++++---------- src/lib.rs | 2 +- tests/crud/create_baker.rs | 4 ++-- tests/crud/create_cake.rs | 8 ++++---- tests/crud/create_lineitem.rs | 14 ++++++------- tests/crud/create_order.rs | 14 ++++++------- tests/crud/deletes.rs | 2 +- tests/crud/mod.rs | 4 ++-- tests/crud/updates.rs | 6 +++--- tests/sequential_op_tests.rs | 8 ++++---- 13 files changed, 61 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index f2d262e9..c625268d 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ let pear = fruit::ActiveModel { }; // insert one -let res: InsertResult = Fruit::insert(pear).exec(db).await?; +let res = Fruit::insert(pear).exec(db).await?; println!("InsertResult: {}", res.last_insert_id); diff --git a/examples/async-std/src/operation.rs b/examples/async-std/src/operation.rs index b1273e10..3fa5cc85 100644 --- a/examples/async-std/src/operation.rs +++ b/examples/async-std/src/operation.rs @@ -20,7 +20,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> { name: Set("pear".to_owned()), ..Default::default() }; - let res: InsertResult = Fruit::insert(pear).exec(db).await?; + let res = Fruit::insert(pear).exec(db).await?; println!(); println!("Inserted: last_insert_id = {}\n", res.last_insert_id); diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 9c45413f..5456d8f7 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -102,24 +102,18 @@ impl From for QueryResult { impl From for ExecResult { fn from(result: PgQueryResult) -> ExecResult { ExecResult { - result: ExecResultHolder::SqlxPostgres { - last_insert_id: 0, - rows_affected: result.rows_affected(), - }, + result: ExecResultHolder::SqlxPostgres(result), } } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result -where - T: TryGetable, -{ - let last_insert_id: T = res.try_get("", "last_insert_id")?; +pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { + let result = match res { + QueryResult::SqlxPostgres(result) => result, + _ => panic!("Should be QueryResult::SqlxPostgres"), + }; Ok(ExecResult { - result: ExecResultHolder::SqlxPostgres { - last_insert_id: last_insert_id as u64, - rows_affected: 0, - }, + result: ExecResultHolder::SqlxPostgres(result), }) } diff --git a/src/executor/execute.rs b/src/executor/execute.rs index 00375bb7..f522aa6c 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,3 +1,5 @@ +use crate::TryGetable; + #[derive(Debug)] pub struct ExecResult { pub(crate) result: ExecResultHolder, @@ -8,10 +10,7 @@ pub(crate) enum ExecResultHolder { #[cfg(feature = "sqlx-mysql")] SqlxMySql(sqlx::mysql::MySqlQueryResult), #[cfg(feature = "sqlx-postgres")] - SqlxPostgres { - last_insert_id: u64, - rows_affected: u64, - }, + SqlxPostgres(sqlx::postgres::PgQueryResult), #[cfg(feature = "sqlx-sqlite")] SqlxSqlite(sqlx::sqlite::SqliteQueryResult), #[cfg(feature = "mock")] @@ -21,23 +20,35 @@ pub(crate) enum ExecResultHolder { // ExecResult // impl ExecResult { - pub fn last_insert_id(&self) -> u64 { + pub fn last_insert_id(&self) -> T + where + T: TryGetable + Default, + { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), + ExecResultHolder::SqlxMySql(result) => { + // result.last_insert_id() + T::default() + } #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres { last_insert_id, .. } => last_insert_id.to_owned(), + ExecResultHolder::SqlxPostgres(result) => { + res.try_get("", "last_insert_id").unwrap_or_default() + } #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - last_insert_rowid as u64 + // last_insert_rowid + T::default() } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => result.last_insert_id, + ExecResultHolder::Mock(result) => { + // result.last_insert_id + T::default() + } } } @@ -46,7 +57,7 @@ impl ExecResult { #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.rows_affected(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres { rows_affected, .. } => rows_affected.to_owned(), + ExecResultHolder::SqlxPostgres(result) => result.rows_affected(), #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => result.rows_affected(), #[cfg(feature = "mock")] diff --git a/src/lib.rs b/src/lib.rs index 377bd62b..8469ede7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ //! }; //! //! // insert one -//! let res: InsertResult = Fruit::insert(pear).exec(db).await?; +//! let res = Fruit::insert(pear).exec(db).await?; //! //! println!("InsertResult: {}", res.last_insert_id); //! # Ok(()) diff --git a/tests/crud/create_baker.rs b/tests/crud/create_baker.rs index 8653b692..085b4005 100644 --- a/tests/crud/create_baker.rs +++ b/tests/crud/create_baker.rs @@ -7,7 +7,7 @@ pub async fn test_create_baker(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -30,7 +30,7 @@ pub async fn test_create_baker(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let res: InsertResult = Baker::insert(baker_bob) + let res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index 07f9c4b3..fb4566eb 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -8,7 +8,7 @@ pub async fn test_create_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -23,7 +23,7 @@ pub async fn test_create_cake(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -38,7 +38,7 @@ pub async fn test_create_cake(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -53,7 +53,7 @@ pub async fn test_create_cake(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index c82958f9..22810913 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -10,7 +10,7 @@ pub async fn test_create_lineitem(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -26,7 +26,7 @@ pub async fn test_create_lineitem(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -41,7 +41,7 @@ pub async fn test_create_lineitem(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -52,7 +52,7 @@ pub async fn test_create_lineitem(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -63,7 +63,7 @@ pub async fn test_create_lineitem(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let customer_insert_res: InsertResult = Customer::insert(customer_kate) + let customer_insert_res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); @@ -76,7 +76,7 @@ pub async fn test_create_lineitem(db: &DbConn) { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let order_insert_res: InsertResult = Order::insert(order_1) + let order_insert_res = Order::insert(order_1) .exec(db) .await .expect("could not insert order"); @@ -89,7 +89,7 @@ pub async fn test_create_lineitem(db: &DbConn) { quantity: Set(1), ..Default::default() }; - let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1) + let lineitem_insert_res = Lineitem::insert(lineitem_1) .exec(db) .await .expect("could not insert lineitem"); diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index 46ebcf09..e613802b 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -10,7 +10,7 @@ pub async fn test_create_order(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -26,7 +26,7 @@ pub async fn test_create_order(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -41,7 +41,7 @@ pub async fn test_create_order(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -52,7 +52,7 @@ pub async fn test_create_order(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -63,7 +63,7 @@ pub async fn test_create_order(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let customer_insert_res: InsertResult = Customer::insert(customer_kate) + let customer_insert_res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); @@ -76,7 +76,7 @@ pub async fn test_create_order(db: &DbConn) { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let order_insert_res: InsertResult = Order::insert(order_1) + let order_insert_res = Order::insert(order_1) .exec(db) .await .expect("could not insert order"); @@ -89,7 +89,7 @@ pub async fn test_create_order(db: &DbConn) { quantity: Set(2), ..Default::default() }; - let _lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1) + let _lineitem_insert_res = Lineitem::insert(lineitem_1) .exec(db) .await .expect("could not insert lineitem"); diff --git a/tests/crud/deletes.rs b/tests/crud/deletes.rs index ebff4b5f..4c34d36b 100644 --- a/tests/crud/deletes.rs +++ b/tests/crud/deletes.rs @@ -10,7 +10,7 @@ pub async fn test_delete_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 457f639d..82bdbc0d 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -15,7 +15,7 @@ pub async fn test_create_bakery(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let res: InsertResult = Bakery::insert(seaside_bakery) + let res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -37,7 +37,7 @@ pub async fn test_create_customer(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let res: InsertResult = Customer::insert(customer_kate) + let res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 505e4837..94c11aaf 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -8,7 +8,7 @@ pub async fn test_update_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -22,7 +22,7 @@ pub async fn test_update_cake(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -62,7 +62,7 @@ pub async fn test_update_bakery(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index a854c768..6e1377bc 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -67,7 +67,7 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -78,7 +78,7 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -200,7 +200,7 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option Option Date: Sun, 29 Aug 2021 19:39:01 +0800 Subject: [PATCH 04/14] WIP --- src/entity/primary_key.rs | 14 ++++++++++++-- src/executor/execute.rs | 24 +++++++++++++----------- tests/crud/mod.rs | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 1d434c13..f24c62df 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,11 +1,21 @@ use super::{ColumnTrait, IdenStatic, Iterable}; use crate::TryGetable; use sea_query::IntoValueTuple; -use std::fmt::{Debug, Display}; +use std::{ + fmt::{Debug, Display}, + str::FromStr, +}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { - type ValueType: Sized + Default + Debug + Display + PartialEq + IntoValueTuple + TryGetable; + type ValueType: Sized + + Default + + Debug + + Display + + PartialEq + + IntoValueTuple + + TryGetable + + FromStr; fn auto_increment() -> bool; } diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f522aa6c..db7e8a39 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,4 +1,5 @@ use crate::TryGetable; +use std::str::FromStr; #[derive(Debug)] pub struct ExecResult { @@ -22,14 +23,15 @@ pub(crate) enum ExecResultHolder { impl ExecResult { pub fn last_insert_id(&self) -> T where - T: TryGetable + Default, + T: TryGetable + Default + FromStr, { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => { - // result.last_insert_id() - T::default() - } + ExecResultHolder::SqlxMySql(result) => result + .last_insert_id() + .to_string() + .parse() + .unwrap_or_default(), #[cfg(feature = "sqlx-postgres")] ExecResultHolder::SqlxPostgres(result) => { res.try_get("", "last_insert_id").unwrap_or_default() @@ -40,15 +42,15 @@ impl ExecResult { if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - // last_insert_rowid - T::default() + last_insert_rowid.to_string().parse().unwrap_or_default() } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => { - // result.last_insert_id - T::default() - } + ExecResultHolder::Mock(result) => result + .last_insert_id + .to_string() + .parse() + .unwrap_or_default(), } } diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 82bdbc0d..7e024055 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -1,4 +1,4 @@ -use sea_orm::{entity::*, DbConn, InsertResult}; +use sea_orm::{entity::*, DbConn}; pub use super::common::bakery_chain::*; From 4e5a8d2dc553a26668c89b5740bb6a0d4b5e33f3 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 20:57:25 +0800 Subject: [PATCH 05/14] WIP --- src/driver/sqlx_postgres.rs | 10 ---------- src/executor/execute.rs | 26 +++++--------------------- src/executor/insert.rs | 16 +++++++++++----- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 5456d8f7..b3d71f9a 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -107,16 +107,6 @@ impl From for ExecResult { } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { - let result = match res { - QueryResult::SqlxPostgres(result) => result, - _ => panic!("Should be QueryResult::SqlxPostgres"), - }; - Ok(ExecResult { - result: ExecResultHolder::SqlxPostgres(result), - }) -} - fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, PgArguments> { let mut query = sqlx::query(&stmt.sql); if let Some(values) = &stmt.values { diff --git a/src/executor/execute.rs b/src/executor/execute.rs index db7e8a39..f05ee963 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,6 +1,3 @@ -use crate::TryGetable; -use std::str::FromStr; - #[derive(Debug)] pub struct ExecResult { pub(crate) result: ExecResultHolder, @@ -21,36 +18,23 @@ pub(crate) enum ExecResultHolder { // ExecResult // impl ExecResult { - pub fn last_insert_id(&self) -> T - where - T: TryGetable + Default + FromStr, - { + pub fn last_insert_id(&self) -> u64 { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => result - .last_insert_id() - .to_string() - .parse() - .unwrap_or_default(), + ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres(result) => { - res.try_get("", "last_insert_id").unwrap_or_default() - } + ExecResultHolder::SqlxPostgres(_) => panic!("Should not retrieve last_insert_id this way"), #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - last_insert_rowid.to_string().parse().unwrap_or_default() + last_insert_rowid as u64 } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => result - .last_insert_id - .to_string() - .parse() - .unwrap_or_default(), + ExecResultHolder::Mock(result) => result.last_insert_id, } } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index a644d6e9..7e5e24aa 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -37,7 +37,7 @@ where let mut query = self.query; #[cfg(feature = "sqlx-postgres")] if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db { - use crate::{EntityTrait, Iterable}; + use crate::Iterable; use sea_query::{Alias, Expr, Query}; for key in ::PrimaryKey::iter() { query.returning( @@ -83,15 +83,21 @@ where A: ActiveModelTrait, { // TODO: Postgres instead use query_one + returning clause - let result = match db { + let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); - crate::query_result_into_exec_result(res)? + res.try_get("", "last_insert_id").unwrap_or_default() } - _ => db.execute(statement).await?, + _ => { + db.execute(statement).await? + .last_insert_id() + .to_string() + .parse() + .unwrap_or_default() + }, }; Ok(InsertResult { - last_insert_id: result.last_insert_id(), + last_insert_id, }) } From 4d0c5f0b0f54a0b20986cb7cc69e54438123578c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 22:11:19 +0800 Subject: [PATCH 06/14] Hotfix - Postgres errors --- tests/crud/create_lineitem.rs | 2 +- tests/crud/create_order.rs | 4 ++-- tests/crud/updates.rs | 2 +- tests/sequential_op_tests.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 22810913..1030651a 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -105,7 +105,7 @@ pub async fn test_create_lineitem(db: &DbConn) { assert_eq!(lineitem_model.price, dec!(7.55)); - let cake: Option = Cake::find_by_id(lineitem_model.cake_id as u64) + let cake: Option = Cake::find_by_id(lineitem_model.cake_id) .one(db) .await .expect("could not find cake"); diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index e613802b..e3b12b52 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -103,7 +103,7 @@ pub async fn test_create_order(db: &DbConn) { let order_model = order.unwrap(); assert_eq!(order_model.total, dec!(15.10)); - let customer: Option = Customer::find_by_id(order_model.customer_id as u64) + let customer: Option = Customer::find_by_id(order_model.customer_id) .one(db) .await .expect("could not find customer"); @@ -111,7 +111,7 @@ pub async fn test_create_order(db: &DbConn) { let customer_model = customer.unwrap(); assert_eq!(customer_model.name, "Kate"); - let bakery: Option = Bakery::find_by_id(order_model.bakery_id as i64) + let bakery: Option = Bakery::find_by_id(order_model.bakery_id) .one(db) .await .expect("could not find bakery"); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 94c11aaf..df797ed3 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -131,7 +131,7 @@ pub async fn test_update_deleted_customer(db: &DbConn) { assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers); let customer: Option = - Customer::find_by_id(customer_id.clone().unwrap() as i64) + Customer::find_by_id(customer_id.clone().unwrap()) .one(db) .await .expect("could not find customer"); diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 6e1377bc..300812cf 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -183,7 +183,7 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold)); - Baker::find_by_id(results.last().unwrap().id as i64) + Baker::find_by_id(results.last().unwrap().id) .one(db) .await .unwrap() From 333f199c1a566066fb06e6bc6d1c5b4158101948 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 22:18:53 +0800 Subject: [PATCH 07/14] cargo fmt --- src/executor/execute.rs | 4 +++- src/executor/insert.rs | 12 +++++------- tests/crud/updates.rs | 9 ++++----- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f05ee963..46ba2d69 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -23,7 +23,9 @@ impl ExecResult { #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres(_) => panic!("Should not retrieve last_insert_id this way"), + ExecResultHolder::SqlxPostgres(_) => { + panic!("Should not retrieve last_insert_id this way") + } #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 7e5e24aa..e806fe62 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -89,15 +89,13 @@ where let res = conn.query_one(statement).await?.unwrap(); res.try_get("", "last_insert_id").unwrap_or_default() } - _ => { - db.execute(statement).await? + _ => db + .execute(statement) + .await? .last_insert_id() .to_string() .parse() - .unwrap_or_default() - }, + .unwrap_or_default(), }; - Ok(InsertResult { - last_insert_id, - }) + Ok(InsertResult { last_insert_id }) } diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index df797ed3..172c9c21 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -130,11 +130,10 @@ pub async fn test_update_deleted_customer(db: &DbConn) { assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers); - let customer: Option = - Customer::find_by_id(customer_id.clone().unwrap()) - .one(db) - .await - .expect("could not find customer"); + let customer: Option = Customer::find_by_id(customer_id.clone().unwrap()) + .one(db) + .await + .expect("could not find customer"); assert_eq!(customer, None); } From 0e0ee0ede6bf37ad9d40634527fb023466334cda Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 16:53:54 +0800 Subject: [PATCH 08/14] Add TryFromU64 & test Uuid as primary key --- src/entity/primary_key.rs | 9 ++-- src/error.rs | 2 +- src/executor/insert.rs | 16 +++---- src/executor/query.rs | 46 ++++++++++++++++++++ tests/common/bakery_chain/metadata.rs | 61 +++++++++++++++++++++++++++ tests/common/bakery_chain/mod.rs | 2 + tests/common/setup/mod.rs | 15 ++++--- tests/common/setup/schema.rs | 17 ++++++++ tests/primary_key_tests.rs | 41 ++++++++++++++++++ 9 files changed, 186 insertions(+), 23 deletions(-) create mode 100644 tests/common/bakery_chain/metadata.rs create mode 100644 tests/primary_key_tests.rs diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index f24c62df..9702b8a1 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,10 +1,7 @@ use super::{ColumnTrait, IdenStatic, Iterable}; -use crate::TryGetable; +use crate::{TryFromU64, TryGetable}; use sea_query::IntoValueTuple; -use std::{ - fmt::{Debug, Display}, - str::FromStr, -}; +use std::fmt::{Debug, Display}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { @@ -15,7 +12,7 @@ pub trait PrimaryKeyTrait: IdenStatic + Iterable { + PartialEq + IntoValueTuple + TryGetable - + FromStr; + + TryFromU64; fn auto_increment() -> bool; } diff --git a/src/error.rs b/src/error.rs index 8a695dac..09f80b0a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum DbErr { Conn(String), Exec(String), diff --git a/src/executor/insert.rs b/src/executor/insert.rs index e806fe62..6e657d23 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,5 +1,6 @@ use crate::{ - error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, Statement, + error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, + Statement, TryFromU64, }; use sea_query::InsertStatement; use std::{future::Future, marker::PhantomData}; @@ -82,20 +83,17 @@ async fn exec_insert( where A: ActiveModelTrait, { - // TODO: Postgres instead use query_one + returning clause + type ValueTypeOf = <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType; let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); res.try_get("", "last_insert_id").unwrap_or_default() } - _ => db - .execute(statement) - .await? - .last_insert_id() - .to_string() - .parse() - .unwrap_or_default(), + _ => { + let last_insert_id = db.execute(statement).await?.last_insert_id(); + ValueTypeOf::::try_from_u64(last_insert_id)? + } }; Ok(InsertResult { last_insert_id }) } diff --git a/src/executor/query.rs b/src/executor/query.rs index b7e1d12e..5420c4c9 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -405,3 +405,49 @@ impl TryGetable for Option { #[cfg(feature = "with-uuid")] try_getable_all!(uuid::Uuid); + +pub trait TryFromU64: Sized { + fn try_from_u64(n: u64) -> Result; +} + +macro_rules! try_from_u64 { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + use std::convert::TryInto; + n.try_into().map_err(|_| { + DbErr::Exec(format!( + "fail to convert '{}' into '{}'", + n, + stringify!($type) + )) + }) + } + } + }; +} + +try_from_u64!(i8); +try_from_u64!(i16); +try_from_u64!(i32); +try_from_u64!(i64); +try_from_u64!(u8); +try_from_u64!(u16); +try_from_u64!(u32); +try_from_u64!(u64); + +macro_rules! try_from_u64_err { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(_: u64) -> Result { + Err(DbErr::Exec(format!( + "{} cannot be converted from u64", + stringify!($type) + ))) + } + } + }; +} + +#[cfg(feature = "with-uuid")] +try_from_u64_err!(uuid::Uuid); diff --git a/tests/common/bakery_chain/metadata.rs b/tests/common/bakery_chain/metadata.rs new file mode 100644 index 00000000..ecef26c3 --- /dev/null +++ b/tests/common/bakery_chain/metadata.rs @@ -0,0 +1,61 @@ +use sea_orm::entity::prelude::*; +use uuid::Uuid; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "metadata" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub uuid: Uuid, + pub key: String, + pub value: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Uuid, + Key, + Value, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Uuid, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = Uuid; + + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation {} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Uuid => ColumnType::Uuid.def(), + Self::Key => ColumnType::String(None).def(), + Self::Value => ColumnType::String(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + unreachable!() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/mod.rs b/tests/common/bakery_chain/mod.rs index 89028aab..3282766d 100644 --- a/tests/common/bakery_chain/mod.rs +++ b/tests/common/bakery_chain/mod.rs @@ -4,6 +4,7 @@ pub mod cake; pub mod cakes_bakers; pub mod customer; pub mod lineitem; +pub mod metadata; pub mod order; pub use super::baker::Entity as Baker; @@ -12,4 +13,5 @@ pub use super::cake::Entity as Cake; pub use super::cakes_bakers::Entity as CakesBakers; pub use super::customer::Entity as Customer; pub use super::lineitem::Entity as Lineitem; +pub use super::metadata::Entity as Metadata; pub use super::order::Entity as Order; diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 7528cf6c..74e35b45 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -45,13 +45,14 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { Database::connect(base_url).await.unwrap() }; - assert!(schema::create_bakery_table(&db).await.is_ok()); - assert!(schema::create_baker_table(&db).await.is_ok()); - assert!(schema::create_customer_table(&db).await.is_ok()); - assert!(schema::create_order_table(&db).await.is_ok()); - assert!(schema::create_cake_table(&db).await.is_ok()); - assert!(schema::create_cakes_bakers_table(&db).await.is_ok()); - assert!(schema::create_lineitem_table(&db).await.is_ok()); + schema::create_bakery_table(&db).await.unwrap(); + schema::create_baker_table(&db).await.unwrap(); + schema::create_customer_table(&db).await.unwrap(); + schema::create_order_table(&db).await.unwrap(); + schema::create_cake_table(&db).await.unwrap(); + schema::create_cakes_bakers_table(&db).await.unwrap(); + schema::create_lineitem_table(&db).await.unwrap(); + schema::create_metadata_table(&db).await.unwrap(); db } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 4eba40ab..0e124fa2 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -254,3 +254,20 @@ pub async fn create_cake_table(db: &DbConn) -> Result { create_table(db, &stmt).await } + +pub async fn create_metadata_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(metadata::Entity) + .if_not_exists() + .col( + ColumnDef::new(metadata::Column::Uuid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(metadata::Column::Key).string().not_null()) + .col(ColumnDef::new(metadata::Column::Value).string().not_null()) + .to_owned(); + + create_table(db, &stmt).await +} diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs new file mode 100644 index 00000000..2f74eafc --- /dev/null +++ b/tests/primary_key_tests.rs @@ -0,0 +1,41 @@ +use sea_orm::{entity::prelude::*, DatabaseConnection, Set}; +pub mod common; +pub use common::{bakery_chain::*, setup::*, TestContext}; +use uuid::Uuid; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +async fn main() -> Result<(), DbErr> { + let ctx = TestContext::new("bakery_chain_schema_primary_key_tests").await; + + create_metadata(&ctx.db).await?; + + ctx.delete().await; + + Ok(()) +} + +async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { + let metadata = metadata::ActiveModel { + uuid: Set(Uuid::new_v4()), + key: Set("markup".to_owned()), + value: Set("1.18".to_owned()), + }; + + let res = Metadata::insert(metadata.clone()).exec(db).await; + + if cfg!(feature = "sqlx-postgres") { + assert_eq!(metadata.uuid.unwrap(), res?.last_insert_id); + } else { + assert_eq!( + res.unwrap_err(), + DbErr::Exec("uuid::Uuid cannot be converted from u64".to_owned()) + ); + } + + Ok(()) +} From a49f880f4f6e64cf65da640149ca35cd82d08683 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 18:23:23 +0800 Subject: [PATCH 09/14] WIP: composite primary keys --- src/executor/insert.rs | 4 +- src/executor/query.rs | 83 +++++++++++++++++++++++++------------- tests/primary_key_tests.rs | 14 +++---- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 6e657d23..ce8f32d7 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -92,7 +92,9 @@ where } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); - ValueTypeOf::::try_from_u64(last_insert_id)? + ValueTypeOf::::try_from_u64(last_insert_id) + .ok() + .unwrap_or_default() } }; Ok(InsertResult { last_insert_id }) diff --git a/src/executor/query.rs b/src/executor/query.rs index 5420c4c9..2d9d4db0 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -410,32 +410,6 @@ pub trait TryFromU64: Sized { fn try_from_u64(n: u64) -> Result; } -macro_rules! try_from_u64 { - ( $type: ty ) => { - impl TryFromU64 for $type { - fn try_from_u64(n: u64) -> Result { - use std::convert::TryInto; - n.try_into().map_err(|_| { - DbErr::Exec(format!( - "fail to convert '{}' into '{}'", - n, - stringify!($type) - )) - }) - } - } - }; -} - -try_from_u64!(i8); -try_from_u64!(i16); -try_from_u64!(i32); -try_from_u64!(i64); -try_from_u64!(u8); -try_from_u64!(u16); -try_from_u64!(u32); -try_from_u64!(u64); - macro_rules! try_from_u64_err { ( $type: ty ) => { impl TryFromU64 for $type { @@ -449,5 +423,60 @@ macro_rules! try_from_u64_err { }; } +macro_rules! try_from_u64_tuple { + ( $type: ty ) => { + try_from_u64_err!(($type, $type)); + try_from_u64_err!(($type, $type, $type)); + }; +} + +macro_rules! try_from_u64_numeric { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + use std::convert::TryInto; + n.try_into().map_err(|_| { + DbErr::Exec(format!( + "fail to convert '{}' into '{}'", + n, + stringify!($type) + )) + }) + } + } + try_from_u64_tuple!($type); + }; +} + +try_from_u64_numeric!(i8); +try_from_u64_numeric!(i16); +try_from_u64_numeric!(i32); +try_from_u64_numeric!(i64); +try_from_u64_numeric!(u8); +try_from_u64_numeric!(u16); +try_from_u64_numeric!(u32); +try_from_u64_numeric!(u64); + +macro_rules! try_from_u64_string { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + Ok(n.to_string()) + } + } + try_from_u64_tuple!($type); + }; +} + +try_from_u64_string!(String); + +macro_rules! try_from_u64_dummy { + ( $type: ty ) => { + try_from_u64_err!($type); + try_from_u64_err!(($type, $type)); + try_from_u64_err!(($type, $type, $type)); + }; +} + #[cfg(feature = "with-uuid")] -try_from_u64_err!(uuid::Uuid); +try_from_u64_dummy!(uuid::Uuid); diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs index 2f74eafc..15fdfe43 100644 --- a/tests/primary_key_tests.rs +++ b/tests/primary_key_tests.rs @@ -26,16 +26,14 @@ async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { value: Set("1.18".to_owned()), }; - let res = Metadata::insert(metadata.clone()).exec(db).await; + let res = Metadata::insert(metadata.clone()).exec(db).await?; - if cfg!(feature = "sqlx-postgres") { - assert_eq!(metadata.uuid.unwrap(), res?.last_insert_id); + let expected_uuid = if cfg!(feature = "sqlx-postgres") { + metadata.uuid.unwrap() } else { - assert_eq!( - res.unwrap_err(), - DbErr::Exec("uuid::Uuid cannot be converted from u64".to_owned()) - ); - } + Uuid::default() + }; + assert_eq!(res.last_insert_id, expected_uuid); Ok(()) } From d14322ad752472b4f2f2d7132922272492fdeeaa Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 19:05:59 +0800 Subject: [PATCH 10/14] WIP (workflow will fail) --- .../async-std/src/example_cake_filling.rs | 2 +- sea-orm-codegen/tests/entity/cake_filling.rs | 2 +- src/entity/active_model.rs | 4 +- src/entity/primary_key.rs | 7 +- src/executor/query.rs | 72 +++++++++++++++++-- src/tests_cfg/cake_filling.rs | 2 +- src/tests_cfg/cake_filling_price.rs | 2 +- tests/common/bakery_chain/cakes_bakers.rs | 2 +- 8 files changed, 77 insertions(+), 16 deletions(-) diff --git a/examples/async-std/src/example_cake_filling.rs b/examples/async-std/src/example_cake_filling.rs index c24e7296..4fa188bc 100644 --- a/examples/async-std/src/example_cake_filling.rs +++ b/examples/async-std/src/example_cake_filling.rs @@ -28,7 +28,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/sea-orm-codegen/tests/entity/cake_filling.rs b/sea-orm-codegen/tests/entity/cake_filling.rs index 227740c1..d100fa8c 100644 --- a/sea-orm-codegen/tests/entity/cake_filling.rs +++ b/sea-orm-codegen/tests/entity/cake_filling.rs @@ -30,7 +30,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 612e285d..f823411c 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -225,16 +225,14 @@ where if ::auto_increment() && res.last_insert_id != ::ValueType::default() { - let last_insert_id = res.last_insert_id.to_string(); let find = E::find_by_id(res.last_insert_id).one(db); let found = find.await; let model: Option = found?; match model { Some(model) => Ok(model.into_active_model()), None => Err(DbErr::Exec(format!( - "Failed to find inserted item: {} {}", + "Failed to find inserted item: {}", E::default().to_string(), - last_insert_id ))), } } else { diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 9702b8a1..b9c381d4 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,17 +1,16 @@ use super::{ColumnTrait, IdenStatic, Iterable}; -use crate::{TryFromU64, TryGetable}; +use crate::{TryFromU64, TryGetableMany}; use sea_query::IntoValueTuple; -use std::fmt::{Debug, Display}; +use std::fmt::Debug; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { type ValueType: Sized + Default + Debug - + Display + PartialEq + IntoValueTuple - + TryGetable + + TryGetableMany + TryFromU64; fn auto_increment() -> bool; diff --git a/src/executor/query.rs b/src/executor/query.rs index 2d9d4db0..88d6a157 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -17,10 +17,8 @@ pub(crate) enum QueryResultRow { Mock(crate::MockRow), } -pub trait TryGetable { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result - where - Self: Sized; +pub trait TryGetable: Sized { + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } // QueryResult // @@ -406,6 +404,72 @@ impl TryGetable for Option { #[cfg(feature = "with-uuid")] try_getable_all!(uuid::Uuid); +// TryGetableMany // + +pub trait TryGetableMany: Sized { + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; +} + +impl TryGetableMany for T +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 1; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + T::try_get(res, pre, &cols[0]) + } +} + +impl TryGetableMany for (T, T) +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 2; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + Ok(( + T::try_get(res, pre, &cols[0])?, + T::try_get(res, pre, &cols[1])?, + )) + } +} + +impl TryGetableMany for (T, T, T) +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 3; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + Ok(( + T::try_get(res, pre, &cols[0])?, + T::try_get(res, pre, &cols[1])?, + T::try_get(res, pre, &cols[2])?, + )) + } +} + +// TryFromU64 // + pub trait TryFromU64: Sized { fn try_from_u64(n: u64) -> Result; } diff --git a/src/tests_cfg/cake_filling.rs b/src/tests_cfg/cake_filling.rs index 4336d2fa..ed6d0af0 100644 --- a/src/tests_cfg/cake_filling.rs +++ b/src/tests_cfg/cake_filling.rs @@ -29,7 +29,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/src/tests_cfg/cake_filling_price.rs b/src/tests_cfg/cake_filling_price.rs index bd2bc8eb..e820ae0f 100644 --- a/src/tests_cfg/cake_filling_price.rs +++ b/src/tests_cfg/cake_filling_price.rs @@ -35,7 +35,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index 853cf6da..98a25682 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -28,7 +28,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false From 23f2a3d7119eb61efdcb70d27a2e8ea2045761aa Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 22:04:43 +0800 Subject: [PATCH 11/14] Hotfix - git merge conflict --- src/executor/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index 8546a5e7..7be17b4b 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -18,7 +18,7 @@ pub(crate) enum QueryResultRow { } pub trait TryGetable: Sized { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } pub enum TryGetError { From d664985ea9fd1a80dd49c1ee798b807ca959eff8 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 23:24:43 +0800 Subject: [PATCH 12/14] WIP --- sea-orm-codegen/src/entity/base_entity.rs | 25 +++++++--- src/executor/insert.rs | 2 +- src/executor/query.rs | 56 ++++++++++------------- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index 2cc6dbb4..7b28f9e4 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -118,12 +118,25 @@ impl Entity { } pub fn get_primary_key_rs_type(&self) -> TokenStream { - if let Some(primary_key) = self.primary_keys.first() { - self.columns - .iter() - .find(|col| col.name.eq(&primary_key.name)) - .unwrap() - .get_rs_type() + let types = self + .primary_keys + .iter() + .map(|primary_key| { + self.columns + .iter() + .find(|col| col.name.eq(&primary_key.name)) + .unwrap() + .get_rs_type() + .to_string() + }) + .collect::>(); + if !types.is_empty() { + let value_type = if types.len() > 1 { + vec!["(".to_owned(), types.join(", "), ")".to_owned()] + } else { + types + }; + value_type.join("").parse().unwrap() } else { TokenStream::new() } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index ce8f32d7..a0577e4b 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -88,7 +88,7 @@ where #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); - res.try_get("", "last_insert_id").unwrap_or_default() + res.try_get_many("", "last_insert_id").unwrap_or_default() } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); diff --git a/src/executor/query.rs b/src/executor/query.rs index 7be17b4b..407e963c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -262,9 +262,12 @@ impl TryGetable for Decimal { .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; use rust_decimal::prelude::FromPrimitive; match val { - Some(v) => Decimal::from_f64(v) - .ok_or_else(|| TryGetError::DbErr(DbErr::Query("Failed to convert f64 into Decimal".to_owned()))), - None => Err(TryGetError::Null) + Some(v) => Decimal::from_f64(v).ok_or_else(|| { + TryGetError::DbErr(DbErr::Query( + "Failed to convert f64 into Decimal".to_owned(), + )) + }), + None => Err(TryGetError::Null), } } #[cfg(feature = "mock")] @@ -282,22 +285,15 @@ try_getable_all!(uuid::Uuid); // TryGetableMany // pub trait TryGetableMany: Sized { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; } impl TryGetableMany for T where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 1; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(1, cols)?; T::try_get(res, pre, &cols[0]) } } @@ -306,15 +302,8 @@ impl TryGetableMany for (T, T) where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 2; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(2, cols)?; Ok(( T::try_get(res, pre, &cols[0])?, T::try_get(res, pre, &cols[1])?, @@ -326,15 +315,8 @@ impl TryGetableMany for (T, T, T) where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 3; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(3, cols)?; Ok(( T::try_get(res, pre, &cols[0])?, T::try_get(res, pre, &cols[1])?, @@ -343,6 +325,18 @@ where } } +fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> { + if cols.len() < len { + Err(TryGetError::DbErr(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + len, + cols.len() + )))) + } else { + Ok(()) + } +} + // TryFromU64 // pub trait TryFromU64: Sized { From e72d8a9e049e4656ca4918021aee1ccfa2cc7fab Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 11:26:40 +0800 Subject: [PATCH 13/14] Use TryGetableMany --- src/executor/insert.rs | 18 +++++++++++------- src/executor/query.rs | 7 +++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/executor/insert.rs b/src/executor/insert.rs index a0577e4b..90065675 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -38,13 +38,12 @@ where let mut query = self.query; #[cfg(feature = "sqlx-postgres")] if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db { - use crate::Iterable; - use sea_query::{Alias, Expr, Query}; - for key in ::PrimaryKey::iter() { + use crate::{sea_query::Query, Iterable}; + if ::PrimaryKey::iter().count() > 0 { query.returning( Query::select() - .expr_as(Expr::col(key), Alias::new("last_insert_id")) - .to_owned(), + .columns(::PrimaryKey::iter()) + .take(), ); } } @@ -83,12 +82,17 @@ async fn exec_insert( where A: ActiveModelTrait, { - type ValueTypeOf = <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType; + type PrimaryKey = <::Entity as EntityTrait>::PrimaryKey; + type ValueTypeOf = as PrimaryKeyTrait>::ValueType; let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { + use crate::{sea_query::Iden, Iterable}; + let cols = PrimaryKey::::iter() + .map(|col| col.to_string()) + .collect::>(); let res = conn.query_one(statement).await?.unwrap(); - res.try_get_many("", "last_insert_id").unwrap_or_default() + res.try_get_many("", cols.as_ref()).unwrap_or_default() } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); diff --git a/src/executor/query.rs b/src/executor/query.rs index 407e963c..1c2ce4a1 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -44,6 +44,13 @@ impl QueryResult { { Ok(T::try_get(self, pre, col)?) } + + pub fn try_get_many(&self, pre: &str, cols: &[String]) -> Result + where + T: TryGetableMany, + { + Ok(T::try_get_many(self, pre, cols)?) + } } impl fmt::Debug for QueryResultRow { From 8e2b7e561e6f1f209f366286bf25a122939ccd20 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 11:47:57 +0800 Subject: [PATCH 14/14] Testing composite primary keys --- tests/crud/create_cake.rs | 10 +++++++++- tests/crud/create_lineitem.rs | 10 +++++++++- tests/crud/create_order.rs | 10 +++++++++- tests/primary_key_tests.rs | 14 ++++++++------ tests/sequential_op_tests.rs | 20 ++++++++++++++++++-- 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index fb4566eb..eea74350 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -53,10 +53,18 @@ pub async fn test_create_cake(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); assert!(cake.is_some()); let cake_model = cake.unwrap(); diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 1030651a..9acba961 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -52,10 +52,18 @@ pub async fn test_create_lineitem(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); // Customer let customer_kate = customer::ActiveModel { diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index e3b12b52..8d10cfcd 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -52,10 +52,18 @@ pub async fn test_create_order(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); // Customer let customer_kate = customer::ActiveModel { diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs index 15fdfe43..74dd46e5 100644 --- a/tests/primary_key_tests.rs +++ b/tests/primary_key_tests.rs @@ -28,12 +28,14 @@ async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { let res = Metadata::insert(metadata.clone()).exec(db).await?; - let expected_uuid = if cfg!(feature = "sqlx-postgres") { - metadata.uuid.unwrap() - } else { - Uuid::default() - }; - assert_eq!(res.last_insert_id, expected_uuid); + assert_eq!( + res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + metadata.uuid.unwrap() + } else { + Default::default() + } + ); Ok(()) } diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 300812cf..6c490762 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -78,10 +78,18 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); let customer_kate = customer::ActiveModel { name: Set("Kate".to_owned()), @@ -211,10 +219,18 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option