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/// # fn main() -> QueryResult<()> {
46/// #
47/// # let connection = &mut establish_connection();
48/// // equivalent to `users::table.select(User::as_select()).first(connection)?;
49/// let first_user = User::query().first(connection)?;
50/// let expected = User { id: 1, name: "Sean".into() };
51/// assert_eq!(expected, first_user);
52///
53/// # Ok(())
54/// # }
55/// ```
56///
57/// ## Manual implementation
58///
59/// ```rust
60/// # extern crate diesel;
61/// # extern crate dotenvy;
62/// # include!("../doctest_setup.rs");
63/// #
64///
65/// // it's important to have the right table in scope
66/// use schema::users;
67///
68/// #[derive(Selectable, Queryable, PartialEq, Debug)]
69/// struct User {
70/// id: i32,
71/// name: String,
72/// }
73///
74/// impl<DB: diesel::backend::Backend> diesel::HasQuery<DB> for User {
75/// type BaseQuery = <users::table as diesel::query_builder::AsQuery>::Query;
76///
77/// // internal not stable method
78/// fn base_query() -> Self::BaseQuery {
79/// use diesel::query_builder::AsQuery;
80/// users::table.as_query()
81/// }
82/// }
83///
84/// # fn main() -> QueryResult<()> {
85/// #
86/// # let connection = &mut establish_connection();
87/// // equivalent to `users::table.select(User::as_select()).first(connection)?;
88/// let first_user = User::query().first(connection)?;
89/// let expected = User { id: 1, name: "Sean".into() };
90/// assert_eq!(expected, first_user);
91///
92/// # Ok(())
93/// # }
94/// ```
95// Ideally we would have a `Queryable<Self::SelectExpression::SqlType, DB> as well here
96// but rustc breaks down if we do that
97// It claimns in that case that it isn't implemented,
98// while it is obviously implemented by the derive
99pub trait HasQuery<DB: Backend>:
100 SelectableHelper<
101 DB,
102 SelectExpression: QueryId
103 // these two bounds are here to get better error messages
104 + AppearsOnTable<<Self::BaseQuery as AcceptedQueries>::From>
105 + ValidGrouping<<Self::BaseQuery as AcceptedQueries>::GroupBy>,
106>
107{
108 /// Base query type defined by the implementing type
109 type BaseQuery: AcceptedQueries + SelectDsl<crate::dsl::AsSelect<Self, DB>, Output: Query>;
110
111 #[doc(hidden)] // that method is for internal use only
112 fn base_query() -> Self::BaseQuery;
113
114 /// Construct the query associated with this type
115 fn query() -> crate::dsl::Select<Self::BaseQuery, crate::dsl::AsSelect<Self, DB>> {
116 Self::base_query().select(Self::as_select())
117 }
118}
119
120use self::private::AcceptedQueries;
121
122mod private {
123 use super::*;
124 pub trait AcceptedQueries {
125 type From;
126 type GroupBy;
127 }
128
129 impl<S, D, W, O, LOf, GB, H, L> AcceptedQueries
130 for SelectStatement<NoFromClause, S, D, W, O, LOf, GB, H, L>
131 where
132 GB: ValidGroupByClause,
133 {
134 type From = NoFromClause;
135
136 type GroupBy = GB::Expressions;
137 }
138
139 impl<F, S, D, W, O, LOf, GB, H, L> AcceptedQueries
140 for SelectStatement<FromClause<F>, S, D, W, O, LOf, GB, H, L>
141 where
142 F: QuerySource,
143 GB: ValidGroupByClause,
144 {
145 type From = F;
146
147 type GroupBy = GB::Expressions;
148 }
149
150 impl<'a, ST, DB, GB> AcceptedQueries for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
151 where
152 GB: ValidGroupByClause,
153 {
154 type From = NoFromClause;
155
156 type GroupBy = GB::Expressions;
157 }
158
159 impl<'a, ST, F, DB, GB> AcceptedQueries for BoxedSelectStatement<'a, ST, FromClause<F>, DB, GB>
160 where
161 F: QuerySource,
162 GB: ValidGroupByClause,
163 {
164 type From = F;
165
166 type GroupBy = GB::Expressions;
167 }
168
169 impl<'a, ST, DB, GB> AcceptedQueries for BoxedCloneSelectStatement<'a, ST, NoFromClause, DB, GB>
170 where
171 GB: ValidGroupByClause,
172 {
173 type From = NoFromClause;
174
175 type GroupBy = GB::Expressions;
176 }
177
178 impl<'a, ST, F, DB, GB> AcceptedQueries for BoxedCloneSelectStatement<'a, ST, FromClause<F>, DB, GB>
179 where
180 F: QuerySource,
181 GB: ValidGroupByClause,
182 {
183 type From = F;
184
185 type GroupBy = GB::Expressions;
186 }
187}