diesel/query_builder/
query_id.rs

1use super::QueryFragment;
2use std::any::{Any, TypeId};
3
4/// Uniquely identifies queries by their type for the purpose of prepared
5/// statement caching.
6///
7/// All types which implement `QueryFragment` should also implement this trait
8/// (It is not an actual supertrait of `QueryFragment` for boxing purposes).
9///
10/// See the documentation of [the `QueryId` type] and [`HAS_STATIC_QUERY_ID`]
11/// for more details.
12///
13/// [the `QueryId` type]: #associatedtype.QueryId
14/// [`HAS_STATIC_QUERY_ID`]: #associatedconstant.HAS_STATIC_QUERY_ID
15///
16/// ### Deriving
17///
18/// This trait can be automatically derived by Diesel.
19/// For example, given this struct:
20///
21/// ```rust
22/// # #[macro_use] extern crate diesel;
23/// #[derive(QueryId)]
24/// pub struct And<Left, Right> {
25///     left: Left,
26///     right: Right,
27/// }
28/// # fn main() {}
29/// ```
30///
31/// the following implementation will be generated
32///
33/// ```rust,ignore
34/// impl<Left, Right> QueryId for And<Left, Right>
35/// where
36///     Left: QueryId,
37///     Right: QueryId,
38/// {
39///     type QueryId = And<Left::QueryId, Right::QueryId>;
40///
41///     const HAS_STATIC_QUERY_ID: bool = Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID;
42/// }
43/// ```
44///
45/// If the SQL generated by a struct is not uniquely identifiable by its type,
46/// meaning that `HAS_STATIC_QUERY_ID` should always be false,
47/// you should not derive this trait.
48/// In that case you should manually implement it instead.
49pub trait QueryId {
50    /// A type which uniquely represents `Self` in a SQL query.
51    ///
52    /// Typically this will be a re-construction of `Self` using the `QueryId`
53    /// type of each of your type parameters. For example, the type `And<Left,
54    /// Right>` would have `type QueryId = And<Left::QueryId, Right::QueryId>`.
55    ///
56    /// The exception to this is when one of your type parameters does not
57    /// affect whether the same prepared statement can be used or not. For
58    /// example, a bind parameter is represented as `Bound<SqlType, RustType>`.
59    /// The actual Rust type we are serializing does not matter for the purposes
60    /// of prepared statement reuse, but a query which has identical SQL but
61    /// different types for its bind parameters requires a new prepared
62    /// statement. For this reason, `Bound` would have `type QueryId =
63    /// Bound<SqlType::QueryId, ()>`.
64    ///
65    /// If `HAS_STATIC_QUERY_ID` is `false`, you can put any type here
66    /// (typically `()`).
67    type QueryId: Any;
68
69    /// Can the SQL generated by `Self` be uniquely identified by its type?
70    ///
71    /// Typically this question can be answered by looking at whether
72    /// `unsafe_to_cache_prepared` is called in your implementation of
73    /// `QueryFragment::walk_ast`. In Diesel itself, the only type which has
74    /// `false` here, but is potentially safe to store in the prepared statement
75    /// cache is a boxed query.
76    const HAS_STATIC_QUERY_ID: bool = true;
77
78    /// Returns the type id of `Self::QueryId` if `Self::HAS_STATIC_QUERY_ID`.
79    /// Returns `None` otherwise.
80    ///
81    /// You should never need to override this method.
82    fn query_id() -> Option<TypeId> {
83        if Self::HAS_STATIC_QUERY_ID {
84            Some(TypeId::of::<Self::QueryId>())
85        } else {
86            None
87        }
88    }
89}
90
91impl QueryId for () {
92    type QueryId = ();
93
94    const HAS_STATIC_QUERY_ID: bool = true;
95}
96
97impl<T: QueryId + ?Sized> QueryId for Box<T> {
98    type QueryId = T::QueryId;
99
100    const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
101}
102
103impl<'a, T: QueryId + ?Sized> QueryId for &'a T {
104    type QueryId = T::QueryId;
105
106    const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
107}
108
109impl<DB> QueryId for dyn QueryFragment<DB> {
110    type QueryId = ();
111
112    const HAS_STATIC_QUERY_ID: bool = false;
113}
114
115#[cfg(test)]
116mod tests {
117    use std::any::TypeId;
118
119    use super::QueryId;
120    use prelude::*;
121
122    table! {
123        users {
124            id -> Integer,
125            name -> VarChar,
126        }
127    }
128
129    fn query_id<T: QueryId>(_: T) -> Option<TypeId> {
130        T::query_id()
131    }
132
133    #[test]
134    fn queries_with_no_dynamic_elements_have_a_static_id() {
135        use self::users::dsl::*;
136        assert!(query_id(users).is_some());
137        assert!(query_id(users.select(name)).is_some());
138        assert!(query_id(users.filter(name.eq("Sean"))).is_some());
139    }
140
141    #[test]
142    fn queries_with_different_types_have_different_ids() {
143        let id1 = query_id(users::table.select(users::name));
144        let id2 = query_id(users::table.select(users::id));
145        assert_ne!(id1, id2);
146    }
147
148    #[test]
149    fn bind_params_use_only_sql_type_for_query_id() {
150        use self::users::dsl::*;
151        let id1 = query_id(users.filter(name.eq("Sean")));
152        let id2 = query_id(users.filter(name.eq("Tess".to_string())));
153
154        assert_eq!(id1, id2);
155    }
156
157    #[test]
158    #[cfg(features = "postgres")]
159    fn boxed_queries_do_not_have_static_query_id() {
160        use pg::Pg;
161        assert!(query_id(users::table.into_boxed::<Pg>()).is_none());
162    }
163}