Update many with returning

This commit is contained in:
Billy Chan 2023-05-30 11:29:02 +08:00 committed by Chris Tsang
parent 19e761e73d
commit 53caf94af9
2 changed files with 161 additions and 2 deletions

View File

@ -45,6 +45,16 @@ where
{
Updater::new(self.query).exec(db).await
}
/// Execute an update operation and return the updated model (use `RETURNING` syntax if database supported)
pub async fn exec_with_returning<C>(self, db: &'a C) -> Result<Vec<E::Model>, DbErr>
where
C: ConnectionTrait,
{
Updater::new(self.query)
.exec_update_with_returning::<E, _>(db)
.await
}
}
impl Updater {
@ -123,6 +133,36 @@ impl Updater {
}
}
async fn exec_update_with_returning<E, C>(mut self, db: &C) -> Result<Vec<E::Model>, DbErr>
where
E: EntityTrait,
C: ConnectionTrait,
{
if self.is_noop() {
return Ok(vec![]);
}
match db.support_returning() {
true => {
let returning =
Query::returning().exprs(E::Column::iter().map(|c| c.select_as(Expr::col(c))));
self.query.returning(returning);
let db_backend = db.get_database_backend();
let models: Vec<E::Model> = SelectorRaw::<SelectModel<E::Model>>::from_statement(
db_backend.build(&self.query),
)
.all(db)
.await?;
// If we got an empty Vec then we are updating a row that does not exist.
match models.is_empty() {
true => Err(DbErr::RecordNotUpdated),
false => Ok(models),
}
}
false => unimplemented!("Database backend doesn't support RETURNING"),
}
}
fn is_noop(&self) -> bool {
self.query.get_values().is_empty()
}

View File

@ -1,8 +1,9 @@
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
use sea_orm::entity::prelude::*;
use sea_query::Query;
pub use sea_orm::{entity::prelude::*, *};
pub use sea_query::{Expr, Query};
use serde_json::json;
#[sea_orm_macros::test]
#[cfg(any(
@ -66,3 +67,121 @@ async fn main() -> Result<(), DbErr> {
Ok(())
}
#[sea_orm_macros::test]
#[cfg(any(
feature = "sqlx-mysql",
feature = "sqlx-sqlite",
feature = "sqlx-postgres"
))]
#[cfg_attr(
any(feature = "sqlx-mysql", feature = "sqlx-sqlite"),
should_panic(expected = "Database backend doesn't support RETURNING")
)]
async fn update_many() {
pub use common::{features::*, setup::*, TestContext};
use edit_log::*;
let run = || async {
let ctx = TestContext::new("returning_tests_update_many").await;
let db = &ctx.db;
create_tables(db).await?;
Entity::insert(
Model {
id: 1,
action: "before_save".into(),
values: json!({ "id": "unique-id-001" }),
}
.into_active_model(),
)
.exec(db)
.await?;
Entity::insert(
Model {
id: 2,
action: "before_save".into(),
values: json!({ "id": "unique-id-002" }),
}
.into_active_model(),
)
.exec(db)
.await?;
Entity::insert(
Model {
id: 3,
action: "before_save".into(),
values: json!({ "id": "unique-id-003" }),
}
.into_active_model(),
)
.exec(db)
.await?;
assert_eq!(
Entity::find().all(db).await?,
[
Model {
id: 1,
action: "before_save".into(),
values: json!({ "id": "unique-id-001" }),
},
Model {
id: 2,
action: "before_save".into(),
values: json!({ "id": "unique-id-002" }),
},
Model {
id: 3,
action: "before_save".into(),
values: json!({ "id": "unique-id-003" }),
},
]
);
// Update many with returning
assert_eq!(
Entity::update_many()
.col_expr(
Column::Values,
Expr::value(json!({ "remarks": "save log" }))
)
.filter(Column::Action.eq("before_save"))
.exec_with_returning(db)
.await?,
[
Model {
id: 1,
action: "before_save".into(),
values: json!({ "remarks": "save log" }),
},
Model {
id: 2,
action: "before_save".into(),
values: json!({ "remarks": "save log" }),
},
Model {
id: 3,
action: "before_save".into(),
values: json!({ "remarks": "save log" }),
},
]
);
// No-op
assert_eq!(
Entity::update_many()
.filter(Column::Action.eq("before_save"))
.exec_with_returning(db)
.await?,
[]
);
Result::<(), DbErr>::Ok(())
};
run().await.unwrap();
}