Skip to main content

diesel/query_builder/
sql_query.rs

1use super::Query;
2use crate::backend::{Backend, DieselReserveSpecialization};
3use crate::query_builder::{AstPass, QueryFragment, QueryId};
4use crate::query_dsl::RunQueryDslSupport;
5use crate::result::QueryResult;
6use crate::serialize::ToSql;
7use crate::sql_types::{HasSqlType, Untyped};
8use alloc::boxed::Box;
9use alloc::string::String;
10use alloc::string::ToString;
11use alloc::sync::Arc;
12use alloc::vec::Vec;
13use core::marker::PhantomData;
14
15#[derive(#[automatically_derived]
impl<Inner: ::core::fmt::Debug> ::core::fmt::Debug for SqlQuery<Inner> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "SqlQuery",
            "inner", &self.inner, "query", &&self.query)
    }
}Debug, #[automatically_derived]
impl<Inner: ::core::clone::Clone> ::core::clone::Clone for SqlQuery<Inner> {
    #[inline]
    fn clone(&self) -> SqlQuery<Inner> {
        SqlQuery {
            inner: ::core::clone::Clone::clone(&self.inner),
            query: ::core::clone::Clone::clone(&self.query),
        }
    }
}Clone)]
16#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
17/// The return value of `sql_query`.
18///
19/// Unlike most queries in Diesel, `SqlQuery` loads its data by column name,
20/// rather than by index. This means that you cannot deserialize this query into
21/// a tuple, and any structs used must implement `QueryableByName`.
22///
23/// See [`sql_query`](crate::sql_query()) for examples.
24pub struct SqlQuery<Inner = self::private::Empty> {
25    inner: Inner,
26    query: String,
27}
28
29impl<Inner> SqlQuery<Inner> {
30    pub(crate) fn new(inner: Inner, query: String) -> Self {
31        SqlQuery { inner, query }
32    }
33
34    /// Bind a value for use with this SQL query. The given query should have
35    /// placeholders that vary based on the database type,
36    /// like [SQLite Parameter](https://sqlite.org/lang_expr.html#varparam) syntax,
37    /// [PostgreSQL PREPARE syntax](https://www.postgresql.org/docs/current/sql-prepare.html),
38    /// or [MySQL bind syntax](https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-bind-param.html).
39    ///
40    /// For binding a variable number of values in a loop, use `into_boxed` first.
41    ///
42    /// # Safety
43    ///
44    /// This function should be used with care, as Diesel cannot validate that
45    /// the value is of the right type nor can it validate that you have passed
46    /// the correct number of parameters.
47    ///
48    /// # Example
49    ///
50    /// ```
51    /// # include!("../doctest_setup.rs");
52    /// #
53    /// # use schema::users;
54    /// #
55    /// # #[derive(QueryableByName, Debug, PartialEq)]
56    /// # struct User {
57    /// #     id: i32,
58    /// #     name: String,
59    /// # }
60    /// #
61    /// # fn main() {
62    /// #     use diesel::sql_query;
63    /// #     use diesel::sql_types::{Integer, Text};
64    /// #
65    /// #     let connection = &mut establish_connection();
66    /// #     diesel::insert_into(users::table)
67    /// #         .values(users::name.eq("Jim"))
68    /// #         .execute(connection).unwrap();
69    /// # #[cfg(feature = "postgres")]
70    /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2");
71    /// # #[cfg(not(feature = "postgres"))]
72    /// // sqlite/mysql bind syntax
73    /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?")
74    /// # ;
75    /// # let users = users
76    ///     .bind::<Integer, _>(1)
77    ///     .bind::<Text, _>("Tess")
78    ///     .get_results(connection);
79    /// let expected_users = vec![User {
80    ///     id: 3,
81    ///     name: "Jim".into(),
82    /// }];
83    /// assert_eq!(Ok(expected_users), users);
84    /// # }
85    /// ```
86    pub fn bind<ST, Value>(self, value: Value) -> UncheckedBind<Self, Value, ST> {
87        UncheckedBind::new(self, value)
88    }
89
90    /// Internally boxes the query, which allows to  calls `bind` and `sql` so that they don't
91    /// change the type nor the instance. This allows to call `bind` or `sql`
92    /// in a loop, e.g.:
93    ///
94    /// ```
95    /// # include!("../doctest_setup.rs");
96    /// #
97    /// # use schema::users;
98    /// #
99    /// # #[derive(QueryableByName, Debug, PartialEq)]
100    /// # struct User {
101    /// #     id: i32,
102    /// #     name: String,
103    /// # }
104    /// #
105    /// # fn main() {
106    /// #     use diesel::sql_query;
107    /// #     use diesel::sql_types::{Integer};
108    /// #
109    /// #     let connection = &mut establish_connection();
110    /// #     diesel::insert_into(users::table)
111    /// #         .values(users::name.eq("Jim"))
112    /// #         .execute(connection).unwrap();
113    /// let mut q = diesel::sql_query("SELECT * FROM users WHERE id IN(").into_boxed();
114    /// for (idx, user_id) in [3, 4, 5].into_iter().enumerate() {
115    ///     if idx != 0 {
116    ///         q = q.sql(", ");
117    ///     }
118    /// # #[cfg(feature = "postgres")]
119    /// # {
120    ///     q = q
121    ///         // postgresql bind syntax
122    ///         .sql(format!("${}", idx + 1))
123    ///         .bind::<Integer, _>(user_id);
124    /// # }
125    /// # #[cfg(not(feature = "postgres"))]
126    /// # {
127    /// #   q = q
128    /// #       .sql(format!("?"))
129    /// #       .bind::<Integer, _>(user_id);
130    /// # }
131    /// }
132    /// let users = q.sql(");").get_results(connection);
133    /// let expected_users = vec![User {
134    ///     id: 3,
135    ///     name: "Jim".into(),
136    /// }];
137    /// assert_eq!(Ok(expected_users), users);
138    /// # }
139    /// ```
140    ///
141    /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a
142    /// loop.
143    ///
144    /// Boxed queries are not cloneable.
145    /// If you need cloning, use [`into_boxed_clone`] to create a [`BoxedCloneSqlQuery`].
146    ///
147    /// [`into_boxed_clone`]: SqlQuery::into_boxed_clone()
148    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
149        BoxedSqlQuery::new(self)
150    }
151
152    /// Internally wraps the query in an [`Arc`], which allows to  calls `bind` and `sql` so that
153    /// they don't change the type nor the instance. This allows to clone the query and call
154    /// `bind` or `sql` in a loop, e.g.:
155    ///
156    /// ```
157    /// # include!("../doctest_setup.rs");
158    /// #
159    /// # use schema::users;
160    /// #
161    /// # #[derive(QueryableByName, Debug, PartialEq)]
162    /// # struct User {
163    /// #     id: i32,
164    /// #     name: String,
165    /// # }
166    /// #
167    /// # fn main() {
168    /// #     use diesel::sql_query;
169    /// #     use diesel::sql_types::{Integer};
170    /// #
171    /// #     let connection = &mut establish_connection();
172    /// #     diesel::insert_into(users::table)
173    /// #         .values(users::name.eq("Jim"))
174    /// #         .execute(connection).unwrap();
175    /// let q = diesel::sql_query("SELECT * FROM users WHERE id IN(").into_boxed_clone();
176    /// let mut q = q.clone();
177    /// for (idx, user_id) in [3, 4, 5].into_iter().enumerate() {
178    ///     if idx != 0 {
179    ///         q = q.sql(", ");
180    ///     }
181    /// # #[cfg(feature = "postgres")]
182    /// # {
183    ///     q = q
184    ///         // postgresql bind syntax
185    ///         .sql(format!("${}", idx + 1))
186    ///         .bind::<Integer, _>(user_id);
187    /// # }
188    /// # #[cfg(not(feature = "postgres"))]
189    /// # {
190    /// #   q = q
191    /// #       .sql(format!("?"))
192    /// #       .bind::<Integer, _>(user_id);
193    /// # }
194    /// }
195    /// let users = q.sql(");").get_results(connection);
196    /// let expected_users = vec![User {
197    ///     id: 3,
198    ///     name: "Jim".into(),
199    /// }];
200    /// assert_eq!(Ok(expected_users), users);
201    /// # }
202    /// ```
203    ///
204    /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a
205    /// loop.
206    ///
207    /// In cases where the query does not need to be cloned, [`BoxedSqlQuery`] (created with
208    /// [`into_boxed`]) provides better performance.
209    ///
210    /// [`into_boxed`]: SqlQuery::into_boxed()
211    pub fn into_boxed_clone<'f, DB: Backend>(self) -> BoxedCloneSqlQuery<'f, DB, Self> {
212        BoxedCloneSqlQuery::new(self)
213    }
214
215    /// Appends a piece of SQL code at the end.
216    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
217        self.query += sql.as_ref();
218        self
219    }
220}
221
222impl SqlQuery {
223    pub(crate) fn from_sql(query: String) -> SqlQuery {
224        Self {
225            inner: self::private::Empty,
226            query,
227        }
228    }
229}
230
231impl<DB, Inner> QueryFragment<DB> for SqlQuery<Inner>
232where
233    DB: Backend + DieselReserveSpecialization,
234    Inner: QueryFragment<DB>,
235{
236    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
237        out.unsafe_to_cache_prepared();
238        self.inner.walk_ast(out.reborrow())?;
239        out.push_sql(&self.query);
240        Ok(())
241    }
242}
243
244impl<Inner> QueryId for SqlQuery<Inner> {
245    type QueryId = ();
246
247    const HAS_STATIC_QUERY_ID: bool = false;
248}
249
250impl<Inner> Query for SqlQuery<Inner> {
251    type SqlType = Untyped;
252}
253
254impl<Inner> RunQueryDslSupport for SqlQuery<Inner> {}
255
256#[derive(#[automatically_derived]
impl<Query: ::core::fmt::Debug, Value: ::core::fmt::Debug,
    ST: ::core::fmt::Debug> ::core::fmt::Debug for
    UncheckedBind<Query, Value, ST> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "UncheckedBind",
            "query", &self.query, "value", &self.value, "_marker",
            &&self._marker)
    }
}Debug, #[automatically_derived]
impl<Query: ::core::clone::Clone, Value: ::core::clone::Clone,
    ST: ::core::clone::Clone> ::core::clone::Clone for
    UncheckedBind<Query, Value, ST> {
    #[inline]
    fn clone(&self) -> UncheckedBind<Query, Value, ST> {
        UncheckedBind {
            query: ::core::clone::Clone::clone(&self.query),
            value: ::core::clone::Clone::clone(&self.value),
            _marker: ::core::clone::Clone::clone(&self._marker),
        }
    }
}Clone, #[automatically_derived]
impl<Query: ::core::marker::Copy, Value: ::core::marker::Copy,
    ST: ::core::marker::Copy> ::core::marker::Copy for
    UncheckedBind<Query, Value, ST> {
}Copy)]
257#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
258/// Returned by the [`SqlQuery::bind()`] method when binding a value to a fragment of SQL.
259pub struct UncheckedBind<Query, Value, ST> {
260    query: Query,
261    value: Value,
262    _marker: PhantomData<ST>,
263}
264
265impl<Query, Value, ST> UncheckedBind<Query, Value, ST> {
266    pub fn new(query: Query, value: Value) -> Self {
267        UncheckedBind {
268            query,
269            value,
270            _marker: PhantomData,
271        }
272    }
273
274    pub fn bind<ST2, Value2>(self, value: Value2) -> UncheckedBind<Self, Value2, ST2> {
275        UncheckedBind::new(self, value)
276    }
277
278    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
279        BoxedSqlQuery::new(self)
280    }
281
282    pub fn into_boxed_clone<'f, DB: Backend>(self) -> BoxedCloneSqlQuery<'f, DB, Self> {
283        BoxedCloneSqlQuery::new(self)
284    }
285
286    /// Construct a full SQL query using raw SQL.
287    ///
288    /// This function exists for cases where a query needs to be written that is not
289    /// supported by the query builder. Unlike most queries in Diesel, `sql_query`
290    /// will deserialize its data by name, not by index. That means that you cannot
291    /// deserialize into a tuple, and structs which you deserialize from this
292    /// function will need to have `#[derive(QueryableByName)]`.
293    ///
294    /// This function is intended for use when you want to write the entire query
295    /// using raw SQL. If you only need a small bit of raw SQL in your query, use
296    /// [`sql`](dsl::sql()) instead.
297    ///
298    /// Query parameters can be bound into the raw query using [`SqlQuery::bind()`].
299    ///
300    /// # Safety
301    ///
302    /// The implementation of `QueryableByName` will assume that columns with a
303    /// given name will have a certain type. The compiler will be unable to verify
304    /// that the given type is correct. If your query returns a column of an
305    /// unexpected type, the result may have the wrong value, or return an error.
306    ///
307    /// # Examples
308    ///
309    /// ```rust
310    /// # include!("../doctest_setup.rs");
311    /// #
312    /// # use schema::users;
313    /// #
314    /// # #[derive(QueryableByName, Debug, PartialEq)]
315    /// # struct User {
316    /// #     id: i32,
317    /// #     name: String,
318    /// # }
319    /// #
320    /// # fn main() {
321    /// #     use diesel::sql_query;
322    /// #     use diesel::sql_types::{Integer, Text};
323    /// #
324    /// #     let connection = &mut establish_connection();
325    /// #     diesel::insert_into(users::table)
326    /// #         .values(users::name.eq("Jim"))
327    /// #         .execute(connection).unwrap();
328    /// # #[cfg(feature = "postgres")]
329    /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2");
330    /// # #[cfg(not(feature = "postgres"))]
331    /// // sqlite/mysql bind syntax
332    /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?")
333    /// # ;
334    /// # let users = users
335    ///     .bind::<Integer, _>(1)
336    ///     .bind::<Text, _>("Tess")
337    ///     .get_results(connection);
338    /// let expected_users = vec![User {
339    ///     id: 3,
340    ///     name: "Jim".into(),
341    /// }];
342    /// assert_eq!(Ok(expected_users), users);
343    /// # }
344    /// ```
345    /// [`SqlQuery::bind()`]: query_builder::SqlQuery::bind()
346    pub fn sql<T: Into<String>>(self, sql: T) -> SqlQuery<Self> {
347        SqlQuery::new(self, sql.into())
348    }
349}
350
351impl<Query, Value, ST> QueryId for UncheckedBind<Query, Value, ST>
352where
353    Query: QueryId,
354    ST: QueryId,
355{
356    type QueryId = UncheckedBind<Query::QueryId, (), ST::QueryId>;
357
358    const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID;
359}
360
361impl<Query, Value, ST, DB> QueryFragment<DB> for UncheckedBind<Query, Value, ST>
362where
363    DB: Backend + HasSqlType<ST> + DieselReserveSpecialization,
364    Query: QueryFragment<DB>,
365    Value: ToSql<ST, DB>,
366{
367    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
368        self.query.walk_ast(out.reborrow())?;
369        out.push_bind_param_value_only(&self.value)?;
370        Ok(())
371    }
372}
373
374impl<Q, Value, ST> Query for UncheckedBind<Q, Value, ST> {
375    type SqlType = Untyped;
376}
377
378impl<Query, Value, ST> RunQueryDslSupport for UncheckedBind<Query, Value, ST> {}
379
380#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
381/// See [`SqlQuery::into_boxed`].
382///
383/// [`SqlQuery::into_boxed`]: SqlQuery::into_boxed()
384#[allow(missing_debug_implementations)]
385pub struct BoxedSqlQuery<'f, DB: Backend, Query> {
386    query: Query,
387    sql: String,
388    binds: Vec<Box<dyn QueryFragment<DB> + Send + 'f>>,
389}
390
391#[derive(#[automatically_derived]
#[allow(missing_debug_implementations)]
impl<'f, DB: ::core::clone::Clone + Backend, Query: ::core::clone::Clone>
    ::core::clone::Clone for BoxedCloneSqlQuery<'f, DB, Query> {
    #[inline]
    fn clone(&self) -> BoxedCloneSqlQuery<'f, DB, Query> {
        BoxedCloneSqlQuery {
            query: ::core::clone::Clone::clone(&self.query),
            sql: ::core::clone::Clone::clone(&self.sql),
            binds: ::core::clone::Clone::clone(&self.binds),
        }
    }
}Clone)]
392#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
393/// See [`SqlQuery::into_boxed_clone`].
394///
395/// [`SqlQuery::into_boxed_clone`]: SqlQuery::into_boxed_clone()
396#[allow(missing_debug_implementations)]
397pub struct BoxedCloneSqlQuery<'f, DB: Backend, Query> {
398    query: Query,
399    sql: String,
400    binds: Vec<Arc<dyn QueryFragment<DB> + Send + Sync + 'f>>,
401}
402
403struct RawBind<ST, U> {
404    value: U,
405    p: PhantomData<ST>,
406}
407
408impl<ST, U, DB> QueryFragment<DB> for RawBind<ST, U>
409where
410    DB: Backend + HasSqlType<ST>,
411    U: ToSql<ST, DB>,
412{
413    fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
414        pass.push_bind_param_value_only(&self.value)
415    }
416}
417
418impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> {
419    pub(crate) fn new(query: Query) -> Self {
420        BoxedSqlQuery {
421            query,
422            sql: "".to_string(),
423            binds: ::alloc::vec::Vec::new()alloc::vec![],
424        }
425    }
426
427    /// See [`SqlQuery::bind`].
428    ///
429    /// [`SqlQuery::bind`]: SqlQuery::bind()
430    pub fn bind<BindSt, Value>(mut self, b: Value) -> Self
431    where
432        DB: HasSqlType<BindSt>,
433        Value: ToSql<BindSt, DB> + Send + 'f,
434        BindSt: Send + 'f,
435    {
436        self.binds.push(Box::new(RawBind {
437            value: b,
438            p: PhantomData,
439        }) as Box<_>);
440        self
441    }
442
443    /// See [`SqlQuery::sql`].
444    ///
445    /// [`SqlQuery::sql`]: SqlQuery::sql()
446    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
447        self.sql += sql.as_ref();
448        self
449    }
450}
451
452impl<DB, Query> QueryFragment<DB> for BoxedSqlQuery<'_, DB, Query>
453where
454    DB: Backend + DieselReserveSpecialization,
455    Query: QueryFragment<DB>,
456{
457    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
458        out.unsafe_to_cache_prepared();
459        self.query.walk_ast(out.reborrow())?;
460        out.push_sql(&self.sql);
461
462        for b in &self.binds {
463            b.walk_ast(out.reborrow())?;
464        }
465        Ok(())
466    }
467}
468
469impl<DB: Backend, Query> QueryId for BoxedSqlQuery<'_, DB, Query> {
470    type QueryId = ();
471
472    const HAS_STATIC_QUERY_ID: bool = false;
473}
474
475impl<DB, Q> Query for BoxedSqlQuery<'_, DB, Q>
476where
477    DB: Backend,
478{
479    type SqlType = Untyped;
480}
481
482impl<DB: Backend, Query> RunQueryDslSupport for BoxedSqlQuery<'_, DB, Query> {}
483
484impl<'f, DB: Backend, Query> BoxedCloneSqlQuery<'f, DB, Query> {
485    pub(crate) fn new(query: Query) -> Self {
486        BoxedCloneSqlQuery {
487            query,
488            sql: "".to_string(),
489            binds: ::alloc::vec::Vec::new()alloc::vec![],
490        }
491    }
492
493    /// See [`SqlQuery::bind`].
494    ///
495    /// [`SqlQuery::bind`]: SqlQuery::bind()
496    pub fn bind<BindSt, Value>(mut self, b: Value) -> Self
497    where
498        DB: HasSqlType<BindSt>,
499        Value: ToSql<BindSt, DB> + Send + Sync + 'f,
500        BindSt: Send + Sync + 'f,
501    {
502        self.binds.push(Arc::new(RawBind {
503            value: b,
504            p: PhantomData,
505        }) as Arc<_>);
506        self
507    }
508
509    /// See [`SqlQuery::sql`].
510    ///
511    /// [`SqlQuery::sql`]: SqlQuery::sql()
512    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
513        self.sql += sql.as_ref();
514        self
515    }
516}
517
518impl<DB, Query> QueryFragment<DB> for BoxedCloneSqlQuery<'_, DB, Query>
519where
520    DB: Backend + DieselReserveSpecialization,
521    Query: QueryFragment<DB>,
522{
523    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
524        out.unsafe_to_cache_prepared();
525        self.query.walk_ast(out.reborrow())?;
526        out.push_sql(&self.sql);
527
528        for b in &self.binds {
529            b.walk_ast(out.reborrow())?;
530        }
531        Ok(())
532    }
533}
534
535impl<DB: Backend, Query> QueryId for BoxedCloneSqlQuery<'_, DB, Query> {
536    type QueryId = ();
537
538    const HAS_STATIC_QUERY_ID: bool = false;
539}
540
541impl<DB, Q> Query for BoxedCloneSqlQuery<'_, DB, Q>
542where
543    DB: Backend,
544{
545    type SqlType = Untyped;
546}
547
548impl<DB: Backend, Query> RunQueryDslSupport for BoxedCloneSqlQuery<'_, DB, Query> {}
549
550mod private {
551    use crate::backend::{Backend, DieselReserveSpecialization};
552    use crate::query_builder::{QueryFragment, QueryId};
553
554    #[derive(#[automatically_derived]
impl ::core::fmt::Debug for Empty {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Empty")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Empty {
    #[inline]
    fn clone(&self) -> Empty { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Empty { }Copy, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl diesel::query_builder::QueryId for Empty {
            type QueryId = Empty<>;
            const HAS_STATIC_QUERY_ID: bool = true;
            const IS_WINDOW_FUNCTION: bool = false;
        }
    };QueryId)]
555    pub struct Empty;
556
557    impl<DB> QueryFragment<DB> for Empty
558    where
559        DB: Backend + DieselReserveSpecialization,
560    {
561        fn walk_ast<'b>(
562            &'b self,
563            _pass: crate::query_builder::AstPass<'_, 'b, DB>,
564        ) -> crate::QueryResult<()> {
565            Ok(())
566        }
567    }
568}
569
570#[cfg(test)]
571mod tests {
572    fn assert_send<S: Send>(_: S) {}
573
574    #[diesel_test_helper::test]
575    fn check_boxed_sql_query_is_send() {
576        let query = crate::sql_query("SELECT 1")
577            .into_boxed::<<crate::test_helpers::TestConnection as crate::Connection>::Backend>(
578        );
579
580        assert_send(query);
581    }
582}