Skip to main content

diesel/query_builder/
ast_pass.rs

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;
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    /// Count the number of bind parameters for the given statement
95    pub(crate) fn count_binds(count: &'a mut usize, backend: &'b DB) -> Self {
96        AstPass {
97            internals: AstPassInternals::CountBinds(count),
98            backend,
99        }
100    }
101
102    /// 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    ))]
111    pub(crate) fn skip_from(&mut self, value: bool) {
112        if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
113            options.skip_from = value
114        }
115    }
116
117    /// 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
127    pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> {
128        let internals = match self.internals {
129            AstPassInternals::ToSql(ref mut builder, ref mut options) => {
130                AstPassInternals::ToSql(*builder, options)
131            }
132            AstPassInternals::CollectBinds {
133                ref mut collector,
134                ref 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        };
146        AstPass {
147            internals,
148            backend: self.backend,
149        }
150    }
151
152    /// 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.
172    pub fn unsafe_to_cache_prepared(&mut self) {
173        if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
174            **result = false
175        }
176    }
177
178    /// 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    /// ```
202    pub fn push_sql(&mut self, sql: &str) {
203        match self.internals {
204            AstPassInternals::ToSql(ref mut builder, _) => builder.push_sql(sql),
205            AstPassInternals::IsNoop(ref mut result) => **result = false,
206            _ => {}
207        }
208    }
209
210    /// 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.
214    pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
215        match self.internals {
216            AstPassInternals::ToSql(ref mut builder, _) => builder.push_identifier(identifier)?,
217            AstPassInternals::IsNoop(ref mut result) => **result = false,
218            _ => {}
219        }
220        Ok(())
221    }
222
223    /// 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.
228    pub fn push_bind_param<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
229    where
230        DB: HasSqlType<T>,
231        U: ToSql<T, DB> + ?Sized,
232    {
233        match self.internals {
234            AstPassInternals::ToSql(ref mut out, _) => out.push_bind_param(),
235            AstPassInternals::CollectBinds {
236                ref mut collector,
237                ref mut metadata_lookup,
238            } => collector.push_bound_value(bind, metadata_lookup)?,
239            AstPassInternals::DebugBinds(ref mut f) => {
240                f.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        }
248        Ok(())
249    }
250
251    /// 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    )]
259    pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
260    where
261        DB: HasSqlType<T>,
262        U: ToSql<T, DB> + ?Sized,
263    {
264        match self.internals {
265            AstPassInternals::CollectBinds { .. } | AstPassInternals::DebugBinds(..) => {
266                self.push_bind_param(bind)?
267            }
268            AstPassInternals::ToSql(ref mut out, _) => {
269                out.push_bind_param_value_only();
270            }
271            AstPassInternals::CountBinds(ref mut count) => {
272                **count += 1;
273            }
274            _ => {}
275        }
276        Ok(())
277    }
278
279    /// 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    )]
287    pub(crate) fn push_bind_collector_data<MD>(
288        &mut self,
289        bind_collector_data: &MD,
290    ) -> QueryResult<()>
291    where
292        MD: BindData,
293        DB: Backend,
294        for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData = MD>,
295    {
296        match self.internals {
297            AstPassInternals::CollectBinds {
298                ref 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        }
309        Ok(())
310    }
311
312    /// 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    )]
321    pub fn backend(&self) -> &DB {
322        self.backend
323    }
324
325    /// 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    )]
334    pub fn should_skip_from(&self) -> bool {
335        if let AstPassInternals::ToSql(_, ref options) = self.internals {
336            options.skip_from
337        } else {
338            false
339        }
340    }
341}
342
343#[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
350    DB: 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}
365
366#[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}
377
378/// This is an internal extension trait with methods required for
379/// `#[derive(MultiConnection)]`
380pub trait AstPassHelper<'a, 'b, DB>
381where
382    DB: 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)
393    fn cast_database<DB2>(
394        self,
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>
400    where
401        DB2: Backend,
402        DB2::QueryBuilder: 'a,
403        DB2::MetadataLookup: 'a,
404        'b: 'a;
405
406    /// This function allows to access the inner bind collector if
407    /// this `AstPass` represents a collect binds pass.
408    fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)>;
409
410    /// This function allows to access the inner debug bind collector pass
411    fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)>;
412
413    /// This function allows to access the inner count binds collector pass
414    fn count_binds(&mut self) -> Option<(&mut usize, &'b DB)>;
415
416    /// Construct a new AstPass for collecting bind values into the provided buffer
417    fn collect_debug_binds_pass(
418        formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
419        backend: &'b DB,
420    ) -> AstPass<'a, 'b, DB>;
421}
422
423impl<'a, 'b, DB> AstPassHelper<'a, 'b, DB> for AstPass<'a, 'b, DB>
424where
425    DB: Backend,
426    DB::QueryBuilder: 'a,
427    DB::MetadataLookup: 'a,
428    'b: 'a,
429{
430    fn cast_database<DB2>(
431        self,
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>
437    where
438        DB2: Backend,
439        DB2::QueryBuilder: 'a,
440        DB2::MetadataLookup: 'a,
441        'b: 'a,
442    {
443        let 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        };
461
462        AstPass {
463            internals: casted_pass,
464            backend: convert_backend(self.backend),
465        }
466    }
467
468    fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)> {
469        if let AstPassInternals::CollectBinds {
470            collector,
471            metadata_lookup,
472        } = &mut self.internals
473        {
474            Some((collector, metadata_lookup))
475        } else {
476            None
477        }
478    }
479
480    fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)> {
481        if let AstPassInternals::DebugBinds(formatter) = &mut self.internals {
482            Some((formatter, self.backend))
483        } else {
484            None
485        }
486    }
487
488    fn collect_debug_binds_pass(
489        formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
490        backend: &'b DB,
491    ) -> AstPass<'a, 'b, DB> {
492        AstPass::debug_binds(formatter, backend)
493    }
494
495    fn count_binds(&mut self) -> Option<(&mut usize, &'b DB)> {
496        if let AstPassInternals::CountBinds(count) = &mut self.internals {
497            Some((count, self.backend))
498        } else {
499            None
500        }
501    }
502}