Skip to content

Commit

Permalink
Returning on delete
Browse files Browse the repository at this point in the history
  • Loading branch information
Huliiiiii committed Nov 30, 2024
1 parent 24b25a7 commit 61cdf8d
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
80 changes: 80 additions & 0 deletions src/executor/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::{error::*, ActiveModelTrait, ConnectionTrait, DeleteMany, DeleteOne,
use sea_query::DeleteStatement;
use std::future::Future;

use super::{SelectModel, SelectorRaw};

/// Handles DELETE operations in a ActiveModel using [DeleteStatement]
#[derive(Clone, Debug)]
pub struct Deleter {
Expand All @@ -27,6 +29,17 @@ where
// so that self is dropped before entering await
exec_delete_only(self.query, db)
}

/// Execute an delete operation and return the deleted model (use `RETURNING` syntax if supported)
pub fn exec_with_returning<C>(
self,
db: &'a C,
) -> impl Future<Output = Result<Option<<A::Entity as EntityTrait>::Model>, DbErr>> + '_
where
C: ConnectionTrait,
{
exec_delete_with_returning_one::<A::Entity, _>(self.query, db)
}
}

impl<'a, E> DeleteMany<E>
Expand All @@ -41,6 +54,18 @@ where
// so that self is dropped before entering await
exec_delete_only(self.query, db)
}

/// Execute an delete operation and return the deleted model (use `RETURNING` syntax if supported)
pub fn exec_with_returning<C>(
self,
db: &C,
) -> impl Future<Output = Result<Vec<E::Model>, DbErr>> + '_
where
E: EntityTrait,
C: ConnectionTrait,
{
exec_delete_with_returning_many::<E, _>(self.query, db)
}
}

impl Deleter {
Expand All @@ -56,6 +81,18 @@ impl Deleter {
{
exec_delete(self.query, db)
}

/// Execute an delete operation and return the deleted model (use `RETURNING` syntax if supported)
pub fn exec_with_returning<E, C>(
self,
db: &C,
) -> impl Future<Output = Result<Vec<E::Model>, DbErr>> + '_
where
E: EntityTrait,
C: ConnectionTrait,
{
exec_delete_with_returning_many::<E, _>(self.query, db)
}
}

async fn exec_delete_only<C>(query: DeleteStatement, db: &C) -> Result<DeleteResult, DbErr>
Expand All @@ -77,3 +114,46 @@ where
rows_affected: result.rows_affected(),
})
}

async fn exec_delete_with_returning_one<E, C>(
mut query: DeleteStatement,
db: &C,
) -> Result<Option<E::Model>, DbErr>
where
E: EntityTrait,
C: ConnectionTrait,
{
let models = match db.support_returning() {
true => {
let db_backend = db.get_database_backend();
let delete_statement = db_backend.build(&query.returning_all().to_owned());
SelectorRaw::<SelectModel<<E>::Model>>::from_statement(delete_statement)
.one(db)
.await?
}
false => unimplemented!("Database backend doesn't support RETURNING"),
};
Ok(models)
}

async fn exec_delete_with_returning_many<E, C>(
mut query: DeleteStatement,
db: &C,
) -> Result<Vec<E::Model>, DbErr>
where
E: EntityTrait,
C: ConnectionTrait,
{
let models = match db.support_returning() {
true => {
let db_backend = db.get_database_backend();
let query = query.returning_all();
let delete_statement = db_backend.build(&query.to_owned());
SelectorRaw::<SelectModel<<E>::Model>>::from_statement(delete_statement)
.all(db)
.await?
}
false => unimplemented!("Database backend doesn't support RETURNING"),
};
Ok(models)
}
94 changes: 94 additions & 0 deletions tests/returning_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,97 @@ async fn update_many() {

run().await.unwrap();
}

#[sea_orm_macros::test]
#[cfg_attr(
any(
feature = "sqlx-mysql",
all(
feature = "sqlx-sqlite",
not(feature = "sqlite-use-returning-for-3_35")
)
),
should_panic(expected = "Database backend doesn't support RETURNING")
)]
async fn delete_many() {
pub use common::{features::*, TestContext};
use edit_log::*;

let run = || async {
let ctx = TestContext::new("returning_tests_delete_many").await;
let db = &ctx.db;

create_tables(db).await?;

let inserted_models = [
Model {
id: 1,
action: "before_save".to_string(),
values: json!({ "id": "unique-id-001" }),
},
Model {
id: 2,
action: "before_save".to_string(),
values: json!({ "id": "unique-id-002" }),
},
];
// Delete many with returning
assert_eq!(
Entity::insert_many(vec![
ActiveModel {
id: NotSet,
action: Set("before_save".to_string()),
values: Set(json!({ "id": "unique-id-001" })),
},
ActiveModel {
id: NotSet,
action: Set("before_save".to_string()),
values: Set(json!({ "id": "unique-id-002" })),
},
])
.exec_with_returning_many(db)
.await?,
inserted_models
);

assert_eq!(
Entity::delete_many()
.filter(Column::Action.eq("before_save"))
.exec_with_returning(db)
.await?,
inserted_models
);

let inserted_model_3 = Model {
id: 3,
action: "before_save".to_string(),
values: json!({ "id": "unique-id-003" }),
};

Entity::insert(ActiveModel {
id: NotSet,
action: Set("before_save".to_string()),
values: Set(json!({ "id": "unique-id-003" })),
})
.exec(db)
.await?;

// One
assert_eq!(
Entity::delete(ActiveModel {
id: Set(3),
..Default::default()
})
.exec_with_returning(db)
.await?,
Some(inserted_model_3)
);

// No-op
assert_eq!(Entity::delete_many().exec_with_returning(db).await?, []);

Result::<(), DbErr>::Ok(())
};

run().await.unwrap();
}

0 comments on commit 61cdf8d

Please sign in to comment.