1use crate::backend::Backend;
2use crate::query_builder::{BindCollector, BindData, MoveableBindCollector, QueryBuilder};
3use crate::result::QueryResult;
4use crate::serialize::ToSql;
5use crate::sql_types::HasSqlType;
6use alloc::boxed::Box;
7use alloc::fmt;
8use alloc::vec::Vec;
910#[allow(missing_debug_implementations)]
11/// The primary type used when walking a Diesel AST during query execution.
12///
13/// Executing a query is generally done in multiple passes. This list includes,
14/// but is not limited to:
15///
16/// - Generating the SQL
17/// - Collecting and serializing bound values (sent separately from the SQL)
18/// - Determining if a query is safe to store in the prepared statement cache
19///
20/// When adding a new type that is used in a Diesel AST, you don't need to care
21/// about which specific passes are being performed, nor is there any way for
22/// you to find out what the current pass is. You should simply call the
23/// relevant methods and trust that they will be a no-op if they're not relevant
24/// to the current pass.
25pub struct AstPass<'a, 'b, DB>
26where
27DB: Backend,
28 DB::QueryBuilder: 'a,
29 DB::MetadataLookup: 'a,
30'b: 'a,
31{
32 internals: AstPassInternals<'a, 'b, DB>,
33 backend: &'b DB,
34}
3536impl<'a, 'b, DB> AstPass<'a, 'b, DB>
37where
38DB: Backend,
39'b: 'a,
40{
41pub(crate) fn to_sql(
42 query_builder: &'a mut DB::QueryBuilder,
43 options: &'a mut AstPassToSqlOptions,
44 backend: &'b DB,
45 ) -> Self {
46AstPass {
47 internals: AstPassInternals::ToSql(query_builder, options),
48backend,
49 }
50 }
5152pub(crate) fn collect_binds(
53 collector: &'a mut DB::BindCollector<'b>,
54 metadata_lookup: &'a mut DB::MetadataLookup,
55 backend: &'b DB,
56 ) -> Self {
57AstPass {
58 internals: AstPassInternals::CollectBinds {
59collector,
60metadata_lookup,
61 },
62backend,
63 }
64 }
6566pub(crate) fn is_safe_to_cache_prepared(result: &'a mut bool, backend: &'b DB) -> Self {
67AstPass {
68 internals: AstPassInternals::IsSafeToCachePrepared(result),
69backend,
70 }
71 }
7273pub(crate) fn debug_binds(
74 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
75 backend: &'b DB,
76 ) -> Self {
77AstPass {
78 internals: AstPassInternals::DebugBinds(formatter),
79backend,
80 }
81 }
8283/// Does running this AST pass have any effect?
84 ///
85 /// The result will be set to `false` if any method that generates SQL
86 /// is called.
87pub(crate) fn is_noop(result: &'a mut bool, backend: &'b DB) -> Self {
88AstPass {
89 internals: AstPassInternals::IsNoop(result),
90backend,
91 }
92 }
9394/// Count the number of bind parameters for the given statement
95pub(crate) fn count_binds(count: &'a mut usize, backend: &'b DB) -> Self {
96AstPass {
97 internals: AstPassInternals::CountBinds(count),
98backend,
99 }
100 }
101102/// Register an internal flag whether or not diesel should
103 /// generate explicitly qualified column names
104#[doc = " Register an internal flag whether or not diesel should"]
#[doc = " generate explicitly qualified column names"]
pub fn skip_from(&mut self, value: bool) {
if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
options.skip_from = value
}
}#[diesel_derives::__diesel_public_if(
105 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
106)]107 #[cfg(any(
108 feature = "__sqlite-shared",
109 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
110))]
111pub(crate) fn skip_from(&mut self, value: bool) {
112if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
113options.skip_from = value114 }
115 }
116117/// Call this method whenever you pass an instance of `AstPass` by value.
118 ///
119 /// Effectively copies `self`, with a narrower lifetime. When passing a
120 /// reference or a mutable reference, this is normally done by rust
121 /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
122 /// even though mutable references are not `Copy`. However, this is only
123 /// done implicitly for references. For structs with lifetimes it must be
124 /// done explicitly. This method matches the semantics of what Rust would do
125 /// implicitly if you were passing a mutable reference
126#[allow(clippy::explicit_auto_deref)] // clippy is wrong here
127pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> {
128let internals = match self.internals {
129 AstPassInternals::ToSql(ref mut builder, ref mut options) => {
130 AstPassInternals::ToSql(*builder, options)
131 }
132 AstPassInternals::CollectBinds {
133ref mut collector,
134ref mut metadata_lookup,
135 } => AstPassInternals::CollectBinds {
136 collector: *collector,
137 metadata_lookup: *metadata_lookup,
138 },
139 AstPassInternals::IsSafeToCachePrepared(ref mut result) => {
140 AstPassInternals::IsSafeToCachePrepared(result)
141 }
142 AstPassInternals::DebugBinds(ref mut f) => AstPassInternals::DebugBinds(f),
143 AstPassInternals::IsNoop(ref mut result) => AstPassInternals::IsNoop(result),
144 AstPassInternals::CountBinds(ref mut count) => AstPassInternals::CountBinds(count),
145 };
146AstPass {
147internals,
148 backend: self.backend,
149 }
150 }
151152/// Mark the current query being constructed as unsafe to store in the
153 /// prepared statement cache.
154 ///
155 /// Diesel caches prepared statements as much as possible. However, it is
156 /// important to ensure that this doesn't result in unbounded memory usage
157 /// on the database server. To ensure this is the case, any logical query
158 /// which could generate a potentially unbounded number of prepared
159 /// statements *must* call this method. Examples of AST nodes which do this
160 /// are:
161 ///
162 /// - `SqlLiteral`. We have no way of knowing if the SQL string was
163 /// constructed dynamically or not, so we must assume it was dynamic.
164 /// - `EqAny` when passed a Rust `Vec`. The `IN` operator requires one bind
165 /// parameter per element, meaning that the query could generate up to
166 /// `usize` unique prepared statements.
167 /// - `InsertStatement`. Unbounded due to the variable number of records
168 /// being inserted generating unique SQL.
169 /// - `UpdateStatement`. The number of potential queries is actually
170 /// technically bounded, but the upper bound is the number of columns on
171 /// the table factorial which is too large to be safe.
172pub fn unsafe_to_cache_prepared(&mut self) {
173if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
174**result = false
175}
176 }
177178/// Push the given SQL string on the end of the query being constructed.
179 ///
180 /// # Example
181 ///
182 /// ```rust
183 /// # use diesel::query_builder::{QueryFragment, AstPass};
184 /// # use diesel::backend::Backend;
185 /// # use diesel::QueryResult;
186 /// # struct And<Left, Right> { left: Left, right: Right }
187 /// impl<Left, Right, DB> QueryFragment<DB> for And<Left, Right>
188 /// where
189 /// DB: Backend,
190 /// Left: QueryFragment<DB>,
191 /// Right: QueryFragment<DB>,
192 /// {
193 /// fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
194 /// self.left.walk_ast(out.reborrow())?;
195 /// out.push_sql(" AND ");
196 /// self.right.walk_ast(out.reborrow())?;
197 /// Ok(())
198 /// }
199 /// }
200 /// # fn main() {}
201 /// ```
202pub fn push_sql(&mut self, sql: &str) {
203match self.internals {
204 AstPassInternals::ToSql(ref mut builder, _) => builder.push_sql(sql),
205 AstPassInternals::IsNoop(ref mut result) => **result = false,
206_ => {}
207 }
208 }
209210/// Push the given SQL identifier on the end of the query being constructed.
211 ///
212 /// The identifier will be quoted using the rules specific to the backend
213 /// the query is being constructed for.
214pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
215match self.internals {
216 AstPassInternals::ToSql(ref mut builder, _) => builder.push_identifier(identifier)?,
217 AstPassInternals::IsNoop(ref mut result) => **result = false,
218_ => {}
219 }
220Ok(())
221 }
222223/// Push a value onto the given query to be sent separate from the SQL
224 ///
225 /// This method affects multiple AST passes. It should be called at the
226 /// point in the query where you'd want the parameter placeholder (`$1` on
227 /// PG, `?` on other backends) to be inserted.
228pub fn push_bind_param<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
229where
230DB: HasSqlType<T>,
231 U: ToSql<T, DB> + ?Sized,
232 {
233match self.internals {
234 AstPassInternals::ToSql(ref mut out, _) => out.push_bind_param(),
235 AstPassInternals::CollectBinds {
236ref mut collector,
237ref mut metadata_lookup,
238 } => collector.push_bound_value(bind, metadata_lookup)?,
239 AstPassInternals::DebugBinds(ref mut f) => {
240f.push(Box::new(bind));
241 }
242 AstPassInternals::IsNoop(ref mut result) => **result = false,
243 AstPassInternals::CountBinds(ref mut count) => {
244**count += 1;
245 }
246_ => {}
247 }
248Ok(())
249 }
250251/// Push a value onto the given query to be sent separate from the SQL
252 ///
253 /// This method affects multiple AST passes. It should be called at the
254 /// point in the raw SQL is inserted. This assumes the parameter placeholder
255 /// (`$1` on PG, `?` on other backends) is already inserted.
256#[doc = " Push a value onto the given query to be sent separate from the SQL"]
#[doc = ""]
#[doc =
" This method affects multiple AST passes. It should be called at the"]
#[doc =
" point in the raw SQL is inserted. This assumes the parameter placeholder"]
#[doc = " (`$1` on PG, `?` on other backends) is already inserted."]
pub fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U)
-> QueryResult<()> where DB: HasSqlType<T>, U: ToSql<T, DB> + ?Sized {
match self.internals {
AstPassInternals::CollectBinds { .. } |
AstPassInternals::DebugBinds(..) => {
self.push_bind_param(bind)?
}
AstPassInternals::ToSql(ref mut out, _) => {
out.push_bind_param_value_only();
}
AstPassInternals::CountBinds(ref mut count) => { **count += 1; }
_ => {}
}
Ok(())
}#[diesel_derives::__diesel_public_if(
257 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
258)]259pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
260where
261DB: HasSqlType<T>,
262 U: ToSql<T, DB> + ?Sized,
263 {
264match self.internals {
265 AstPassInternals::CollectBinds { .. } | AstPassInternals::DebugBinds(..) => {
266self.push_bind_param(bind)?
267}
268 AstPassInternals::ToSql(ref mut out, _) => {
269out.push_bind_param_value_only();
270 }
271 AstPassInternals::CountBinds(ref mut count) => {
272**count += 1;
273 }
274_ => {}
275 }
276Ok(())
277 }
278279/// Push bind collector values from its data onto the query
280 ///
281 /// This method works with [MoveableBindCollector] data [MoveableBindCollector::BindData]
282 /// and is used with already collected query meaning its SQL is already built and its
283 /// bind data already collected.
284#[doc = " Push bind collector values from its data onto the query"]
#[doc = ""]
#[doc =
" This method works with [MoveableBindCollector] data [MoveableBindCollector::BindData]"]
#[doc =
" and is used with already collected query meaning its SQL is already built and its"]
#[doc = " bind data already collected."]
pub fn push_bind_collector_data<MD>(&mut self, bind_collector_data: &MD)
-> QueryResult<()> where MD: BindData, DB: Backend,
for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData =
MD> {
match self.internals {
AstPassInternals::CollectBinds { ref mut collector, metadata_lookup: _
} => collector.append_bind_data(bind_collector_data),
AstPassInternals::DebugBinds(ref mut f) => {
DB::BindCollector::push_debug_binds(bind_collector_data, f);
}
AstPassInternals::CountBinds(ref mut count) => {
**count += bind_collector_data.len();
}
_ => {}
}
Ok(())
}#[diesel_derives::__diesel_public_if(
285 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
286)]287pub(crate) fn push_bind_collector_data<MD>(
288&mut self,
289 bind_collector_data: &MD,
290 ) -> QueryResult<()>
291where
292MD: BindData,
293 DB: Backend,
294for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData = MD>,
295 {
296match self.internals {
297 AstPassInternals::CollectBinds {
298ref mut collector,
299 metadata_lookup: _,
300 } => collector.append_bind_data(bind_collector_data),
301 AstPassInternals::DebugBinds(ref mut f) => {
302 DB::BindCollector::push_debug_binds(bind_collector_data, f);
303 }
304 AstPassInternals::CountBinds(ref mut count) => {
305**count += bind_collector_data.len();
306 }
307_ => {}
308 }
309Ok(())
310 }
311312/// Get information about the backend that will consume this query
313#[cfg_attr(
314 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
315 doc(hidden)
316 )] // This is used by the `define_sql_function` macro
317#[cfg_attr(
318 diesel_docsrs,
319 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
320 )]
321pub fn backend(&self) -> &DB {
322self.backend
323 }
324325/// Get if the query should be rendered with from clauses or not
326#[cfg_attr(
327 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
328 doc(hidden)
329 )] // This is used by the `__diesel_column` macro
330#[cfg_attr(
331 diesel_docsrs,
332 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
333 )]
334pub fn should_skip_from(&self) -> bool {
335if let AstPassInternals::ToSql(_, ref options) = self.internals {
336options.skip_from
337 } else {
338false
339}
340 }
341}
342343#[allow(missing_debug_implementations)]
344/// This is separate from the struct to cause the enum to be opaque, forcing
345/// usage of the methods provided rather than matching on the enum directly.
346/// This essentially mimics the capabilities that would be available if
347/// `AstPass` were a trait.
348enum AstPassInternals<'a, 'b, DB>
349where
350DB: Backend,
351 DB::QueryBuilder: 'a,
352 DB::MetadataLookup: 'a,
353'b: 'a,
354{
355 ToSql(&'a mut DB::QueryBuilder, &'a mut AstPassToSqlOptions),
356 CollectBinds {
357 collector: &'a mut DB::BindCollector<'b>,
358 metadata_lookup: &'a mut DB::MetadataLookup,
359 },
360 IsSafeToCachePrepared(&'a mut bool),
361 DebugBinds(&'a mut Vec<Box<dyn fmt::Debug + 'b>>),
362 IsNoop(&'a mut bool),
363 CountBinds(&'a mut usize),
364}
365366#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
#[doc = " This is used to pass down additional settings to the `AstPass`"]
#[doc = " when rendering the sql string."]
#[non_exhaustive]
pub struct AstPassToSqlOptions {
skip_from: bool,
}#[diesel_derives::__diesel_public_if(
367 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
368)]369#[allow(missing_debug_implementations)]
370#[allow(missing_copy_implementations)]
371#[derive(#[automatically_derived]#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
impl ::core::default::Default for AstPassToSqlOptions {
#[inline]
fn default() -> Self {
AstPassToSqlOptions { skip_from: ::core::default::Default::default() }
}
}Default)]
372/// This is used to pass down additional settings to the `AstPass`
373/// when rendering the sql string.
374pub(crate) struct AstPassToSqlOptions {
375 skip_from: bool,
376}
377378/// This is an internal extension trait with methods required for
379/// `#[derive(MultiConnection)]`
380pub trait AstPassHelper<'a, 'b, DB>
381where
382DB: Backend,
383 DB::QueryBuilder: 'a,
384 DB::MetadataLookup: 'a,
385'b: 'a,
386{
387/// This function converts the given `AstPass` instance to
388 /// an `AstPass` instance for another database system. This requires that the
389 /// given instance contains compatible BindCollector/QueryBuilder/… implementations
390 /// for the target backend. We use explicit conversion functions here instead of relaying on
391 /// `From` impls because generating them as part of `#[derive(MultiConnection)]` is not possible
392 /// due to [compiler bugs](https://github.com/rust-lang/rust/issues/100712)
393fn cast_database<DB2>(
394self,
395 convert_bind_collector: impl Fn(&'a mut DB::BindCollector<'b>) -> &'a mut DB2::BindCollector<'b>,
396 convert_query_builder: impl Fn(&mut DB::QueryBuilder) -> &mut DB2::QueryBuilder,
397 convert_backend: impl Fn(&DB) -> &DB2,
398 convert_lookup: impl Fn(&'a mut DB::MetadataLookup) -> &'a mut DB2::MetadataLookup,
399 ) -> AstPass<'a, 'b, DB2>
400where
401DB2: Backend,
402 DB2::QueryBuilder: 'a,
403 DB2::MetadataLookup: 'a,
404'b: 'a;
405406/// This function allows to access the inner bind collector if
407 /// this `AstPass` represents a collect binds pass.
408fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)>;
409410/// This function allows to access the inner debug bind collector pass
411fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)>;
412413/// This function allows to access the inner count binds collector pass
414fn count_binds(&mut self) -> Option<(&mut usize, &'b DB)>;
415416/// Construct a new AstPass for collecting bind values into the provided buffer
417fn collect_debug_binds_pass(
418 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
419 backend: &'b DB,
420 ) -> AstPass<'a, 'b, DB>;
421}
422423impl<'a, 'b, DB> AstPassHelper<'a, 'b, DB> for AstPass<'a, 'b, DB>
424where
425DB: Backend,
426 DB::QueryBuilder: 'a,
427 DB::MetadataLookup: 'a,
428'b: 'a,
429{
430fn cast_database<DB2>(
431self,
432 convert_bind_collector: impl Fn(&'a mut DB::BindCollector<'b>) -> &'a mut DB2::BindCollector<'b>,
433 convert_query_builder: impl Fn(&mut DB::QueryBuilder) -> &mut DB2::QueryBuilder,
434 convert_backend: impl Fn(&DB) -> &DB2,
435 convert_lookup: impl Fn(&'a mut DB::MetadataLookup) -> &'a mut DB2::MetadataLookup,
436 ) -> AstPass<'a, 'b, DB2>
437where
438DB2: Backend,
439 DB2::QueryBuilder: 'a,
440 DB2::MetadataLookup: 'a,
441'b: 'a,
442 {
443let casted_pass = match self.internals {
444 AstPassInternals::ToSql(qb, opts) => {
445 AstPassInternals::ToSql(convert_query_builder(qb), opts)
446 }
447 AstPassInternals::CollectBinds {
448 collector,
449 metadata_lookup,
450 } => AstPassInternals::CollectBinds {
451 collector: convert_bind_collector(collector),
452 metadata_lookup: convert_lookup(metadata_lookup),
453 },
454 AstPassInternals::IsSafeToCachePrepared(b) => {
455 AstPassInternals::IsSafeToCachePrepared(b)
456 }
457 AstPassInternals::DebugBinds(b) => AstPassInternals::DebugBinds(b),
458 AstPassInternals::IsNoop(b) => AstPassInternals::IsNoop(b),
459 AstPassInternals::CountBinds(count) => AstPassInternals::CountBinds(count),
460 };
461462AstPass {
463 internals: casted_pass,
464 backend: convert_backend(self.backend),
465 }
466 }
467468fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)> {
469if let AstPassInternals::CollectBinds {
470 collector,
471 metadata_lookup,
472 } = &mut self.internals
473 {
474Some((collector, metadata_lookup))
475 } else {
476None477 }
478 }
479480fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)> {
481if let AstPassInternals::DebugBinds(formatter) = &mut self.internals {
482Some((formatter, self.backend))
483 } else {
484None485 }
486 }
487488fn collect_debug_binds_pass(
489 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
490 backend: &'b DB,
491 ) -> AstPass<'a, 'b, DB> {
492AstPass::debug_binds(formatter, backend)
493 }
494495fn count_binds(&mut self) -> Option<(&mut usize, &'b DB)> {
496if let AstPassInternals::CountBinds(count) = &mut self.internals {
497Some((count, self.backend))
498 } else {
499None500 }
501 }
502}