diesel/query_dsl/
save_changes_dsl.rs

1use associations::HasTable;
2#[cfg(any(feature = "sqlite", feature = "mysql"))]
3use associations::Identifiable;
4use connection::Connection;
5#[cfg(any(feature = "sqlite", feature = "mysql"))]
6use dsl::Find;
7#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))]
8use dsl::Update;
9use query_builder::{AsChangeset, IntoUpdateTarget};
10#[cfg(any(feature = "sqlite", feature = "mysql"))]
11use query_dsl::methods::{ExecuteDsl, FindDsl};
12#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))]
13use query_dsl::{LoadQuery, RunQueryDsl};
14use result::QueryResult;
15
16/// A trait defining how to update a record and fetch the updated entry
17/// on a certain backend.
18///
19/// The only case where it is required to work with this trait is while
20/// implementing a new connection type.
21/// Otherwise use [`SaveChangesDsl`](trait.SaveChangesDsl.html)
22///
23/// For implementing this trait for a custom backend:
24/// * The `Changes` generic parameter represents the changeset that should be stored
25/// * The `Output` generic parameter represents the type of the response.
26pub trait UpdateAndFetchResults<Changes, Output>: Connection {
27    /// See the traits documentation.
28    fn update_and_fetch(&self, changeset: Changes) -> QueryResult<Output>;
29}
30
31#[cfg(feature = "postgres")]
32use pg::PgConnection;
33
34#[cfg(feature = "postgres")]
35impl<Changes, Output> UpdateAndFetchResults<Changes, Output> for PgConnection
36where
37    Changes: Copy + AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
38    Update<Changes, Changes>: LoadQuery<PgConnection, Output>,
39{
40    fn update_and_fetch(&self, changeset: Changes) -> QueryResult<Output> {
41        ::update(changeset).set(changeset).get_result(self)
42    }
43}
44
45#[cfg(feature = "sqlite")]
46use sqlite::SqliteConnection;
47
48#[cfg(feature = "sqlite")]
49impl<Changes, Output> UpdateAndFetchResults<Changes, Output> for SqliteConnection
50where
51    Changes: Copy + Identifiable,
52    Changes: AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
53    Changes::Table: FindDsl<Changes::Id>,
54    Update<Changes, Changes>: ExecuteDsl<SqliteConnection>,
55    Find<Changes::Table, Changes::Id>: LoadQuery<SqliteConnection, Output>,
56{
57    fn update_and_fetch(&self, changeset: Changes) -> QueryResult<Output> {
58        ::update(changeset).set(changeset).execute(self)?;
59        Changes::table().find(changeset.id()).get_result(self)
60    }
61}
62
63#[cfg(feature = "mysql")]
64use mysql::MysqlConnection;
65
66#[cfg(feature = "mysql")]
67impl<Changes, Output> UpdateAndFetchResults<Changes, Output> for MysqlConnection
68where
69    Changes: Copy + Identifiable,
70    Changes: AsChangeset<Target = <Changes as HasTable>::Table> + IntoUpdateTarget,
71    Changes::Table: FindDsl<Changes::Id>,
72    Update<Changes, Changes>: ExecuteDsl<MysqlConnection>,
73    Find<Changes::Table, Changes::Id>: LoadQuery<MysqlConnection, Output>,
74{
75    fn update_and_fetch(&self, changeset: Changes) -> QueryResult<Output> {
76        ::update(changeset).set(changeset).execute(self)?;
77        Changes::table().find(changeset.id()).get_result(self)
78    }
79}
80
81/// Sugar for types which implement both `AsChangeset` and `Identifiable`
82///
83/// On backends which support the `RETURNING` keyword,
84/// `foo.save_changes(&conn)` is equivalent to
85/// `update(&foo).set(&foo).get_result(&conn)`.
86/// On other backends, two queries will be executed.
87///
88/// # Example
89///
90/// ```rust
91/// # #[macro_use] extern crate diesel;
92/// # include!("../doctest_setup.rs");
93/// # use schema::animals;
94/// #
95/// #[derive(Queryable, Debug, PartialEq)]
96/// struct Animal {
97///    id: i32,
98///    species: String,
99///    legs: i32,
100///    name: Option<String>,
101/// }
102///
103/// #[derive(AsChangeset, Identifiable)]
104/// #[table_name = "animals"]
105/// struct AnimalForm<'a> {
106///     id: i32,
107///     name: &'a str,
108/// }
109///
110/// # fn main() {
111/// #     run_test();
112/// # }
113/// #
114/// # fn run_test() -> QueryResult<()> {
115/// #     use animals::dsl::*;
116/// #     let connection = establish_connection();
117/// let form = AnimalForm { id: 2, name: "Super scary" };
118/// let changed_animal = form.save_changes(&connection)?;
119/// let expected_animal = Animal {
120///     id: 2,
121///     species: String::from("spider"),
122///     legs: 8,
123///     name: Some(String::from("Super scary")),
124/// };
125/// assert_eq!(expected_animal, changed_animal);
126/// #     Ok(())
127/// # }
128/// ```
129pub trait SaveChangesDsl<Conn> {
130    /// See the trait documentation.
131    fn save_changes<T>(self, connection: &Conn) -> QueryResult<T>
132    where
133        Self: Sized,
134        Conn: UpdateAndFetchResults<Self, T>,
135    {
136        connection.update_and_fetch(self)
137    }
138}
139
140impl<T, Conn> SaveChangesDsl<Conn> for T where
141    T: Copy + AsChangeset<Target = <T as HasTable>::Table> + IntoUpdateTarget
142{
143}