diesel/upsert/on_conflict_extension.rs
1use crate::expression::Expression;
2use crate::query_builder::update_statement::SetAutoTypeHelper;
3use crate::query_builder::upsert::into_conflict_clause::IntoConflictValueClause;
4use crate::query_builder::upsert::on_conflict_actions::*;
5use crate::query_builder::upsert::on_conflict_clause::*;
6use crate::query_builder::upsert::on_conflict_target::*;
7pub use crate::query_builder::upsert::on_conflict_target_decorations::DecoratableTarget;
8use crate::query_builder::where_clause::{NoWhereClause, WhereAnd, WhereOr};
9use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord};
10use crate::query_dsl::filter_dsl::FilterDsl;
11use crate::query_dsl::methods::OrFilterDsl;
12use crate::query_source::QuerySource;
13use crate::sql_types::BoolOrNullableBool;
14
15impl<T, U, Op, Ret> InsertStatement<T, U, Op, Ret>
16where
17 T: QuerySource,
18 U: UndecoratedInsertRecord<T> + IntoConflictValueClause,
19{
20 /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without
21 /// specifying any columns or constraints to restrict the conflict to.
22 ///
23 /// # Examples
24 ///
25 /// ### Single Record
26 ///
27 /// ```rust
28 /// # include!("on_conflict_docs_setup.rs");
29 /// #
30 /// # fn main() {
31 /// # run_test().unwrap()
32 /// # }
33 /// # fn run_test() -> QueryResult<()> {
34 /// # use self::users::dsl::*;
35 /// # let conn = &mut establish_connection();
36 /// # #[cfg(feature = "postgres")]
37 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
38 /// # #[cfg(any(feature = "__sqlite-shared", feature = "mysql"))]
39 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
40 /// let user = User {
41 /// id: 1,
42 /// name: "Sean",
43 /// };
44 ///
45 /// let user_count = users.count().get_result::<i64>(conn)?;
46 /// assert_eq!(user_count, 0);
47 ///
48 /// diesel::insert_into(users)
49 /// .values(&user)
50 /// .on_conflict_do_nothing()
51 /// .execute(conn)?;
52 /// let user_count = users.count().get_result::<i64>(conn)?;
53 /// assert_eq!(user_count, 1);
54 ///
55 /// diesel::insert_into(users)
56 /// .values(&user)
57 /// .on_conflict_do_nothing()
58 /// .execute(conn)?;
59 /// let user_count = users.count().get_result::<i64>(conn)?;
60 /// assert_eq!(user_count, 1);
61 /// # Ok(())
62 /// # }
63 /// ```
64 ///
65 /// ### Vec of Records
66 ///
67 /// ```rust
68 /// # include!("on_conflict_docs_setup.rs");
69 /// #
70 /// # fn main() {
71 /// # run_test().unwrap()
72 /// # }
73 /// #
74 /// # fn run_test() -> diesel::QueryResult<()> {
75 /// # use self::users::dsl::*;
76 /// # let conn = &mut establish_connection();
77 /// # #[cfg(feature = "postgres")]
78 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
79 /// # #[cfg(any(feature = "mysql", feature = "__sqlite-shared"))]
80 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
81 /// # #[cfg(any(feature = "postgres", feature = "mysql"))]
82 /// let user = User {
83 /// id: 1,
84 /// name: "Sean",
85 /// };
86 ///
87 /// # #[cfg(any(feature = "postgres", feature = "mysql"))]
88 /// let inserted_row_count = diesel::insert_into(users)
89 /// .values(&vec![user, user])
90 /// .on_conflict_do_nothing()
91 /// .execute(conn)?;
92 /// # #[cfg(any(feature = "postgres", feature = "mysql"))]
93 /// let user_count = users.count().get_result::<i64>(conn)?;
94 /// # #[cfg(any(feature = "postgres", feature = "mysql"))]
95 /// assert_eq!(user_count, 1);
96 /// # Ok(())
97 /// # }
98 /// ```
99 pub fn on_conflict_do_nothing(self) -> crate::dsl::OnConflictDoNothing<Self> {
100 self.replace_values(|values| OnConflictValues::do_nothing(values.into_value_clause()))
101 }
102
103 /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs
104 /// for the given unique constraint.
105 ///
106 /// `Target` can be one of:
107 ///
108 /// - A column
109 /// - A tuple of columns
110 /// - [`on_constraint("constraint_name")`][`on_constraint`]
111 ///
112 /// # Examples
113 ///
114 /// ### Specifying a column as the target
115 ///
116 /// This is supported by sqlite and postgres only
117 ///
118 /// ```rust
119 /// # include!("../doctest_setup.rs");
120 /// #
121 /// # table! {
122 /// # users {
123 /// # id -> Integer,
124 /// # name -> VarChar,
125 /// # hair_color -> VarChar,
126 /// # }
127 /// # }
128 /// #
129 /// # #[derive(Clone, Copy, Insertable)]
130 /// # #[diesel(table_name = users)]
131 /// # struct User<'a> {
132 /// # id: i32,
133 /// # name: &'a str,
134 /// # }
135 /// #
136 /// # fn main() {
137 /// # run_test().unwrap()
138 /// # }
139 /// # #[cfg(any(feature = "postgres", feature = "__sqlite-shared"))]
140 /// # fn run_test() -> diesel::QueryResult<()> {
141 /// # use self::users::dsl::*;
142 /// use diesel::upsert::*;
143 ///
144 /// # let conn = &mut establish_connection();
145 /// # #[cfg(any(feature = "__sqlite-shared", feature = "postgres"))]
146 /// # diesel::sql_query("DROP TABLE users").execute(conn).unwrap();
147 /// # #[cfg(any(feature = "__sqlite-shared", feature = "postgres"))]
148 /// # diesel::sql_query("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT)").execute(conn).unwrap();
149 /// diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)").execute(conn).unwrap();
150 /// let user = User { id: 1, name: "Sean" };
151 /// let same_name_different_id = User { id: 2, name: "Sean" };
152 /// let same_id_different_name = User { id: 1, name: "Pascal" };
153 /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn));
154 ///
155 /// let query = diesel::insert_into(users)
156 /// .values(&same_id_different_name)
157 /// .on_conflict(id)
158 /// .do_nothing()
159 /// .execute(conn)?;
160 ///
161 /// let user_names = users.select(name).load::<String>(conn)?;
162 /// assert_eq!(user_names, vec![String::from("Sean")]);
163 ///
164 /// let idx_conflict_result = diesel::insert_into(users)
165 /// .values(&same_name_different_id)
166 /// .on_conflict(id)
167 /// .do_nothing()
168 /// .execute(conn);
169 /// assert!(idx_conflict_result.is_err());
170 /// # Ok(())
171 /// # }
172 /// #[cfg(feature = "mysql")]
173 /// fn run_test() -> diesel::QueryResult<()> { Ok(()) }
174 /// ```
175 ///
176 /// ### Specifying multiple columns as the target
177 ///
178 /// This is supported by sqlite and postgres only
179 ///
180 /// ```rust
181 /// # include!("../doctest_setup.rs");
182 /// #
183 /// # table! {
184 /// # users {
185 /// # id -> Integer,
186 /// # name -> VarChar,
187 /// # hair_color -> VarChar,
188 /// # }
189 /// # }
190 /// #
191 /// # #[derive(Clone, Copy, Insertable)]
192 /// # #[diesel(table_name = users)]
193 /// # struct User<'a> {
194 /// # id: i32,
195 /// # name: &'a str,
196 /// # hair_color: &'a str,
197 /// # }
198 /// #
199 /// # #[cfg(any(feature = "__sqlite-shared", feature = "postgres"))]
200 /// # fn main() {
201 /// # use self::users::dsl::*;
202 /// use diesel::upsert::*;
203 ///
204 /// # let conn = &mut establish_connection();
205 /// # diesel::sql_query("DROP TABLE users").execute(conn).unwrap();
206 /// # diesel::sql_query("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").execute(conn).unwrap();
207 /// diesel::sql_query("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").execute(conn).unwrap();
208 /// let user = User { id: 1, name: "Sean", hair_color: "black" };
209 /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" };
210 /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" };
211 ///
212 /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn));
213 ///
214 /// let inserted_row_count = diesel::insert_into(users)
215 /// .values(&same_name_different_hair_color)
216 /// .on_conflict((name, hair_color))
217 /// .do_nothing()
218 /// .execute(conn);
219 /// assert_eq!(Ok(1), inserted_row_count);
220 ///
221 /// let inserted_row_count = diesel::insert_into(users)
222 /// .values(&same_name_same_hair_color)
223 /// .on_conflict((name, hair_color))
224 /// .do_nothing()
225 /// .execute(conn);
226 /// assert_eq!(Ok(0), inserted_row_count);
227 /// # }
228 ///
229 /// #[cfg(feature = "mysql")]
230 /// fn main() {}
231 /// ```
232 ///
233 /// ### ON DUPLICATE KEY
234 ///
235 /// Mysql supports only catching all duplicated keys at once:
236 ///
237 /// ```
238 /// # include!("../doctest_setup.rs");
239 /// #
240 /// # table! {
241 /// # users {
242 /// # id -> Integer,
243 /// # name -> VarChar,
244 /// # hair_color -> VarChar,
245 /// # }
246 /// # }
247 /// #
248 /// # #[derive(Clone, Copy, Insertable)]
249 /// # #[diesel(table_name = users)]
250 /// # struct User<'a> {
251 /// # id: i32,
252 /// # name: &'a str,
253 /// # }
254 /// #
255 /// # fn main() {
256 /// # run_test().unwrap()
257 /// # }
258 /// # #[cfg(feature = "mysql")]
259 /// # fn run_test() -> diesel::QueryResult<()> {
260 /// # use self::users::dsl::*;
261 /// use diesel::upsert::*;
262 ///
263 /// # let conn = &mut establish_connection();
264 /// # diesel::sql_query("DROP TABLE users").execute(conn).unwrap();
265 /// # diesel::sql_query("CREATE TEMPORARY TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(255), hair_color VARCHAR(255))").execute(conn).unwrap();
266 /// diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)").execute(conn).unwrap();
267 /// let user = User { id: 1, name: "Sean" };
268 /// let same_name_different_id = User { id: 2, name: "Sean" };
269 /// let same_id_different_name = User { id: 1, name: "Pascal" };
270 ///
271 /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn));
272 ///
273 /// # diesel::delete(users.filter(name.ne("Sean"))).execute(conn)?;
274 /// let user_names = users.select(name).load::<String>(conn)?;
275 /// assert_eq!(user_names, vec![String::from("Sean")]);
276 ///
277 /// let query = diesel::insert_into(users)
278 /// .values(&same_id_different_name)
279 /// .on_conflict(diesel::dsl::DuplicatedKeys)
280 /// .do_nothing()
281 /// .execute(conn)?;
282 ///
283 /// let user_names = users.select(name).load::<String>(conn)?;
284 /// assert_eq!(user_names, vec![String::from("Sean")]);
285 ///
286 /// let idx_conflict_result = diesel::insert_into(users)
287 /// .values(&same_name_different_id)
288 /// .on_conflict(diesel::dsl::DuplicatedKeys)
289 /// .do_nothing()
290 /// .execute(conn)?;
291 ///
292 /// let user_names = users.select(name).load::<String>(conn)?;
293 /// assert_eq!(user_names, vec![String::from("Sean")]);
294 /// # Ok(())
295 /// # }
296 /// #[cfg(not(feature = "mysql"))]
297 /// fn run_test() -> diesel::QueryResult<()> {Ok(())}
298 /// ```
299 ///
300 /// See the documentation for [`on_constraint`] and [`do_update`] for
301 /// more examples.
302 ///
303 /// [`on_constraint`]: ../upsert/fn.on_constraint.html
304 /// [`do_update`]: crate::upsert::IncompleteOnConflict::do_update()
305 pub fn on_conflict<Target>(self, target: Target) -> crate::dsl::OnConflict<Self, Target>
306 where
307 ConflictTarget<Target>: OnConflictTarget<T>,
308 {
309 IncompleteOnConflict {
310 stmt: self.replace_values(IntoConflictValueClause::into_value_clause),
311 target: ConflictTarget::new(target),
312 }
313 }
314}
315
316impl<Stmt, T, P> DecoratableTarget<P> for IncompleteOnConflict<Stmt, T>
317where
318 P: Expression,
319 P::SqlType: BoolOrNullableBool,
320 T: DecoratableTarget<P>,
321{
322 type FilterOutput = IncompleteOnConflict<Stmt, <T as DecoratableTarget<P>>::FilterOutput>;
323 fn filter_target(self, predicate: P) -> Self::FilterOutput {
324 IncompleteOnConflict {
325 stmt: self.stmt,
326 target: self.target.filter_target(predicate),
327 }
328 }
329}
330
331/// A partially constructed `ON CONFLICT` clause.
332#[derive(Debug, Clone, Copy)]
333pub struct IncompleteOnConflict<Stmt, Target> {
334 stmt: Stmt,
335 target: Target,
336}
337
338/// Helper trait for `#[auto_type]`
339///
340/// This trait allows extracting the internal types of a `IncompleteOnConflict`
341/// struct, which is needed to define the `DoNothing` and `DoUpdate` type aliases
342/// in `diesel::dsl`.
343#[allow(unreachable_pub)]
344pub trait OnConflictHelper {
345 /// The table on which the `INSERT` statement acts
346 type Table;
347 /// The `VALUES` clause of the `INSERT` statement
348 type Values;
349 /// The conflict target (e.g., column or constraint) specified in the `ON CONFLICT` clause
350 type Target;
351 /// The SQL definition of the insert operation
352 type Op;
353 /// The `RETURNING` clause of the statement
354 type Ret;
355}
356
357impl<T, U, Op, Ret, Target> OnConflictHelper
358 for IncompleteOnConflict<InsertStatement<T, U, Op, Ret>, Target>
359where
360 T: QuerySource,
361{
362 type Table = T;
363 type Values = U;
364 type Target = Target;
365 type Op = Op;
366 type Ret = Ret;
367}
368
369impl<Stmt, Target, Changes> SetAutoTypeHelper<Changes> for IncompleteDoUpdate<Stmt, Target>
370where
371 Stmt: crate::query_builder::insert_statement::InsertAutoTypeHelper,
372 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table: QuerySource,
373 Changes: AsChangeset<
374 Target = <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table,
375 >,
376{
377 type Out = InsertStatement<
378 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table,
379 OnConflictValues<
380 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Values,
381 Target,
382 DoUpdate<
383 Changes::Changeset,
384 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table,
385 >,
386 >,
387 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Op,
388 <Stmt as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Ret,
389 >;
390}
391
392impl<T: QuerySource, U, Op, Ret, Target>
393 IncompleteOnConflict<InsertStatement<T, U, Op, Ret>, Target>
394{
395 /// Creates a query with `ON CONFLICT (target) DO NOTHING`
396 ///
397 /// If you want to do nothing when *any* constraint conflicts, use
398 /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage
399 /// examples.
400 ///
401 /// [`on_conflict_do_nothing`]: crate::query_builder::InsertStatement::on_conflict_do_nothing()
402 /// [`on_conflict`]: crate::query_builder::InsertStatement::on_conflict()
403 pub fn do_nothing(self) -> crate::dsl::DoNothing<Self> {
404 let target = self.target;
405 self.stmt.replace_values(|values| {
406 OnConflictValues::new(values, target, DoNothing::new(), NoWhereClause)
407 })
408 }
409
410 /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ... [WHERE ...]`
411 ///
412 /// Call `.set` on the result of this function with the changes you want to
413 /// apply. The argument to `set` can be anything that implements `AsChangeset`
414 /// (e.g. anything you could pass to `set` on a normal update statement).
415 ///
416 /// Note: When inserting more than one row at a time, this query can still fail
417 /// if the rows being inserted conflict with each other.
418 ///
419 /// For some backends (PostgreSQL, SQLite) a `WHERE` clause can be used to limit the rows actually updated.
420 /// For PostgreSQL and SQLite you can use the `.filter()` method to add conditions like that.
421 ///
422 /// # Examples
423 ///
424 /// ## Set specific value on conflict
425 ///
426 /// PostgreSQL/SQLite:
427 ///
428 /// ```rust
429 /// # include!("on_conflict_docs_setup.rs");
430 /// #
431 /// # #[cfg(not(feature = "mysql"))]
432 /// # fn main() {
433 /// # use self::users::dsl::*;
434 /// # let conn = &mut establish_connection();
435 /// # #[cfg(feature = "postgres")]
436 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
437 /// # #[cfg(feature = "__sqlite-shared")]
438 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
439 /// let user = User {
440 /// id: 1,
441 /// name: "Pascal",
442 /// };
443 /// let user2 = User {
444 /// id: 1,
445 /// name: "Sean",
446 /// };
447 ///
448 /// assert_eq!(
449 /// Ok(1),
450 /// diesel::insert_into(users).values(&user).execute(conn)
451 /// );
452 ///
453 /// let insert_count = diesel::insert_into(users)
454 /// .values(&user2)
455 /// .on_conflict(id)
456 /// .do_update()
457 /// .set(name.eq("I DONT KNOW ANYMORE"))
458 /// .execute(conn);
459 /// # #[cfg(any(feature = "__sqlite-shared", feature = "postgres"))]
460 /// assert_eq!(Ok(1), insert_count);
461 /// # #[cfg(feature = "mysql")]
462 /// assert_eq!(Ok(2), insert_count);
463 ///
464 /// let users_in_db = users.load(conn);
465 /// assert_eq!(
466 /// Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]),
467 /// users_in_db
468 /// );
469 /// # }
470 /// # #[cfg(feature = "mysql")]
471 /// # fn main() {}
472 /// ```
473 ///
474 /// MySQL:
475 ///
476 /// ```rust
477 /// # include!("on_conflict_docs_setup.rs");
478 /// #
479 /// # #[cfg(feature = "mysql")]
480 /// # fn main() -> diesel::QueryResult<()> {
481 /// # use self::users::dsl::*;
482 /// # let conn = &mut establish_connection();
483 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
484 /// let user = User {
485 /// id: 1,
486 /// name: "Pascal",
487 /// };
488 /// let user2 = User {
489 /// id: 1,
490 /// name: "Sean",
491 /// };
492 ///
493 /// assert_eq!(
494 /// Ok(1),
495 /// diesel::insert_into(users).values(&user).execute(conn)
496 /// );
497 ///
498 /// diesel::insert_into(users)
499 /// .values(&user2)
500 /// .on_conflict(diesel::dsl::DuplicatedKeys)
501 /// .do_update()
502 /// .set(name.eq("I DONT KNOW ANYMORE"))
503 /// .execute(conn)?;
504 ///
505 /// let users_in_db = users.load(conn);
506 /// assert_eq!(
507 /// Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]),
508 /// users_in_db
509 /// );
510 /// # Ok(())
511 /// # }
512 /// # #[cfg(not(feature = "mysql"))]
513 /// # fn main() {}
514 /// ```
515 ///
516 /// ## Set `AsChangeset` struct on conflict
517 ///
518 /// PostgreSQL & SQLite:
519 ///
520 /// ```rust
521 /// # include!("on_conflict_docs_setup.rs");
522 /// #
523 /// # #[cfg(not(feature = "mysql"))]
524 /// # fn main() {
525 /// # use self::users::dsl::*;
526 /// # let conn = &mut establish_connection();
527 /// # #[cfg(feature = "postgres")]
528 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
529 /// # #[cfg(feature = "__sqlite-shared")]
530 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
531 /// let user = User {
532 /// id: 1,
533 /// name: "Pascal",
534 /// };
535 /// let user2 = User {
536 /// id: 1,
537 /// name: "Sean",
538 /// };
539 ///
540 /// assert_eq!(
541 /// Ok(1),
542 /// diesel::insert_into(users).values(&user).execute(conn)
543 /// );
544 ///
545 /// let insert_count = diesel::insert_into(users)
546 /// .values(&user2)
547 /// .on_conflict(id)
548 /// .do_update()
549 /// .set(&user2)
550 /// .execute(conn);
551 /// assert_eq!(Ok(1), insert_count);
552 ///
553 /// let users_in_db = users.load(conn);
554 /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db);
555 /// # }
556 /// # #[cfg(feature = "mysql")]
557 /// # fn main() {}
558 /// ```
559 ///
560 /// MySQL:
561 ///
562 /// ```rust
563 /// # include!("on_conflict_docs_setup.rs");
564 ///
565 /// # #[cfg(feature = "mysql")]
566 /// # fn main() -> diesel::QueryResult<()> {
567 /// # use self::users::dsl::*;
568 /// # let conn = &mut establish_connection();
569 /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap();
570 /// let user = User {
571 /// id: 1,
572 /// name: "Pascal",
573 /// };
574 /// let user2 = User {
575 /// id: 1,
576 /// name: "Sean",
577 /// };
578 ///
579 /// assert_eq!(
580 /// Ok(1),
581 /// diesel::insert_into(users).values(&user).execute(conn)
582 /// );
583 ///
584 /// diesel::insert_into(users)
585 /// .values(&user2)
586 /// .on_conflict(diesel::dsl::DuplicatedKeys)
587 /// .do_update()
588 /// .set(&user2)
589 /// .execute(conn)?;
590 ///
591 /// let users_in_db = users.load(conn);
592 /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db);
593 /// # Ok(())
594 /// # }
595 ///
596 /// # #[cfg(not(feature = "mysql"))]
597 /// # fn main() {}
598 /// ```
599 ///
600 /// ## Use `excluded` to get the rejected value
601 ///
602 /// ```rust
603 /// # include!("on_conflict_docs_setup.rs");
604 /// #
605 /// # #[cfg(any(feature = "__sqlite-shared", feature = "postgres"))]
606 /// # fn main() {
607 /// # use self::users::dsl::*;
608 /// use diesel::upsert::excluded;
609 ///
610 /// # let conn = &mut establish_connection();
611 /// # #[cfg(feature = "postgres")]
612 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
613 /// let user = User {
614 /// id: 1,
615 /// name: "Pascal",
616 /// };
617 /// let user2 = User {
618 /// id: 1,
619 /// name: "Sean",
620 /// };
621 /// let user3 = User {
622 /// id: 2,
623 /// name: "Tess",
624 /// };
625 ///
626 /// # #[cfg(feature = "postgres")]
627 /// assert_eq!(
628 /// Ok(1),
629 /// diesel::insert_into(users).values(&user).execute(conn)
630 /// );
631 ///
632 /// #[cfg(feature = "postgres")]
633 /// let insert_count = diesel::insert_into(users)
634 /// .values(&vec![user2, user3])
635 /// .on_conflict(id)
636 /// .do_update()
637 /// .set(name.eq(excluded(name)))
638 /// .execute(conn);
639 /// # #[cfg(feature = "postgres")]
640 /// assert_eq!(Ok(2), insert_count);
641 ///
642 /// # #[cfg(feature = "postgres")]
643 /// let users_in_db = users.load(conn);
644 /// # #[cfg(feature = "postgres")]
645 /// assert_eq!(
646 /// Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]),
647 /// users_in_db
648 /// );
649 /// # }
650 /// # #[cfg(feature = "mysql")]
651 /// # fn main() {}
652 /// ```
653 ///
654 /// ## Use `.filter()`method to limit the rows actually updated
655 ///
656 /// ```rust
657 /// # include!("on_conflict_docs_setup.rs");
658 /// #
659 /// # #[cfg(not(feature = "mysql"))]
660 /// # fn main() {
661 /// # use diesel::QueryDsl;
662 /// # use diesel::query_dsl::methods::FilterDsl;
663 /// use self::users::dsl::*;
664 /// # let conn = &mut establish_connection();
665 /// # #[cfg(feature = "postgres")]
666 /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap();
667 /// # #[cfg(feature = "__sqlite-shared")]
668 /// # diesel::delete(users).execute(conn).unwrap();
669 /// let user = User {
670 /// id: 1,
671 /// name: "Pascal",
672 /// };
673 /// let user2 = User {
674 /// id: 1,
675 /// name: "Sean",
676 /// };
677 ///
678 /// assert_eq!(
679 /// Ok(1),
680 /// diesel::insert_into(users).values(&user).execute(conn)
681 /// );
682 ///
683 /// let insert_count = diesel::insert_into(users)
684 /// .values(&user2)
685 /// .on_conflict(id)
686 /// .do_update()
687 /// .set(&user2)
688 /// .filter(id.ge(5))
689 /// .execute(conn);
690 /// assert_eq!(Ok(0), insert_count);
691 ///
692 /// let users_in_db = users.load(conn);
693 /// assert_eq!(Ok(vec![(1, "Pascal".to_string())]), users_in_db);
694 /// # }
695 /// # #[cfg(feature = "mysql")]
696 /// # fn main() {}
697 /// ```
698 pub fn do_update(self) -> crate::dsl::DoUpdate<Self> {
699 IncompleteDoUpdate {
700 stmt: self.stmt,
701 target: self.target,
702 }
703 }
704}
705
706/// A partially constructed `ON CONFLICT DO UPDATE` clause.
707#[derive(Debug, Clone, Copy)]
708pub struct IncompleteDoUpdate<Stmt, Target> {
709 stmt: Stmt,
710 target: Target,
711}
712
713impl<T: QuerySource, U, Op, Ret, Target>
714 IncompleteDoUpdate<InsertStatement<T, U, Op, Ret>, Target>
715{
716 /// See [`do_update`] for usage examples.
717 ///
718 /// [`do_update`]: IncompleteOnConflict::do_update()
719 pub fn set<Changes>(self, changes: Changes) -> crate::dsl::Set<Self, Changes>
720 where
721 T: QuerySource,
722 Changes: AsChangeset<Target = T>,
723 {
724 let target = self.target;
725 self.stmt.replace_values(|values| {
726 OnConflictValues::new(
727 values,
728 target,
729 DoUpdate::new(changes.as_changeset()),
730 NoWhereClause,
731 )
732 })
733 }
734}
735
736impl<T, U, Op, Ret, Target, Action, WhereClause, Predicate> FilterDsl<Predicate>
737 for InsertStatement<T, OnConflictValues<U, Target, Action, WhereClause>, Op, Ret>
738where
739 T: QuerySource,
740 WhereClause: WhereAnd<Predicate>,
741{
742 type Output =
743 InsertStatement<T, OnConflictValues<U, Target, Action, WhereClause::Output>, Op, Ret>;
744
745 fn filter(self, predicate: Predicate) -> Self::Output {
746 self.replace_values(|values| {
747 values.replace_where(|where_clause| where_clause.and(predicate))
748 })
749 }
750}
751
752impl<T, U, Op, Ret, Target, Action, WhereClause, Predicate> OrFilterDsl<Predicate>
753 for InsertStatement<T, OnConflictValues<U, Target, Action, WhereClause>, Op, Ret>
754where
755 T: QuerySource,
756 WhereClause: WhereOr<Predicate>,
757{
758 type Output =
759 InsertStatement<T, OnConflictValues<U, Target, Action, WhereClause::Output>, Op, Ret>;
760
761 fn or_filter(self, predicate: Predicate) -> Self::Output {
762 self.replace_values(|values| {
763 values.replace_where(|where_clause| where_clause.or(predicate))
764 })
765 }
766}