Skip to main content

diesel/query_builder/
sql_query.rs

1use super::Query;
2use crate::backend::{Backend, DieselReserveSpecialization};
3use crate::connection::Connection;
4use crate::query_builder::{AstPass, QueryFragment, QueryId};
5use crate::query_dsl::RunQueryDsl;
6use crate::result::QueryResult;
7use crate::serialize::ToSql;
8use crate::sql_types::{HasSqlType, Untyped};
9use alloc::boxed::Box;
10use alloc::string::String;
11use alloc::string::ToString;
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    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
144        BoxedSqlQuery::new(self)
145    }
146
147    /// Appends a piece of SQL code at the end.
148    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
149        self.query += sql.as_ref();
150        self
151    }
152}
153
154impl SqlQuery {
155    pub(crate) fn from_sql(query: String) -> SqlQuery {
156        Self {
157            inner: self::private::Empty,
158            query,
159        }
160    }
161}
162
163impl<DB, Inner> QueryFragment<DB> for SqlQuery<Inner>
164where
165    DB: Backend + DieselReserveSpecialization,
166    Inner: QueryFragment<DB>,
167{
168    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
169        out.unsafe_to_cache_prepared();
170        self.inner.walk_ast(out.reborrow())?;
171        out.push_sql(&self.query);
172        Ok(())
173    }
174}
175
176impl<Inner> QueryId for SqlQuery<Inner> {
177    type QueryId = ();
178
179    const HAS_STATIC_QUERY_ID: bool = false;
180}
181
182impl<Inner> Query for SqlQuery<Inner> {
183    type SqlType = Untyped;
184}
185
186impl<Inner, Conn> RunQueryDsl<Conn> for SqlQuery<Inner> {}
187
188#[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)]
189#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
190/// Returned by the [`SqlQuery::bind()`] method when binding a value to a fragment of SQL.
191pub struct UncheckedBind<Query, Value, ST> {
192    query: Query,
193    value: Value,
194    _marker: PhantomData<ST>,
195}
196
197impl<Query, Value, ST> UncheckedBind<Query, Value, ST> {
198    pub fn new(query: Query, value: Value) -> Self {
199        UncheckedBind {
200            query,
201            value,
202            _marker: PhantomData,
203        }
204    }
205
206    pub fn bind<ST2, Value2>(self, value: Value2) -> UncheckedBind<Self, Value2, ST2> {
207        UncheckedBind::new(self, value)
208    }
209
210    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
211        BoxedSqlQuery::new(self)
212    }
213
214    /// Construct a full SQL query using raw SQL.
215    ///
216    /// This function exists for cases where a query needs to be written that is not
217    /// supported by the query builder. Unlike most queries in Diesel, `sql_query`
218    /// will deserialize its data by name, not by index. That means that you cannot
219    /// deserialize into a tuple, and structs which you deserialize from this
220    /// function will need to have `#[derive(QueryableByName)]`.
221    ///
222    /// This function is intended for use when you want to write the entire query
223    /// using raw SQL. If you only need a small bit of raw SQL in your query, use
224    /// [`sql`](dsl::sql()) instead.
225    ///
226    /// Query parameters can be bound into the raw query using [`SqlQuery::bind()`].
227    ///
228    /// # Safety
229    ///
230    /// The implementation of `QueryableByName` will assume that columns with a
231    /// given name will have a certain type. The compiler will be unable to verify
232    /// that the given type is correct. If your query returns a column of an
233    /// unexpected type, the result may have the wrong value, or return an error.
234    ///
235    /// # Examples
236    ///
237    /// ```rust
238    /// # include!("../doctest_setup.rs");
239    /// #
240    /// # use schema::users;
241    /// #
242    /// # #[derive(QueryableByName, Debug, PartialEq)]
243    /// # struct User {
244    /// #     id: i32,
245    /// #     name: String,
246    /// # }
247    /// #
248    /// # fn main() {
249    /// #     use diesel::sql_query;
250    /// #     use diesel::sql_types::{Integer, Text};
251    /// #
252    /// #     let connection = &mut establish_connection();
253    /// #     diesel::insert_into(users::table)
254    /// #         .values(users::name.eq("Jim"))
255    /// #         .execute(connection).unwrap();
256    /// # #[cfg(feature = "postgres")]
257    /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2");
258    /// # #[cfg(not(feature = "postgres"))]
259    /// // sqlite/mysql bind syntax
260    /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?")
261    /// # ;
262    /// # let users = users
263    ///     .bind::<Integer, _>(1)
264    ///     .bind::<Text, _>("Tess")
265    ///     .get_results(connection);
266    /// let expected_users = vec![User {
267    ///     id: 3,
268    ///     name: "Jim".into(),
269    /// }];
270    /// assert_eq!(Ok(expected_users), users);
271    /// # }
272    /// ```
273    /// [`SqlQuery::bind()`]: query_builder::SqlQuery::bind()
274    pub fn sql<T: Into<String>>(self, sql: T) -> SqlQuery<Self> {
275        SqlQuery::new(self, sql.into())
276    }
277}
278
279impl<Query, Value, ST> QueryId for UncheckedBind<Query, Value, ST>
280where
281    Query: QueryId,
282    ST: QueryId,
283{
284    type QueryId = UncheckedBind<Query::QueryId, (), ST::QueryId>;
285
286    const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID;
287}
288
289impl<Query, Value, ST, DB> QueryFragment<DB> for UncheckedBind<Query, Value, ST>
290where
291    DB: Backend + HasSqlType<ST> + DieselReserveSpecialization,
292    Query: QueryFragment<DB>,
293    Value: ToSql<ST, DB>,
294{
295    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
296        self.query.walk_ast(out.reborrow())?;
297        out.push_bind_param_value_only(&self.value)?;
298        Ok(())
299    }
300}
301
302impl<Q, Value, ST> Query for UncheckedBind<Q, Value, ST> {
303    type SqlType = Untyped;
304}
305
306impl<Conn, Query, Value, ST> RunQueryDsl<Conn> for UncheckedBind<Query, Value, ST> {}
307
308#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
309/// See [`SqlQuery::into_boxed`].
310///
311/// [`SqlQuery::into_boxed`]: SqlQuery::into_boxed()
312#[allow(missing_debug_implementations)]
313pub struct BoxedSqlQuery<'f, DB: Backend, Query> {
314    query: Query,
315    sql: String,
316    binds: Vec<Box<dyn QueryFragment<DB> + Send + 'f>>,
317}
318
319struct RawBind<ST, U> {
320    value: U,
321    p: PhantomData<ST>,
322}
323
324impl<ST, U, DB> QueryFragment<DB> for RawBind<ST, U>
325where
326    DB: Backend + HasSqlType<ST>,
327    U: ToSql<ST, DB>,
328{
329    fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
330        pass.push_bind_param_value_only(&self.value)
331    }
332}
333
334impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> {
335    pub(crate) fn new(query: Query) -> Self {
336        BoxedSqlQuery {
337            query,
338            sql: "".to_string(),
339            binds: ::alloc::vec::Vec::new()alloc::vec![],
340        }
341    }
342
343    /// See [`SqlQuery::bind`].
344    ///
345    /// [`SqlQuery::bind`]: SqlQuery::bind()
346    pub fn bind<BindSt, Value>(mut self, b: Value) -> Self
347    where
348        DB: HasSqlType<BindSt>,
349        Value: ToSql<BindSt, DB> + Send + 'f,
350        BindSt: Send + 'f,
351    {
352        self.binds.push(Box::new(RawBind {
353            value: b,
354            p: PhantomData,
355        }) as Box<_>);
356        self
357    }
358
359    /// See [`SqlQuery::sql`].
360    ///
361    /// [`SqlQuery::sql`]: SqlQuery::sql()
362    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
363        self.sql += sql.as_ref();
364        self
365    }
366}
367
368impl<DB, Query> QueryFragment<DB> for BoxedSqlQuery<'_, DB, Query>
369where
370    DB: Backend + DieselReserveSpecialization,
371    Query: QueryFragment<DB>,
372{
373    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
374        out.unsafe_to_cache_prepared();
375        self.query.walk_ast(out.reborrow())?;
376        out.push_sql(&self.sql);
377
378        for b in &self.binds {
379            b.walk_ast(out.reborrow())?;
380        }
381        Ok(())
382    }
383}
384
385impl<DB: Backend, Query> QueryId for BoxedSqlQuery<'_, DB, Query> {
386    type QueryId = ();
387
388    const HAS_STATIC_QUERY_ID: bool = false;
389}
390
391impl<DB, Q> Query for BoxedSqlQuery<'_, DB, Q>
392where
393    DB: Backend,
394{
395    type SqlType = Untyped;
396}
397
398impl<Conn: Connection, Query> RunQueryDsl<Conn> for BoxedSqlQuery<'_, Conn::Backend, Query> {}
399
400mod private {
401    use crate::backend::{Backend, DieselReserveSpecialization};
402    use crate::query_builder::{QueryFragment, QueryId};
403
404    #[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)]
405    pub struct Empty;
406
407    impl<DB> QueryFragment<DB> for Empty
408    where
409        DB: Backend + DieselReserveSpecialization,
410    {
411        fn walk_ast<'b>(
412            &'b self,
413            _pass: crate::query_builder::AstPass<'_, 'b, DB>,
414        ) -> crate::QueryResult<()> {
415            Ok(())
416        }
417    }
418}
419
420#[cfg(test)]
421mod tests {
422    fn assert_send<S: Send>(_: S) {}
423
424    #[diesel_test_helper::test]
425    fn check_boxed_sql_query_is_send() {
426        let query = crate::sql_query("SELECT 1")
427            .into_boxed::<<crate::test_helpers::TestConnection as crate::Connection>::Backend>(
428        );
429
430        assert_send(query);
431    }
432}