Skip to content

Commit

Permalink
Do nothing on conflict (#1712)
Browse files Browse the repository at this point in the history
* added Conflicted to TryInsertResult

clippy changes

fmt

change from basic if statement to matches

* changed to early return
  • Loading branch information
darkmmon committed Jun 21, 2023
1 parent f7398d1 commit 6e79501
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 13 deletions.
31 changes: 20 additions & 11 deletions src/executor/insert.rs
Expand Up @@ -30,9 +30,9 @@ where
/// The types of results for an INSERT operation
#[derive(Debug)]
pub enum TryInsertResult<T> {
/// The INSERT operation did not insert any value
/// The INSERT statement did not have any value to insert
Empty,
/// Reserved
/// The INSERT operation did not insert any valid value
Conflicted,
/// Successfully inserted
Inserted(T),
Expand All @@ -50,10 +50,13 @@ where
A: 'a,
{
if self.insert_struct.columns.is_empty() {
TryInsertResult::Empty
} else {
TryInsertResult::Inserted(self.insert_struct.exec(db).await)
return TryInsertResult::Empty;
}
let temp = self.insert_struct.exec(db).await;
if matches!(temp, Err(DbErr::RecordNotInserted)) {
return TryInsertResult::Conflicted;
}
TryInsertResult::Inserted(temp)
}

/// Execute an insert operation without returning (don't use `RETURNING` syntax)
Expand All @@ -68,10 +71,13 @@ where
A: 'a,
{
if self.insert_struct.columns.is_empty() {
TryInsertResult::Empty
} else {
TryInsertResult::Inserted(self.insert_struct.exec_without_returning(db).await)
return TryInsertResult::Empty;
}
let temp = self.insert_struct.exec_without_returning(db).await;
if matches!(temp, Err(DbErr::RecordNotInserted)) {
return TryInsertResult::Conflicted;
}
TryInsertResult::Inserted(temp)
}

/// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported)
Expand All @@ -85,10 +91,13 @@ where
A: 'a,
{
if self.insert_struct.columns.is_empty() {
TryInsertResult::Empty
} else {
TryInsertResult::Inserted(self.insert_struct.exec_with_returning(db).await)
return TryInsertResult::Empty;
}
let temp = self.insert_struct.exec_with_returning(db).await;
if matches!(temp, Err(DbErr::RecordNotInserted)) {
return TryInsertResult::Conflicted;
}
TryInsertResult::Inserted(temp)
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/query/insert.rs
Expand Up @@ -211,6 +211,14 @@ where

/// Allow insert statement return safely if inserting nothing.
/// The database will not be affected.
pub fn do_nothing(self) -> TryInsert<A>
where
A: ActiveModelTrait,
{
TryInsert::from_insert(self)
}

/// alias to do_nothing
pub fn on_empty_do_nothing(self) -> TryInsert<A>
where
A: ActiveModelTrait,
Expand Down Expand Up @@ -309,7 +317,7 @@ where
self
}

// helper function for on_empty_do_nothing in Insert<A>
// helper function for do_nothing in Insert<A>
pub fn from_insert(insert: Insert<A>) -> Self {
Self {
insert_struct: insert,
Expand Down
16 changes: 15 additions & 1 deletion tests/upsert_tests.rs
Expand Up @@ -3,6 +3,7 @@ pub mod common;
pub use common::{features::*, setup::*, TestContext};
use pretty_assertions::assert_eq;
use sea_orm::entity::prelude::*;
use sea_orm::TryInsertResult;
use sea_orm::{sea_query::OnConflict, Set};

#[sea_orm_macros::test]
Expand Down Expand Up @@ -50,11 +51,24 @@ pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr>
ActiveModel { id: Set(3) },
ActiveModel { id: Set(4) },
])
.on_conflict(on_conflict)
.on_conflict(on_conflict.clone())
.exec(db)
.await;

assert_eq!(res.err(), Some(DbErr::RecordNotInserted));

let res = Entity::insert_many([
ActiveModel { id: Set(1) },
ActiveModel { id: Set(2) },
ActiveModel { id: Set(3) },
ActiveModel { id: Set(4) },
])
.on_conflict(on_conflict)
.do_nothing()
.exec(db)
.await;

assert!(matches!(res, TryInsertResult::Conflicted));

Ok(())
}

0 comments on commit 6e79501

Please sign in to comment.