Skip to main content

diesel/connection/statement_cache/
mod.rs

1//! Helper types for prepared statement caching
2//!
3//! A primer on prepared statement caching in Diesel
4//! ------------------------------------------------
5//!
6//! Diesel uses prepared statements for virtually all queries. This is most
7//! visible in our lack of any sort of "quoting" API. Values must always be
8//! transmitted as bind parameters, we do not support direct interpolation. The
9//! only method in the public API that doesn't require the use of prepared
10//! statements is [`SimpleConnection::batch_execute`](super::SimpleConnection::batch_execute).
11//!
12//! In order to avoid the cost of re-parsing and planning subsequent queries,
13//! by default Diesel caches the prepared statement whenever possible. This
14//! can be customized by calling
15//! [`Connection::set_cache_size`](super::Connection::set_prepared_statement_cache_size).
16//!
17//! Queries will fall into one of three buckets:
18//!
19//! - Unsafe to cache
20//! - Cached by SQL
21//! - Cached by type
22//!
23//! A query is considered unsafe to cache if it represents a potentially
24//! unbounded number of queries. This is communicated to the connection through
25//! [`QueryFragment::is_safe_to_cache_prepared`]. While this is done as a full AST
26//! pass, after monomorphisation and inlining this will usually be optimized to
27//! a constant. Only boxed queries will need to do actual work to answer this
28//! question.
29//!
30//! The majority of AST nodes are safe to cache if their components are safe to
31//! cache. There are at least 4 cases where a query is unsafe to cache:
32//!
33//! - queries containing `IN` with bind parameters
34//!     - This requires 1 bind parameter per value, and is therefore unbounded
35//!     - `IN` with subselects are cached (assuming the subselect is safe to
36//!       cache)
37//!     - `IN` statements for postgresql are cached as they use `= ANY($1)` instead
38//!       which does not cause an unbound number of binds
39//! - `INSERT` statements with a variable number of rows
40//!     - The SQL varies based on the number of rows being inserted.
41//! - `UPDATE` statements
42//!     - Technically it's bounded on "number of optional values being passed to
43//!       `SET` factorial" but that's still quite high, and not worth caching
44//!       for the same reason as single row inserts
45//! - `SqlLiteral` nodes
46//!     - We have no way of knowing whether the SQL was generated dynamically or
47//!       not, so we must assume that it's unbounded
48//!
49//! For queries which are unsafe to cache, the statement cache will never insert
50//! them. They will be prepared and immediately released after use (or in the
51//! case of PG they will use the unnamed prepared statement).
52//!
53//! For statements which are able to be cached, we then have to determine what
54//! to use as the cache key. The standard method that virtually all ORMs or
55//! database access layers use in the wild is to store the statements in a
56//! hash map, using the SQL as the key.
57//!
58//! However, the majority of queries using Diesel that are safe to cache as
59//! prepared statements will be uniquely identified by their type. For these
60//! queries, we can bypass the query builder entirely. Since our AST is
61//! generally optimized away by the compiler, for these queries the cost of
62//! fetching a prepared statement from the cache is the cost of [`HashMap<u32,
63//! _>::get`](std::collections::HashMap::get), where the key we're fetching by is a compile time constant. For
64//! these types, the AST pass to gather the bind parameters will also be
65//! optimized to accessing each parameter individually.
66//!
67//! Determining if a query can be cached by type is the responsibility of the
68//! [`QueryId`] trait. This trait is quite similar to `Any`, but with a few
69//! differences:
70//!
71//! - No `'static` bound
72//!     - Something being a reference never changes the SQL that is generated,
73//!       so `&T` has the same query id as `T`.
74//! - `Option<TypeId>` instead of `TypeId`
75//!     - We need to be able to constrain on this trait being implemented, but
76//!       not all types will actually have a static query id. Hopefully once
77//!       specialization is stable we can remove the `QueryId` bound and
78//!       specialize on it instead (or provide a blanket impl for all `T`)
79//! - Implementors give a more broad type than `Self`
80//!     - This really only affects bind parameters. There are 6 different Rust
81//!       types which can be used for a parameter of type `timestamp`. The same
82//!       statement can be used regardless of the Rust type, so [`Bound<ST, T>`](crate::expression::bound::Bound)
83//!       defines its [`QueryId`] as [`Bound<ST, ()>`](crate::expression::bound::Bound).
84//!
85//! A type returning `Some(id)` or `None` for its query ID is based on whether
86//! the SQL it generates can change without the type changing. At the moment,
87//! the only type which is safe to cache as a prepared statement but does not
88//! have a static query ID is something which has been boxed.
89//!
90//! One potential optimization that we don't perform is storing the queries
91//! which are cached by type ID in a separate map. Since a type ID is a u64,
92//! this would allow us to use a specialized map which knows that there will
93//! never be hashing collisions (also known as a perfect hashing function),
94//! which would mean lookups are always constant time. However, this would save
95//! nanoseconds on an operation that will take microseconds or even
96//! milliseconds.
97
98use crate::util::std_compat::Entry;
99use alloc::borrow::Cow;
100use alloc::boxed::Box;
101use alloc::string::String;
102use alloc::vec::Vec;
103use core::any::TypeId;
104use core::hash::Hash;
105use core::ops::{Deref, DerefMut};
106
107use strategy::{
108    LookupStatementResult, StatementCacheStrategy, WithCacheStrategy, WithoutCacheStrategy,
109};
110
111use crate::backend::Backend;
112use crate::connection::InstrumentationEvent;
113use crate::query_builder::*;
114use crate::result::QueryResult;
115
116use super::{CacheSize, Instrumentation};
117
118/// Various interfaces and implementations to control connection statement caching.
119#[allow(unreachable_pub)]
120pub mod strategy;
121
122/// A prepared statement cache
123#[allow(missing_debug_implementations, unreachable_pub)]
124#[cfg_attr(
125    diesel_docsrs,
126    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
127)]
128pub struct StatementCache<DB: Backend, Statement> {
129    cache: Box<dyn StatementCacheStrategy<DB, Statement>>,
130    // increment every time a query is cached
131    // some backends might use it to create unique prepared statement names
132    cache_counter: u64,
133}
134
135/// A helper type that indicates if a certain query
136/// is cached inside of the prepared statement cache or not
137///
138/// This information can be used by the connection implementation
139/// to signal this fact to the database while actually
140/// preparing the statement
141#[derive(#[automatically_derived]#[allow(unreachable_pub)]
impl ::core::fmt::Debug for PrepareForCache {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Yes { counter: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "Yes counter", &__self_0),
            Self::No => ::core::fmt::Formatter::write_str(f, "No"),
        }
    }
}Debug, #[automatically_derived]#[doc(hidden)]#[allow(unreachable_pub)]
unsafe impl ::core::clone::TrivialClone for PrepareForCache { }
#[automatically_derived]#[allow(unreachable_pub)]
impl ::core::clone::Clone for PrepareForCache {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u64>;
        *self
    }
}Clone, #[automatically_derived]#[allow(unreachable_pub)]
impl ::core::marker::Copy for PrepareForCache { }Copy)]
142#[cfg_attr(
143    diesel_docsrs,
144    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
145)]
146#[allow(unreachable_pub)]
147pub enum PrepareForCache {
148    /// The statement will be cached
149    Yes {
150        /// Counter might be used as unique identifier for prepared statement.
151        #[allow(dead_code)]
152        counter: u64,
153    },
154    /// The statement won't be cached
155    No,
156}
157
158/// A trait marking a backend specific prepared statement type
159#[cfg_attr(
160    diesel_docsrs,
161    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
162)]
163#[allow(unreachable_pub)]
164pub trait PreparedStatement: Send + 'static {
165    /// Number of parameters for this prepared statement
166    ///
167    /// This is used for consistency checks in the prepared statement cache to not
168    /// accidentally reuse an existing prepared statement with the wrong number of
169    /// bind parameters
170    fn parameter_count(&self) -> u64;
171}
172
173#[allow(clippy::new_without_default, unreachable_pub)]
174impl<DB, Statement> StatementCache<DB, Statement>
175where
176    DB: Backend + 'static,
177    Statement: PreparedStatement,
178    DB::TypeMetadata: Send + Clone,
179    DB::QueryBuilder: Default,
180    StatementCacheKey<DB>: Hash + Eq,
181{
182    /// Create a new prepared statement cache using [`CacheSize::Unbounded`] as caching strategy.
183    #[allow(unreachable_pub)]
184    pub fn new() -> Self {
185        StatementCache {
186            cache: Box::new(WithCacheStrategy::default()),
187            cache_counter: 0,
188        }
189    }
190
191    /// Set caching strategy from predefined implementations
192    pub fn set_cache_size(&mut self, size: CacheSize) {
193        if self.cache.cache_size() != size {
194            self.cache = match size {
195                CacheSize::Unbounded => Box::new(WithCacheStrategy::default()),
196                CacheSize::Disabled => Box::new(WithoutCacheStrategy::default()),
197            }
198        }
199    }
200
201    /// Removes all cached statements so subsequent queries are re-prepared,
202    /// while keeping the configured caching strategy.
203    // Currently used only by the SQLite authorizer, so it is compiled only for
204    // backends that provide that caller.
205    #[cfg(feature = "__sqlite-shared")]
206    pub(crate) fn clear(&mut self) {
207        self.cache.clear();
208    }
209
210    /// Setting custom caching strategy. It is used in tests, to verify caching logic
211    #[allow(dead_code)]
212    pub(crate) fn set_strategy<Strategy>(&mut self, s: Strategy)
213    where
214        Strategy: StatementCacheStrategy<DB, Statement> + 'static,
215    {
216        self.cache = Box::new(s);
217    }
218
219    /// Prepare a query as prepared statement
220    ///
221    /// This functions returns a prepared statement corresponding to the
222    /// query passed as `source` with the bind values passed as `bind_types`.
223    /// If the query is already cached inside this prepared statement cache
224    /// the cached prepared statement will be returned, otherwise `prepare_fn`
225    /// will be called to create a new prepared statement for this query source.
226    /// The first parameter of the callback contains the query string, the second
227    /// parameter indicates if the constructed prepared statement will be cached or not.
228    /// See the [module](self) documentation for details
229    /// about which statements are cached and which are not cached.
230    //
231    // Notes:
232    // This function takes explicitly a connection and a function pointer (and no generic callback)
233    // as argument to ensure that we don't leak generic query types into the prepare function
234    #[allow(unreachable_pub)]
235    #[cfg(any(
236        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
237        feature = "__sqlite-shared",
238        feature = "mysql",
239        feature = "mariadb"
240    ))]
241    pub fn cached_statement<'a, T, R, C>(
242        &'a mut self,
243        source: &T,
244        backend: &DB,
245        bind_types: &[DB::TypeMetadata],
246        conn: C,
247        prepare_fn: fn(C, &str, PrepareForCache, &[DB::TypeMetadata]) -> R,
248        instrumentation: &mut dyn Instrumentation,
249    ) -> R::Return<'a>
250    where
251        T: QueryFragment<DB> + QueryId,
252        R: StatementCallbackReturnType<Statement, C> + 'a,
253    {
254        self.cached_statement_non_generic(
255            T::query_id(),
256            source,
257            backend,
258            bind_types,
259            conn,
260            prepare_fn,
261            instrumentation,
262        )
263    }
264
265    /// Prepare a query as prepared statement
266    ///
267    /// This function closely mirrors `Self::cached_statement` but
268    /// eliminates the generic query type in favour of a trait object
269    ///
270    /// This can be easier to use in situations where you already turned
271    /// the query type into a concrete SQL string
272    // Notes:
273    // This function takes explicitly a connection and a function pointer (and no generic callback)
274    // as argument to ensure that we don't leak generic query types into the prepare function
275    #[allow(unreachable_pub)]
276    #[allow(clippy::too_many_arguments)] // we need all of them
277    pub fn cached_statement_non_generic<'a, R, C>(
278        &'a mut self,
279        maybe_type_id: Option<TypeId>,
280        source: &dyn QueryFragmentForCachedStatement<DB>,
281        backend: &DB,
282        bind_types: &[DB::TypeMetadata],
283        conn: C,
284        prepare_fn: fn(C, &str, PrepareForCache, &[DB::TypeMetadata]) -> R,
285        instrumentation: &mut dyn Instrumentation,
286    ) -> R::Return<'a>
287    where
288        R: StatementCallbackReturnType<Statement, C> + 'a,
289    {
290        Self::cached_statement_non_generic_impl(
291            self.cache.as_mut(),
292            maybe_type_id,
293            source,
294            backend,
295            bind_types,
296            conn,
297            |conn, sql, is_cached| {
298                if is_cached {
299                    instrumentation.on_connection_event(InstrumentationEvent::CacheQuery { sql });
300                    self.cache_counter += 1;
301                    prepare_fn(
302                        conn,
303                        sql,
304                        PrepareForCache::Yes {
305                            counter: self.cache_counter,
306                        },
307                        bind_types,
308                    )
309                } else {
310                    prepare_fn(conn, sql, PrepareForCache::No, bind_types)
311                }
312            },
313        )
314    }
315
316    /// Reduce the amount of monomorphized code by factoring this via dynamic dispatch
317    /// There will be only one instance of `R` for diesel (and a different single instance for diesel-async)
318    /// There will be only a instance per connection type `C` for each connection that
319    /// uses this prepared statement impl, this closely correlates to the types `DB` and `Statement`
320    /// for the overall statement cache impl
321    fn cached_statement_non_generic_impl<'a, R, C>(
322        cache: &'a mut dyn StatementCacheStrategy<DB, Statement>,
323        maybe_type_id: Option<TypeId>,
324        source: &dyn QueryFragmentForCachedStatement<DB>,
325        backend: &DB,
326        bind_types: &[DB::TypeMetadata],
327        conn: C,
328        prepare_fn: impl FnOnce(C, &str, bool) -> R,
329    ) -> R::Return<'a>
330    where
331        R: StatementCallbackReturnType<Statement, C> + 'a,
332    {
333        // this function cannot use the `?` operator
334        // as we want to abstract over returning `QueryResult<MaybeCached>` and
335        // `impl Future<Output = QueryResult<MaybeCached>>` here
336        // to share the prepared statement cache implementation between diesel and
337        // diesel_async
338        //
339        // For this reason we need to match explicitly on each error and call `R::from_error()`
340        // to construct the right error return variant
341        let cache_key =
342            match StatementCacheKey::for_source(maybe_type_id, source, bind_types, backend) {
343                Ok(o) => o,
344                Err(e) => return R::from_error(e),
345            };
346        let is_safe_to_cache_prepared = match source.is_safe_to_cache_prepared(backend) {
347            Ok(o) => o,
348            Err(e) => return R::from_error(e),
349        };
350        // early return if the statement cannot be cached
351        if !is_safe_to_cache_prepared {
352            let sql = match cache_key.sql(source, backend) {
353                Ok(sql) => sql,
354                Err(e) => return R::from_error(e),
355            };
356            return prepare_fn(conn, &sql, false).map_to_no_cache();
357        }
358        let entry = cache.lookup_statement(cache_key);
359        match entry {
360            // The statement is already cached
361            LookupStatementResult::CacheEntry(Entry::Occupied(e)) => {
362                let stmt = e.into_mut();
363                let param_count = match source.parameter_count(backend) {
364                    Ok(o) => o,
365                    Err(e) => return R::from_error(e),
366                };
367                // verify that the cached statement uses the same number of bind parameters
368                // than the provided query, otherwise something went wrong with
369                // our statement caching and we rather fail than produce invalid results
370                if stmt.parameter_count()
371                    == u64::try_from(param_count).expect("usize is not larger than 64 bits")
372                {
373                    R::map_to_cache(stmt, conn)
374                } else {
375                    R::from_error(invalid_prepared_statement_parameter_count(
376                        stmt.parameter_count().try_into().unwrap_or(usize::MAX),
377                        param_count,
378                    ))
379                }
380            }
381            // The statement is not cached but there is capacity to cache it
382            LookupStatementResult::CacheEntry(Entry::Vacant(e)) => {
383                let sql = match e.key().sql(source, backend) {
384                    Ok(sql) => sql,
385                    Err(e) => return R::from_error(e),
386                };
387                let st = prepare_fn(conn, &sql, true);
388                st.register_cache(|stmt| e.insert(stmt))
389            }
390            // The statement is not cached and there is no capacity to cache it
391            LookupStatementResult::NoCache(cache_key) => {
392                let sql = match cache_key.sql(source, backend) {
393                    Ok(sql) => sql,
394                    Err(e) => return R::from_error(e),
395                };
396                prepare_fn(conn, &sql, false).map_to_no_cache()
397            }
398        }
399    }
400}
401
402/// Implemented for all `QueryFragment`s, dedicated to dynamic dispatch within the context of
403/// `statement_cache`
404///
405/// We want the generated code to be as small as possible, so for each query passed to
406/// [`StatementCache::cached_statement`] the generated assembly will just call a non generic
407/// version with dynamic dispatch pointing to the VTABLE of this minimal trait
408///
409/// This preserves the opportunity for the compiler to entirely optimize the `construct_sql`
410/// function as a function that simply returns a constant `String`.
411#[allow(unreachable_pub)]
412#[cfg_attr(
413    diesel_docsrs,
414    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
415)]
416pub trait QueryFragmentForCachedStatement<DB> {
417    /// Convert the query fragment into a SQL string for the given backend
418    fn construct_sql(&self, backend: &DB) -> QueryResult<String>;
419
420    /// Check whether it's safe to cache the query
421    fn is_safe_to_cache_prepared(&self, backend: &DB) -> QueryResult<bool>;
422
423    /// Count the number of bind parameters in a given statement
424    fn parameter_count(&self, backend: &DB) -> QueryResult<usize>;
425}
426
427impl<T, DB> QueryFragmentForCachedStatement<DB> for T
428where
429    DB: Backend,
430    DB::QueryBuilder: Default,
431    T: QueryFragment<DB>,
432{
433    fn construct_sql(&self, backend: &DB) -> QueryResult<String> {
434        let mut query_builder = DB::QueryBuilder::default();
435        self.to_sql(&mut query_builder, backend)?;
436        Ok(query_builder.finish())
437    }
438
439    fn is_safe_to_cache_prepared(&self, backend: &DB) -> QueryResult<bool> {
440        <T as QueryFragment<DB>>::is_safe_to_cache_prepared(self, backend)
441    }
442
443    fn parameter_count(&self, backend: &DB) -> QueryResult<usize> {
444        <T as QueryFragment<DB>>::count_binds(self, backend)
445    }
446}
447
448/// Wraps a possibly cached prepared statement
449///
450/// Essentially a customized version of [`Cow`]
451/// that does not depend on [`ToOwned`]
452#[allow(missing_debug_implementations, unreachable_pub)]
453#[cfg_attr(
454    diesel_docsrs,
455    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
456)]
457#[non_exhaustive]
458pub enum MaybeCached<'a, T: 'a> {
459    /// Contains a not cached prepared statement
460    CannotCache(T),
461    /// Contains a reference cached prepared statement
462    Cached(&'a mut T),
463}
464
465/// This trait abstracts over the type returned by the prepare statement function
466///
467/// The main use-case for this abstraction is to share the same statement cache implementation
468/// between diesel and diesel-async.
469#[cfg_attr(
470    diesel_docsrs,
471    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
472)]
473#[allow(unreachable_pub)]
474pub trait StatementCallbackReturnType<S: 'static, C> {
475    /// The return type of `StatementCache::cached_statement`
476    ///
477    /// Either a `QueryResult<MaybeCached<S>>` or a future of that result type
478    type Return<'a>;
479
480    /// Create the return type from an error
481    fn from_error<'a>(e: diesel::result::Error) -> Self::Return<'a>;
482
483    /// Map the callback return type to the `MaybeCached::CannotCache` variant
484    fn map_to_no_cache<'a>(self) -> Self::Return<'a>
485    where
486        Self: 'a;
487
488    /// Map the cached statement to the `MaybeCached::Cached` variant
489    fn map_to_cache(stmt: &mut S, conn: C) -> Self::Return<'_>;
490
491    /// Insert the created statement into the cache via the provided callback
492    /// and then turn the returned reference into `MaybeCached::Cached`
493    fn register_cache<'a>(
494        self,
495        callback: impl FnOnce(S) -> &'a mut S + Send + 'a,
496    ) -> Self::Return<'a>
497    where
498        Self: 'a;
499}
500
501impl<S, C> StatementCallbackReturnType<S, C> for QueryResult<S>
502where
503    S: 'static,
504{
505    type Return<'a> = QueryResult<MaybeCached<'a, S>>;
506
507    fn from_error<'a>(e: diesel::result::Error) -> Self::Return<'a> {
508        Err(e)
509    }
510
511    fn map_to_no_cache<'a>(self) -> Self::Return<'a> {
512        self.map(MaybeCached::CannotCache)
513    }
514
515    fn map_to_cache(stmt: &mut S, _conn: C) -> Self::Return<'_> {
516        Ok(MaybeCached::Cached(stmt))
517    }
518
519    fn register_cache<'a>(
520        self,
521        callback: impl FnOnce(S) -> &'a mut S + Send + 'a,
522    ) -> Self::Return<'a>
523    where
524        Self: 'a,
525    {
526        Ok(MaybeCached::Cached(callback(self?)))
527    }
528}
529
530impl<T> Deref for MaybeCached<'_, T> {
531    type Target = T;
532
533    fn deref(&self) -> &Self::Target {
534        match *self {
535            MaybeCached::CannotCache(ref x) => x,
536            MaybeCached::Cached(ref x) => x,
537        }
538    }
539}
540
541impl<T> DerefMut for MaybeCached<'_, T> {
542    fn deref_mut(&mut self) -> &mut Self::Target {
543        match *self {
544            MaybeCached::CannotCache(ref mut x) => x,
545            MaybeCached::Cached(ref mut x) => x,
546        }
547    }
548}
549
550/// The lookup key used by [`StatementCache`] internally
551///
552/// This can contain either a at compile time known type id
553/// (representing a statically known query) or a at runtime
554/// calculated query string + parameter types (for queries
555/// that may change depending on their parameters)
556#[allow(missing_debug_implementations, unreachable_pub)]
557#[derive(#[automatically_derived]#[allow(missing_debug_implementations,
unreachable_pub)]
impl<DB: ::core::hash::Hash + Backend> ::core::hash::Hash for
    StatementCacheKey<DB> where DB::TypeMetadata: ::core::hash::Hash {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
            state);
        match self {
            Self::Type(__self_0) => ::core::hash::Hash::hash(__self_0, state),
            Self::Sql { sql: __self_0, bind_types: __self_1 } => {
                ::core::hash::Hash::hash(__self_0, state);
                ::core::hash::Hash::hash(__self_1, state)
            }
        }
    }
}Hash, #[automatically_derived]#[allow(missing_debug_implementations,
unreachable_pub)]
impl<DB: ::core::cmp::PartialEq + Backend> ::core::marker::StructuralPartialEq
    for StatementCacheKey<DB> where DB::TypeMetadata: ::core::cmp::PartialEq {
}
#[automatically_derived]#[allow(missing_debug_implementations,
unreachable_pub)]
impl<DB: ::core::cmp::PartialEq + Backend> ::core::cmp::PartialEq for
    StatementCacheKey<DB> where DB::TypeMetadata: ::core::cmp::PartialEq {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Type(__self_0), Self::Type(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Self::Sql { sql: __self_0, bind_types: __self_1 },
                    Self::Sql { sql: __arg1_0, bind_types: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]#[allow(missing_debug_implementations,
unreachable_pub)]
impl<DB: ::core::cmp::Eq + Backend> ::core::cmp::Eq for StatementCacheKey<DB>
    where DB::TypeMetadata: ::core::cmp::Eq {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<TypeId>;
        let _: ::core::cmp::AssertParamIsEq<String>;
        let _: ::core::cmp::AssertParamIsEq<Vec<DB::TypeMetadata>>;
    }
}Eq)]
558#[cfg_attr(
559    diesel_docsrs,
560    doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
561)]
562pub enum StatementCacheKey<DB: Backend> {
563    /// Represents a at compile time known query
564    ///
565    /// Calculated via [`QueryId::QueryId`]
566    Type(TypeId),
567    /// Represents a dynamically constructed query
568    ///
569    /// This variant is used if [`QueryId::HAS_STATIC_QUERY_ID`]
570    /// is `false` and [`AstPass::unsafe_to_cache_prepared`] is not
571    /// called for a given query.
572    Sql {
573        /// contains the sql query string
574        sql: String,
575        /// contains the types of any bind parameter passed to the query
576        bind_types: Vec<DB::TypeMetadata>,
577    },
578}
579
580impl<DB> StatementCacheKey<DB>
581where
582    DB: Backend,
583    DB::QueryBuilder: Default,
584    DB::TypeMetadata: Clone,
585{
586    /// Create a new statement cache key for the given query source
587    // Note: Intentionally monomorphic over source.
588    #[allow(unreachable_pub)]
589    pub fn for_source(
590        maybe_type_id: Option<TypeId>,
591        source: &dyn QueryFragmentForCachedStatement<DB>,
592        bind_types: &[DB::TypeMetadata],
593        backend: &DB,
594    ) -> QueryResult<Self> {
595        match maybe_type_id {
596            Some(id) => Ok(StatementCacheKey::Type(id)),
597            None => {
598                let sql = source.construct_sql(backend)?;
599                Ok(StatementCacheKey::Sql {
600                    sql,
601                    bind_types: bind_types.into(),
602                })
603            }
604        }
605    }
606
607    /// Get the sql for a given query source based
608    ///
609    /// This is an optimization that may skip constructing the query string
610    /// twice if it's already part of the current cache key
611    // Note: Intentionally monomorphic over source.
612    #[allow(unreachable_pub)]
613    pub fn sql(
614        &self,
615        source: &dyn QueryFragmentForCachedStatement<DB>,
616        backend: &DB,
617    ) -> QueryResult<Cow<'_, str>> {
618        match *self {
619            StatementCacheKey::Type(_) => source.construct_sql(backend).map(Cow::Owned),
620            StatementCacheKey::Sql { ref sql, .. } => Ok(Cow::Borrowed(sql)),
621        }
622    }
623}
624
625pub(crate) fn invalid_prepared_statement_parameter_count(
626    expected: usize,
627    provided: usize,
628) -> crate::result::Error {
629    crate::result::Error::QueryBuilderError(
630        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("prepared statement expected a different number of parameters than provided: Expected {0}, provided: {1}",
                expected, provided))
    })alloc::format!(
631            "prepared statement expected a different number of parameters than provided: \
632                     Expected {expected}, provided: {provided}",
633        )
634        .into(),
635    )
636}