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    /// 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    ))]
102    pub(crate) fn skip_from(&mut self, value: bool) {
103        if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
104            options.skip_from = value
105        }
106    }
107
108    /// 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
118    pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> {
119        let internals = match self.internals {
120            AstPassInternals::ToSql(ref mut builder, ref mut options) => {
121                AstPassInternals::ToSql(*builder, options)
122            }
123            AstPassInternals::CollectBinds {
124                ref mut collector,
125                ref 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        };
136        AstPass {
137            internals,
138            backend: self.backend,
139        }
140    }
141
142    /// 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.
162    pub fn unsafe_to_cache_prepared(&mut self) {
163        if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
164            **result = false
165        }
166    }
167
168    /// 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    /// ```
192    pub fn push_sql(&mut self, sql: &str) {
193        match self.internals {
194            AstPassInternals::ToSql(ref mut builder, _) => builder.push_sql(sql),
195            AstPassInternals::IsNoop(ref mut result) => **result = false,
196            _ => {}
197        }
198    }
199
200    /// 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.
204    pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
205        match self.internals {
206            AstPassInternals::ToSql(ref mut builder, _) => builder.push_identifier(identifier)?,
207            AstPassInternals::IsNoop(ref mut result) => **result = false,
208            _ => {}
209        }
210        Ok(())
211    }
212
213    /// 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.
218    pub fn push_bind_param<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
219    where
220        DB: HasSqlType<T>,
221        U: ToSql<T, DB> + ?Sized,
222    {
223        match self.internals {
224            AstPassInternals::ToSql(ref mut out, _) => out.push_bind_param(),
225            AstPassInternals::CollectBinds {
226                ref mut collector,
227                ref mut metadata_lookup,
228            } => collector.push_bound_value(bind, metadata_lookup)?,
229            AstPassInternals::DebugBinds(ref mut f) => {
230                f.push(Box::new(bind));
231            }
232            AstPassInternals::IsNoop(ref mut result) => **result = false,
233            _ => {}
234        }
235        Ok(())
236    }
237
238    /// 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    )]
246    pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
247    where
248        DB: HasSqlType<T>,
249        U: ToSql<T, DB> + ?Sized,
250    {
251        match self.internals {
252            AstPassInternals::CollectBinds { .. } | AstPassInternals::DebugBinds(..) => {
253                self.push_bind_param(bind)?
254            }
255            AstPassInternals::ToSql(ref mut out, _) => {
256                out.push_bind_param_value_only();
257            }
258            _ => {}
259        }
260        Ok(())
261    }
262
263    /// 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    )]
271    pub(crate) fn push_bind_collector_data<MD>(
272        &mut self,
273        bind_collector_data: &MD,
274    ) -> QueryResult<()>
275    where
276        DB: Backend,
277        for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData = MD>,
278    {
279        match self.internals {
280            AstPassInternals::CollectBinds {
281                ref mut collector,
282                metadata_lookup: _,
283            } => collector.append_bind_data(bind_collector_data),
284            AstPassInternals::DebugBinds(ref mut f) => {
285                f.push(Box::new("Opaque bind collector data"))
286            }
287            _ => {}
288        }
289        Ok(())
290    }
291
292    /// 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        docsrs,
299        doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
300    )]
301    pub fn backend(&self) -> &DB {
302        self.backend
303    }
304
305    /// 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        docsrs,
312        doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
313    )]
314    pub fn should_skip_from(&self) -> bool {
315        if let AstPassInternals::ToSql(_, ref options) = self.internals {
316            options.skip_from
317        } else {
318            false
319        }
320    }
321}
322
323#[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
330    DB: 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}
344
345#[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(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}
356
357/// This is an internal extension trait with methods required for
358/// `#[derive(MultiConnection)]`
359pub trait AstPassHelper<'a, 'b, DB>
360where
361    DB: 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)
372    fn cast_database<DB2>(
373        self,
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>
379    where
380        DB2: Backend,
381        DB2::QueryBuilder: 'a,
382        DB2::MetadataLookup: 'a,
383        'b: 'a;
384
385    /// This function allows to access the inner bind collector if
386    /// this `AstPass` represents a collect binds pass.
387    fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)>;
388}
389
390impl<'a, 'b, DB> AstPassHelper<'a, 'b, DB> for AstPass<'a, 'b, DB>
391where
392    DB: Backend,
393    DB::QueryBuilder: 'a,
394    DB::MetadataLookup: 'a,
395    'b: 'a,
396{
397    fn cast_database<DB2>(
398        self,
399        convert_bind_collector: impl Fn(&'a mut DB::BindCollector<'b>) -> &'a mut DB2::BindCollector<'b>,
400        convert_query_builder: impl Fn(&mut DB::QueryBuilder) -> &mut DB2::QueryBuilder,
401        convert_backend: impl Fn(&DB) -> &DB2,
402        convert_lookup: impl Fn(&'a mut DB::MetadataLookup) -> &'a mut DB2::MetadataLookup,
403    ) -> AstPass<'a, 'b, DB2>
404    where
405        DB2: Backend,
406        DB2::QueryBuilder: 'a,
407        DB2::MetadataLookup: 'a,
408        'b: 'a,
409    {
410        let casted_pass = match self.internals {
411            AstPassInternals::ToSql(qb, opts) => {
412                AstPassInternals::ToSql(convert_query_builder(qb), opts)
413            }
414            AstPassInternals::CollectBinds {
415                collector,
416                metadata_lookup,
417            } => AstPassInternals::CollectBinds {
418                collector: convert_bind_collector(collector),
419                metadata_lookup: convert_lookup(metadata_lookup),
420            },
421            AstPassInternals::IsSafeToCachePrepared(b) => {
422                AstPassInternals::IsSafeToCachePrepared(b)
423            }
424            AstPassInternals::DebugBinds(b) => AstPassInternals::DebugBinds(b),
425            AstPassInternals::IsNoop(b) => AstPassInternals::IsNoop(b),
426        };
427
428        AstPass {
429            internals: casted_pass,
430            backend: convert_backend(self.backend),
431        }
432    }
433
434    fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)> {
435        if let AstPassInternals::CollectBinds {
436            collector,
437            metadata_lookup,
438        } = &mut self.internals
439        {
440            Some((collector, metadata_lookup))
441        } else {
442            None
443        }
444    }
445}