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}