1//! Types related to managing bind parameters during query construction.
23use crate::backend::Backend;
4use crate::result::Error::SerializationError;
5use crate::result::QueryResult;
6use crate::serialize::{IsNull, Output, ToSql};
7use crate::sql_types::{HasSqlType, TypeMetadata};
89#[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;
1415/// 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
24type Buffer;
2526/// Serializes the given bind value, and collects the result.
27fn push_bound_value<T, U>(
28&mut self,
29 bind: &'a U,
30 metadata_lookup: &mut DB::MetadataLookup,
31 ) -> QueryResult<()>
32where
33DB: Backend + HasSqlType<T>,
34 U: ToSql<T, DB> + ?Sized + 'a;
3536/// Push a null value with the given type information onto the bind collector
37 ///
38// For backward compatibility reasons we provide a default implementation
39 // but custom backends that want to support `#[derive(MultiConnection)]`
40 // need to provide a customized implementation of this function
41#[diesel_derives::__diesel_public_if(
42 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
43)]
44fn push_null_value(&mut self, _metadata: DB::TypeMetadata) -> QueryResult<()> {
45Ok(())
46 }
47}
4849/// A movable version of the bind collector which allows it to be extracted, moved and refilled.
50///
51/// This is mostly useful in async context where bind data needs to be moved across threads.
52#[diesel_derives::__diesel_public_if(
53 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
54)]
55pub trait MoveableBindCollector<DB: TypeMetadata> {
56/// The movable bind data of this bind collector
57type BindData: Send + 'static;
5859/// Builds a movable version of the bind collector
60fn moveable(&self) -> Self::BindData;
6162/// Refill the bind collector with its bind data
63fn append_bind_data(&mut self, from: &Self::BindData);
64}
6566#[derive(Debug)]
67/// A bind collector used by backends which transmit bind parameters as an
68/// opaque blob of bytes.
69///
70/// For most backends, this is the concrete implementation of `BindCollector`
71/// that should be used.
72#[diesel_derives::__diesel_public_if(
73 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
74 public_fields(metadata, binds)
75)]
76pub struct RawBytesBindCollector<DB: Backend + TypeMetadata> {
77/// The metadata associated with each bind parameter.
78 ///
79 /// This vec is guaranteed to be the same length as `binds`.
80pub(crate) metadata: Vec<DB::TypeMetadata>,
81/// The serialized bytes for each bind parameter.
82 ///
83 /// This vec is guaranteed to be the same length as `metadata`.
84pub(crate) binds: Vec<Option<Vec<u8>>>,
85}
8687impl<DB: Backend + TypeMetadata> Default for RawBytesBindCollector<DB> {
88fn default() -> Self {
89Self::new()
90 }
91}
9293#[allow(clippy::new_without_default)]
94impl<DB: Backend + TypeMetadata> RawBytesBindCollector<DB> {
95/// Construct an empty `RawBytesBindCollector`
96pub fn new() -> Self {
97 RawBytesBindCollector {
98 metadata: Vec::new(),
99 binds: Vec::new(),
100 }
101 }
102103pub(crate) fn reborrow_buffer<'a: 'b, 'b>(b: &'b mut ByteWrapper<'a>) -> ByteWrapper<'b> {
104 ByteWrapper(b.0)
105 }
106}
107108impl<'a, DB> BindCollector<'a, DB> for RawBytesBindCollector<DB>
109where
110 for<'b> DB: Backend<BindCollector<'b> = Self> + TypeMetadata,
111{
112type Buffer = ByteWrapper<'a>;
113114fn push_bound_value<T, U>(
115&mut self,
116 bind: &U,
117 metadata_lookup: &mut DB::MetadataLookup,
118 ) -> QueryResult<()>
119where
120DB: HasSqlType<T>,
121 U: ToSql<T, DB> + ?Sized,
122 {
123let mut bytes = Vec::new();
124let is_null = {
125let mut to_sql_output = Output::new(ByteWrapper(&mut bytes), metadata_lookup);
126 bind.to_sql(&mut to_sql_output)
127 .map_err(SerializationError)?
128};
129let metadata = <DB as HasSqlType<T>>::metadata(metadata_lookup);
130match is_null {
131 IsNull::No => self.binds.push(Some(bytes)),
132 IsNull::Yes => self.binds.push(None),
133 }
134self.metadata.push(metadata);
135Ok(())
136 }
137138fn push_null_value(&mut self, metadata: DB::TypeMetadata) -> QueryResult<()> {
139self.metadata.push(metadata);
140self.binds.push(None);
141Ok(())
142 }
143}
144145impl<DB> MoveableBindCollector<DB> for RawBytesBindCollector<DB>
146where
147 for<'a> DB: Backend<BindCollector<'a> = Self> + TypeMetadata + 'static,
148 <DB as TypeMetadata>::TypeMetadata: Clone + Send,
149{
150type BindData = Self;
151152fn moveable(&self) -> Self::BindData {
153 RawBytesBindCollector {
154 binds: self.binds.clone(),
155 metadata: self.metadata.clone(),
156 }
157 }
158159fn append_bind_data(&mut self, from: &Self::BindData) {
160self.binds.extend(from.binds.iter().cloned());
161self.metadata.extend(from.metadata.clone());
162 }
163}
164165// This is private for now as we may want to add `Into` impls for the wrapper type
166// later on
167mod private {
168/// A type wrapper for raw bytes
169#[derive(Debug)]
170pub struct ByteWrapper<'a>(pub(crate) &'a mut Vec<u8>);
171}