diesel/query_builder/
sql_query.rs

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