Skip to main content

diesel/query_dsl/
save_changes_dsl.rs

1#[cfg(any(
2    feature = "__sqlite-shared",
3    feature = "postgres",
4    feature = "mysql",
5    feature = "mariadb"
6))]
7use crate::Table;
8use crate::associations::HasTable;
9#[cfg(any(feature = "__sqlite-shared", feature = "mysql", feature = "mariadb"))]
10use crate::associations::Identifiable;
11use crate::connection::Connection;
12#[cfg(any(feature = "__sqlite-shared", feature = "mysql", feature = "mariadb"))]
13use crate::dsl::Find;
14#[cfg(any(
15    feature = "__sqlite-shared",
16    feature = "postgres",
17    feature = "mysql",
18    feature = "mariadb"
19))]
20use crate::dsl::Update;
21#[cfg(any(
22    feature = "__sqlite-shared",
23    feature = "postgres",
24    feature = "mysql",
25    feature = "mariadb"
26))]
27use crate::expression::{MixedAggregates, ValidGrouping, is_aggregate};
28use crate::query_builder::{AsChangeset, IntoUpdateTarget};
29#[cfg(any(feature = "__sqlite-shared", feature = "mysql", feature = "mariadb"))]
30use crate::query_dsl::methods::{ExecuteDsl, FindDsl};
31#[cfg(any(
32    feature = "__sqlite-shared",
33    feature = "postgres",
34    feature = "mysql",
35    feature = "mariadb"
36))]
37use crate::query_dsl::{LoadQuery, RunQueryDsl};
38use crate::result::QueryResult;
39
40/// A trait defining how to update a record and fetch the updated entry
41/// on a certain backend.
42///
43/// The only case where it is required to work with this trait is while
44/// implementing a new connection type.
45/// Otherwise use [`SaveChangesDsl`]
46///
47/// For implementing this trait for a custom backend:
48/// * The `Changes` generic parameter represents the changeset that should be stored
49/// * The `Output` generic parameter represents the type of the response.
50pub trait UpdateAndFetchResults<Changes, Output>: Connection {
51    /// See the traits documentation.
52    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output>;
53}
54
55#[cfg(feature = "postgres")]
56use crate::pg::PgConnection;
57
58#[cfg(feature = "postgres")]
59impl<'b, Changes, Output> UpdateAndFetchResults<Changes, Output> for PgConnection
60where
61    Changes: Copy + AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
62    Update<Changes, Changes>: LoadQuery<'b, PgConnection, Output>,
63    <Changes::Table as Table>::AllColumns: ValidGrouping<()>
64        + crate::expression::SelectableExpression<
65            crate::query_builder::returning::ReturningQuerySource<
66                crate::query_builder::returning::UpdateStmt,
67                Changes::Table,
68            >,
69        >,
70    <<Changes::Table as Table>::AllColumns as ValidGrouping<()>>::IsAggregate:
71        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
72{
73    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
74        crate::update(changeset).set(changeset).get_result(self)
75    }
76}
77
78#[cfg(feature = "__sqlite-shared")]
79use crate::sqlite::SqliteConnection;
80
81#[cfg(feature = "__sqlite-shared")]
82impl<'b, Changes, Output> UpdateAndFetchResults<Changes, Output> for SqliteConnection
83where
84    Changes: Copy + Identifiable,
85    Changes: AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
86    Changes::Table: FindDsl<Changes::Id>,
87    Update<Changes, Changes>: ExecuteDsl<SqliteConnection>,
88    Find<Changes::Table, Changes::Id>: LoadQuery<'b, SqliteConnection, Output>,
89    <Changes::Table as Table>::AllColumns: ValidGrouping<()>
90        + crate::expression::SelectableExpression<
91            crate::query_builder::returning::ReturningQuerySource<
92                crate::query_builder::returning::UpdateStmt,
93                Changes::Table,
94            >,
95        >,
96    <<Changes::Table as Table>::AllColumns as ValidGrouping<()>>::IsAggregate:
97        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
98{
99    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
100        crate::update(changeset).set(changeset).execute(self)?;
101        Changes::table().find(changeset.id()).get_result(self)
102    }
103}
104
105#[cfg(feature = "mysql")]
106use crate::mysql::MysqlConnection;
107
108#[cfg(feature = "mysql")]
109impl<'b, Changes, Output> UpdateAndFetchResults<Changes, Output> for MysqlConnection
110where
111    Changes: Copy + Identifiable,
112    Changes: AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
113    Changes::Table: FindDsl<Changes::Id>,
114    Update<Changes, Changes>: ExecuteDsl<MysqlConnection>,
115    Find<Changes::Table, Changes::Id>: LoadQuery<'b, MysqlConnection, Output>,
116    <Changes::Table as Table>::AllColumns: ValidGrouping<()>
117        + crate::expression::SelectableExpression<
118            crate::query_builder::returning::ReturningQuerySource<
119                crate::query_builder::returning::UpdateStmt,
120                Changes::Table,
121            >,
122        >,
123    <<Changes::Table as Table>::AllColumns as ValidGrouping<()>>::IsAggregate:
124        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
125{
126    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
127        crate::update(changeset).set(changeset).execute(self)?;
128        Changes::table().find(changeset.id()).get_result(self)
129    }
130}
131
132#[cfg(feature = "mariadb")]
133use crate::mariadb::MariadbConnection;
134
135#[cfg(feature = "mariadb")]
136impl<'b, Changes, Output> UpdateAndFetchResults<Changes, Output> for MariadbConnection
137where
138    Changes: Copy + Identifiable,
139    Changes: AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
140    Changes::Table: FindDsl<Changes::Id>,
141    Update<Changes, Changes>: ExecuteDsl<MariadbConnection>,
142    Find<Changes::Table, Changes::Id>: LoadQuery<'b, MariadbConnection, Output>,
143    <Changes::Table as Table>::AllColumns: ValidGrouping<()>
144        + crate::expression::SelectableExpression<
145            crate::query_builder::returning::ReturningQuerySource<
146                crate::query_builder::returning::UpdateStmt,
147                Changes::Table,
148            >,
149        >,
150    <<Changes::Table as Table>::AllColumns as ValidGrouping<()>>::IsAggregate:
151        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
152{
153    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
154        crate::update(changeset).set(changeset).execute(self)?;
155        Changes::table().find(changeset.id()).get_result(self)
156    }
157}
158
159/// Sugar for types which implement both `AsChangeset` and `Identifiable`
160///
161/// On backends which support the `RETURNING` keyword,
162/// `foo.save_changes(&conn)` is equivalent to
163/// `update(&foo).set(&foo).get_result(&conn)`.
164/// On other backends, two queries will be executed.
165///
166/// # Example
167///
168/// ```rust
169/// # include!("../doctest_setup.rs");
170/// # use schema::animals;
171/// #
172/// #[derive(Queryable, Debug, PartialEq)]
173/// struct Animal {
174///     id: i32,
175///     species: String,
176///     legs: i32,
177///     name: Option<String>,
178/// }
179///
180/// #[derive(AsChangeset, Identifiable)]
181/// #[diesel(table_name = animals)]
182/// struct AnimalForm<'a> {
183///     id: i32,
184///     name: &'a str,
185/// }
186///
187/// # fn main() {
188/// #     run_test();
189/// # }
190/// #
191/// # fn run_test() -> QueryResult<()> {
192/// #     use self::animals::dsl::*;
193/// #     let connection = &mut establish_connection();
194/// let form = AnimalForm {
195///     id: 2,
196///     name: "Super scary",
197/// };
198/// let changed_animal = form.save_changes(connection)?;
199/// let expected_animal = Animal {
200///     id: 2,
201///     species: String::from("spider"),
202///     legs: 8,
203///     name: Some(String::from("Super scary")),
204/// };
205/// assert_eq!(expected_animal, changed_animal);
206/// #     Ok(())
207/// # }
208/// ```
209pub trait SaveChangesDsl<Conn> {
210    /// See the trait documentation.
211    fn save_changes<T>(self, connection: &mut Conn) -> QueryResult<T>
212    where
213        Self: Sized,
214        Conn: UpdateAndFetchResults<Self, T>,
215    {
216        connection.update_and_fetch(self)
217    }
218}
219
220impl<T, Conn> SaveChangesDsl<Conn> for T where
221    T: Copy + AsChangeset<Target = <T as HasTable>::Table> + IntoUpdateTarget
222{
223}