Skip to main content

diesel/query_builder/
bind_collector.rs

1//! Types related to managing bind parameters during query construction.
2
3use crate::backend::Backend;
4use crate::result::Error::SerializationError;
5use crate::result::QueryResult;
6use crate::serialize::{IsNull, Output, ToSql};
7use crate::sql_types::{HasSqlType, TypeMetadata};
8use alloc::boxed::Box;
9use alloc::vec::Vec;
10
11#[doc(inline)]
12#[doc(inline)]
pub use self::private::ByteWrapper;#[diesel_derives::__diesel_public_if(
13    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
14)]
15pub(crate) use self::private::ByteWrapper;
16
17/// A type which manages serializing bind parameters during query construction.
18///
19/// The only reason you would ever need to interact with this trait is if you
20/// are adding support for a new backend to Diesel. Plugins which are extending
21/// the query builder will use [`AstPass::push_bind_param`] instead.
22///
23/// [`AstPass::push_bind_param`]: crate::query_builder::AstPass::push_bind_param()
24pub trait BindCollector<'a, DB: TypeMetadata>: Sized {
25    /// The internal buffer type used by this bind collector
26    type Buffer;
27
28    /// Serializes the given bind value, and collects the result.
29    fn push_bound_value<T, U>(
30        &mut self,
31        bind: &'a U,
32        metadata_lookup: &mut DB::MetadataLookup,
33    ) -> QueryResult<()>
34    where
35        DB: Backend + HasSqlType<T>,
36        U: ToSql<T, DB> + ?Sized + 'a;
37
38    /// Push a null value with the given type information onto the bind collector
39    // For backward compatibility reasons we provide a default implementation
40    // but custom backends that want to support `#[derive(MultiConnection)]`
41    // need to provide a customized implementation of this function
42    #[diesel_derives::__diesel_public_if(
43        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
44    )]
45    fn push_null_value(&mut self, _metadata: DB::TypeMetadata) -> QueryResult<()> {
46        Ok(())
47    }
48}
49
50/// A helper trait indicating that this is a bind data container
51#[doc = " A helper trait indicating that this is a bind data container"]
pub trait BindData: Send + 'static {
    /// How many bind values does this container contain
    fn len(&self)
    -> usize;
    /// Is this set of bind data empty
    fn is_empty(&self) -> bool { self.len() == 0 }
}#[diesel_derives::__diesel_public_if(
52    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
53)]
54pub trait BindData: Send + 'static {
55    /// How many bind values does this container contain
56    fn len(&self) -> usize;
57
58    /// Is this set of bind data empty
59    // This mostly exists to satisfy clippy::len_without_is_empty
60    fn is_empty(&self) -> bool {
61        self.len() == 0
62    }
63}
64
65/// A movable version of the bind collector which allows it to be extracted, moved and refilled.
66///
67/// This is mostly useful in async context where bind data needs to be moved across threads.
68#[doc =
" A movable version of the bind collector which allows it to be extracted, moved and refilled."]
#[doc = ""]
#[doc =
" This is mostly useful in async context where bind data needs to be moved across threads."]
pub trait MoveableBindCollector<DB: TypeMetadata> {
    /// The movable bind data of this bind collector
    type BindData: BindData;
    /// Builds a movable version of the bind collector
    fn moveable(&self)
    -> Self::BindData;
    /// Refill the bind collector with its bind data
    fn append_bind_data(&mut self, from: &Self::BindData);
    /// Push bind data as debug representation
    fn push_debug_binds<'a,
    'b>(bind_data: &Self::BindData,
    f: &'a mut Vec<Box<dyn core::fmt::Debug + 'b>>);
}#[diesel_derives::__diesel_public_if(
69    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
70)]
71pub trait MoveableBindCollector<DB: TypeMetadata> {
72    /// The movable bind data of this bind collector
73    type BindData: BindData;
74
75    /// Builds a movable version of the bind collector
76    fn moveable(&self) -> Self::BindData;
77
78    /// Refill the bind collector with its bind data
79    fn append_bind_data(&mut self, from: &Self::BindData);
80
81    /// Push bind data as debug representation
82    fn push_debug_binds<'a, 'b>(
83        bind_data: &Self::BindData,
84        f: &'a mut Vec<Box<dyn core::fmt::Debug + 'b>>,
85    );
86}
87
88#[derive(#[automatically_derived]impl<DB: ::core::fmt::Debug + Backend + TypeMetadata>
    ::core::fmt::Debug for RawBytesBindCollector<DB> where
    DB::TypeMetadata: ::core::fmt::Debug {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "RawBytesBindCollector metadata binds", &self.metadata,
            &&self.binds)
    }
}Debug)]
89/// A bind collector used by backends which transmit bind parameters as an
90/// opaque blob of bytes.
91///
92/// For most backends, this is the concrete implementation of `BindCollector`
93/// that should be used.
94#[doc =
" A bind collector used by backends which transmit bind parameters as an"]
#[doc = " opaque blob of bytes."]
#[doc = ""]
#[doc =
" For most backends, this is the concrete implementation of `BindCollector`"]
#[doc = " that should be used."]
#[non_exhaustive]
pub struct RawBytesBindCollector<DB: Backend + TypeMetadata> {
    #[doc = " The metadata associated with each bind parameter."]
    #[doc = ""]
    #[doc = " This vec is guaranteed to be the same length as `binds`."]
    pub metadata: Vec<DB::TypeMetadata>,
    #[doc = " The serialized bytes for each bind parameter."]
    #[doc = ""]
    #[doc = " This vec is guaranteed to be the same length as `metadata`."]
    pub binds: Vec<Option<Vec<u8>>>,
}#[diesel_derives::__diesel_public_if(
95    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
96    public_fields(metadata, binds)
97)]
98pub struct RawBytesBindCollector<DB: Backend + TypeMetadata> {
99    /// The metadata associated with each bind parameter.
100    ///
101    /// This vec is guaranteed to be the same length as `binds`.
102    pub(crate) metadata: Vec<DB::TypeMetadata>,
103    /// The serialized bytes for each bind parameter.
104    ///
105    /// This vec is guaranteed to be the same length as `metadata`.
106    pub(crate) binds: Vec<Option<Vec<u8>>>,
107}
108
109impl<DB: Backend + TypeMetadata> Default for RawBytesBindCollector<DB> {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115#[allow(clippy::new_without_default)]
116impl<DB: Backend + TypeMetadata> RawBytesBindCollector<DB> {
117    /// Construct an empty `RawBytesBindCollector`
118    pub fn new() -> Self {
119        RawBytesBindCollector {
120            metadata: Vec::new(),
121            binds: Vec::new(),
122        }
123    }
124
125    pub(crate) fn reborrow_buffer<'a: 'b, 'b>(b: &'b mut ByteWrapper<'a>) -> ByteWrapper<'b> {
126        ByteWrapper(b.0)
127    }
128}
129
130impl<'a, DB> BindCollector<'a, DB> for RawBytesBindCollector<DB>
131where
132    for<'b> DB: Backend<BindCollector<'b> = Self> + TypeMetadata,
133{
134    type Buffer = ByteWrapper<'a>;
135
136    fn push_bound_value<T, U>(
137        &mut self,
138        bind: &U,
139        metadata_lookup: &mut DB::MetadataLookup,
140    ) -> QueryResult<()>
141    where
142        DB: HasSqlType<T>,
143        U: ToSql<T, DB> + ?Sized,
144    {
145        let mut bytes = Vec::new();
146        let is_null = {
147            let mut to_sql_output = Output::new(ByteWrapper(&mut bytes), metadata_lookup);
148            bind.to_sql(&mut to_sql_output)
149                .map_err(SerializationError)?
150        };
151        let metadata = <DB as HasSqlType<T>>::metadata(metadata_lookup);
152        match is_null {
153            IsNull::No => self.binds.push(Some(bytes)),
154            IsNull::Yes => self.binds.push(None),
155        }
156        self.metadata.push(metadata);
157        Ok(())
158    }
159
160    fn push_null_value(&mut self, metadata: DB::TypeMetadata) -> QueryResult<()> {
161        self.metadata.push(metadata);
162        self.binds.push(None);
163        Ok(())
164    }
165}
166
167impl<DB> MoveableBindCollector<DB> for RawBytesBindCollector<DB>
168where
169    for<'a> DB: Backend<BindCollector<'a> = Self> + TypeMetadata + 'static,
170    <DB as TypeMetadata>::TypeMetadata: core::fmt::Debug + Clone + Send,
171{
172    type BindData = Self;
173
174    fn moveable(&self) -> Self::BindData {
175        RawBytesBindCollector {
176            binds: self.binds.clone(),
177            metadata: self.metadata.clone(),
178        }
179    }
180
181    fn append_bind_data(&mut self, from: &Self::BindData) {
182        self.binds.extend(from.binds.iter().cloned());
183        self.metadata.extend(from.metadata.clone());
184    }
185
186    fn push_debug_binds<'a, 'b>(
187        bind_data: &Self::BindData,
188        f: &'a mut Vec<Box<dyn core::fmt::Debug + 'b>>,
189    ) {
190        f.extend(
191            bind_data
192                .metadata
193                .iter()
194                .map(|m| Box::new(m.clone()) as Box<dyn core::fmt::Debug>),
195        );
196    }
197}
198
199impl<DB> BindData for RawBytesBindCollector<DB>
200where
201    DB: Backend + TypeMetadata,
202    for<'a> DB: Backend<BindCollector<'a> = Self> + TypeMetadata + 'static,
203    <DB as TypeMetadata>::TypeMetadata: core::fmt::Debug + Clone + Send,
204{
205    fn len(&self) -> usize {
206        self.binds.len()
207    }
208}
209
210// This is private for now as we may want to add `Into` impls for the wrapper type
211// later on
212mod private {
213    use alloc::vec::Vec;
214
215    /// A type wrapper for raw bytes
216    #[derive(#[automatically_derived]impl<'a> ::core::fmt::Debug for ByteWrapper<'a> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "ByteWrapper",
            &&self.0)
    }
}Debug)]
217    pub struct ByteWrapper<'a>(pub(crate) &'a mut Vec<u8>);
218}