diesel/query_builder/
query_id.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use super::QueryFragment;
use std::any::{Any, TypeId};

/// Uniquely identifies queries by their type for the purpose of prepared
/// statement caching.
///
/// All types which implement `QueryFragment` should also implement this trait
/// (It is not an actual supertrait of `QueryFragment` for boxing purposes).
///
/// See the documentation of [the `QueryId` type] and [`HAS_STATIC_QUERY_ID`]
/// for more details.
///
/// [the `QueryId` type]: QueryId::QueryId
/// [`HAS_STATIC_QUERY_ID`]: QueryId::HAS_STATIC_QUERY_ID
///
/// ### Deriving
///
/// This trait can [be automatically derived](derive@QueryId)
/// by Diesel.
/// For example, given this struct:
///
/// If the SQL generated by a struct is not uniquely identifiable by its type,
/// meaning that `HAS_STATIC_QUERY_ID` should always be false,
/// you should not derive this trait.
/// In that case you should manually implement it instead.
pub trait QueryId {
    /// A type which uniquely represents `Self` in a SQL query.
    ///
    /// Typically this will be a re-construction of `Self` using the `QueryId`
    /// type of each of your type parameters. For example, the type `And<Left,
    /// Right>` would have `type QueryId = And<Left::QueryId, Right::QueryId>`.
    ///
    /// The exception to this is when one of your type parameters does not
    /// affect whether the same prepared statement can be used or not. For
    /// example, a bind parameter is represented as `Bound<SqlType, RustType>`.
    /// The actual Rust type we are serializing does not matter for the purposes
    /// of prepared statement reuse, but a query which has identical SQL but
    /// different types for its bind parameters requires a new prepared
    /// statement. For this reason, `Bound` would have `type QueryId =
    /// Bound<SqlType::QueryId, ()>`.
    ///
    /// If `HAS_STATIC_QUERY_ID` is `false`, you can put any type here
    /// (typically `()`).
    type QueryId: Any;

    /// Can the SQL generated by `Self` be uniquely identified by its type?
    ///
    /// Typically this question can be answered by looking at whether
    /// `unsafe_to_cache_prepared` is called in your implementation of
    /// `QueryFragment::walk_ast`. In Diesel itself, the only type which has
    /// `false` here, but is potentially safe to store in the prepared statement
    /// cache is a boxed query.
    const HAS_STATIC_QUERY_ID: bool = true;

    /// Returns the type id of `Self::QueryId` if `Self::HAS_STATIC_QUERY_ID`.
    /// Returns `None` otherwise.
    ///
    /// You should never need to override this method.
    fn query_id() -> Option<TypeId> {
        if Self::HAS_STATIC_QUERY_ID {
            Some(TypeId::of::<Self::QueryId>())
        } else {
            None
        }
    }
}

#[doc(inline)]
pub use diesel_derives::QueryId;

impl QueryId for () {
    type QueryId = ();

    const HAS_STATIC_QUERY_ID: bool = true;
}

impl<T: QueryId + ?Sized> QueryId for Box<T> {
    type QueryId = T::QueryId;

    const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
}

impl<'a, T: QueryId + ?Sized> QueryId for &'a T {
    type QueryId = T::QueryId;

    const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
}

impl<DB> QueryId for dyn QueryFragment<DB> {
    type QueryId = ();

    const HAS_STATIC_QUERY_ID: bool = false;
}

#[cfg(test)]
#[allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved.
mod tests {
    use std::any::TypeId;

    use super::QueryId;
    use crate::prelude::*;

    table! {
        users {
            id -> Integer,
            name -> VarChar,
        }
    }

    fn query_id<T: QueryId>(_: T) -> Option<TypeId> {
        T::query_id()
    }

    #[test]
    fn queries_with_no_dynamic_elements_have_a_static_id() {
        use self::users::dsl::*;
        assert!(query_id(users).is_some());
        assert!(query_id(users.select(name)).is_some());
        assert!(query_id(users.filter(name.eq("Sean"))).is_some());
    }

    #[test]
    fn queries_with_different_types_have_different_ids() {
        let id1 = query_id(users::table.select(users::name));
        let id2 = query_id(users::table.select(users::id));
        assert_ne!(id1, id2);
    }

    #[test]
    fn bind_params_use_only_sql_type_for_query_id() {
        use self::users::dsl::*;
        let id1 = query_id(users.filter(name.eq("Sean")));
        let id2 = query_id(users.filter(name.eq("Tess".to_string())));

        assert_eq!(id1, id2);
    }

    #[test]
    #[cfg(feature = "postgres")]
    fn boxed_queries_do_not_have_static_query_id() {
        use crate::pg::Pg;
        assert!(query_id(users::table.into_boxed::<Pg>()).is_none());
    }
}