diesel/query_builder/
ast_pass.rs

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