Skip to main content

diesel/query_builder/
has_query.rs

1use super::group_by_clause::ValidGroupByClause;
2use super::{
3    BoxedCloneSelectStatement, BoxedSelectStatement, FromClause, NoFromClause, Query, QueryId,
4    SelectStatement,
5};
6use crate::backend::Backend;
7use crate::expression::ValidGrouping;
8use crate::query_dsl::methods::SelectDsl;
9use crate::{AppearsOnTable, QuerySource, SelectableHelper};
10pub use diesel_derives::HasQuery;
11
12/// Trait indicating that a base query can be constructed for this type
13///
14/// Types which implement `HasQuery` have a default query for loading
15/// the relevant data from the database associated with this type.
16///
17/// Consumers of this trait should use the `query()` associated function
18/// to construct a query including a matching select clause for their type
19///
20/// This trait can be [derived](derive@HasQuery)
21///
22/// It's important to note that for Diesel mappings between the database and rust types always happen
23/// on query and not on table level. This enables you to write several queries related to the
24/// same table, while a single query could be related to zero or multiple tables.
25///
26/// # Example
27///
28/// ## With derive
29///
30/// ```rust
31/// # extern crate diesel;
32/// # extern crate dotenvy;
33/// # include!("../doctest_setup.rs");
34/// #
35///
36/// // it's important to have the right table in scope
37/// use schema::users;
38///
39/// #[derive(HasQuery, PartialEq, Debug)]
40/// struct User {
41///     id: i32,
42///     name: String,
43/// }
44///
45/// # // Reading the query result requires reading memory allocated by the
46/// # // native sqlite library, which miri cannot track provenance for.
47/// # #[cfg(miri)]
48/// # fn main() {}
49/// # #[cfg(not(miri))]
50/// # fn main() -> QueryResult<()> {
51/// #
52/// #     let connection = &mut establish_connection();
53/// // equivalent to `users::table.select(User::as_select()).first(connection)?;
54/// let first_user = User::query().first(connection)?;
55/// let expected = User { id: 1, name: "Sean".into() };
56/// assert_eq!(expected, first_user);
57///
58/// #     Ok(())
59/// # }
60/// ```
61///
62/// ## Manual implementation
63///
64/// ```rust
65/// # extern crate diesel;
66/// # extern crate dotenvy;
67/// # include!("../doctest_setup.rs");
68/// #
69///
70/// // it's important to have the right table in scope
71/// use schema::users;
72///
73/// #[derive(Selectable, Queryable, PartialEq, Debug)]
74/// struct User {
75///     id: i32,
76///     name: String,
77/// }
78///
79/// impl<DB: diesel::backend::Backend> diesel::HasQuery<DB> for User {
80///    type BaseQuery = <users::table as diesel::query_builder::AsQuery>::Query;
81///
82///    // internal not stable method
83///    fn base_query() -> Self::BaseQuery {
84///        use diesel::query_builder::AsQuery;
85///        users::table.as_query()
86///    }
87/// }
88///
89/// # // Reading the query result requires reading memory allocated by the
90/// # // native sqlite library, which miri cannot track provenance for.
91/// # #[cfg(miri)]
92/// # fn main() {}
93/// # #[cfg(not(miri))]
94/// # fn main() -> QueryResult<()> {
95/// #
96/// #     let connection = &mut establish_connection();
97/// // equivalent to `users::table.select(User::as_select()).first(connection)?;
98/// let first_user = User::query().first(connection)?;
99/// let expected = User { id: 1, name: "Sean".into() };
100/// assert_eq!(expected, first_user);
101///
102/// #     Ok(())
103/// # }
104/// ```
105// Ideally we would have a `Queryable<Self::SelectExpression::SqlType, DB> as well here
106// but rustc breaks down if we do that
107// It claimns in that case that it isn't implemented,
108// while it is obviously implemented by the derive
109pub trait HasQuery<DB: Backend>:
110    SelectableHelper<
111    DB,
112    SelectExpression: QueryId
113            // these two bounds are here to get better error messages
114                          + AppearsOnTable<<Self::BaseQuery as AcceptedQueries>::From>
115                          + ValidGrouping<<Self::BaseQuery as AcceptedQueries>::GroupBy>,
116>
117{
118    /// Base query type defined by the implementing type
119    type BaseQuery: AcceptedQueries + SelectDsl<crate::dsl::AsSelect<Self, DB>, Output: Query>;
120
121    #[doc(hidden)] // that method is for internal use only
122    fn base_query() -> Self::BaseQuery;
123
124    /// Construct the query associated with this type
125    fn query() -> crate::dsl::Select<Self::BaseQuery, crate::dsl::AsSelect<Self, DB>> {
126        Self::base_query().select(Self::as_select())
127    }
128}
129
130use self::private::AcceptedQueries;
131
132mod private {
133    use super::*;
134    pub trait AcceptedQueries {
135        type From;
136        type GroupBy;
137    }
138
139    impl<S, D, W, O, LOf, GB, H, L> AcceptedQueries
140        for SelectStatement<NoFromClause, S, D, W, O, LOf, GB, H, L>
141    where
142        GB: ValidGroupByClause,
143    {
144        type From = NoFromClause;
145
146        type GroupBy = GB::Expressions;
147    }
148
149    impl<F, S, D, W, O, LOf, GB, H, L> AcceptedQueries
150        for SelectStatement<FromClause<F>, S, D, W, O, LOf, GB, H, L>
151    where
152        F: QuerySource,
153        GB: ValidGroupByClause,
154    {
155        type From = F;
156
157        type GroupBy = GB::Expressions;
158    }
159
160    impl<'a, ST, DB, GB> AcceptedQueries for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
161    where
162        GB: ValidGroupByClause,
163    {
164        type From = NoFromClause;
165
166        type GroupBy = GB::Expressions;
167    }
168
169    impl<'a, ST, F, DB, GB> AcceptedQueries for BoxedSelectStatement<'a, ST, FromClause<F>, DB, GB>
170    where
171        F: QuerySource,
172        GB: ValidGroupByClause,
173    {
174        type From = F;
175
176        type GroupBy = GB::Expressions;
177    }
178
179    impl<'a, ST, DB, GB> AcceptedQueries for BoxedCloneSelectStatement<'a, ST, NoFromClause, DB, GB>
180    where
181        GB: ValidGroupByClause,
182    {
183        type From = NoFromClause;
184
185        type GroupBy = GB::Expressions;
186    }
187
188    impl<'a, ST, F, DB, GB> AcceptedQueries for BoxedCloneSelectStatement<'a, ST, FromClause<F>, DB, GB>
189    where
190        F: QuerySource,
191        GB: ValidGroupByClause,
192    {
193        type From = F;
194
195        type GroupBy = GB::Expressions;
196    }
197}