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::vec::Vec;
12use core::marker::PhantomData;
13
14#[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)]
15#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
16/// The return value of `sql_query`.
17///
18/// Unlike most queries in Diesel, `SqlQuery` loads its data by column name,
19/// rather than by index. This means that you cannot deserialize this query into
20/// a tuple, and any structs used must implement `QueryableByName`.
21///
22/// See [`sql_query`](crate::sql_query()) for examples.
23pub struct SqlQuery<Inner = self::private::Empty> {
24    inner: Inner,
25    query: String,
26}
27
28impl<Inner> SqlQuery<Inner> {
29    pub(crate) fn new(inner: Inner, query: String) -> Self {
30        SqlQuery { inner, query }
31    }
32
33    /// Bind a value for use with this SQL query. The given query should have
34    /// placeholders that vary based on the database type,
35    /// like [SQLite Parameter](https://sqlite.org/lang_expr.html#varparam) syntax,
36    /// [PostgreSQL PREPARE syntax](https://www.postgresql.org/docs/current/sql-prepare.html),
37    /// or [MySQL bind syntax](https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-bind-param.html).
38    ///
39    /// For binding a variable number of values in a loop, use `into_boxed` first.
40    ///
41    /// # Safety
42    ///
43    /// This function should be used with care, as Diesel cannot validate that
44    /// the value is of the right type nor can it validate that you have passed
45    /// the correct number of parameters.
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// # include!("../doctest_setup.rs");
51    /// #
52    /// # use schema::users;
53    /// #
54    /// # #[derive(QueryableByName, Debug, PartialEq)]
55    /// # struct User {
56    /// #     id: i32,
57    /// #     name: String,
58    /// # }
59    /// #
60    /// # fn main() {
61    /// #     use diesel::sql_query;
62    /// #     use diesel::sql_types::{Integer, Text};
63    /// #
64    /// #     let connection = &mut establish_connection();
65    /// #     diesel::insert_into(users::table)
66    /// #         .values(users::name.eq("Jim"))
67    /// #         .execute(connection).unwrap();
68    /// # #[cfg(feature = "postgres")]
69    /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2");
70    /// # #[cfg(not(feature = "postgres"))]
71    /// // sqlite/mysql bind syntax
72    /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?")
73    /// # ;
74    /// # let users = users
75    ///     .bind::<Integer, _>(1)
76    ///     .bind::<Text, _>("Tess")
77    ///     .get_results(connection);
78    /// let expected_users = vec![User {
79    ///     id: 3,
80    ///     name: "Jim".into(),
81    /// }];
82    /// assert_eq!(Ok(expected_users), users);
83    /// # }
84    /// ```
85    pub fn bind<ST, Value>(self, value: Value) -> UncheckedBind<Self, Value, ST> {
86        UncheckedBind::new(self, value)
87    }
88
89    /// Internally boxes the query, which allows to  calls `bind` and `sql` so that they don't
90    /// change the type nor the instance. This allows to call `bind` or `sql`
91    /// in a loop, e.g.:
92    ///
93    /// ```
94    /// # include!("../doctest_setup.rs");
95    /// #
96    /// # use schema::users;
97    /// #
98    /// # #[derive(QueryableByName, Debug, PartialEq)]
99    /// # struct User {
100    /// #     id: i32,
101    /// #     name: String,
102    /// # }
103    /// #
104    /// # fn main() {
105    /// #     use diesel::sql_query;
106    /// #     use diesel::sql_types::{Integer};
107    /// #
108    /// #     let connection = &mut establish_connection();
109    /// #     diesel::insert_into(users::table)
110    /// #         .values(users::name.eq("Jim"))
111    /// #         .execute(connection).unwrap();
112    /// let mut q = diesel::sql_query("SELECT * FROM users WHERE id IN(").into_boxed();
113    /// for (idx, user_id) in [3, 4, 5].into_iter().enumerate() {
114    ///     if idx != 0 {
115    ///         q = q.sql(", ");
116    ///     }
117    /// # #[cfg(feature = "postgres")]
118    /// # {
119    ///     q = q
120    ///         // postgresql bind syntax
121    ///         .sql(format!("${}", idx + 1))
122    ///         .bind::<Integer, _>(user_id);
123    /// # }
124    /// # #[cfg(not(feature = "postgres"))]
125    /// # {
126    /// #   q = q
127    /// #       .sql(format!("?"))
128    /// #       .bind::<Integer, _>(user_id);
129    /// # }
130    /// }
131    /// let users = q.sql(");").get_results(connection);
132    /// let expected_users = vec![User {
133    ///     id: 3,
134    ///     name: "Jim".into(),
135    /// }];
136    /// assert_eq!(Ok(expected_users), users);
137    /// # }
138    /// ```
139    ///
140    /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a
141    /// loop.
142    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
143        BoxedSqlQuery::new(self)
144    }
145
146    /// Appends a piece of SQL code at the end.
147    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
148        self.query += sql.as_ref();
149        self
150    }
151}
152
153impl SqlQuery {
154    pub(crate) fn from_sql(query: String) -> SqlQuery {
155        Self {
156            inner: self::private::Empty,
157            query,
158        }
159    }
160}
161
162impl<DB, Inner> QueryFragment<DB> for SqlQuery<Inner>
163where
164    DB: Backend + DieselReserveSpecialization,
165    Inner: QueryFragment<DB>,
166{
167    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
168        out.unsafe_to_cache_prepared();
169        self.inner.walk_ast(out.reborrow())?;
170        out.push_sql(&self.query);
171        Ok(())
172    }
173}
174
175impl<Inner> QueryId for SqlQuery<Inner> {
176    type QueryId = ();
177
178    const HAS_STATIC_QUERY_ID: bool = false;
179}
180
181impl<Inner> Query for SqlQuery<Inner> {
182    type SqlType = Untyped;
183}
184
185impl<Inner> RunQueryDslSupport for SqlQuery<Inner> {}
186
187#[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)]
188#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
189/// Returned by the [`SqlQuery::bind()`] method when binding a value to a fragment of SQL.
190pub struct UncheckedBind<Query, Value, ST> {
191    query: Query,
192    value: Value,
193    _marker: PhantomData<ST>,
194}
195
196impl<Query, Value, ST> UncheckedBind<Query, Value, ST> {
197    pub fn new(query: Query, value: Value) -> Self {
198        UncheckedBind {
199            query,
200            value,
201            _marker: PhantomData,
202        }
203    }
204
205    pub fn bind<ST2, Value2>(self, value: Value2) -> UncheckedBind<Self, Value2, ST2> {
206        UncheckedBind::new(self, value)
207    }
208
209    pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
210        BoxedSqlQuery::new(self)
211    }
212
213    /// Construct a full SQL query using raw SQL.
214    ///
215    /// This function exists for cases where a query needs to be written that is not
216    /// supported by the query builder. Unlike most queries in Diesel, `sql_query`
217    /// will deserialize its data by name, not by index. That means that you cannot
218    /// deserialize into a tuple, and structs which you deserialize from this
219    /// function will need to have `#[derive(QueryableByName)]`.
220    ///
221    /// This function is intended for use when you want to write the entire query
222    /// using raw SQL. If you only need a small bit of raw SQL in your query, use
223    /// [`sql`](dsl::sql()) instead.
224    ///
225    /// Query parameters can be bound into the raw query using [`SqlQuery::bind()`].
226    ///
227    /// # Safety
228    ///
229    /// The implementation of `QueryableByName` will assume that columns with a
230    /// given name will have a certain type. The compiler will be unable to verify
231    /// that the given type is correct. If your query returns a column of an
232    /// unexpected type, the result may have the wrong value, or return an error.
233    ///
234    /// # Examples
235    ///
236    /// ```rust
237    /// # include!("../doctest_setup.rs");
238    /// #
239    /// # use schema::users;
240    /// #
241    /// # #[derive(QueryableByName, Debug, PartialEq)]
242    /// # struct User {
243    /// #     id: i32,
244    /// #     name: String,
245    /// # }
246    /// #
247    /// # fn main() {
248    /// #     use diesel::sql_query;
249    /// #     use diesel::sql_types::{Integer, Text};
250    /// #
251    /// #     let connection = &mut establish_connection();
252    /// #     diesel::insert_into(users::table)
253    /// #         .values(users::name.eq("Jim"))
254    /// #         .execute(connection).unwrap();
255    /// # #[cfg(feature = "postgres")]
256    /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2");
257    /// # #[cfg(not(feature = "postgres"))]
258    /// // sqlite/mysql bind syntax
259    /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?")
260    /// # ;
261    /// # let users = users
262    ///     .bind::<Integer, _>(1)
263    ///     .bind::<Text, _>("Tess")
264    ///     .get_results(connection);
265    /// let expected_users = vec![User {
266    ///     id: 3,
267    ///     name: "Jim".into(),
268    /// }];
269    /// assert_eq!(Ok(expected_users), users);
270    /// # }
271    /// ```
272    /// [`SqlQuery::bind()`]: query_builder::SqlQuery::bind()
273    pub fn sql<T: Into<String>>(self, sql: T) -> SqlQuery<Self> {
274        SqlQuery::new(self, sql.into())
275    }
276}
277
278impl<Query, Value, ST> QueryId for UncheckedBind<Query, Value, ST>
279where
280    Query: QueryId,
281    ST: QueryId,
282{
283    type QueryId = UncheckedBind<Query::QueryId, (), ST::QueryId>;
284
285    const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID;
286}
287
288impl<Query, Value, ST, DB> QueryFragment<DB> for UncheckedBind<Query, Value, ST>
289where
290    DB: Backend + HasSqlType<ST> + DieselReserveSpecialization,
291    Query: QueryFragment<DB>,
292    Value: ToSql<ST, DB>,
293{
294    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
295        self.query.walk_ast(out.reborrow())?;
296        out.push_bind_param_value_only(&self.value)?;
297        Ok(())
298    }
299}
300
301impl<Q, Value, ST> Query for UncheckedBind<Q, Value, ST> {
302    type SqlType = Untyped;
303}
304
305impl<Query, Value, ST> RunQueryDslSupport for UncheckedBind<Query, Value, ST> {}
306
307#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
308/// See [`SqlQuery::into_boxed`].
309///
310/// [`SqlQuery::into_boxed`]: SqlQuery::into_boxed()
311#[allow(missing_debug_implementations)]
312pub struct BoxedSqlQuery<'f, DB: Backend, Query> {
313    query: Query,
314    sql: String,
315    binds: Vec<Box<dyn QueryFragment<DB> + Send + 'f>>,
316}
317
318struct RawBind<ST, U> {
319    value: U,
320    p: PhantomData<ST>,
321}
322
323impl<ST, U, DB> QueryFragment<DB> for RawBind<ST, U>
324where
325    DB: Backend + HasSqlType<ST>,
326    U: ToSql<ST, DB>,
327{
328    fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
329        pass.push_bind_param_value_only(&self.value)
330    }
331}
332
333impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> {
334    pub(crate) fn new(query: Query) -> Self {
335        BoxedSqlQuery {
336            query,
337            sql: "".to_string(),
338            binds: ::alloc::vec::Vec::new()alloc::vec![],
339        }
340    }
341
342    /// See [`SqlQuery::bind`].
343    ///
344    /// [`SqlQuery::bind`]: SqlQuery::bind()
345    pub fn bind<BindSt, Value>(mut self, b: Value) -> Self
346    where
347        DB: HasSqlType<BindSt>,
348        Value: ToSql<BindSt, DB> + Send + 'f,
349        BindSt: Send + 'f,
350    {
351        self.binds.push(Box::new(RawBind {
352            value: b,
353            p: PhantomData,
354        }) as Box<_>);
355        self
356    }
357
358    /// See [`SqlQuery::sql`].
359    ///
360    /// [`SqlQuery::sql`]: SqlQuery::sql()
361    pub fn sql<T: AsRef<str>>(mut self, sql: T) -> Self {
362        self.sql += sql.as_ref();
363        self
364    }
365}
366
367impl<DB, Query> QueryFragment<DB> for BoxedSqlQuery<'_, DB, Query>
368where
369    DB: Backend + DieselReserveSpecialization,
370    Query: QueryFragment<DB>,
371{
372    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
373        out.unsafe_to_cache_prepared();
374        self.query.walk_ast(out.reborrow())?;
375        out.push_sql(&self.sql);
376
377        for b in &self.binds {
378            b.walk_ast(out.reborrow())?;
379        }
380        Ok(())
381    }
382}
383
384impl<DB: Backend, Query> QueryId for BoxedSqlQuery<'_, DB, Query> {
385    type QueryId = ();
386
387    const HAS_STATIC_QUERY_ID: bool = false;
388}
389
390impl<DB, Q> Query for BoxedSqlQuery<'_, DB, Q>
391where
392    DB: Backend,
393{
394    type SqlType = Untyped;
395}
396
397impl<DB: Backend, Query> RunQueryDslSupport for BoxedSqlQuery<'_, DB, Query> {}
398
399mod private {
400    use crate::backend::{Backend, DieselReserveSpecialization};
401    use crate::query_builder::{QueryFragment, QueryId};
402
403    #[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)]
404    pub struct Empty;
405
406    impl<DB> QueryFragment<DB> for Empty
407    where
408        DB: Backend + DieselReserveSpecialization,
409    {
410        fn walk_ast<'b>(
411            &'b self,
412            _pass: crate::query_builder::AstPass<'_, 'b, DB>,
413        ) -> crate::QueryResult<()> {
414            Ok(())
415        }
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    fn assert_send<S: Send>(_: S) {}
422
423    #[diesel_test_helper::test]
424    fn check_boxed_sql_query_is_send() {
425        let query = crate::sql_query("SELECT 1")
426            .into_boxed::<<crate::test_helpers::TestConnection as crate::Connection>::Backend>(
427        );
428
429        assert_send(query);
430    }
431}