Skip to main content

diesel/query_builder/
ast_pass.rs

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