MySQL insert on conflict do nothing (#2244)
* MySQL insert on conflict do nothing * on_conflict_do_nothing * clippy
This commit is contained in:
parent
ad9f3c4112
commit
b4506c0647
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel,
|
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, DbBackend, EntityTrait, Insert,
|
||||||
Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, SelectModel, SelectorRaw, TryFromU64, TryInsert,
|
IntoActiveModel, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, SelectModel, SelectorRaw,
|
||||||
|
TryFromU64, TryInsert,
|
||||||
};
|
};
|
||||||
use sea_query::{FromValueTuple, Iden, InsertStatement, Query, ValueTuple};
|
use sea_query::{FromValueTuple, Iden, InsertStatement, Query, ValueTuple};
|
||||||
use std::{future::Future, marker::PhantomData};
|
use std::{future::Future, marker::PhantomData};
|
||||||
@ -245,6 +246,14 @@ where
|
|||||||
return Err(DbErr::RecordNotInserted);
|
return Err(DbErr::RecordNotInserted);
|
||||||
}
|
}
|
||||||
let last_insert_id = res.last_insert_id();
|
let last_insert_id = res.last_insert_id();
|
||||||
|
// For MySQL, the affected-rows number:
|
||||||
|
// - The affected-rows value per row is `1` if the row is inserted as a new row,
|
||||||
|
// - `2` if an existing row is updated,
|
||||||
|
// - and `0` if an existing row is set to its current values.
|
||||||
|
// Reference: https://dev.mysql.com/doc/refman/8.4/en/insert-on-duplicate.html
|
||||||
|
if db_backend == DbBackend::MySql && last_insert_id == 0 {
|
||||||
|
return Err(DbErr::RecordNotInserted);
|
||||||
|
}
|
||||||
ValueTypeOf::<A>::try_from_u64(last_insert_id).map_err(|_| DbErr::UnpackInsertId)?
|
ValueTypeOf::<A>::try_from_u64(last_insert_id).map_err(|_| DbErr::UnpackInsertId)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -225,6 +225,21 @@ where
|
|||||||
{
|
{
|
||||||
TryInsert::from_insert(self)
|
TryInsert::from_insert(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On conflict do nothing
|
||||||
|
pub fn on_conflict_do_nothing(mut self) -> TryInsert<A>
|
||||||
|
where
|
||||||
|
A: ActiveModelTrait,
|
||||||
|
{
|
||||||
|
let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
|
||||||
|
self.query.on_conflict(
|
||||||
|
OnConflict::columns(primary_keys.clone())
|
||||||
|
.do_nothing_on(primary_keys)
|
||||||
|
.to_owned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
TryInsert::from_insert(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> QueryTrait for Insert<A>
|
impl<A> QueryTrait for Insert<A>
|
||||||
|
@ -34,12 +34,19 @@ pub async fn test(db: &DbConn) {
|
|||||||
|
|
||||||
assert!(matches!(res, Ok(TryInsertResult::Inserted(_))));
|
assert!(matches!(res, Ok(TryInsertResult::Inserted(_))));
|
||||||
|
|
||||||
let _double_seaside_bakery = bakery::ActiveModel {
|
let double_seaside_bakery = bakery::ActiveModel {
|
||||||
name: Set("SeaSide Bakery".to_owned()),
|
name: Set("SeaSide Bakery".to_owned()),
|
||||||
profit_margin: Set(10.4),
|
profit_margin: Set(10.4),
|
||||||
id: Set(1),
|
id: Set(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let conflict_insert = Bakery::insert_many([double_seaside_bakery])
|
||||||
|
.on_conflict_do_nothing()
|
||||||
|
.exec(db)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(matches!(conflict_insert, Ok(TryInsertResult::Conflicted)));
|
||||||
|
|
||||||
let empty_insert = Bakery::insert_many(std::iter::empty::<bakery::ActiveModel>())
|
let empty_insert = Bakery::insert_many(std::iter::empty::<bakery::ActiveModel>())
|
||||||
.on_empty_do_nothing()
|
.on_empty_do_nothing()
|
||||||
.exec(db)
|
.exec(db)
|
||||||
|
@ -9,7 +9,6 @@ use sea_orm::TryInsertResult;
|
|||||||
use sea_orm::{sea_query::OnConflict, Set};
|
use sea_orm::{sea_query::OnConflict, Set};
|
||||||
|
|
||||||
#[sea_orm_macros::test]
|
#[sea_orm_macros::test]
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
|
||||||
async fn main() -> Result<(), DbErr> {
|
async fn main() -> Result<(), DbErr> {
|
||||||
let ctx = TestContext::new("upsert_tests").await;
|
let ctx = TestContext::new("upsert_tests").await;
|
||||||
create_tables(&ctx.db).await?;
|
create_tables(&ctx.db).await?;
|
||||||
@ -22,7 +21,9 @@ async fn main() -> Result<(), DbErr> {
|
|||||||
pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> {
|
pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||||
use insert_default::*;
|
use insert_default::*;
|
||||||
|
|
||||||
let on_conflict = OnConflict::column(Column::Id).do_nothing().to_owned();
|
let on_conflict = OnConflict::column(Column::Id)
|
||||||
|
.do_nothing_on([Column::Id])
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
let res = Entity::insert_many([
|
let res = Entity::insert_many([
|
||||||
ActiveModel { id: Set(1) },
|
ActiveModel { id: Set(1) },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user