1use std::fmt;
23use crate::backend::Backend;
4use crate::query_builder::{BindCollector, MoveableBindCollector, QueryBuilder};
5use crate::result::QueryResult;
6use crate::serialize::ToSql;
7use crate::sql_types::HasSqlType;
89#[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, 'b, DB>
25where
26DB: Backend,
27 DB::QueryBuilder: 'a,
28 DB::MetadataLookup: 'a,
29'b: 'a,
30{
31 internals: AstPassInternals<'a, 'b, DB>,
32 backend: &'b DB,
33}
3435impl<'a, 'b, DB> AstPass<'a, 'b, DB>
36where
37DB: Backend,
38'b: 'a,
39{
40pub(crate) fn to_sql(
41 query_builder: &'a mut DB::QueryBuilder,
42 options: &'a mut AstPassToSqlOptions,
43 backend: &'b DB,
44 ) -> Self {
45AstPass {
46 internals: AstPassInternals::ToSql(query_builder, options),
47backend,
48 }
49 }
5051pub(crate) fn collect_binds(
52 collector: &'a mut DB::BindCollector<'b>,
53 metadata_lookup: &'a mut DB::MetadataLookup,
54 backend: &'b DB,
55 ) -> Self {
56AstPass {
57 internals: AstPassInternals::CollectBinds {
58collector,
59metadata_lookup,
60 },
61backend,
62 }
63 }
6465pub(crate) fn is_safe_to_cache_prepared(result: &'a mut bool, backend: &'b DB) -> Self {
66AstPass {
67 internals: AstPassInternals::IsSafeToCachePrepared(result),
68backend,
69 }
70 }
7172pub(crate) fn debug_binds(
73 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
74 backend: &'b DB,
75 ) -> Self {
76AstPass {
77 internals: AstPassInternals::DebugBinds(formatter),
78backend,
79 }
80 }
8182/// Does running this AST pass have any effect?
83 ///
84 /// The result will be set to `false` if any method that generates SQL
85 /// is called.
86pub(crate) fn is_noop(result: &'a mut bool, backend: &'b DB) -> Self {
87AstPass {
88 internals: AstPassInternals::IsNoop(result),
89backend,
90 }
91 }
9293/// Register an internal flag whether or not diesel should
94 /// generate explicitly qualified column names
95#[diesel_derives::__diesel_public_if(
96 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
97)]
98 #[cfg(any(
99 feature = "sqlite",
100 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
101))]
102pub(crate) fn skip_from(&mut self, value: bool) {
103if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
104options.skip_from = value105 }
106 }
107108/// Call this method whenever you pass an instance of `AstPass` by value.
109 ///
110 /// Effectively copies `self`, with a narrower lifetime. When passing a
111 /// reference or a mutable reference, this is normally done by rust
112 /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
113 /// even though mutable references are not `Copy`. However, this is only
114 /// done implicitly for references. For structs with lifetimes it must be
115 /// done explicitly. This method matches the semantics of what Rust would do
116 /// implicitly if you were passing a mutable reference
117#[allow(clippy::explicit_auto_deref)] // clippy is wrong here
118pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> {
119let internals = match self.internals {
120 AstPassInternals::ToSql(ref mut builder, ref mut options) => {
121 AstPassInternals::ToSql(*builder, options)
122 }
123 AstPassInternals::CollectBinds {
124ref mut collector,
125ref mut metadata_lookup,
126 } => AstPassInternals::CollectBinds {
127 collector: *collector,
128 metadata_lookup: *metadata_lookup,
129 },
130 AstPassInternals::IsSafeToCachePrepared(ref mut result) => {
131 AstPassInternals::IsSafeToCachePrepared(result)
132 }
133 AstPassInternals::DebugBinds(ref mut f) => AstPassInternals::DebugBinds(f),
134 AstPassInternals::IsNoop(ref mut result) => AstPassInternals::IsNoop(result),
135 };
136AstPass {
137internals,
138 backend: self.backend,
139 }
140 }
141142/// Mark the current query being constructed as unsafe to store in the
143 /// prepared statement cache.
144 ///
145 /// Diesel caches prepared statements as much as possible. However, it is
146 /// important to ensure that this doesn't result in unbounded memory usage
147 /// on the database server. To ensure this is the case, any logical query
148 /// which could generate a potentially unbounded number of prepared
149 /// statements *must* call this method. Examples of AST nodes which do this
150 /// are:
151 ///
152 /// - `SqlLiteral`. We have no way of knowing if the SQL string was
153 /// constructed dynamically or not, so we must assume it was dynamic.
154 /// - `EqAny` when passed a Rust `Vec`. The `IN` operator requires one bind
155 /// parameter per element, meaning that the query could generate up to
156 /// `usize` unique prepared statements.
157 /// - `InsertStatement`. Unbounded due to the variable number of records
158 /// being inserted generating unique SQL.
159 /// - `UpdateStatement`. The number of potential queries is actually
160 /// technically bounded, but the upper bound is the number of columns on
161 /// the table factorial which is too large to be safe.
162pub fn unsafe_to_cache_prepared(&mut self) {
163if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
164**result = false
165}
166 }
167168/// Push the given SQL string on the end of the query being constructed.
169 ///
170 /// # Example
171 ///
172 /// ```rust
173 /// # use diesel::query_builder::{QueryFragment, AstPass};
174 /// # use diesel::backend::Backend;
175 /// # use diesel::QueryResult;
176 /// # struct And<Left, Right> { left: Left, right: Right }
177 /// impl<Left, Right, DB> QueryFragment<DB> for And<Left, Right>
178 /// where
179 /// DB: Backend,
180 /// Left: QueryFragment<DB>,
181 /// Right: QueryFragment<DB>,
182 /// {
183 /// fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
184 /// self.left.walk_ast(out.reborrow())?;
185 /// out.push_sql(" AND ");
186 /// self.right.walk_ast(out.reborrow())?;
187 /// Ok(())
188 /// }
189 /// }
190 /// # fn main() {}
191 /// ```
192pub fn push_sql(&mut self, sql: &str) {
193match self.internals {
194 AstPassInternals::ToSql(ref mut builder, _) => builder.push_sql(sql),
195 AstPassInternals::IsNoop(ref mut result) => **result = false,
196_ => {}
197 }
198 }
199200/// Push the given SQL identifier on the end of the query being constructed.
201 ///
202 /// The identifier will be quoted using the rules specific to the backend
203 /// the query is being constructed for.
204pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
205match self.internals {
206 AstPassInternals::ToSql(ref mut builder, _) => builder.push_identifier(identifier)?,
207 AstPassInternals::IsNoop(ref mut result) => **result = false,
208_ => {}
209 }
210Ok(())
211 }
212213/// Push a value onto the given query to be sent separate from the SQL
214 ///
215 /// This method affects multiple AST passes. It should be called at the
216 /// point in the query where you'd want the parameter placeholder (`$1` on
217 /// PG, `?` on other backends) to be inserted.
218pub fn push_bind_param<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
219where
220DB: HasSqlType<T>,
221 U: ToSql<T, DB> + ?Sized,
222 {
223match self.internals {
224 AstPassInternals::ToSql(ref mut out, _) => out.push_bind_param(),
225 AstPassInternals::CollectBinds {
226ref mut collector,
227ref mut metadata_lookup,
228 } => collector.push_bound_value(bind, metadata_lookup)?,
229 AstPassInternals::DebugBinds(ref mut f) => {
230f.push(Box::new(bind));
231 }
232 AstPassInternals::IsNoop(ref mut result) => **result = false,
233_ => {}
234 }
235Ok(())
236 }
237238/// Push a value onto the given query to be sent separate from the SQL
239 ///
240 /// This method affects multiple AST passes. It should be called at the
241 /// point in the raw SQL is inserted. This assumes the parameter placeholder
242 /// (`$1` on PG, `?` on other backends) is already inserted.
243#[diesel_derives::__diesel_public_if(
244 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
245)]
246pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
247where
248DB: HasSqlType<T>,
249 U: ToSql<T, DB> + ?Sized,
250 {
251match self.internals {
252 AstPassInternals::CollectBinds { .. } | AstPassInternals::DebugBinds(..) => {
253self.push_bind_param(bind)?
254}
255 AstPassInternals::ToSql(ref mut out, _) => {
256out.push_bind_param_value_only();
257 }
258_ => {}
259 }
260Ok(())
261 }
262263/// Push bind collector values from its data onto the query
264 ///
265 /// This method works with [MoveableBindCollector] data [MoveableBindCollector::BindData]
266 /// and is used with already collected query meaning its SQL is already built and its
267 /// bind data already collected.
268#[diesel_derives::__diesel_public_if(
269 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
270)]
271pub(crate) fn push_bind_collector_data<MD>(
272&mut self,
273 bind_collector_data: &MD,
274 ) -> QueryResult<()>
275where
276DB: Backend,
277for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData = MD>,
278 {
279match self.internals {
280 AstPassInternals::CollectBinds {
281ref mut collector,
282 metadata_lookup: _,
283 } => collector.append_bind_data(bind_collector_data),
284 AstPassInternals::DebugBinds(ref mut f) => {
285 DB::BindCollector::push_debug_binds(bind_collector_data, f);
286 }
287_ => {}
288 }
289Ok(())
290 }
291292/// Get information about the backend that will consume this query
293#[cfg_attr(
294 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
295 doc(hidden)
296 )] // This is used by the `define_sql_function` macro
297#[cfg_attr(
298 diesel_docsrs,
299 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
300 )]
301pub fn backend(&self) -> &DB {
302self.backend
303 }
304305/// Get if the query should be rendered with from clauses or not
306#[cfg_attr(
307 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
308 doc(hidden)
309 )] // This is used by the `__diesel_column` macro
310#[cfg_attr(
311 diesel_docsrs,
312 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
313 )]
314pub fn should_skip_from(&self) -> bool {
315if let AstPassInternals::ToSql(_, ref options) = self.internals {
316options.skip_from
317 } else {
318false
319}
320 }
321}
322323#[allow(missing_debug_implementations)]
324/// This is separate from the struct to cause the enum to be opaque, forcing
325/// usage of the methods provided rather than matching on the enum directly.
326/// This essentially mimics the capabilities that would be available if
327/// `AstPass` were a trait.
328enum AstPassInternals<'a, 'b, DB>
329where
330DB: Backend,
331 DB::QueryBuilder: 'a,
332 DB::MetadataLookup: 'a,
333'b: 'a,
334{
335 ToSql(&'a mut DB::QueryBuilder, &'a mut AstPassToSqlOptions),
336 CollectBinds {
337 collector: &'a mut DB::BindCollector<'b>,
338 metadata_lookup: &'a mut DB::MetadataLookup,
339 },
340 IsSafeToCachePrepared(&'a mut bool),
341 DebugBinds(&'a mut Vec<Box<dyn fmt::Debug + 'b>>),
342 IsNoop(&'a mut bool),
343}
344345#[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(
346 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
347)]348#[allow(missing_debug_implementations)]
349#[allow(missing_copy_implementations)]
350#[derive(#[automatically_derived]
#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
impl ::core::default::Default for AstPassToSqlOptions {
#[inline]
fn default() -> AstPassToSqlOptions {
AstPassToSqlOptions { skip_from: ::core::default::Default::default() }
}
}Default)]
351/// This is used to pass down additional settings to the `AstPass`
352/// when rendering the sql string.
353pub(crate) struct AstPassToSqlOptions {
354 skip_from: bool,
355}
356357/// This is an internal extension trait with methods required for
358/// `#[derive(MultiConnection)]`
359pub trait AstPassHelper<'a, 'b, DB>
360where
361DB: Backend,
362 DB::QueryBuilder: 'a,
363 DB::MetadataLookup: 'a,
364'b: 'a,
365{
366/// This function converts the given `AstPass` instance to
367 /// an `AstPass` instance for another database system. This requires that the
368 /// given instance contains compatible BindCollector/QueryBuilder/… implementations
369 /// for the target backend. We use explicit conversion functions here instead of relaying on
370 /// `From` impls because generating them as part of `#[derive(MultiConnection)]` is not possible
371 /// due to [compiler bugs](https://github.com/rust-lang/rust/issues/100712)
372fn cast_database<DB2>(
373self,
374 convert_bind_collector: impl Fn(&'a mut DB::BindCollector<'b>) -> &'a mut DB2::BindCollector<'b>,
375 convert_query_builder: impl Fn(&mut DB::QueryBuilder) -> &mut DB2::QueryBuilder,
376 convert_backend: impl Fn(&DB) -> &DB2,
377 convert_lookup: impl Fn(&'a mut DB::MetadataLookup) -> &'a mut DB2::MetadataLookup,
378 ) -> AstPass<'a, 'b, DB2>
379where
380DB2: Backend,
381 DB2::QueryBuilder: 'a,
382 DB2::MetadataLookup: 'a,
383'b: 'a;
384385/// This function allows to access the inner bind collector if
386 /// this `AstPass` represents a collect binds pass.
387fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)>;
388389/// This function allows to access the inner debug bind collector pass
390fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)>;
391392/// Construct a new AstPass for collecting bind values into the provided buffer
393fn collect_debug_binds_pass(
394 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
395 backend: &'b DB,
396 ) -> AstPass<'a, 'b, DB>;
397}
398399impl<'a, 'b, DB> AstPassHelper<'a, 'b, DB> for AstPass<'a, 'b, DB>
400where
401DB: Backend,
402 DB::QueryBuilder: 'a,
403 DB::MetadataLookup: 'a,
404'b: 'a,
405{
406fn cast_database<DB2>(
407self,
408 convert_bind_collector: impl Fn(&'a mut DB::BindCollector<'b>) -> &'a mut DB2::BindCollector<'b>,
409 convert_query_builder: impl Fn(&mut DB::QueryBuilder) -> &mut DB2::QueryBuilder,
410 convert_backend: impl Fn(&DB) -> &DB2,
411 convert_lookup: impl Fn(&'a mut DB::MetadataLookup) -> &'a mut DB2::MetadataLookup,
412 ) -> AstPass<'a, 'b, DB2>
413where
414DB2: Backend,
415 DB2::QueryBuilder: 'a,
416 DB2::MetadataLookup: 'a,
417'b: 'a,
418 {
419let casted_pass = match self.internals {
420 AstPassInternals::ToSql(qb, opts) => {
421 AstPassInternals::ToSql(convert_query_builder(qb), opts)
422 }
423 AstPassInternals::CollectBinds {
424 collector,
425 metadata_lookup,
426 } => AstPassInternals::CollectBinds {
427 collector: convert_bind_collector(collector),
428 metadata_lookup: convert_lookup(metadata_lookup),
429 },
430 AstPassInternals::IsSafeToCachePrepared(b) => {
431 AstPassInternals::IsSafeToCachePrepared(b)
432 }
433 AstPassInternals::DebugBinds(b) => AstPassInternals::DebugBinds(b),
434 AstPassInternals::IsNoop(b) => AstPassInternals::IsNoop(b),
435 };
436437AstPass {
438 internals: casted_pass,
439 backend: convert_backend(self.backend),
440 }
441 }
442443fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)> {
444if let AstPassInternals::CollectBinds {
445 collector,
446 metadata_lookup,
447 } = &mut self.internals
448 {
449Some((collector, metadata_lookup))
450 } else {
451None452 }
453 }
454455fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)> {
456if let AstPassInternals::DebugBinds(formatter) = &mut self.internals {
457Some((formatter, self.backend))
458 } else {
459None460 }
461 }
462463fn collect_debug_binds_pass(
464 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
465 backend: &'b DB,
466 ) -> AstPass<'a, 'b, DB> {
467AstPass::debug_binds(formatter, backend)
468 }
469}