Skip to content

Commit fbfaa13

Browse files
author
Chris Pick
committed
Add insert_or_ignore_into() "INSERT [OR] IGNORE"
Add `insert_or_ignore_into()` which provides Sqlite's "INSERT OR IGNORE" or Mysql's "INSERT IGNORE". When using `insert_or_ignore_into()`, if a constraint violation fails, the database will ignore the offending row and continue processing any subsequent rows. This was implemented as a separate function (as opposed to something like `insert.or(ignore)`) based on the logic layed out in diesel-rs#297 (0ee8363). Fixes diesel-rs#1303.
1 parent 24819da commit fbfaa13

4 files changed

Lines changed: 101 additions & 2 deletions

File tree

diesel/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ pub use prelude::*;
267267
#[doc(inline)]
268268
pub use query_builder::debug_query;
269269
#[doc(inline)]
270-
pub use query_builder::functions::{delete, insert_into, replace_into, select, sql_query, update};
270+
pub use query_builder::functions::{delete, insert_into, insert_or_ignore_into, replace_into,
271+
select, sql_query, update};
271272
pub use result::Error::NotFound;
272273
#[doc(inline)]
273274
pub use types::structs::data_types;

diesel/src/query_builder/functions.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use dsl::Select;
22
use expression::Expression;
33
use query_dsl::methods::SelectDsl;
44
use super::delete_statement::DeleteStatement;
5-
use super::insert_statement::{Insert, Replace};
5+
use super::insert_statement::{Insert, InsertOrIgnore, Replace};
66
use super::{IncompleteInsertStatement, IncompleteUpdateStatement, IntoUpdateTarget,
77
SelectStatement, SqlQuery};
88

@@ -275,6 +275,58 @@ pub fn insert_into<T>(target: T) -> IncompleteInsertStatement<T, Insert> {
275275
IncompleteInsertStatement::new(target, Insert)
276276
}
277277

278+
/// Creates an `INSERT [OR] IGNORE` statement.
279+
///
280+
/// If a constraint violation fails, the database will ignore the offending
281+
/// row and continue processing any subsequent rows. This function is only
282+
/// available with MySQL and SQLite.
283+
///
284+
/// With PostgreSQL, similar functionality is provided by [`on_conflict_do_nothing`].
285+
///
286+
/// [`on_conflict_do_nothing`]: query_builder/insert_statement/struct.InsertStatement.html#method.on_conflict_do_nothing
287+
///
288+
/// # Example
289+
///
290+
/// ```rust
291+
/// # #[macro_use] extern crate diesel;
292+
/// # include!("../doctest_setup.rs");
293+
/// #
294+
/// # fn main() {
295+
/// # run_test().unwrap();
296+
/// # }
297+
/// #
298+
/// # #[cfg(not(feature = "postgres"))]
299+
/// # fn run_test() -> QueryResult<()> {
300+
/// # use schema::users::dsl::*;
301+
/// # use diesel::{delete, insert_or_ignore_into};
302+
/// #
303+
/// # let connection = establish_connection();
304+
/// # diesel::delete(users).execute(&connection)?;
305+
/// insert_or_ignore_into(users)
306+
/// .values((id.eq(1), name.eq("Jim")))
307+
/// .execute(&connection)?;
308+
///
309+
/// insert_or_ignore_into(users)
310+
/// .values(&vec![
311+
/// (id.eq(1), name.eq("Sean")),
312+
/// (id.eq(2), name.eq("Tess")),
313+
/// ])
314+
/// .execute(&connection)?;
315+
///
316+
/// let names = users.select(name).order(id).load::<String>(&connection)?;
317+
/// assert_eq!(vec![String::from("Jim"), String::from("Tess")], names);
318+
/// # Ok(())
319+
/// # }
320+
/// #
321+
/// # #[cfg(feature = "postgres")]
322+
/// # fn run_test() -> QueryResult<()> {
323+
/// # Ok(())
324+
/// # }
325+
/// ```
326+
pub fn insert_or_ignore_into<T>(target: T) -> IncompleteInsertStatement<T, InsertOrIgnore> {
327+
IncompleteInsertStatement::new(target, InsertOrIgnore)
328+
}
329+
278330
/// Creates a bare select statement, with no from clause. Primarily used for
279331
/// testing diesel itself, but likely useful for third party crates as well. The
280332
/// given expressions must be selectable from anywhere.

diesel/src/query_builder/insert_statement.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,28 @@ impl<DB: Backend> QueryFragment<DB> for Insert {
281281

282282
impl_query_id!(Insert);
283283

284+
#[derive(Debug, Copy, Clone)]
285+
#[doc(hidden)]
286+
pub struct InsertOrIgnore;
287+
288+
#[cfg(feature = "sqlite")]
289+
impl QueryFragment<Sqlite> for InsertOrIgnore {
290+
fn walk_ast(&self, mut out: AstPass<Sqlite>) -> QueryResult<()> {
291+
out.push_sql("INSERT OR IGNORE");
292+
Ok(())
293+
}
294+
}
295+
296+
#[cfg(feature = "mysql")]
297+
impl QueryFragment<Mysql> for InsertOrIgnore {
298+
fn walk_ast(&self, mut out: AstPass<Mysql>) -> QueryResult<()> {
299+
out.push_sql("INSERT IGNORE");
300+
Ok(())
301+
}
302+
}
303+
304+
impl_query_id!(InsertOrIgnore);
305+
284306
#[derive(Debug, Copy, Clone)]
285307
#[doc(hidden)]
286308
pub struct Replace;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#[macro_use] extern crate diesel;
2+
3+
use diesel::*;
4+
5+
table! {
6+
users {
7+
id -> Integer,
8+
}
9+
}
10+
11+
#[derive(Insertable)]
12+
#[table_name="users"]
13+
struct User {
14+
id: i32,
15+
}
16+
17+
fn main() {
18+
let connection = PgConnection::establish("").unwrap();
19+
insert_or_ignore_into(users::table)
20+
.values(users::id.eq(1))
21+
.execute(&connection)
22+
//~^ ERROR E0277
23+
.unwrap();
24+
}

0 commit comments

Comments
 (0)