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."]
14pub 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 pub fn bind<ST, Value>(self, value: Value) -> UncheckedBind<Self, Value, ST> {
80 UncheckedBind::new(self, value)
81 }
82
83 pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
89 BoxedSqlQuery::new(self)
90 }
91
92 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."]
135pub 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 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#[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 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 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}