Skip to main content

diesel/sqlite/connection/
bind_collector.rs

1use crate::QueryResult;
2use crate::query_builder::{BindCollector, MoveableBindCollector};
3use crate::serialize::{IsNull, Output};
4use crate::sql_types::HasSqlType;
5use crate::sqlite::{Sqlite, SqliteType};
6use alloc::boxed::Box;
7use alloc::string::String;
8use alloc::vec::Vec;
9#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
10use libsqlite3_sys as ffi;
11#[cfg(all(target_family = "wasm", target_os = "unknown"))]
12use sqlite_wasm_rs as ffi;
13
14/// The [`BindCollector`] used by the SQLite backend.
15///
16/// You only interact with this when adding a third-party SQLite backend.
17#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for SqliteBindCollector<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "SqliteBindCollector", "binds", &&self.binds)
    }
}Debug, #[automatically_derived]
impl<'a> ::core::default::Default for SqliteBindCollector<'a> {
    #[inline]
    fn default() -> SqliteBindCollector<'a> {
        SqliteBindCollector { binds: ::core::default::Default::default() }
    }
}Default)]
18pub struct SqliteBindCollector<'a> {
19    pub(in crate::sqlite) binds: Vec<(SqliteBindValueRef<'a>, SqliteType)>,
20}
21
22impl<'a> SqliteBindCollector<'a> {
23    /// Construct an empty `SqliteBindCollector`
24    #[doc = " Construct an empty `SqliteBindCollector`"]
pub fn new() -> Self { Self { binds: Vec::new() } }#[diesel_derives::__diesel_public_if(
25        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
26    )]
27    pub(in crate::sqlite) fn new() -> Self {
28        Self { binds: Vec::new() }
29    }
30
31    /// Iterate over the collected bind values and their SQLite storage classes,
32    /// in positional order.
33    ///
34    /// Each yielded tuple carries a reference to the [`SqliteBindValueRef`] as
35    /// it lives inside the collector, so borrowed string and blob variants are
36    /// exposed without an intervening copy. If the caller needs a
37    /// [`Send`] snapshot instead, use [`MoveableBindCollector::moveable`]
38    /// and inspect [`SqliteBindCollectorData`].
39    ///
40    /// # Example
41    ///
42    /// A third-party backend can render a typed Diesel query into
43    /// placeholder SQL and recover the ordered bind values:
44    ///
45    /// ```rust
46    /// use diesel::expression::IntoSql;
47    /// use diesel::query_builder::{QueryBuilder, QueryFragment};
48    /// use diesel::sql_types::{BigInt, Integer, Text};
49    /// use diesel::sqlite::{
50    ///     Sqlite, SqliteBindCollector, SqliteBindValueRef, SqliteQueryBuilder, SqliteType,
51    /// };
52    ///
53    /// fn render<Q: QueryFragment<Sqlite>>(query: &Q) -> (String, Vec<SqliteType>) {
54    ///     let mut qb = SqliteQueryBuilder::new();
55    ///     query.to_sql(&mut qb, &Sqlite).unwrap();
56    ///
57    ///     let mut collector = SqliteBindCollector::new();
58    ///     query.collect_binds(&mut collector, &mut (), &Sqlite).unwrap();
59    ///
60    ///     let binds: Vec<(&SqliteBindValueRef<'_>, SqliteType)> = collector.binds().collect();
61    ///     assert!(matches!(binds[0].0, SqliteBindValueRef::I32(1)));
62    ///     assert!(matches!(binds[1].0, SqliteBindValueRef::I64(2)));
63    ///     assert!(matches!(binds[2].0, SqliteBindValueRef::BorrowedString("hi")));
64    ///
65    ///     (qb.finish(), binds.iter().map(|(_, t)| *t).collect())
66    /// }
67    ///
68    /// let query = diesel::select((
69    ///     1_i32.into_sql::<Integer>(),
70    ///     2_i64.into_sql::<BigInt>(),
71    ///     "hi".into_sql::<Text>(),
72    /// ));
73    ///
74    /// let (sql, types) = render(&query);
75    /// assert!(sql.contains('?'));
76    /// assert_eq!(types, [SqliteType::Integer, SqliteType::Long, SqliteType::Text]);
77    /// ```
78    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
79    pub fn binds(&self) -> impl ExactSizeIterator<Item = (&SqliteBindValueRef<'a>, SqliteType)> {
80        self.binds.iter().map(|(v, t)| (v, *t))
81    }
82}
83
84/// This type represents a value bound to
85/// a sqlite prepared statement
86///
87/// It can be constructed via the various `From<T>` implementations
88#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for SqliteBindValue<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "SqliteBindValue", "inner", &&self.inner)
    }
}Debug)]
89pub struct SqliteBindValue<'a> {
90    pub(in crate::sqlite) inner: SqliteBindValueRef<'a>,
91}
92
93impl From<i32> for SqliteBindValue<'_> {
94    fn from(i: i32) -> Self {
95        Self {
96            inner: SqliteBindValueRef::I32(i),
97        }
98    }
99}
100
101impl From<i64> for SqliteBindValue<'_> {
102    fn from(i: i64) -> Self {
103        Self {
104            inner: SqliteBindValueRef::I64(i),
105        }
106    }
107}
108
109impl From<f64> for SqliteBindValue<'_> {
110    fn from(f: f64) -> Self {
111        Self {
112            inner: SqliteBindValueRef::F64(f),
113        }
114    }
115}
116
117impl<'a, T> From<Option<T>> for SqliteBindValue<'a>
118where
119    T: Into<SqliteBindValue<'a>>,
120{
121    fn from(o: Option<T>) -> Self {
122        match o {
123            Some(v) => v.into(),
124            None => Self {
125                inner: SqliteBindValueRef::Null,
126            },
127        }
128    }
129}
130
131impl<'a> From<&'a str> for SqliteBindValue<'a> {
132    fn from(s: &'a str) -> Self {
133        Self {
134            inner: SqliteBindValueRef::BorrowedString(s),
135        }
136    }
137}
138
139impl From<String> for SqliteBindValue<'_> {
140    fn from(s: String) -> Self {
141        Self {
142            inner: SqliteBindValueRef::String(s.into_boxed_str()),
143        }
144    }
145}
146
147impl From<Vec<u8>> for SqliteBindValue<'_> {
148    fn from(b: Vec<u8>) -> Self {
149        Self {
150            inner: SqliteBindValueRef::Binary(b.into_boxed_slice()),
151        }
152    }
153}
154
155impl<'a> From<&'a [u8]> for SqliteBindValue<'a> {
156    fn from(b: &'a [u8]) -> Self {
157        Self {
158            inner: SqliteBindValueRef::BorrowedBinary(b),
159        }
160    }
161}
162
163/// The concrete bind value carried by a live [`SqliteBindCollector`].
164///
165/// Distinct from [`OwnedSqliteBindValue`] (the moved snapshot stored in
166/// [`SqliteBindCollectorData`]) in that borrowed and owned string or blob
167/// variants are kept separate, which lets third-party backends read the
168/// collector without cloning transient buffers.
169#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for SqliteBindValueRef<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            SqliteBindValueRef::BorrowedString(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "BorrowedString", &__self_0),
            SqliteBindValueRef::String(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "String",
                    &__self_0),
            SqliteBindValueRef::BorrowedBinary(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "BorrowedBinary", &__self_0),
            SqliteBindValueRef::Binary(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Binary",
                    &__self_0),
            SqliteBindValueRef::I32(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "I32",
                    &__self_0),
            SqliteBindValueRef::I64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "I64",
                    &__self_0),
            SqliteBindValueRef::F64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "F64",
                    &__self_0),
            SqliteBindValueRef::Null =>
                ::core::fmt::Formatter::write_str(f, "Null"),
        }
    }
}Debug)]
170#[doc = " The concrete bind value carried by a live [`SqliteBindCollector`]."]
#[doc = ""]
#[doc =
" Distinct from [`OwnedSqliteBindValue`] (the moved snapshot stored in"]
#[doc =
" [`SqliteBindCollectorData`]) in that borrowed and owned string or blob"]
#[doc =
" variants are kept separate, which lets third-party backends read the"]
#[doc = " collector without cloning transient buffers."]
pub enum SqliteBindValueRef<'a> {

    /// A `TEXT` value that borrows from the query.
    BorrowedString(&'a str),

    /// A `TEXT` value the collector owns.
    String(Box<str>),

    /// A `BLOB` value that borrows from the query.
    BorrowedBinary(&'a [u8]),

    /// A `BLOB` value the collector owns.
    Binary(Box<[u8]>),

    /// An `INTEGER` value that fits in an `i32`.
    I32(i32),

    /// An `INTEGER` value that requires an `i64`.
    I64(i64),

    /// A `REAL` value.
    F64(f64),

    /// A `NULL` value.
    Null,
}#[diesel_derives::__diesel_public_if(
171    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
172)]
173pub(crate) enum SqliteBindValueRef<'a> {
174    /// A `TEXT` value that borrows from the query.
175    BorrowedString(&'a str),
176    /// A `TEXT` value the collector owns.
177    String(Box<str>),
178    /// A `BLOB` value that borrows from the query.
179    BorrowedBinary(&'a [u8]),
180    /// A `BLOB` value the collector owns.
181    Binary(Box<[u8]>),
182    /// An `INTEGER` value that fits in an `i32`.
183    I32(i32),
184    /// An `INTEGER` value that requires an `i64`.
185    I64(i64),
186    /// A `REAL` value.
187    F64(f64),
188    /// A `NULL` value.
189    Null,
190}
191
192impl core::fmt::Display for SqliteBindValueRef<'_> {
193    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194        let n = match self {
195            SqliteBindValueRef::BorrowedString(_) | SqliteBindValueRef::String(_) => "Text",
196            SqliteBindValueRef::BorrowedBinary(_) | SqliteBindValueRef::Binary(_) => "Binary",
197            SqliteBindValueRef::I32(_) | SqliteBindValueRef::I64(_) => "Integer",
198            SqliteBindValueRef::F64(_) => "Float",
199            SqliteBindValueRef::Null => "Null",
200        };
201        f.write_str(n)
202    }
203}
204
205impl SqliteBindValueRef<'_> {
206    #[allow(unsafe_code)] // ffi function calls
207    pub(in crate::sqlite) fn result_of(
208        self,
209        ctx: &mut ffi::sqlite3_context,
210    ) -> Result<(), core::num::TryFromIntError> {
211        use core::ffi as libc;
212        // This unsafe block assumes the following invariants:
213        //
214        // - `ctx` points to valid memory
215        unsafe {
216            match self {
217                SqliteBindValueRef::BorrowedString(s) => ffi::sqlite3_result_text(
218                    ctx,
219                    s.as_ptr() as *const libc::c_char,
220                    s.len().try_into()?,
221                    ffi::SQLITE_TRANSIENT(),
222                ),
223                SqliteBindValueRef::String(s) => ffi::sqlite3_result_text(
224                    ctx,
225                    s.as_ptr() as *const libc::c_char,
226                    s.len().try_into()?,
227                    ffi::SQLITE_TRANSIENT(),
228                ),
229                SqliteBindValueRef::Binary(b) => ffi::sqlite3_result_blob(
230                    ctx,
231                    b.as_ptr() as *const libc::c_void,
232                    b.len().try_into()?,
233                    ffi::SQLITE_TRANSIENT(),
234                ),
235                SqliteBindValueRef::BorrowedBinary(b) => ffi::sqlite3_result_blob(
236                    ctx,
237                    b.as_ptr() as *const libc::c_void,
238                    b.len().try_into()?,
239                    ffi::SQLITE_TRANSIENT(),
240                ),
241                SqliteBindValueRef::I32(i) => ffi::sqlite3_result_int(ctx, i as libc::c_int),
242                SqliteBindValueRef::I64(l) => ffi::sqlite3_result_int64(ctx, l),
243                SqliteBindValueRef::F64(d) => ffi::sqlite3_result_double(ctx, d as libc::c_double),
244                SqliteBindValueRef::Null => ffi::sqlite3_result_null(ctx),
245            }
246        }
247        Ok(())
248    }
249}
250
251impl<'a> BindCollector<'a, Sqlite> for SqliteBindCollector<'a> {
252    type Buffer = SqliteBindValue<'a>;
253
254    fn push_bound_value<T, U>(&mut self, bind: &'a U, metadata_lookup: &mut ()) -> QueryResult<()>
255    where
256        Sqlite: crate::sql_types::HasSqlType<T>,
257        U: crate::serialize::ToSql<T, Sqlite> + ?Sized,
258    {
259        let value = SqliteBindValue {
260            inner: SqliteBindValueRef::Null,
261        };
262        let mut to_sql_output = Output::new(value, metadata_lookup);
263        let is_null = bind
264            .to_sql(&mut to_sql_output)
265            .map_err(crate::result::Error::SerializationError)?;
266        let bind = to_sql_output.into_inner();
267        let metadata = Sqlite::metadata(metadata_lookup);
268        self.binds.push((
269            match is_null {
270                IsNull::No => bind.inner,
271                IsNull::Yes => SqliteBindValueRef::Null,
272            },
273            metadata,
274        ));
275        Ok(())
276    }
277
278    fn push_null_value(&mut self, metadata: SqliteType) -> QueryResult<()> {
279        self.binds.push((SqliteBindValueRef::Null, metadata));
280        Ok(())
281    }
282}
283
284/// An owned value bound to a SQLite prepared statement.
285///
286/// The readable counterpart to the values a [`SqliteBindCollector`] holds.
287#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OwnedSqliteBindValue {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            OwnedSqliteBindValue::String(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "String",
                    &__self_0),
            OwnedSqliteBindValue::Binary(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Binary",
                    &__self_0),
            OwnedSqliteBindValue::I32(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "I32",
                    &__self_0),
            OwnedSqliteBindValue::I64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "I64",
                    &__self_0),
            OwnedSqliteBindValue::F64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "F64",
                    &__self_0),
            OwnedSqliteBindValue::Null =>
                ::core::fmt::Formatter::write_str(f, "Null"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OwnedSqliteBindValue {
    #[inline]
    fn clone(&self) -> OwnedSqliteBindValue {
        match self {
            OwnedSqliteBindValue::String(__self_0) =>
                OwnedSqliteBindValue::String(::core::clone::Clone::clone(__self_0)),
            OwnedSqliteBindValue::Binary(__self_0) =>
                OwnedSqliteBindValue::Binary(::core::clone::Clone::clone(__self_0)),
            OwnedSqliteBindValue::I32(__self_0) =>
                OwnedSqliteBindValue::I32(::core::clone::Clone::clone(__self_0)),
            OwnedSqliteBindValue::I64(__self_0) =>
                OwnedSqliteBindValue::I64(::core::clone::Clone::clone(__self_0)),
            OwnedSqliteBindValue::F64(__self_0) =>
                OwnedSqliteBindValue::F64(::core::clone::Clone::clone(__self_0)),
            OwnedSqliteBindValue::Null => OwnedSqliteBindValue::Null,
        }
    }
}Clone)]
288#[doc = " An owned value bound to a SQLite prepared statement."]
#[doc = ""]
#[doc =
" The readable counterpart to the values a [`SqliteBindCollector`] holds."]
pub enum OwnedSqliteBindValue {

    /// A `TEXT` value.
    String(Box<str>),

    /// A `BLOB` value.
    Binary(Box<[u8]>),

    /// An `INTEGER` value that fits in an `i32`.
    I32(i32),

    /// An `INTEGER` value that requires an `i64`.
    I64(i64),

    /// A `REAL` value.
    F64(f64),

    /// A `NULL` value.
    Null,
}#[diesel_derives::__diesel_public_if(
289    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
290)]
291enum OwnedSqliteBindValue {
292    /// A `TEXT` value.
293    String(Box<str>),
294    /// A `BLOB` value.
295    Binary(Box<[u8]>),
296    /// An `INTEGER` value that fits in an `i32`.
297    I32(i32),
298    /// An `INTEGER` value that requires an `i64`.
299    I64(i64),
300    /// A `REAL` value.
301    F64(f64),
302    /// A `NULL` value.
303    Null,
304}
305
306impl<'a> core::convert::From<&SqliteBindValueRef<'a>> for OwnedSqliteBindValue {
307    fn from(value: &SqliteBindValueRef<'a>) -> Self {
308        match value {
309            SqliteBindValueRef::String(s) => Self::String(s.clone()),
310            SqliteBindValueRef::BorrowedString(s) => {
311                Self::String(String::from(*s).into_boxed_str())
312            }
313            SqliteBindValueRef::Binary(b) => Self::Binary(b.clone()),
314            SqliteBindValueRef::BorrowedBinary(s) => Self::Binary(Vec::from(*s).into_boxed_slice()),
315            SqliteBindValueRef::I32(val) => Self::I32(*val),
316            SqliteBindValueRef::I64(val) => Self::I64(*val),
317            SqliteBindValueRef::F64(val) => Self::F64(*val),
318            SqliteBindValueRef::Null => Self::Null,
319        }
320    }
321}
322
323impl core::convert::From<&OwnedSqliteBindValue> for SqliteBindValueRef<'_> {
324    fn from(value: &OwnedSqliteBindValue) -> Self {
325        match value {
326            OwnedSqliteBindValue::String(s) => Self::String(s.clone()),
327            OwnedSqliteBindValue::Binary(b) => Self::Binary(b.clone()),
328            OwnedSqliteBindValue::I32(val) => Self::I32(*val),
329            OwnedSqliteBindValue::I64(val) => Self::I64(*val),
330            OwnedSqliteBindValue::F64(val) => Self::F64(*val),
331            OwnedSqliteBindValue::Null => Self::Null,
332        }
333    }
334}
335
336/// SQLite bind collector data that is movable across threads.
337///
338/// This is the [`Send`] snapshot produced by [`MoveableBindCollector::moveable`]
339/// on a [`SqliteBindCollector`]. Both borrowed and owned string or blob variants
340/// of [`SqliteBindValueRef`] collapse into their [`OwnedSqliteBindValue`]
341/// counterparts, so a caller crossing a thread boundary carries no borrows from
342/// the original query. For a zero-copy view of the live collector see
343/// [`SqliteBindCollector::binds`].
344#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SqliteBindCollectorData {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "SqliteBindCollectorData", "binds", &&self.binds)
    }
}Debug)]
345#[doc = " SQLite bind collector data that is movable across threads."]
#[doc = ""]
#[doc =
" This is the [`Send`] snapshot produced by [`MoveableBindCollector::moveable`]"]
#[doc =
" on a [`SqliteBindCollector`]. Both borrowed and owned string or blob variants"]
#[doc =
" of [`SqliteBindValueRef`] collapse into their [`OwnedSqliteBindValue`]"]
#[doc =
" counterparts, so a caller crossing a thread boundary carries no borrows from"]
#[doc = " the original query. For a zero-copy view of the live collector see"]
#[doc = " [`SqliteBindCollector::binds`]."]
#[non_exhaustive]
pub struct SqliteBindCollectorData {
    #[doc =
    " The collected bind values, in the order they appear in the query."]
    binds: Vec<(OwnedSqliteBindValue, SqliteType)>,
}#[diesel_derives::__diesel_public_if(
346    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
347)]
348pub struct SqliteBindCollectorData {
349    /// The collected bind values, in the order they appear in the query.
350    binds: Vec<(OwnedSqliteBindValue, SqliteType)>,
351}
352
353impl SqliteBindCollectorData {
354    /// Iterate over the collected bind values and their SQLite storage classes,
355    /// in positional order.
356    ///
357    /// The values yielded are the moved [`OwnedSqliteBindValue`] snapshot, so
358    /// this method is safe to call from any thread. For a zero-copy view of the
359    /// live collector see [`SqliteBindCollector::binds`].
360    ///
361    /// # Example
362    ///
363    /// A third-party backend can snapshot the ordered bind values of a typed
364    /// Diesel query and inspect the owned variants:
365    ///
366    /// ```rust
367    /// use diesel::expression::IntoSql;
368    /// use diesel::query_builder::{MoveableBindCollector, QueryFragment};
369    /// use diesel::sql_types::{Binary, Integer, Nullable, Text};
370    /// use diesel::sqlite::{
371    ///     OwnedSqliteBindValue, Sqlite, SqliteBindCollector, SqliteBindCollectorData, SqliteType,
372    /// };
373    ///
374    /// fn snapshot<Q: QueryFragment<Sqlite>>(query: &Q) -> SqliteBindCollectorData {
375    ///     let mut collector = SqliteBindCollector::new();
376    ///     query.collect_binds(&mut collector, &mut (), &Sqlite).unwrap();
377    ///     collector.moveable()
378    /// }
379    ///
380    /// let query = diesel::select((
381    ///     42_i32.into_sql::<Integer>(),
382    ///     "hi".into_sql::<Text>(),
383    ///     vec![1_u8, 2, 3].into_sql::<Binary>(),
384    ///     None::<i32>.into_sql::<Nullable<Integer>>(),
385    /// ));
386    ///
387    /// let data = snapshot(&query);
388    ///
389    /// // Types survive the move.
390    /// let types: Vec<_> = data.binds().map(|(_, t)| t).collect();
391    /// assert_eq!(
392    ///     types,
393    ///     [SqliteType::Integer, SqliteType::Text, SqliteType::Binary, SqliteType::Integer],
394    /// );
395    ///
396    /// // Borrowed variants normalize into their owned counterparts.
397    /// let binds: Vec<_> = data.binds().collect();
398    /// assert!(matches!(binds[0].0, OwnedSqliteBindValue::I32(42)));
399    /// assert!(matches!(binds[1].0, OwnedSqliteBindValue::String(s) if &**s == "hi"));
400    /// assert!(matches!(binds[2].0, OwnedSqliteBindValue::Binary(b) if **b == [1, 2, 3]));
401    /// assert!(matches!(binds[3].0, OwnedSqliteBindValue::Null));
402    /// ```
403    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
404    pub fn binds(&self) -> impl ExactSizeIterator<Item = (&OwnedSqliteBindValue, SqliteType)> {
405        self.binds.iter().map(|(v, t)| (v, *t))
406    }
407}
408
409impl MoveableBindCollector<Sqlite> for SqliteBindCollector<'_> {
410    type BindData = SqliteBindCollectorData;
411
412    fn moveable(&self) -> Self::BindData {
413        let mut binds = Vec::with_capacity(self.binds.len());
414        for b in self
415            .binds
416            .iter()
417            .map(|(bind, tpe)| (OwnedSqliteBindValue::from(bind), *tpe))
418        {
419            binds.push(b);
420        }
421        SqliteBindCollectorData { binds }
422    }
423
424    fn append_bind_data(&mut self, from: &Self::BindData) {
425        self.binds.reserve_exact(from.binds.len());
426        self.binds.extend(
427            from.binds
428                .iter()
429                .map(|(bind, tpe)| (SqliteBindValueRef::from(bind), *tpe)),
430        );
431    }
432
433    fn push_debug_binds<'a, 'b>(
434        bind_data: &Self::BindData,
435        f: &'a mut Vec<Box<dyn core::fmt::Debug + 'b>>,
436    ) {
437        f.extend(
438            bind_data
439                .binds
440                .iter()
441                .map(|(b, _)| Box::new(b.clone()) as Box<dyn core::fmt::Debug>),
442        );
443    }
444}
445
446#[cfg(test)]
447mod tests {
448    use super::{
449        OwnedSqliteBindValue, SqliteBindCollector, SqliteBindCollectorData, SqliteBindValueRef,
450    };
451    use crate::expression::IntoSql;
452    use crate::query_builder::{MoveableBindCollector, QueryFragment};
453    use crate::sql_types::{BigInt, Binary, Double, Integer, Nullable, Text};
454    use crate::sqlite::{Sqlite, SqliteType};
455
456    // Collecting binds needs no connection, the property downstream callers rely on.
457    fn collect<Q: QueryFragment<Sqlite>>(query: Q) -> SqliteBindCollectorData {
458        let mut collector = SqliteBindCollector::new();
459        query
460            .collect_binds(&mut collector, &mut (), &Sqlite)
461            .unwrap();
462        collector.moveable()
463    }
464
465    #[diesel_test_helper::test]
466    fn collected_binds_are_readable_in_positional_order_with_their_type() {
467        let data = collect(crate::select((
468            1_i32.into_sql::<Integer>(),
469            2_i64.into_sql::<BigInt>(),
470            3.5_f64.into_sql::<Double>(),
471            "hello".into_sql::<Text>(),
472            vec![1_u8, 2, 3].into_sql::<Binary>(),
473            None::<i32>.into_sql::<Nullable<Integer>>(),
474        )));
475
476        let types: Vec<_> = data.binds.iter().map(|(_, t)| *t).collect();
477        assert_eq!(
478            types,
479            [
480                SqliteType::Integer,
481                SqliteType::Long,
482                SqliteType::Double,
483                SqliteType::Text,
484                SqliteType::Binary,
485                SqliteType::Integer,
486            ]
487        );
488
489        assert!(matches!(data.binds[0].0, OwnedSqliteBindValue::I32(1)));
490        assert!(matches!(data.binds[1].0, OwnedSqliteBindValue::I64(2)));
491        assert!(matches!(data.binds[2].0, OwnedSqliteBindValue::F64(f) if f == 3.5));
492        assert!(matches!(&data.binds[3].0, OwnedSqliteBindValue::String(s) if &**s == "hello"));
493        assert!(matches!(&data.binds[4].0, OwnedSqliteBindValue::Binary(b) if **b == [1, 2, 3]));
494        assert!(matches!(data.binds[5].0, OwnedSqliteBindValue::Null));
495    }
496
497    // `moveable` must own every internal variant, borrowed and owned alike.
498    #[diesel_test_helper::test]
499    fn moveable_owns_every_internal_variant() {
500        let collector = SqliteBindCollector {
501            binds: vec![
502                (
503                    SqliteBindValueRef::BorrowedString("borrowed"),
504                    SqliteType::Text,
505                ),
506                (SqliteBindValueRef::String("owned".into()), SqliteType::Text),
507                (
508                    SqliteBindValueRef::BorrowedBinary(&[1, 2]),
509                    SqliteType::Binary,
510                ),
511                (
512                    SqliteBindValueRef::Binary(vec![3, 4].into()),
513                    SqliteType::Binary,
514                ),
515                (SqliteBindValueRef::I32(7), SqliteType::Integer),
516                (SqliteBindValueRef::I64(8), SqliteType::Long),
517                (SqliteBindValueRef::F64(9.0), SqliteType::Double),
518                (SqliteBindValueRef::Null, SqliteType::Text),
519            ],
520        };
521
522        let data = collector.moveable();
523        assert!(matches!(&data.binds[0].0, OwnedSqliteBindValue::String(s) if &**s == "borrowed"));
524        assert!(matches!(&data.binds[1].0, OwnedSqliteBindValue::String(s) if &**s == "owned"));
525        assert!(matches!(&data.binds[2].0, OwnedSqliteBindValue::Binary(b) if **b == [1, 2]));
526        assert!(matches!(&data.binds[3].0, OwnedSqliteBindValue::Binary(b) if **b == [3, 4]));
527        assert!(matches!(data.binds[4].0, OwnedSqliteBindValue::I32(7)));
528        assert!(matches!(data.binds[5].0, OwnedSqliteBindValue::I64(8)));
529        assert!(matches!(data.binds[6].0, OwnedSqliteBindValue::F64(f) if f == 9.0));
530        assert!(matches!(data.binds[7].0, OwnedSqliteBindValue::Null));
531    }
532
533    // `SqliteBindCollector::binds` exposes the live enum without cloning
534    // borrowed variants, which is the reason it exists next to `moveable()`.
535    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
536    #[diesel_test_helper::test]
537    fn binds_iterator_yields_live_ref_without_cloning() {
538        let collector = SqliteBindCollector {
539            binds: vec![
540                (
541                    SqliteBindValueRef::BorrowedString("borrowed"),
542                    SqliteType::Text,
543                ),
544                (
545                    SqliteBindValueRef::BorrowedBinary(&[9, 9]),
546                    SqliteType::Binary,
547                ),
548                (SqliteBindValueRef::I32(1), SqliteType::Integer),
549                (SqliteBindValueRef::Null, SqliteType::Text),
550            ],
551        };
552
553        let seen: Vec<_> = collector.binds().collect();
554        assert_eq!(seen.len(), 4);
555        assert_eq!(seen[0].1, SqliteType::Text);
556        assert_eq!(seen[1].1, SqliteType::Binary);
557        assert!(matches!(
558            seen[0].0,
559            SqliteBindValueRef::BorrowedString("borrowed")
560        ));
561        assert!(matches!(seen[1].0, SqliteBindValueRef::BorrowedBinary(b) if *b == [9, 9]));
562        assert!(matches!(seen[2].0, SqliteBindValueRef::I32(1)));
563        assert!(matches!(seen[3].0, SqliteBindValueRef::Null));
564    }
565
566    // Appending an owned snapshot back into a collector, the reverse conversion.
567    #[diesel_test_helper::test]
568    fn append_bind_data_round_trips_the_owned_snapshot() {
569        let data = collect(crate::select((
570            42_i32.into_sql::<Integer>(),
571            "text".into_sql::<Text>(),
572            None::<i32>.into_sql::<Nullable<Integer>>(),
573        )));
574
575        let mut collector = SqliteBindCollector::new();
576        collector.append_bind_data(&data);
577        let round_tripped = collector.moveable();
578
579        assert!(matches!(
580            round_tripped.binds[0].0,
581            OwnedSqliteBindValue::I32(42)
582        ));
583        assert!(
584            matches!(&round_tripped.binds[1].0, OwnedSqliteBindValue::String(s) if &**s == "text")
585        );
586        assert!(matches!(
587            round_tripped.binds[2].0,
588            OwnedSqliteBindValue::Null
589        ));
590    }
591}