diesel/query_builder/ast_pass.rs
1use std::{fmt, mem};
2
3use backend::Backend;
4use query_builder::{BindCollector, QueryBuilder};
5use result::QueryResult;
6use serialize::ToSql;
7use sql_types::HasSqlType;
8
9#[allow(missing_debug_implementations)]
10/// The primary type used when walking a Diesel AST during query execution.
11///
12/// Executing a query is generally done in multiple passes. This list includes,
13/// but is not limited to:
14///
15/// - Generating the SQL
16/// - Collecting and serializing bound values (sent separately from the SQL)
17/// - Determining if a query is safe to store in the prepared statement cache
18///
19/// When adding a new type that is used in a Diesel AST, you don't need to care
20/// about which specific passes are being performed, nor is there any way for
21/// you to find out what the current pass is. You should simply call the
22/// relevant methods and trust that they will be a no-op if they're not relevant
23/// to the current pass.
24pub struct AstPass<'a, DB>
25where
26 DB: Backend,
27 DB::QueryBuilder: 'a,
28 DB::BindCollector: 'a,
29 DB::MetadataLookup: 'a,
30{
31 internals: AstPassInternals<'a, DB>,
32}
33
34impl<'a, DB> AstPass<'a, DB>
35where
36 DB: Backend,
37{
38 #[doc(hidden)]
39 #[allow(clippy::wrong_self_convention)]
40 pub fn to_sql(query_builder: &'a mut DB::QueryBuilder) -> Self {
41 AstPass {
42 internals: AstPassInternals::ToSql(query_builder),
43 }
44 }
45
46 #[doc(hidden)]
47 pub fn collect_binds(
48 collector: &'a mut DB::BindCollector,
49 metadata_lookup: &'a DB::MetadataLookup,
50 ) -> Self {
51 AstPass {
52 internals: AstPassInternals::CollectBinds {
53 collector,
54 metadata_lookup,
55 },
56 }
57 }
58
59 #[doc(hidden)]
60 pub fn is_safe_to_cache_prepared(result: &'a mut bool) -> Self {
61 AstPass {
62 internals: AstPassInternals::IsSafeToCachePrepared(result),
63 }
64 }
65
66 #[doc(hidden)]
67 pub fn debug_binds(formatter: &'a mut fmt::DebugList<'a, 'a>) -> Self {
68 AstPass {
69 internals: AstPassInternals::DebugBinds(formatter),
70 }
71 }
72
73 /// Does running this AST pass have any effect?
74 ///
75 /// The result will be set to `false` if any method that generates SQL
76 /// is called.
77 pub(crate) fn is_noop(result: &'a mut bool) -> Self {
78 AstPass {
79 internals: AstPassInternals::IsNoop(result),
80 }
81 }
82
83 /// Call this method whenever you pass an instance of `AstPass` by value.
84 ///
85 /// Effectively copies `self`, with a narrower lifetime. When passing a
86 /// reference or a mutable reference, this is normally done by rust
87 /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
88 /// even though mutable references are not `Copy`. However, this is only
89 /// done implicitly for references. For structs with lifetimes it must be
90 /// done explicitly. This method matches the semantics of what Rust would do
91 /// implicitly if you were passing a mutable reference
92 // Clippy is wrong, this cannot be expressed with pointer casting
93 #[allow(clippy::transmute_ptr_to_ptr)]
94 pub fn reborrow(&mut self) -> AstPass<DB> {
95 use self::AstPassInternals::*;
96 let internals = match self.internals {
97 ToSql(ref mut builder) => ToSql(&mut **builder),
98 CollectBinds {
99 ref mut collector,
100 metadata_lookup,
101 } => CollectBinds {
102 collector: &mut **collector,
103 metadata_lookup: &*metadata_lookup,
104 },
105 IsSafeToCachePrepared(ref mut result) => IsSafeToCachePrepared(&mut **result),
106 DebugBinds(ref mut f) => {
107 // Safe because the lifetime is always being shortened.
108 let f_with_shorter_lifetime = unsafe { mem::transmute(&mut **f) };
109 DebugBinds(f_with_shorter_lifetime)
110 }
111 IsNoop(ref mut result) => IsNoop(&mut **result),
112 };
113 AstPass { internals }
114 }
115
116 /// Mark the current query being constructed as unsafe to store in the
117 /// prepared statement cache.
118 ///
119 /// Diesel caches prepared statements as much as possible. However, it is
120 /// important to ensure that this doesn't result in unbounded memory usage
121 /// on the database server. To ensure this is the case, any logical query
122 /// which could generate a potentially unbounded number of prepared
123 /// statements *must* call this method. Examples of AST nodes which do this
124 /// are:
125 ///
126 /// - `SqlLiteral`. We have no way of knowing if the SQL string was
127 /// constructed dynamically or not, so we must assume it was dynamic.
128 /// - `EqAny` when passed a Rust `Vec`. The `IN` operator requires one bind
129 /// parameter per element, meaning that the query could generate up to
130 /// `usize` unique prepared statements.
131 /// - `InsertStatement`. Unbounded due to the variable number of records
132 /// being inserted generating unique SQL.
133 /// - `UpdateStatement`. The number of potential queries is actually
134 /// technically bounded, but the upper bound is the number of columns on
135 /// the table factorial which is too large to be safe.
136 pub fn unsafe_to_cache_prepared(&mut self) {
137 if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
138 **result = false
139 }
140 }
141
142 /// Push the given SQL string on the end of the query being constructed.
143 ///
144 /// # Example
145 ///
146 /// ```rust
147 /// # extern crate diesel;
148 /// # use diesel::query_builder::{QueryFragment, AstPass};
149 /// # use diesel::backend::Backend;
150 /// # use diesel::QueryResult;
151 /// # struct And<Left, Right> { left: Left, right: Right }
152 /// impl<Left, Right, DB> QueryFragment<DB> for And<Left, Right>
153 /// where
154 /// DB: Backend,
155 /// Left: QueryFragment<DB>,
156 /// Right: QueryFragment<DB>,
157 /// {
158 /// fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
159 /// self.left.walk_ast(out.reborrow())?;
160 /// out.push_sql(" AND ");
161 /// self.right.walk_ast(out.reborrow())?;
162 /// Ok(())
163 /// }
164 /// }
165 /// # fn main() {}
166 /// ```
167 pub fn push_sql(&mut self, sql: &str) {
168 match self.internals {
169 AstPassInternals::ToSql(ref mut builder) => builder.push_sql(sql),
170 AstPassInternals::IsNoop(ref mut result) => **result = false,
171 _ => {}
172 }
173 }
174
175 /// Push the given SQL identifier on the end of the query being constructed.
176 ///
177 /// The identifier will be quoted using the rules specific to the backend
178 /// the query is being constructed for.
179 pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
180 match self.internals {
181 AstPassInternals::ToSql(ref mut builder) => builder.push_identifier(identifier)?,
182 AstPassInternals::IsNoop(ref mut result) => **result = false,
183 _ => {}
184 }
185 Ok(())
186 }
187
188 /// Push a value onto the given query to be sent separate from the SQL
189 ///
190 /// This method affects multiple AST passes. It should be called at the
191 /// point in the query where you'd want the parameter placeholder (`$1` on
192 /// PG, `?` on other backends) to be inserted.
193 pub fn push_bind_param<T, U>(&mut self, bind: &U) -> QueryResult<()>
194 where
195 DB: HasSqlType<T>,
196 U: ToSql<T, DB>,
197 {
198 use self::AstPassInternals::*;
199 match self.internals {
200 ToSql(ref mut out) => out.push_bind_param(),
201 CollectBinds {
202 ref mut collector,
203 metadata_lookup,
204 } => collector.push_bound_value(bind, metadata_lookup)?,
205 DebugBinds(ref mut f) => {
206 f.entry(bind);
207 }
208 IsNoop(ref mut result) => **result = false,
209 _ => {}
210 }
211 Ok(())
212 }
213
214 #[doc(hidden)]
215 pub fn push_bind_param_value_only<T, U>(&mut self, bind: &U) -> QueryResult<()>
216 where
217 DB: HasSqlType<T>,
218 U: ToSql<T, DB>,
219 {
220 use self::AstPassInternals::*;
221 match self.internals {
222 CollectBinds { .. } | DebugBinds(..) => self.push_bind_param(bind)?,
223 _ => {}
224 }
225 Ok(())
226 }
227}
228
229#[allow(missing_debug_implementations)]
230/// This is separate from the struct to cause the enum to be opaque, forcing
231/// usage of the methods provided rather than matching on the enum directly.
232/// This essentially mimics the capabilities that would be available if
233/// `AstPass` were a trait.
234enum AstPassInternals<'a, DB>
235where
236 DB: Backend,
237 DB::QueryBuilder: 'a,
238 DB::BindCollector: 'a,
239 DB::MetadataLookup: 'a,
240{
241 ToSql(&'a mut DB::QueryBuilder),
242 CollectBinds {
243 collector: &'a mut DB::BindCollector,
244 metadata_lookup: &'a DB::MetadataLookup,
245 },
246 IsSafeToCachePrepared(&'a mut bool),
247 DebugBinds(&'a mut fmt::DebugList<'a, 'a>),
248 IsNoop(&'a mut bool),
249}