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