diesel/pg/upsert/
on_conflict_extension.rs

1use super::on_conflict_actions::*;
2use super::on_conflict_clause::*;
3use super::on_conflict_target::*;
4use query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord};
5use query_source::QuerySource;
6
7impl<T, U, Op, Ret> InsertStatement<T, U, Op, Ret>
8where
9    U: UndecoratedInsertRecord<T>,
10{
11    /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without
12    /// specifying any columns or constraints to restrict the conflict to.
13    ///
14    /// # Examples
15    ///
16    /// ### Single Record
17    ///
18    /// ```rust
19    /// # #[macro_use] extern crate diesel;
20    /// # include!("on_conflict_docs_setup.rs");
21    /// #
22    /// # fn main() {
23    /// #     use users::dsl::*;
24    /// #     let conn = establish_connection();
25    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
26    /// let user = User { id: 1, name: "Sean", };
27    ///
28    /// let inserted_row_count = diesel::insert_into(users)
29    ///     .values(&user)
30    ///     .on_conflict_do_nothing()
31    ///     .execute(&conn);
32    /// assert_eq!(Ok(1), inserted_row_count);
33    ///
34    /// let inserted_row_count = diesel::insert_into(users)
35    ///     .values(&user)
36    ///     .on_conflict_do_nothing()
37    ///     .execute(&conn);
38    /// assert_eq!(Ok(0), inserted_row_count);
39    /// # }
40    /// ```
41    ///
42    /// ### Vec of Records
43    ///
44    /// ```rust
45    /// # #[macro_use] extern crate diesel;
46    /// # include!("on_conflict_docs_setup.rs");
47    /// #
48    /// # fn main() {
49    /// #     use users::dsl::*;
50    /// #     let conn = establish_connection();
51    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
52    /// let user = User { id: 1, name: "Sean", };
53    ///
54    /// let inserted_row_count = diesel::insert_into(users)
55    ///     .values(&vec![user, user])
56    ///     .on_conflict_do_nothing()
57    ///     .execute(&conn);
58    /// assert_eq!(Ok(1), inserted_row_count);
59    /// # }
60    /// ```
61    pub fn on_conflict_do_nothing(
62        self,
63    ) -> InsertStatement<T, OnConflictValues<U, NoConflictTarget, DoNothing>, Op, Ret> {
64        self.replace_values(OnConflictValues::do_nothing)
65    }
66
67    /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs
68    /// for the given unique constraint.
69    ///
70    /// `Target` can be one of:
71    ///
72    /// - A column
73    /// - A tuple of columns
74    /// - [`on_constraint("constraint_name")`][`on_constraint`]
75    ///
76    /// # Examples
77    ///
78    /// ### Specifying a column as the target
79    ///
80    /// ```rust
81    /// # #[macro_use] extern crate diesel;
82    /// # include!("on_conflict_docs_setup.rs");
83    /// #
84    /// # fn main() {
85    /// #     use users::dsl::*;
86    /// #     let conn = establish_connection();
87    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
88    /// conn.execute("CREATE UNIQUE INDEX users_name ON users (name)").unwrap();
89    /// let user = User { id: 1, name: "Sean", };
90    /// let same_name_different_id = User { id: 2, name: "Sean" };
91    /// let same_id_different_name = User { id: 1, name: "Pascal" };
92    ///
93    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
94    ///
95    /// let inserted_row_count = diesel::insert_into(users)
96    ///     .values(&same_name_different_id)
97    ///     .on_conflict(name)
98    ///     .do_nothing()
99    ///     .execute(&conn);
100    /// assert_eq!(Ok(0), inserted_row_count);
101    ///
102    /// let pk_conflict_result = diesel::insert_into(users)
103    ///     .values(&same_id_different_name)
104    ///     .on_conflict(name)
105    ///     .do_nothing()
106    ///     .execute(&conn);
107    /// assert!(pk_conflict_result.is_err());
108    /// # }
109    /// ```
110    ///
111    /// ### Specifying multiple columns as the target
112    ///
113    /// ```rust
114    /// # #[macro_use] extern crate diesel;
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    /// # #[table_name="users"]
127    /// # struct User<'a> {
128    /// #     id: i32,
129    /// #     name: &'a str,
130    /// #     hair_color: &'a str,
131    /// # }
132    /// #
133    /// # fn main() {
134    /// #     use users::dsl::*;
135    /// use diesel::pg::upsert::*;
136    ///
137    /// #     let conn = establish_connection();
138    /// #     conn.execute("DROP TABLE users").unwrap();
139    /// #     conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").unwrap();
140    /// conn.execute("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").unwrap();
141    /// let user = User { id: 1, name: "Sean", hair_color: "black" };
142    /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" };
143    /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" };
144    ///
145    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
146    ///
147    /// let inserted_row_count = diesel::insert_into(users)
148    ///     .values(&same_name_different_hair_color)
149    ///     .on_conflict((name, hair_color))
150    ///     .do_nothing()
151    ///     .execute(&conn);
152    /// assert_eq!(Ok(1), inserted_row_count);
153    ///
154    /// let inserted_row_count = diesel::insert_into(users)
155    ///     .values(&same_name_same_hair_color)
156    ///     .on_conflict((name, hair_color))
157    ///     .do_nothing()
158    ///     .execute(&conn);
159    /// assert_eq!(Ok(0), inserted_row_count);
160    /// # }
161    /// ```
162    ///
163    /// See the documentation for [`on_constraint`] and [`do_update`] for
164    /// more examples.
165    ///
166    /// [`on_constraint`]: ../pg/upsert/fn.on_constraint.html
167    /// [`do_update`]: ../pg/upsert/struct.IncompleteOnConflict.html#method.do_update
168    pub fn on_conflict<Target>(
169        self,
170        target: Target,
171    ) -> IncompleteOnConflict<Self, ConflictTarget<Target>>
172    where
173        ConflictTarget<Target>: OnConflictTarget<T>,
174    {
175        IncompleteOnConflict {
176            stmt: self,
177            target: ConflictTarget(target),
178        }
179    }
180}
181
182/// A partially constructed `ON CONFLICT` clause.
183#[derive(Debug, Clone, Copy)]
184pub struct IncompleteOnConflict<Stmt, Target> {
185    stmt: Stmt,
186    target: Target,
187}
188
189impl<T, U, Op, Ret, Target> IncompleteOnConflict<InsertStatement<T, U, Op, Ret>, Target> {
190    /// Creates a query with `ON CONFLICT (target) DO NOTHING`
191    ///
192    /// If you want to do nothing when *any* constraint conflicts, use
193    /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage
194    /// examples.
195    ///
196    /// [`on_conflict_do_nothing`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict_do_nothing
197    /// [`on_conflict`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict
198    pub fn do_nothing(self) -> InsertStatement<T, OnConflictValues<U, Target, DoNothing>, Op, Ret> {
199        let target = self.target;
200        self.stmt
201            .replace_values(|values| OnConflictValues::new(values, target, DoNothing))
202    }
203}
204
205impl<Stmt, Target> IncompleteOnConflict<Stmt, Target> {
206    /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ...`
207    ///
208    /// Call `.set` on the result of this function with the changes you want to
209    /// apply. The argument to `set` can be anything that implements `AsChangeset`
210    /// (e.g. anything you could pass to `set` on a normal update statement).
211    ///
212    /// Note: When inserting more than one row at a time, this query can still fail
213    /// if the rows being inserted conflict with each other.
214    ///
215    /// # Examples
216    ///
217    /// ## Set specific value on conflict
218    ///
219    /// ```rust
220    /// # #[macro_use] extern crate diesel;
221    /// # include!("on_conflict_docs_setup.rs");
222    /// #
223    /// # fn main() {
224    /// #     use users::dsl::*;
225    /// #     let conn = establish_connection();
226    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
227    /// let user = User { id: 1, name: "Pascal" };
228    /// let user2 = User { id: 1, name: "Sean" };
229    ///
230    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
231    ///
232    /// let insert_count = diesel::insert_into(users)
233    ///     .values(&user2)
234    ///     .on_conflict(id)
235    ///     .do_update()
236    ///     .set(name.eq("I DONT KNOW ANYMORE"))
237    ///     .execute(&conn);
238    /// assert_eq!(Ok(1), insert_count);
239    ///
240    /// let users_in_db = users.load(&conn);
241    /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db);
242    /// # }
243    /// ```
244    ///
245    /// ## Set `AsChangeset` struct on conflict
246    ///
247    /// ```rust
248    /// # #[macro_use] extern crate diesel;
249    /// # include!("on_conflict_docs_setup.rs");
250    /// #
251    /// # fn main() {
252    /// #     use users::dsl::*;
253    /// #     let conn = establish_connection();
254    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
255    /// let user = User { id: 1, name: "Pascal" };
256    /// let user2 = User { id: 1, name: "Sean" };
257    ///
258    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
259    ///
260    /// let insert_count = diesel::insert_into(users)
261    ///     .values(&user2)
262    ///     .on_conflict(id)
263    ///     .do_update()
264    ///     .set(&user2)
265    ///     .execute(&conn);
266    /// assert_eq!(Ok(1), insert_count);
267    ///
268    /// let users_in_db = users.load(&conn);
269    /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db);
270    /// # }
271    /// ```
272    ///
273    /// ## Use `excluded` to get the rejected value
274    ///
275    /// ```rust
276    /// # #[macro_use] extern crate diesel;
277    /// # include!("on_conflict_docs_setup.rs");
278    /// #
279    /// # fn main() {
280    /// #     use users::dsl::*;
281    /// use diesel::pg::upsert::excluded;
282    ///
283    /// #     let conn = establish_connection();
284    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
285    /// let user = User { id: 1, name: "Pascal" };
286    /// let user2 = User { id: 1, name: "Sean" };
287    /// let user3 = User { id: 2, name: "Tess" };
288    ///
289    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
290    ///
291    /// let insert_count = diesel::insert_into(users)
292    ///     .values(&vec![user2, user3])
293    ///     .on_conflict(id)
294    ///     .do_update()
295    ///     .set(name.eq(excluded(name)))
296    ///     .execute(&conn);
297    /// assert_eq!(Ok(2), insert_count);
298    ///
299    /// let users_in_db = users.load(&conn);
300    /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db);
301    /// # }
302    /// ```
303    pub fn do_update(self) -> IncompleteDoUpdate<Stmt, Target> {
304        IncompleteDoUpdate {
305            stmt: self.stmt,
306            target: self.target,
307        }
308    }
309}
310
311/// A partially constructed `ON CONFLICT DO UPDATE` clause.
312#[derive(Debug, Clone, Copy)]
313pub struct IncompleteDoUpdate<Stmt, Target> {
314    stmt: Stmt,
315    target: Target,
316}
317
318impl<T, U, Op, Ret, Target> IncompleteDoUpdate<InsertStatement<T, U, Op, Ret>, Target> {
319    /// See [`do_update`] for usage examples.
320    ///
321    /// [`do_update`]: struct.IncompleteOnConflict.html#method.do_update
322    pub fn set<Changes>(
323        self,
324        changes: Changes,
325    ) -> InsertStatement<T, OnConflictValues<U, Target, DoUpdate<Changes::Changeset>>, Op, Ret>
326    where
327        T: QuerySource,
328        Changes: AsChangeset<Target = T>,
329    {
330        let target = self.target;
331        self.stmt.replace_values(|values| {
332            OnConflictValues::new(values, target, DoUpdate::new(changes.as_changeset()))
333        })
334    }
335}