Skip to content

Commit 97e5cba

Browse files
committed
Refactor the query builder to make it easier to add new AST passes
I had initially wanted to move us towards a more of a visitor pattern, where the `AstVisitor` trait would have most of the methods from `QueryBuilder`, with `BindCollector` and `fn unsafe_to_cache_prepared(&mut self)` tacked on. However, I quickly hit a wall there when I realized that `BindCollector` cannot be made object safe, which would prevent me from making `QueryFragment` object safe, so that won't work. The benefit of using the visitor pattern here would have been that it would be easier for code *outside* of Diesel to add new AST passes if it wanted to (within limits, I don't want to go so far as to have `visit_specific_type` as that would be even more painful than what we have now). This form doesn't bring that benefit. However, using an enum instead does make it easier for us to add new passes within Diesel itself, since we now have a single function which will apply to all future AST passes for the majority of nodes (most nodes don't care about any pass other than `to_sql`, they just call their children). The plan is to follow this up by moving `to_sql` into the `walk_ast` method as well, and eliminate `Changeset`, and `InsertValues` since it'll now be easier to add the new "this is like that other pass, but slightly different for this one case" type passes. The signature is a little funky here, since we're passing a mutable reference to an enum which just wraps a mutable reference. I really only want the inner `&mut`, since I want implementors to be able to mutate the `QueryBuilder` or `BindCollector`, but not change the variant. The reason for the outer `&mut` is because reborrowing doesn't apply to structs. We can't pass `AstPass<'a>` multiple times, as it's not `Copy`. When we pass a mutable reference directly, Rust creates a new reference with a narrowed lifetime. We can't do that here. The reason I kept the inner `&mut` instead of just having `AstPass` own the value is that we'd have no way to cleanly get the value being mutated back out of it when we were done.
1 parent ea76269 commit 97e5cba

37 files changed

Lines changed: 257 additions & 523 deletions

diesel/src/connection/statement_cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl<DB, Statement> StatementCache<DB, Statement> where
137137

138138
let cache_key = try!(StatementCacheKey::for_source(source, bind_types));
139139

140-
if !source.is_safe_to_cache_prepared() {
140+
if !source.is_safe_to_cache_prepared()? {
141141
let sql = try!(cache_key.sql(source));
142142
return prepare_fn(&sql).map(MaybeCached::CannotCache)
143143
}

diesel/src/expression/array_comparison.rs

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use backend::Backend;
22
use expression::*;
33
use expression::helper_types::SqlTypeOf;
4-
use query_builder::{Query, QueryBuilder, QueryFragment, BuildQueryResult};
4+
use query_builder::*;
55
use result::QueryResult;
66
use types::Bool;
77

@@ -79,16 +79,11 @@ impl<T, U, DB> QueryFragment<DB> for In<T, U> where
7979
Ok(())
8080
}
8181

82-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
83-
try!(self.left.collect_binds(out));
84-
try!(self.values.collect_binds(out));
82+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
83+
self.left.walk_ast(pass)?;
84+
self.values.walk_ast(pass)?;
8585
Ok(())
8686
}
87-
88-
fn is_safe_to_cache_prepared(&self) -> bool {
89-
self.left.is_safe_to_cache_prepared() &&
90-
self.values.is_safe_to_cache_prepared()
91-
}
9287
}
9388

9489
impl<T, U, DB> QueryFragment<DB> for NotIn<T, U> where
@@ -108,16 +103,11 @@ impl<T, U, DB> QueryFragment<DB> for NotIn<T, U> where
108103
Ok(())
109104
}
110105

111-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
112-
try!(self.left.collect_binds(out));
113-
try!(self.values.collect_binds(out));
106+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
107+
self.left.walk_ast(pass)?;
108+
self.values.walk_ast(pass)?;
114109
Ok(())
115110
}
116-
117-
fn is_safe_to_cache_prepared(&self) -> bool {
118-
self.left.is_safe_to_cache_prepared() &&
119-
self.values.is_safe_to_cache_prepared()
120-
}
121111
}
122112

123113
impl_query_id!(In<T, U>);
@@ -212,16 +202,16 @@ impl<T, DB> QueryFragment<DB> for Many<T> where
212202
Ok(())
213203
}
214204

215-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
216-
for value in &self.0 {
217-
try!(value.collect_binds(out));
205+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
206+
if let AstPass::IsSafeToCachePrepared(ref mut result) = *pass {
207+
**result = false;
208+
} else {
209+
for value in &self.0 {
210+
value.walk_ast(pass)?;
211+
}
218212
}
219213
Ok(())
220214
}
221-
222-
fn is_safe_to_cache_prepared(&self) -> bool {
223-
false
224-
}
225215
}
226216

227217
impl_query_id!(noop: Many<T>);
@@ -262,12 +252,8 @@ impl<T, ST, DB> QueryFragment<DB> for Subselect<T, ST> where
262252
self.values.to_sql(out)
263253
}
264254

265-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
266-
self.values.collect_binds(out)
267-
}
268-
269-
fn is_safe_to_cache_prepared(&self) -> bool {
270-
self.values.is_safe_to_cache_prepared()
255+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
256+
self.values.walk_ast(pass)
271257
}
272258
}
273259

diesel/src/expression/bound.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,11 @@ impl<T, U, DB> QueryFragment<DB> for Bound<T, U> where
3434
Ok(())
3535
}
3636

37-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
38-
out.push_bound_value(&self.item)
39-
}
40-
41-
fn is_safe_to_cache_prepared(&self) -> bool {
42-
true
37+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
38+
if let AstPass::CollectBinds(ref mut out) = *pass {
39+
out.push_bound_value(&self.item)?;
40+
}
41+
Ok(())
4342
}
4443
}
4544

diesel/src/expression/coerce.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,8 @@ impl<T, ST, DB> QueryFragment<DB> for Coerce<T, ST> where
5656
self.expr.to_sql(out)
5757
}
5858

59-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
60-
self.expr.collect_binds(out)
61-
}
62-
63-
fn is_safe_to_cache_prepared(&self) -> bool {
64-
self.expr.is_safe_to_cache_prepared()
59+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
60+
self.expr.walk_ast(pass)
6561
}
6662
}
6763

diesel/src/expression/count.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,8 @@ impl<T: QueryFragment<DB>, DB: Backend> QueryFragment<DB> for Count<T> {
8888
Ok(())
8989
}
9090

91-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
92-
try!(self.target.collect_binds(out));
93-
Ok(())
94-
}
95-
96-
fn is_safe_to_cache_prepared(&self) -> bool {
97-
self.target.is_safe_to_cache_prepared()
91+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
92+
self.target.walk_ast(pass)
9893
}
9994
}
10095

@@ -115,13 +110,9 @@ impl<DB: Backend> QueryFragment<DB> for CountStar {
115110
Ok(())
116111
}
117112

118-
fn collect_binds(&self, _out: &mut DB::BindCollector) -> QueryResult<()> {
113+
fn walk_ast(&self, _: &mut AstPass<DB>) -> QueryResult<()> {
119114
Ok(())
120115
}
121-
122-
fn is_safe_to_cache_prepared(&self) -> bool {
123-
true
124-
}
125116
}
126117

127118
impl_query_id!(CountStar);

diesel/src/expression/exists.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,10 @@ impl<T, DB> QueryFragment<DB> for Exists<T> where
6363
Ok(())
6464
}
6565

66-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
67-
try!(self.0.collect_binds(out));
66+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
67+
self.0.walk_ast(pass)?;
6868
Ok(())
6969
}
70-
71-
fn is_safe_to_cache_prepared(&self) -> bool {
72-
self.0.is_safe_to_cache_prepared()
73-
}
7470
}
7571

7672
impl_query_id!(Exists<T>);

diesel/src/expression/functions/aggregate_folding.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,10 @@ macro_rules! fold_function {
3939
Ok(())
4040
}
4141

42-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
43-
try!(self.target.collect_binds(out));
42+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
43+
self.target.walk_ast(pass)?;
4444
Ok(())
4545
}
46-
47-
fn is_safe_to_cache_prepared(&self) -> bool {
48-
self.target.is_safe_to_cache_prepared()
49-
}
5046
}
5147

5248
impl_query_id!($type_name<T>);

diesel/src/expression/functions/aggregate_ordering.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,10 @@ macro_rules! ord_function {
3838
Ok(())
3939
}
4040

41-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
42-
try!(self.target.collect_binds(out));
41+
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
42+
self.target.walk_ast(pass)?;
4343
Ok(())
4444
}
45-
46-
fn is_safe_to_cache_prepared(&self) -> bool {
47-
self.target.is_safe_to_cache_prepared()
48-
}
4945
}
5046

5147
impl_query_id!($type_name<T>);

diesel/src/expression/functions/date_and_time.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,9 @@ impl<DB: Backend> QueryFragment<DB> for now {
2323
Ok(())
2424
}
2525

26-
fn collect_binds(&self, _out: &mut DB::BindCollector) -> QueryResult<()> {
26+
fn walk_ast(&self, _: &mut AstPass<DB>) -> QueryResult<()> {
2727
Ok(())
2828
}
29-
30-
fn is_safe_to_cache_prepared(&self) -> bool {
31-
true
32-
}
3329
}
3430

3531
impl_query_id!(now);

diesel/src/expression/functions/mod.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,11 @@ macro_rules! sql_function_body {
4747
Ok(())
4848
}
4949

50-
fn collect_binds(&self, out: &mut DB::BindCollector) -> $crate::result::QueryResult<()> {
51-
try!($crate::query_builder::QueryFragment::collect_binds(
52-
&($(&self.$arg_name),*), out));
50+
fn walk_ast(&self, pass: &mut $crate::query_builder::AstPass<DB>) -> $crate::result::QueryResult<()> {
51+
try!($crate::query_builder::QueryFragment::walk_ast(
52+
&($(&self.$arg_name),*), pass));
5353
Ok(())
5454
}
55-
56-
fn is_safe_to_cache_prepared(&self) -> bool {
57-
($(&self.$arg_name),*).is_safe_to_cache_prepared()
58-
}
5955
}
6056

6157
impl_query_id!($struct_name<$($arg_name),+>);
@@ -166,13 +162,9 @@ macro_rules! no_arg_sql_function_body {
166162
Ok(())
167163
}
168164

169-
fn collect_binds(&self, _out: &mut DB::BindCollector) -> $crate::result::QueryResult<()> {
165+
fn walk_ast(&self, _: &mut $crate::query_builder::AstPass<DB>) -> QueryResult<()> {
170166
Ok(())
171167
}
172-
173-
fn is_safe_to_cache_prepared(&self) -> bool {
174-
true
175-
}
176168
}
177169
};
178170

@@ -188,13 +180,9 @@ macro_rules! no_arg_sql_function_body {
188180
Ok(())
189181
}
190182

191-
fn collect_binds(&self, _out: &mut DB::BindCollector) -> $crate::result::QueryResult<()> {
183+
fn walk_ast(&self, _: &mut $crate::query_builder::AstPass<DB>) -> QueryResult<()> {
192184
Ok(())
193185
}
194-
195-
fn is_safe_to_cache_prepared(&self) -> bool {
196-
true
197-
}
198186
}
199187
};
200188
}

0 commit comments

Comments
 (0)