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;
910#[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
27DB: Backend,
28 DB::QueryBuilder: 'a,
29 DB::MetadataLookup: 'a,
30'b: 'a,
31{
32 internals: AstPassInternals<'a, 'b, DB>,
33 backend: &'b DB,
34}
3536impl<'a, 'b, DB> AstPass<'a, 'b, DB>
37where
38DB: Backend,
39'b: 'a,
40{
41pub(crate) fn to_sql(
42 query_builder: &'a mut DB::QueryBuilder,
43 options: &'a mut AstPassToSqlOptions,
44 backend: &'b DB,
45 ) -> Self {
46AstPass {
47 internals: AstPassInternals::ToSql(query_builder, options),
48backend,
49 }
50 }
5152pub(crate) fn collect_binds(
53 collector: &'a mut DB::BindCollector<'b>,
54 metadata_lookup: &'a mut DB::MetadataLookup,
55 backend: &'b DB,
56 ) -> Self {
57AstPass {
58 internals: AstPassInternals::CollectBinds {
59collector,
60metadata_lookup,
61 },
62backend,
63 }
64 }
6566pub(crate) fn is_safe_to_cache_prepared(result: &'a mut bool, backend: &'b DB) -> Self {
67AstPass {
68 internals: AstPassInternals::IsSafeToCachePrepared(result),
69backend,
70 }
71 }
7273pub(crate) fn debug_binds(
74 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
75 backend: &'b DB,
76 ) -> Self {
77AstPass {
78 internals: AstPassInternals::DebugBinds(formatter),
79backend,
80 }
81 }
8283/// 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.
87pub(crate) fn is_noop(result: &'a mut bool, backend: &'b DB) -> Self {
88AstPass {
89 internals: AstPassInternals::IsNoop(result),
90backend,
91 }
92 }
9394/// 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))]
103pub(crate) fn skip_from(&mut self, value: bool) {
104if let AstPassInternals::ToSql(_, ref mut options) = self.internals {
105options.skip_from = value106 }
107 }
108109/// 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
119pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> {
120let internals = match self.internals {
121 AstPassInternals::ToSql(ref mut builder, ref mut options) => {
122 AstPassInternals::ToSql(*builder, options)
123 }
124 AstPassInternals::CollectBinds {
125ref mut collector,
126ref 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 };
137AstPass {
138internals,
139 backend: self.backend,
140 }
141 }
142143/// 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.
163pub fn unsafe_to_cache_prepared(&mut self) {
164if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
165**result = false
166}
167 }
168169/// 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 /// ```
193pub fn push_sql(&mut self, sql: &str) {
194match self.internals {
195 AstPassInternals::ToSql(ref mut builder, _) => builder.push_sql(sql),
196 AstPassInternals::IsNoop(ref mut result) => **result = false,
197_ => {}
198 }
199 }
200201/// 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.
205pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
206match self.internals {
207 AstPassInternals::ToSql(ref mut builder, _) => builder.push_identifier(identifier)?,
208 AstPassInternals::IsNoop(ref mut result) => **result = false,
209_ => {}
210 }
211Ok(())
212 }
213214/// 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.
219pub fn push_bind_param<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
220where
221DB: HasSqlType<T>,
222 U: ToSql<T, DB> + ?Sized,
223 {
224match self.internals {
225 AstPassInternals::ToSql(ref mut out, _) => out.push_bind_param(),
226 AstPassInternals::CollectBinds {
227ref mut collector,
228ref mut metadata_lookup,
229 } => collector.push_bound_value(bind, metadata_lookup)?,
230 AstPassInternals::DebugBinds(ref mut f) => {
231f.push(Box::new(bind));
232 }
233 AstPassInternals::IsNoop(ref mut result) => **result = false,
234_ => {}
235 }
236Ok(())
237 }
238239/// 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)]
247pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
248where
249DB: HasSqlType<T>,
250 U: ToSql<T, DB> + ?Sized,
251 {
252match self.internals {
253 AstPassInternals::CollectBinds { .. } | AstPassInternals::DebugBinds(..) => {
254self.push_bind_param(bind)?
255}
256 AstPassInternals::ToSql(ref mut out, _) => {
257out.push_bind_param_value_only();
258 }
259_ => {}
260 }
261Ok(())
262 }
263264/// 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)]
272pub(crate) fn push_bind_collector_data<MD>(
273&mut self,
274 bind_collector_data: &MD,
275 ) -> QueryResult<()>
276where
277DB: Backend,
278for<'bc> DB::BindCollector<'bc>: MoveableBindCollector<DB, BindData = MD>,
279 {
280match self.internals {
281 AstPassInternals::CollectBinds {
282ref 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 }
290Ok(())
291 }
292293/// 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 )]
302pub fn backend(&self) -> &DB {
303self.backend
304 }
305306/// 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 )]
315pub fn should_skip_from(&self) -> bool {
316if let AstPassInternals::ToSql(_, ref options) = self.internals {
317options.skip_from
318 } else {
319false
320}
321 }
322}
323324#[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
331DB: 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}
345346#[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}
357358/// This is an internal extension trait with methods required for
359/// `#[derive(MultiConnection)]`
360pub trait AstPassHelper<'a, 'b, DB>
361where
362DB: 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)
373fn cast_database<DB2>(
374self,
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>
380where
381DB2: Backend,
382 DB2::QueryBuilder: 'a,
383 DB2::MetadataLookup: 'a,
384'b: 'a;
385386/// This function allows to access the inner bind collector if
387 /// this `AstPass` represents a collect binds pass.
388fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)>;
389390/// This function allows to access the inner debug bind collector pass
391fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)>;
392393/// Construct a new AstPass for collecting bind values into the provided buffer
394fn collect_debug_binds_pass(
395 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
396 backend: &'b DB,
397 ) -> AstPass<'a, 'b, DB>;
398}
399400impl<'a, 'b, DB> AstPassHelper<'a, 'b, DB> for AstPass<'a, 'b, DB>
401where
402DB: Backend,
403 DB::QueryBuilder: 'a,
404 DB::MetadataLookup: 'a,
405'b: 'a,
406{
407fn cast_database<DB2>(
408self,
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>
414where
415DB2: Backend,
416 DB2::QueryBuilder: 'a,
417 DB2::MetadataLookup: 'a,
418'b: 'a,
419 {
420let 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 };
437438AstPass {
439 internals: casted_pass,
440 backend: convert_backend(self.backend),
441 }
442 }
443444fn bind_collector(&mut self) -> Option<(&mut DB::BindCollector<'b>, &mut DB::MetadataLookup)> {
445if let AstPassInternals::CollectBinds {
446 collector,
447 metadata_lookup,
448 } = &mut self.internals
449 {
450Some((collector, metadata_lookup))
451 } else {
452None453 }
454 }
455456fn debug_binds(&mut self) -> Option<(&mut Vec<Box<dyn fmt::Debug + 'b>>, &'b DB)> {
457if let AstPassInternals::DebugBinds(formatter) = &mut self.internals {
458Some((formatter, self.backend))
459 } else {
460None461 }
462 }
463464fn collect_debug_binds_pass(
465 formatter: &'a mut Vec<Box<dyn fmt::Debug + 'b>>,
466 backend: &'b DB,
467 ) -> AstPass<'a, 'b, DB> {
468AstPass::debug_binds(formatter, backend)
469 }
470}