diesel_async/run_query_dsl/mod.rs
1use crate::AsyncConnection;
2use diesel::associations::HasTable;
3use diesel::query_builder::IntoUpdateTarget;
4use diesel::result::QueryResult;
5use diesel::AsChangeset;
6use futures_core::future::BoxFuture;
7use futures_core::Stream;
8use futures_util::{future, stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
9use std::future::Future;
10use std::pin::Pin;
11
12/// The traits used by `QueryDsl`.
13///
14/// Each trait in this module represents exactly one method from [`RunQueryDsl`].
15/// Apps should general rely on [`RunQueryDsl`] directly, rather than these traits.
16/// However, generic code may need to include a where clause that references
17/// these traits.
18pub mod methods {
19 use super::*;
20 use diesel::backend::Backend;
21 use diesel::deserialize::FromSqlRow;
22 use diesel::expression::QueryMetadata;
23 use diesel::query_builder::{AsQuery, QueryFragment, QueryId};
24 use diesel::query_dsl::CompatibleType;
25 use futures_util::{Future, Stream, TryFutureExt};
26
27 /// The `execute` method
28 ///
29 /// This trait should not be relied on directly by most apps. Its behavior is
30 /// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait
31 /// to call `execute` from generic code.
32 ///
33 /// [`RunQueryDsl`]: super::RunQueryDsl
34 pub trait ExecuteDsl<Conn, DB = <Conn as AsyncConnection>::Backend>
35 where
36 Conn: AsyncConnection<Backend = DB>,
37 DB: Backend,
38 {
39 /// Execute this command
40 fn execute<'conn, 'query>(
41 query: Self,
42 conn: &'conn mut Conn,
43 ) -> Conn::ExecuteFuture<'conn, 'query>
44 where
45 Self: 'query;
46 }
47
48 impl<Conn, DB, T> ExecuteDsl<Conn, DB> for T
49 where
50 Conn: AsyncConnection<Backend = DB>,
51 DB: Backend,
52 T: QueryFragment<DB> + QueryId + Send,
53 {
54 fn execute<'conn, 'query>(
55 query: Self,
56 conn: &'conn mut Conn,
57 ) -> Conn::ExecuteFuture<'conn, 'query>
58 where
59 Self: 'query,
60 {
61 conn.execute_returning_count(query)
62 }
63 }
64
65 /// The `load` method
66 ///
67 /// This trait should not be relied on directly by most apps. Its behavior is
68 /// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait
69 /// to call `load` from generic code.
70 ///
71 /// [`RunQueryDsl`]: super::RunQueryDsl
72 pub trait LoadQuery<'query, Conn: AsyncConnection, U> {
73 /// The future returned by [`LoadQuery::internal_load`]
74 type LoadFuture<'conn>: Future<Output = QueryResult<Self::Stream<'conn>>> + Send
75 where
76 Conn: 'conn;
77 /// The inner stream returned by [`LoadQuery::internal_load`]
78 type Stream<'conn>: Stream<Item = QueryResult<U>> + Send
79 where
80 Conn: 'conn;
81
82 /// Load this query
83 fn internal_load(self, conn: &mut Conn) -> Self::LoadFuture<'_>;
84 }
85
86 impl<'query, Conn, DB, T, U, ST> LoadQuery<'query, Conn, U> for T
87 where
88 Conn: AsyncConnection<Backend = DB>,
89 U: Send,
90 DB: Backend + 'static,
91 T: AsQuery + Send + 'query,
92 T::Query: QueryFragment<DB> + QueryId + Send + 'query,
93 T::SqlType: CompatibleType<U, DB, SqlType = ST>,
94 U: FromSqlRow<ST, DB> + Send + 'static,
95 DB: QueryMetadata<T::SqlType>,
96 ST: 'static,
97 {
98 type LoadFuture<'conn>
99 = future::MapOk<
100 Conn::LoadFuture<'conn, 'query>,
101 fn(Conn::Stream<'conn, 'query>) -> Self::Stream<'conn>,
102 >
103 where
104 Conn: 'conn;
105
106 type Stream<'conn>
107 = stream::Map<
108 Conn::Stream<'conn, 'query>,
109 fn(QueryResult<Conn::Row<'conn, 'query>>) -> QueryResult<U>,
110 >
111 where
112 Conn: 'conn;
113
114 fn internal_load(self, conn: &mut Conn) -> Self::LoadFuture<'_> {
115 conn.load(self)
116 .map_ok(map_result_stream_future::<U, _, _, DB, ST>)
117 }
118 }
119
120 #[allow(clippy::type_complexity)]
121 fn map_result_stream_future<'s, 'a, U, S, R, DB, ST>(
122 stream: S,
123 ) -> stream::Map<S, fn(QueryResult<R>) -> QueryResult<U>>
124 where
125 S: Stream<Item = QueryResult<R>> + Send + 's,
126 R: diesel::row::Row<'a, DB> + 's,
127 DB: Backend + 'static,
128 U: FromSqlRow<ST, DB> + 'static,
129 ST: 'static,
130 {
131 stream.map(map_row_helper::<_, DB, U, ST>)
132 }
133
134 fn map_row_helper<'a, R, DB, U, ST>(row: QueryResult<R>) -> QueryResult<U>
135 where
136 U: FromSqlRow<ST, DB>,
137 R: diesel::row::Row<'a, DB>,
138 DB: Backend,
139 {
140 U::build_from_row(&row?).map_err(diesel::result::Error::DeserializationError)
141 }
142}
143
144/// The return types produced by the various [`RunQueryDsl`] methods
145///
146// We cannot box these types as this would require specifying a lifetime,
147// but concrete connection implementations might want to have control
148// about that so that they can support multiple simultaneous queries on
149// the same connection
150#[allow(type_alias_bounds)] // we need these bounds otherwise we cannot use GAT's
151pub mod return_futures {
152 use super::methods::LoadQuery;
153 use diesel::QueryResult;
154 use futures_util::{future, stream};
155 use std::pin::Pin;
156
157 /// The future returned by [`RunQueryDsl::load`](super::RunQueryDsl::load)
158 /// and [`RunQueryDsl::get_results`](super::RunQueryDsl::get_results)
159 ///
160 /// This is essentially `impl Future<Output = QueryResult<Vec<U>>>`
161 pub type LoadFuture<'conn, 'query, Q: LoadQuery<'query, Conn, U>, Conn, U> = future::AndThen<
162 Q::LoadFuture<'conn>,
163 stream::TryCollect<Q::Stream<'conn>, Vec<U>>,
164 fn(Q::Stream<'conn>) -> stream::TryCollect<Q::Stream<'conn>, Vec<U>>,
165 >;
166
167 /// The future returned by [`RunQueryDsl::get_result`](super::RunQueryDsl::get_result)
168 ///
169 /// This is essentially `impl Future<Output = QueryResult<U>>`
170 pub type GetResult<'conn, 'query, Q: LoadQuery<'query, Conn, U>, Conn, U> = future::AndThen<
171 Q::LoadFuture<'conn>,
172 future::Map<
173 stream::StreamFuture<Pin<Box<Q::Stream<'conn>>>>,
174 fn((Option<QueryResult<U>>, Pin<Box<Q::Stream<'conn>>>)) -> QueryResult<U>,
175 >,
176 fn(
177 Q::Stream<'conn>,
178 ) -> future::Map<
179 stream::StreamFuture<Pin<Box<Q::Stream<'conn>>>>,
180 fn((Option<QueryResult<U>>, Pin<Box<Q::Stream<'conn>>>)) -> QueryResult<U>,
181 >,
182 >;
183}
184
185/// Methods used to execute queries.
186pub trait RunQueryDsl<Conn>: Sized {
187 /// Executes the given command, returning the number of rows affected.
188 ///
189 /// `execute` is usually used in conjunction with [`insert_into`](diesel::insert_into()),
190 /// [`update`](diesel::update()) and [`delete`](diesel::delete()) where the number of
191 /// affected rows is often enough information.
192 ///
193 /// When asking the database to return data from a query, [`load`](crate::run_query_dsl::RunQueryDsl::load()) should
194 /// probably be used instead.
195 ///
196 /// # Example
197 ///
198 /// ```rust
199 /// # include!("../doctest_setup.rs");
200 /// #
201 /// use diesel_async::RunQueryDsl;
202 ///
203 /// # #[tokio::main(flavor = "current_thread")]
204 /// # async fn main() {
205 /// # run_test().await;
206 /// # }
207 /// #
208 /// # async fn run_test() -> QueryResult<()> {
209 /// # use diesel::insert_into;
210 /// # use schema::users::dsl::*;
211 /// # let connection = &mut establish_connection().await;
212 /// let inserted_rows = insert_into(users)
213 /// .values(name.eq("Ruby"))
214 /// .execute(connection)
215 /// .await?;
216 /// assert_eq!(1, inserted_rows);
217 ///
218 /// # #[cfg(not(feature = "sqlite"))]
219 /// let inserted_rows = insert_into(users)
220 /// .values(&vec![name.eq("Jim"), name.eq("James")])
221 /// .execute(connection)
222 /// .await?;
223 /// # #[cfg(not(feature = "sqlite"))]
224 /// assert_eq!(2, inserted_rows);
225 /// # Ok(())
226 /// # }
227 /// ```
228 fn execute<'conn, 'query>(self, conn: &'conn mut Conn) -> Conn::ExecuteFuture<'conn, 'query>
229 where
230 Conn: AsyncConnection + Send,
231 Self: methods::ExecuteDsl<Conn> + 'query,
232 {
233 methods::ExecuteDsl::execute(self, conn)
234 }
235
236 /// Executes the given query, returning a [`Vec`] with the returned rows.
237 ///
238 /// When using the query builder, the return type can be
239 /// a tuple of the values, or a struct which implements [`Queryable`].
240 ///
241 /// When this method is called on [`sql_query`],
242 /// the return type can only be a struct which implements [`QueryableByName`]
243 ///
244 /// For insert, update, and delete operations where only a count of affected is needed,
245 /// [`execute`] should be used instead.
246 ///
247 /// [`Queryable`]: diesel::deserialize::Queryable
248 /// [`QueryableByName`]: diesel::deserialize::QueryableByName
249 /// [`execute`]: crate::run_query_dsl::RunQueryDsl::execute()
250 /// [`sql_query`]: diesel::sql_query()
251 ///
252 /// # Examples
253 ///
254 /// ## Returning a single field
255 ///
256 /// ```rust
257 /// # include!("../doctest_setup.rs");
258 /// #
259 /// use diesel_async::{RunQueryDsl, AsyncConnection};
260 ///
261 /// #
262 /// # #[tokio::main(flavor = "current_thread")]
263 /// # async fn main() {
264 /// # run_test().await;
265 /// # }
266 /// #
267 /// # async fn run_test() -> QueryResult<()> {
268 /// # use diesel::insert_into;
269 /// # use schema::users::dsl::*;
270 /// # let connection = &mut establish_connection().await;
271 /// let data = users.select(name)
272 /// .load::<String>(connection)
273 /// .await?;
274 /// assert_eq!(vec!["Sean", "Tess"], data);
275 /// # Ok(())
276 /// # }
277 /// ```
278 ///
279 /// ## Returning a tuple
280 ///
281 /// ```rust
282 /// # include!("../doctest_setup.rs");
283 /// use diesel_async::RunQueryDsl;
284 ///
285 /// #
286 /// # #[tokio::main(flavor = "current_thread")]
287 /// # async fn main() {
288 /// # run_test().await;
289 /// # }
290 /// #
291 /// # async fn run_test() -> QueryResult<()> {
292 /// # use diesel::insert_into;
293 /// # use schema::users::dsl::*;
294 /// # let connection = &mut establish_connection().await;
295 /// let data = users
296 /// .load::<(i32, String)>(connection)
297 /// .await?;
298 /// let expected_data = vec![
299 /// (1, String::from("Sean")),
300 /// (2, String::from("Tess")),
301 /// ];
302 /// assert_eq!(expected_data, data);
303 /// # Ok(())
304 /// # }
305 /// ```
306 ///
307 /// ## Returning a struct
308 ///
309 /// ```rust
310 /// # include!("../doctest_setup.rs");
311 /// use diesel_async::RunQueryDsl;
312 ///
313 /// #
314 /// #[derive(Queryable, PartialEq, Debug)]
315 /// struct User {
316 /// id: i32,
317 /// name: String,
318 /// }
319 ///
320 /// # #[tokio::main(flavor = "current_thread")]
321 /// # async fn main() {
322 /// # run_test().await;
323 /// # }
324 /// #
325 /// # async fn run_test() -> QueryResult<()> {
326 /// # use diesel::insert_into;
327 /// # use schema::users::dsl::*;
328 /// # let connection = &mut establish_connection().await;
329 /// let data = users
330 /// .load::<User>(connection)
331 /// .await?;
332 /// let expected_data = vec![
333 /// User { id: 1, name: String::from("Sean") },
334 /// User { id: 2, name: String::from("Tess") },
335 /// ];
336 /// assert_eq!(expected_data, data);
337 /// # Ok(())
338 /// # }
339 /// ```
340 fn load<'query, 'conn, U>(
341 self,
342 conn: &'conn mut Conn,
343 ) -> return_futures::LoadFuture<'conn, 'query, Self, Conn, U>
344 where
345 U: Send,
346 Conn: AsyncConnection,
347 Self: methods::LoadQuery<'query, Conn, U> + 'query,
348 {
349 fn collect_result<U, S>(stream: S) -> stream::TryCollect<S, Vec<U>>
350 where
351 S: Stream<Item = QueryResult<U>>,
352 {
353 stream.try_collect()
354 }
355 self.internal_load(conn).and_then(collect_result::<U, _>)
356 }
357
358 /// Executes the given query, returning a [`Stream`] with the returned rows.
359 ///
360 /// **You should normally prefer to use [`RunQueryDsl::load`] instead**. This method
361 /// is provided for situations where the result needs to be collected into a different
362 /// container than a [`Vec`]
363 ///
364 /// When using the query builder, the return type can be
365 /// a tuple of the values, or a struct which implements [`Queryable`].
366 ///
367 /// When this method is called on [`sql_query`],
368 /// the return type can only be a struct which implements [`QueryableByName`]
369 ///
370 /// For insert, update, and delete operations where only a count of affected is needed,
371 /// [`execute`] should be used instead.
372 ///
373 /// [`Queryable`]: diesel::deserialize::Queryable
374 /// [`QueryableByName`]: diesel::deserialize::QueryableByName
375 /// [`execute`]: crate::run_query_dsl::RunQueryDsl::execute()
376 /// [`sql_query`]: diesel::sql_query()
377 ///
378 /// # Examples
379 ///
380 /// ## Returning a single field
381 ///
382 /// ```rust
383 /// # include!("../doctest_setup.rs");
384 /// #
385 /// use diesel_async::RunQueryDsl;
386 ///
387 /// # #[tokio::main(flavor = "current_thread")]
388 /// # async fn main() {
389 /// # run_test().await;
390 /// # }
391 /// #
392 /// # async fn run_test() -> QueryResult<()> {
393 /// # use diesel::insert_into;
394 /// # use schema::users::dsl::*;
395 /// # use futures_util::stream::TryStreamExt;
396 /// # let connection = &mut establish_connection().await;
397 /// let data = users.select(name)
398 /// .load_stream::<String>(connection)
399 /// .await?
400 /// .try_fold(Vec::new(), |mut acc, item| {
401 /// acc.push(item);
402 /// std::future::ready(Ok(acc))
403 /// })
404 /// .await?;
405 /// assert_eq!(vec!["Sean", "Tess"], data);
406 /// # Ok(())
407 /// # }
408 /// ```
409 ///
410 /// ## Returning a tuple
411 ///
412 /// ```rust
413 /// # include!("../doctest_setup.rs");
414 /// use diesel_async::RunQueryDsl;
415 /// #
416 /// # #[tokio::main(flavor = "current_thread")]
417 /// # async fn main() {
418 /// # run_test().await;
419 /// # }
420 /// #
421 /// # async fn run_test() -> QueryResult<()> {
422 /// # use diesel::insert_into;
423 /// # use schema::users::dsl::*;
424 /// # use futures_util::stream::TryStreamExt;
425 /// # let connection = &mut establish_connection().await;
426 /// let data = users
427 /// .load_stream::<(i32, String)>(connection)
428 /// .await?
429 /// .try_fold(Vec::new(), |mut acc, item| {
430 /// acc.push(item);
431 /// std::future::ready(Ok(acc))
432 /// })
433 /// .await?;
434 /// let expected_data = vec![
435 /// (1, String::from("Sean")),
436 /// (2, String::from("Tess")),
437 /// ];
438 /// assert_eq!(expected_data, data);
439 /// # Ok(())
440 /// # }
441 /// ```
442 ///
443 /// ## Returning a struct
444 ///
445 /// ```rust
446 /// # include!("../doctest_setup.rs");
447 /// #
448 /// use diesel_async::RunQueryDsl;
449 ///
450 /// #[derive(Queryable, PartialEq, Debug)]
451 /// struct User {
452 /// id: i32,
453 /// name: String,
454 /// }
455 ///
456 /// # #[tokio::main(flavor = "current_thread")]
457 /// # async fn main() {
458 /// # run_test().await;
459 /// # }
460 /// #
461 /// # async fn run_test() -> QueryResult<()> {
462 /// # use diesel::insert_into;
463 /// # use schema::users::dsl::*;
464 /// # use futures_util::stream::TryStreamExt;
465 /// # let connection = &mut establish_connection().await;
466 /// let data = users
467 /// .load_stream::<User>(connection)
468 /// .await?
469 /// .try_fold(Vec::new(), |mut acc, item| {
470 /// acc.push(item);
471 /// std::future::ready(Ok(acc))
472 /// })
473 /// .await?;
474 /// let expected_data = vec![
475 /// User { id: 1, name: String::from("Sean") },
476 /// User { id: 2, name: String::from("Tess") },
477 /// ];
478 /// assert_eq!(expected_data, data);
479 /// # Ok(())
480 /// # }
481 /// ```
482 fn load_stream<'conn, 'query, U>(self, conn: &'conn mut Conn) -> Self::LoadFuture<'conn>
483 where
484 Conn: AsyncConnection,
485 U: 'conn,
486 Self: methods::LoadQuery<'query, Conn, U> + 'query,
487 {
488 self.internal_load(conn)
489 }
490
491 /// Runs the command, and returns the affected row.
492 ///
493 /// `Err(NotFound)` will be returned if the query affected 0 rows. You can
494 /// call `.optional()` on the result of this if the command was optional to
495 /// get back a `Result<Option<U>>`
496 ///
497 /// When this method is called on an insert, update, or delete statement,
498 /// it will implicitly add a `RETURNING *` to the query,
499 /// unless a returning clause was already specified.
500 ///
501 /// This method only returns the first row that was affected, even if more
502 /// rows are affected.
503 ///
504 /// # Example
505 ///
506 /// ```rust
507 /// # include!("../doctest_setup.rs");
508 /// use diesel_async::RunQueryDsl;
509 ///
510 /// #
511 /// # #[tokio::main(flavor = "current_thread")]
512 /// # async fn main() {
513 /// # run_test().await;
514 /// # }
515 /// #
516 /// # #[cfg(feature = "postgres")]
517 /// # async fn run_test() -> QueryResult<()> {
518 /// # use diesel::{insert_into, update};
519 /// # use schema::users::dsl::*;
520 /// # let connection = &mut establish_connection().await;
521 /// let inserted_row = insert_into(users)
522 /// .values(name.eq("Ruby"))
523 /// .get_result(connection)
524 /// .await?;
525 /// assert_eq!((3, String::from("Ruby")), inserted_row);
526 ///
527 /// // This will return `NotFound`, as there is no user with ID 4
528 /// let update_result = update(users.find(4))
529 /// .set(name.eq("Jim"))
530 /// .get_result::<(i32, String)>(connection)
531 /// .await;
532 /// assert_eq!(Err(diesel::NotFound), update_result);
533 /// # Ok(())
534 /// # }
535 /// #
536 /// # #[cfg(not(feature = "postgres"))]
537 /// # async fn run_test() -> QueryResult<()> {
538 /// # Ok(())
539 /// # }
540 /// ```
541 fn get_result<'query, 'conn, U>(
542 self,
543 conn: &'conn mut Conn,
544 ) -> return_futures::GetResult<'conn, 'query, Self, Conn, U>
545 where
546 U: Send + 'conn,
547 Conn: AsyncConnection,
548 Self: methods::LoadQuery<'query, Conn, U> + 'query,
549 {
550 #[allow(clippy::type_complexity)]
551 fn get_next_stream_element<S, U>(
552 stream: S,
553 ) -> future::Map<
554 stream::StreamFuture<Pin<Box<S>>>,
555 fn((Option<QueryResult<U>>, Pin<Box<S>>)) -> QueryResult<U>,
556 >
557 where
558 S: Stream<Item = QueryResult<U>>,
559 {
560 fn map_option_to_result<U, S>(
561 (o, _): (Option<QueryResult<U>>, Pin<Box<S>>),
562 ) -> QueryResult<U> {
563 match o {
564 Some(s) => s,
565 None => Err(diesel::result::Error::NotFound),
566 }
567 }
568
569 Box::pin(stream).into_future().map(map_option_to_result)
570 }
571
572 self.load_stream(conn).and_then(get_next_stream_element)
573 }
574
575 /// Runs the command, returning an `Vec` with the affected rows.
576 ///
577 /// This method is an alias for [`load`], but with a name that makes more
578 /// sense for insert, update, and delete statements.
579 ///
580 /// [`load`]: crate::run_query_dsl::RunQueryDsl::load()
581 fn get_results<'query, 'conn, U>(
582 self,
583 conn: &'conn mut Conn,
584 ) -> return_futures::LoadFuture<'conn, 'query, Self, Conn, U>
585 where
586 U: Send,
587 Conn: AsyncConnection,
588 Self: methods::LoadQuery<'query, Conn, U> + 'query,
589 {
590 self.load(conn)
591 }
592
593 /// Attempts to load a single record.
594 ///
595 /// This method is equivalent to `.limit(1).get_result()`
596 ///
597 /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are
598 /// returned. If the query truly is optional, you can call `.optional()` on
599 /// the result of this to get a `Result<Option<U>>`.
600 ///
601 /// # Example:
602 ///
603 /// ```rust
604 /// # include!("../doctest_setup.rs");
605 /// use diesel_async::RunQueryDsl;
606 ///
607 /// #
608 /// # #[tokio::main(flavor = "current_thread")]
609 /// # async fn main() {
610 /// # run_test();
611 /// # }
612 /// #
613 /// # async fn run_test() -> QueryResult<()> {
614 /// # use schema::users::dsl::*;
615 /// # let connection = &mut establish_connection().await;
616 /// for n in &["Sean", "Pascal"] {
617 /// diesel::insert_into(users)
618 /// .values(name.eq(n))
619 /// .execute(connection)
620 /// .await?;
621 /// }
622 ///
623 /// let first_name = users.order(id)
624 /// .select(name)
625 /// .first(connection)
626 /// .await;
627 /// assert_eq!(Ok(String::from("Sean")), first_name);
628 ///
629 /// let not_found = users
630 /// .filter(name.eq("Foo"))
631 /// .first::<(i32, String)>(connection)
632 /// .await;
633 /// assert_eq!(Err(diesel::NotFound), not_found);
634 /// # Ok(())
635 /// # }
636 /// ```
637 fn first<'query, 'conn, U>(
638 self,
639 conn: &'conn mut Conn,
640 ) -> return_futures::GetResult<'conn, 'query, diesel::dsl::Limit<Self>, Conn, U>
641 where
642 U: Send + 'conn,
643 Conn: AsyncConnection,
644 Self: diesel::query_dsl::methods::LimitDsl,
645 diesel::dsl::Limit<Self>: methods::LoadQuery<'query, Conn, U> + Send + 'query,
646 {
647 diesel::query_dsl::methods::LimitDsl::limit(self, 1).get_result(conn)
648 }
649}
650
651impl<T, Conn> RunQueryDsl<Conn> for T {}
652
653/// Sugar for types which implement both `AsChangeset` and `Identifiable`
654///
655/// On backends which support the `RETURNING` keyword,
656/// `foo.save_changes(&conn)` is equivalent to
657/// `update(&foo).set(&foo).get_result(&conn)`.
658/// On other backends, two queries will be executed.
659///
660/// # Example
661///
662/// ```rust
663/// # include!("../doctest_setup.rs");
664/// # use schema::animals;
665/// #
666/// use diesel_async::{SaveChangesDsl, AsyncConnection};
667///
668/// #[derive(Queryable, Debug, PartialEq)]
669/// struct Animal {
670/// id: i32,
671/// species: String,
672/// legs: i32,
673/// name: Option<String>,
674/// }
675///
676/// #[derive(AsChangeset, Identifiable)]
677/// #[diesel(table_name = animals)]
678/// struct AnimalForm<'a> {
679/// id: i32,
680/// name: &'a str,
681/// }
682///
683/// # #[tokio::main(flavor = "current_thread")]
684/// # async fn main() {
685/// # run_test().await.unwrap();
686/// # }
687/// #
688/// # async fn run_test() -> QueryResult<()> {
689/// # use self::animals::dsl::*;
690/// # let connection = &mut establish_connection().await;
691/// let form = AnimalForm { id: 2, name: "Super scary" };
692/// # #[cfg(not(feature = "sqlite"))]
693/// let changed_animal = form.save_changes(connection).await?;
694/// let expected_animal = Animal {
695/// id: 2,
696/// species: String::from("spider"),
697/// legs: 8,
698/// name: Some(String::from("Super scary")),
699/// };
700/// # #[cfg(not(feature = "sqlite"))]
701/// assert_eq!(expected_animal, changed_animal);
702/// # Ok(())
703/// # }
704/// ```
705pub trait SaveChangesDsl<Conn> {
706 /// See the trait documentation
707 fn save_changes<'life0, 'async_trait, T>(
708 self,
709 connection: &'life0 mut Conn,
710 ) -> impl Future<Output = QueryResult<T>> + Send + 'async_trait
711 where
712 Self: Sized + diesel::prelude::Identifiable,
713 Conn: UpdateAndFetchResults<Self, T>,
714 T: 'async_trait,
715 'life0: 'async_trait,
716 Self: ::core::marker::Send + 'async_trait,
717 {
718 connection.update_and_fetch(self)
719 }
720}
721
722impl<T, Conn> SaveChangesDsl<Conn> for T where
723 T: Copy + AsChangeset<Target = <T as HasTable>::Table> + IntoUpdateTarget
724{
725}
726
727/// A trait defining how to update a record and fetch the updated entry
728/// on a certain backend.
729///
730/// The only case where it is required to work with this trait is while
731/// implementing a new connection type.
732/// Otherwise use [`SaveChangesDsl`]
733///
734/// For implementing this trait for a custom backend:
735/// * The `Changes` generic parameter represents the changeset that should be stored
736/// * The `Output` generic parameter represents the type of the response.
737pub trait UpdateAndFetchResults<Changes, Output>: AsyncConnection
738where
739 Changes: diesel::prelude::Identifiable + HasTable,
740{
741 /// See the traits documentation.
742 fn update_and_fetch<'conn, 'changes>(
743 &'conn mut self,
744 changeset: Changes,
745 ) -> BoxFuture<'changes, QueryResult<Output>>
746 // cannot use impl future due to rustc bugs
747 // https://github.com/rust-lang/rust/issues/135619
748 //impl Future<Output = QueryResult<Output>> + Send + 'changes
749 where
750 Changes: 'changes,
751 'conn: 'changes,
752 Self: 'changes;
753}
754
755#[cfg(feature = "mysql")]
756impl<'b, Changes, Output, Tab, V> UpdateAndFetchResults<Changes, Output>
757 for crate::AsyncMysqlConnection
758where
759 Output: Send + 'static,
760 Changes:
761 Copy + AsChangeset<Target = Tab> + Send + diesel::associations::Identifiable<Table = Tab>,
762 Tab: diesel::Table + diesel::query_dsl::methods::FindDsl<Changes::Id> + 'b,
763 diesel::dsl::Find<Tab, Changes::Id>: IntoUpdateTarget<Table = Tab, WhereClause = V>,
764 diesel::query_builder::UpdateStatement<Tab, V, Changes::Changeset>:
765 diesel::query_builder::AsQuery,
766 diesel::dsl::Update<Changes, Changes>: methods::ExecuteDsl<Self>,
767 V: Send + 'b,
768 Changes::Changeset: Send + 'b,
769 Changes::Id: 'b,
770 Tab::FromClause: Send,
771 diesel::dsl::Find<Changes::Table, Changes::Id>:
772 methods::LoadQuery<'b, crate::AsyncMysqlConnection, Output> + Send,
773{
774 fn update_and_fetch<'conn, 'changes>(
775 &'conn mut self,
776 changeset: Changes,
777 ) -> BoxFuture<'changes, QueryResult<Output>>
778 where
779 Changes: 'changes,
780 Changes::Changeset: 'changes,
781 'conn: 'changes,
782 Self: 'changes,
783 {
784 async move {
785 diesel::update(changeset)
786 .set(changeset)
787 .execute(self)
788 .await?;
789 Changes::table().find(changeset.id()).get_result(self).await
790 }
791 .boxed()
792 }
793}
794
795#[cfg(feature = "postgres")]
796impl<'b, Changes, Output, Tab, V> UpdateAndFetchResults<Changes, Output>
797 for crate::AsyncPgConnection
798where
799 Output: Send + 'static,
800 Changes:
801 Copy + AsChangeset<Target = Tab> + Send + diesel::associations::Identifiable<Table = Tab>,
802 Tab: diesel::Table + diesel::query_dsl::methods::FindDsl<Changes::Id> + 'b,
803 diesel::dsl::Find<Tab, Changes::Id>: IntoUpdateTarget<Table = Tab, WhereClause = V>,
804 diesel::query_builder::UpdateStatement<Tab, V, Changes::Changeset>:
805 diesel::query_builder::AsQuery,
806 diesel::dsl::Update<Changes, Changes>: methods::LoadQuery<'b, Self, Output>,
807 V: Send + 'b,
808 Changes::Changeset: Send + 'b,
809 Tab::FromClause: Send,
810{
811 fn update_and_fetch<'conn, 'changes>(
812 &'conn mut self,
813 changeset: Changes,
814 ) -> BoxFuture<'changes, QueryResult<Output>>
815 where
816 Changes: 'changes,
817 Changes::Changeset: 'changes,
818 'conn: 'changes,
819 Self: 'changes,
820 {
821 async move {
822 diesel::update(changeset)
823 .set(changeset)
824 .get_result(self)
825 .await
826 }
827 .boxed()
828 }
829}