Skip to content

Commit 5dab697

Browse files
committed
Adds BoxedSqlQuery
Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
1 parent 03ec1d9 commit 5dab697

2 files changed

Lines changed: 98 additions & 2 deletions

File tree

diesel/src/query_builder/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use self::insert_statement::{
4242
pub use self::query_id::QueryId;
4343
#[doc(hidden)]
4444
pub use self::select_statement::{BoxedSelectStatement, SelectStatement};
45-
pub use self::sql_query::SqlQuery;
45+
pub use self::sql_query::{SqlQuery, BoxedSqlQuery};
4646
#[doc(inline)]
4747
pub use self::update_statement::{
4848
AsChangeset, BoxedUpdateStatement, IntoUpdateTarget, UpdateStatement, UpdateTarget,

diesel/src/query_builder/sql_query.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use std::fmt::{self, Debug};
12
use std::marker::PhantomData;
23

34
use backend::Backend;
45
use connection::Connection;
56
use deserialize::QueryableByName;
6-
use query_builder::{AstPass, QueryFragment, QueryId};
7+
use query_builder::{AstPass, QueryFragment, QueryId, self, debug_query};
78
use query_dsl::{LoadQuery, RunQueryDsl};
89
use result::QueryResult;
910
use serialize::ToSql;
@@ -76,6 +77,15 @@ impl SqlQuery {
7677
pub fn bind<ST, Value>(self, value: Value) -> UncheckedBind<Self, Value, ST> {
7778
UncheckedBind::new(self, value)
7879
}
80+
81+
/// Internally boxes future calls on `bind` and `sql` so that they don't
82+
/// change the type.
83+
///
84+
/// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a
85+
/// loop.
86+
pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
87+
BoxedSqlQuery::new(self)
88+
}
7989
}
8090

8191
impl<DB> QueryFragment<DB> for SqlQuery
@@ -127,6 +137,10 @@ impl<Query, Value, ST> UncheckedBind<Query, Value, ST> {
127137
pub fn bind<ST2, Value2>(self, value: Value2) -> UncheckedBind<Self, Value2, ST2> {
128138
UncheckedBind::new(self, value)
129139
}
140+
141+
pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> {
142+
BoxedSqlQuery::new(self)
143+
}
130144
}
131145

132146
impl<Query, Value, ST> QueryId for UncheckedBind<Query, Value, ST>
@@ -164,3 +178,85 @@ where
164178
}
165179

166180
impl<Conn, Query, Value, ST> RunQueryDsl<Conn> for UncheckedBind<Query, Value, ST> {}
181+
182+
#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
183+
/// See `SqlQuery`
184+
pub struct BoxedSqlQuery<'f, DB: Backend, Inner> {
185+
inner: Inner,
186+
sql: String,
187+
binds: Vec<Box<dyn Fn(AstPass<DB>) -> QueryResult<()> + 'f>>,
188+
}
189+
190+
impl<DB: Backend, Inner> Debug for BoxedSqlQuery<'_, DB, Inner>
191+
where
192+
for<'a> debug_query::DebugQuery<'a, Self, DB>: Debug,
193+
{
194+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195+
query_builder::debug_query::<DB, _>(self).fmt(f)
196+
}
197+
}
198+
199+
impl<'f, DB: Backend, Inner> BoxedSqlQuery<'f, DB, Inner> {
200+
pub(crate) fn new(inner: Inner) -> Self {
201+
BoxedSqlQuery {
202+
inner,
203+
sql: "".to_string(),
204+
binds: vec![],
205+
}
206+
}
207+
208+
/// See `SqlQuery`'s `bind`
209+
pub fn bind<BindSt, Value>(mut self, b: Value) -> Self
210+
where
211+
BindSt: QueryId,
212+
DB: HasSqlType<BindSt>,
213+
Value: ToSql<BindSt, DB> + 'f,
214+
{
215+
self.binds.push(Box::new(move |mut out| {
216+
out.push_bind_param_value_only(&b)
217+
}));
218+
self
219+
}
220+
221+
/// See `SqlQuery`'s `sql`
222+
pub fn sql(mut self, sql: &str) -> Self {
223+
self.sql += sql;
224+
self
225+
}
226+
}
227+
228+
impl<DB, Inner> QueryFragment<DB> for BoxedSqlQuery<'_, DB, Inner>
229+
where
230+
DB: Backend,
231+
Inner: QueryFragment<DB>,
232+
{
233+
fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
234+
out.unsafe_to_cache_prepared();
235+
self.inner.walk_ast(out.reborrow())?;
236+
out.push_sql(&self.sql);
237+
238+
for b in &self.binds {
239+
b(out.reborrow())?;
240+
}
241+
Ok(())
242+
}
243+
}
244+
245+
impl<DB: Backend, Inner> QueryId for BoxedSqlQuery<'_, DB, Inner> {
246+
type QueryId = ();
247+
248+
const HAS_STATIC_QUERY_ID: bool = false;
249+
}
250+
251+
impl<Conn, T, Inner> LoadQuery<Conn, T> for BoxedSqlQuery<'_, Conn::Backend, Inner>
252+
where
253+
Conn: Connection,
254+
T: QueryableByName<Conn::Backend>,
255+
Self: QueryFragment<Conn::Backend> + QueryId,
256+
{
257+
fn internal_load(self, conn: &Conn) -> QueryResult<Vec<T>> {
258+
conn.query_by_name(&self)
259+
}
260+
}
261+
262+
impl<Conn: Connection, Inner> RunQueryDsl<Conn> for BoxedSqlQuery<'_, Conn::Backend, Inner> {}

0 commit comments

Comments
 (0)