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