Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returning #292

Merged
merged 33 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c5468eb
Use "marlon-sousa/sea-query"
billy1624 Nov 5, 2021
c39a3b8
Insert with returning for Postgres
billy1624 Nov 5, 2021
52ff943
Docs
billy1624 Nov 5, 2021
a977572
Update with returning for Postgres
billy1624 Nov 5, 2021
50605c7
FIXME: breaking behaviors
billy1624 Nov 5, 2021
6238736
Handle "None of the database rows are affected" for Postgres
billy1624 Nov 8, 2021
2f7cffa
Fix test cases
billy1624 Nov 8, 2021
732d080
Update docs
billy1624 Nov 8, 2021
0eafacc
Try returning on MariaDB
billy1624 Nov 8, 2021
80c0d69
Merge remote-tracking branch 'origin/master' into returning
billy1624 Nov 8, 2021
30f43b6
Fixup
billy1624 Nov 8, 2021
2f0ac4c
Fixup
billy1624 Nov 8, 2021
afdb1af
This will fail loll
billy1624 Nov 8, 2021
1723206
This will fail loll
billy1624 Nov 8, 2021
3e6423a
This will fail loll
billy1624 Nov 8, 2021
30a50ca
Try
billy1624 Nov 9, 2021
429b920
Fixup
billy1624 Nov 9, 2021
24fab66
Try
billy1624 Nov 9, 2021
8020ae1
Fixup
billy1624 Nov 9, 2021
533c3cf
Try
billy1624 Nov 9, 2021
ec637b2
Returning support for SQLite
billy1624 Nov 9, 2021
c1fae1b
Debug print
billy1624 Nov 9, 2021
cc035d7
Refactoring
billy1624 Nov 9, 2021
66c23c8
Revert MySQL & SQLite returning support
billy1624 Nov 10, 2021
257a893
Use `sea-query` master
billy1624 Nov 10, 2021
4d44827
Docs
billy1624 Nov 11, 2021
fd50ffd
Merge remote-tracking branch 'origin/master' into returning
billy1624 Nov 16, 2021
d5de8b1
Should fail
billy1624 Nov 16, 2021
9655805
Will fail, as expected
billy1624 Nov 16, 2021
4c147a2
Rewrite doctests
billy1624 Nov 16, 2021
f9d04fc
Hotfix - separate counter for mock exec & query
billy1624 Nov 16, 2021
7298fde
Rewrite doctests
billy1624 Nov 16, 2021
42404eb
Fixup
billy1624 Nov 16, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ jobs:
name: Examples
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
path: [basic, actix_example, actix4_example, axum_example, rocket_example]
Expand All @@ -312,6 +313,7 @@ jobs:
if: ${{ (needs.init.outputs.run-partial == 'true' && needs.init.outputs.run-issues == 'true') }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
path: [86, 249, 262]
Expand Down Expand Up @@ -350,6 +352,7 @@ jobs:
env:
DATABASE_URL: "sqlite::memory:"
strategy:
fail-fast: false
matrix:
runtime: [async-std, actix, tokio]
tls: [native-tls, rustls]
Expand Down Expand Up @@ -392,6 +395,7 @@ jobs:
env:
DATABASE_URL: "mysql://root:@localhost"
strategy:
fail-fast: false
matrix:
version: [8.0, 5.7]
runtime: [async-std, actix, tokio]
Expand Down Expand Up @@ -452,8 +456,9 @@ jobs:
env:
DATABASE_URL: "mysql://root:@localhost"
strategy:
fail-fast: false
matrix:
version: [10.6]
version: [10.6, 10.5, 10.4]
runtime: [async-std, actix, tokio]
tls: [native-tls]
services:
Expand Down Expand Up @@ -512,8 +517,9 @@ jobs:
env:
DATABASE_URL: "postgres://root:root@localhost"
strategy:
fail-fast: false
matrix:
version: [13.3, 12.7, 11.12, 10.17, 9.6.22]
version: [13, 12, 11, 10, 9]
runtime: [tokio]
tls: [native-tls]
services:
Expand Down
6 changes: 6 additions & 0 deletions src/database/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ pub trait ConnectionTrait<'a>: Sync {
T: Send,
E: std::error::Error + Send;

/// Check if the connection supports `RETURNING` syntax on insert and update
fn support_returning(&self) -> bool {
let db_backend = self.get_database_backend();
db_backend.support_returning()
}

/// Check if the connection is a test connection for the Mock database
fn is_mock_connection(&self) -> bool {
false
Expand Down
5 changes: 5 additions & 0 deletions src/database/db_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ impl DbBackend {
Self::Sqlite => Box::new(SqliteQueryBuilder),
}
}

/// Check if the database supports `RETURNING` syntax on insert and update
pub fn support_returning(&self) -> bool {
matches!(self, Self::Postgres)
tyt2y3 marked this conversation as resolved.
Show resolved Hide resolved
}
}

#[cfg(test)]
Expand Down
10 changes: 3 additions & 7 deletions src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,16 @@ pub trait ActiveModelTrait: Clone + Debug {
C: ConnectionTrait<'a>,
{
let am = ActiveModelBehavior::before_save(self, true)?;
let res = <Self::Entity as EntityTrait>::insert(am).exec(db).await?;
let found = <Self::Entity as EntityTrait>::find_by_id(res.last_insert_id)
.one(db)
let am = <Self::Entity as EntityTrait>::insert(am)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SO we need new test cases here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test cases added

.exec_with_returning(db)
.await?;
let am = match found {
Some(model) => model.into_active_model(),
None => return Err(DbErr::Exec("Failed to find inserted item".to_owned())),
};
ActiveModelBehavior::after_save(am, true)
}

/// Perform the `UPDATE` operation on an ActiveModel
async fn update<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait<'a>,
{
Expand Down
171 changes: 160 additions & 11 deletions src/entity/base_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ pub trait EntityTrait: EntityName {

/// Insert an model into database
///
/// # Example
/// # Example (Postgres)
billy1624 marked this conversation as resolved.
Show resolved Hide resolved
///
/// ```
/// # #[cfg(feature = "mock")]
Expand All @@ -299,15 +299,56 @@ pub trait EntityTrait: EntityName {
/// 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!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#, vec!["Apple Pie".into()]
/// DbBackend::Postgres,
/// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#,
tyt2y3 marked this conversation as resolved.
Show resolved Hide resolved
/// vec!["Apple Pie".into()]
/// )]);
/// ```
///
/// # Example (MySQL)
///
/// ```
billy1624 marked this conversation as resolved.
Show resolved Hide resolved
/// # #[cfg(feature = "mock")]
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
/// #
/// # let db = MockDatabase::new(DbBackend::MySql)
/// # .append_exec_results(vec![
/// # MockExecResult {
/// # last_insert_id: 15,
/// # rows_affected: 1,
/// # },
/// # ])
/// # .into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
///
/// let apple = cake::ActiveModel {
/// name: Set("Apple Pie".to_owned()),
/// ..Default::default()
/// };
///
/// # let _: Result<(), DbErr> = smol::block_on(async {
/// #
/// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
///
/// assert_eq!(insert_result.last_insert_id, 15);
/// #
/// # Ok(())
/// # });
///
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::MySql,
/// r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
/// vec!["Apple Pie".into()]
/// )]);
/// ```
fn insert<A>(model: A) -> Insert<A>
Expand All @@ -319,7 +360,7 @@ pub trait EntityTrait: EntityName {

/// Insert many models into database
///
/// # Example
/// # Example (Postgres)
///
/// ```
/// # #[cfg(feature = "mock")]
Expand Down Expand Up @@ -350,15 +391,59 @@ pub trait EntityTrait: EntityName {
/// 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!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
/// DbBackend::Postgres,
/// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
/// vec!["Apple Pie".into(), "Orange Scone".into()]
/// )]);
/// ```
///
/// # Example (MySQL)
///
/// ```
/// # #[cfg(feature = "mock")]
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
/// #
/// # let db = MockDatabase::new(DbBackend::MySql)
/// # .append_exec_results(vec![
/// # MockExecResult {
/// # last_insert_id: 28,
/// # rows_affected: 2,
/// # },
/// # ])
/// # .into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
///
/// let apple = cake::ActiveModel {
/// name: Set("Apple Pie".to_owned()),
/// ..Default::default()
/// };
/// let orange = cake::ActiveModel {
/// name: Set("Orange Scone".to_owned()),
/// ..Default::default()
/// };
///
/// # let _: Result<(), DbErr> = smol::block_on(async {
/// #
/// let insert_result = cake::Entity::insert_many(vec![apple, orange]).exec(&db).await?;
///
/// assert_eq!(insert_result.last_insert_id, 28);
/// #
/// # Ok(())
/// # });
///
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::MySql,
/// r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
/// vec!["Apple Pie".into(), "Orange Scone".into()]
/// )]);
/// ```
Expand All @@ -374,7 +459,7 @@ pub trait EntityTrait: EntityName {
///
/// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
///
/// # Example
/// # Example (Postgres)
///
/// ```
/// # #[cfg(feature = "mock")]
Expand Down Expand Up @@ -413,10 +498,69 @@ pub trait EntityTrait: EntityName {
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3"#,
/// DbBackend::Postgres,
/// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
tyt2y3 marked this conversation as resolved.
Show resolved Hide resolved
/// vec!["Orange".into(), 1i32.into(), "%orange%".into()]
/// )]);
/// ```
///
/// # Example (MySQL)
///
/// ```
/// # #[cfg(feature = "mock")]
/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, Transaction, DbBackend};
/// #
/// # let db = MockDatabase::new(DbBackend::MySql)
/// # .append_query_results(vec![
/// # vec![fruit::Model {
/// # id: 1,
/// # name: "Orange".to_owned(),
/// # cake_id: None,
/// # }],
/// # ])
/// # .append_exec_results(vec![
/// # MockExecResult {
/// # last_insert_id: 0,
/// # rows_affected: 1,
/// # },
/// # ])
/// # .into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
///
/// let orange = fruit::ActiveModel {
/// id: Set(1),
/// name: Set("Orange".to_owned()),
/// ..Default::default()
/// };
///
/// # let _: Result<(), DbErr> = smol::block_on(async {
/// #
/// assert_eq!(
/// fruit::Entity::update(orange.clone())
/// .filter(fruit::Column::Name.contains("orange"))
/// .exec(&db)
/// .await?,
/// orange
/// );
/// #
/// # Ok(())
/// # });
///
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![
/// Transaction::from_sql_and_values(
/// DbBackend::MySql,
/// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
/// vec!["Orange".into(), 1i32.into(), "%orange%".into()]
/// ),
/// Transaction::from_sql_and_values(
/// DbBackend::MySql,
/// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
/// vec![1i32.into(), 1u64.into()]
/// )]);
/// ```
fn update<A>(model: A) -> UpdateOne<A>
where
A: ActiveModelTrait<Entity = Self>,
Expand Down Expand Up @@ -461,7 +605,9 @@ pub trait EntityTrait: EntityName {
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#, vec![Value::Int(None), "%Apple%".into()]
/// DbBackend::Postgres,
/// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
/// vec![Value::Int(None), "%Apple%".into()]
/// )]);
/// ```
fn update_many() -> UpdateMany<Self> {
Expand Down Expand Up @@ -506,7 +652,8 @@ pub trait EntityTrait: EntityName {
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#, vec![3i32.into()]
/// DbBackend::Postgres, r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
/// vec![3i32.into()]
/// )]);
/// ```
fn delete<A>(model: A) -> DeleteOne<A>
Expand Down Expand Up @@ -552,7 +699,9 @@ pub trait EntityTrait: EntityName {
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// DbBackend::Postgres, r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#, vec!["%Apple%".into()]
/// DbBackend::Postgres,
/// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
/// vec!["%Apple%".into()]
/// )]);
/// ```
fn delete_many() -> DeleteMany<Self> {
Expand Down
Loading