diesel/query_builder/
ast_pass.rs

1use std::{fmt, mem};
2
3use backend::Backend;
4use query_builder::{BindCollector, QueryBuilder};
5use result::QueryResult;
6use serialize::ToSql;
7use 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, DB>
25where
26    DB: Backend,
27    DB::QueryBuilder: 'a,
28    DB::BindCollector: 'a,
29    DB::MetadataLookup: 'a,
30{
31    internals: AstPassInternals<'a, DB>,
32}
33
34impl<'a, DB> AstPass<'a, DB>
35where
36    DB: Backend,
37{
38    #[doc(hidden)]
39    #[allow(clippy::wrong_self_convention)]
40    pub fn to_sql(query_builder: &'a mut DB::QueryBuilder) -> Self {
41        AstPass {
42            internals: AstPassInternals::ToSql(query_builder),
43        }
44    }
45
46    #[doc(hidden)]
47    pub fn collect_binds(
48        collector: &'a mut DB::BindCollector,
49        metadata_lookup: &'a DB::MetadataLookup,
50    ) -> Self {
51        AstPass {
52            internals: AstPassInternals::CollectBinds {
53                collector,
54                metadata_lookup,
55            },
56        }
57    }
58
59    #[doc(hidden)]
60    pub fn is_safe_to_cache_prepared(result: &'a mut bool) -> Self {
61        AstPass {
62            internals: AstPassInternals::IsSafeToCachePrepared(result),
63        }
64    }
65
66    #[doc(hidden)]
67    pub fn debug_binds(formatter: &'a mut fmt::DebugList<'a, 'a>) -> Self {
68        AstPass {
69            internals: AstPassInternals::DebugBinds(formatter),
70        }
71    }
72
73    /// Does running this AST pass have any effect?
74    ///
75    /// The result will be set to `false` if any method that generates SQL
76    /// is called.
77    pub(crate) fn is_noop(result: &'a mut bool) -> Self {
78        AstPass {
79            internals: AstPassInternals::IsNoop(result),
80        }
81    }
82
83    /// Call this method whenever you pass an instance of `AstPass` by value.
84    ///
85    /// Effectively copies `self`, with a narrower lifetime. When passing a
86    /// reference or a mutable reference, this is normally done by rust
87    /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
88    /// even though mutable references are not `Copy`. However, this is only
89    /// done implicitly for references. For structs with lifetimes it must be
90    /// done explicitly. This method matches the semantics of what Rust would do
91    /// implicitly if you were passing a mutable reference
92    // Clippy is wrong, this cannot be expressed with pointer casting
93    #[allow(clippy::transmute_ptr_to_ptr)]
94    pub fn reborrow(&mut self) -> AstPass<DB> {
95        use self::AstPassInternals::*;
96        let internals = match self.internals {
97            ToSql(ref mut builder) => ToSql(&mut **builder),
98            CollectBinds {
99                ref mut collector,
100                metadata_lookup,
101            } => CollectBinds {
102                collector: &mut **collector,
103                metadata_lookup: &*metadata_lookup,
104            },
105            IsSafeToCachePrepared(ref mut result) => IsSafeToCachePrepared(&mut **result),
106            DebugBinds(ref mut f) => {
107                // Safe because the lifetime is always being shortened.
108                let f_with_shorter_lifetime = unsafe { mem::transmute(&mut **f) };
109                DebugBinds(f_with_shorter_lifetime)
110            }
111            IsNoop(ref mut result) => IsNoop(&mut **result),
112        };
113        AstPass { internals }
114    }
115
116    /// Mark the current query being constructed as unsafe to store in the
117    /// prepared statement cache.
118    ///
119    /// Diesel caches prepared statements as much as possible. However, it is
120    /// important to ensure that this doesn't result in unbounded memory usage
121    /// on the database server. To ensure this is the case, any logical query
122    /// which could generate a potentially unbounded number of prepared
123    /// statements *must* call this method. Examples of AST nodes which do this
124    /// are:
125    ///
126    /// - `SqlLiteral`. We have no way of knowing if the SQL string was
127    ///   constructed dynamically or not, so we must assume it was dynamic.
128    /// - `EqAny` when passed a Rust `Vec`. The `IN` operator requires one bind
129    ///   parameter per element, meaning that the query could generate up to
130    ///   `usize` unique prepared statements.
131    /// - `InsertStatement`. Unbounded due to the variable number of records
132    ///   being inserted generating unique SQL.
133    /// - `UpdateStatement`. The number of potential queries is actually
134    ///   technically bounded, but the upper bound is the number of columns on
135    ///   the table factorial which is too large to be safe.
136    pub fn unsafe_to_cache_prepared(&mut self) {
137        if let AstPassInternals::IsSafeToCachePrepared(ref mut result) = self.internals {
138            **result = false
139        }
140    }
141
142    /// Push the given SQL string on the end of the query being constructed.
143    ///
144    /// # Example
145    ///
146    /// ```rust
147    /// # extern crate diesel;
148    /// # use diesel::query_builder::{QueryFragment, AstPass};
149    /// # use diesel::backend::Backend;
150    /// # use diesel::QueryResult;
151    /// # struct And<Left, Right> { left: Left, right: Right }
152    /// impl<Left, Right, DB> QueryFragment<DB> for And<Left, Right>
153    /// where
154    ///     DB: Backend,
155    ///     Left: QueryFragment<DB>,
156    ///     Right: QueryFragment<DB>,
157    /// {
158    ///     fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
159    ///         self.left.walk_ast(out.reborrow())?;
160    ///         out.push_sql(" AND ");
161    ///         self.right.walk_ast(out.reborrow())?;
162    ///         Ok(())
163    ///     }
164    /// }
165    /// # fn main() {}
166    /// ```
167    pub fn push_sql(&mut self, sql: &str) {
168        match self.internals {
169            AstPassInternals::ToSql(ref mut builder) => builder.push_sql(sql),
170            AstPassInternals::IsNoop(ref mut result) => **result = false,
171            _ => {}
172        }
173    }
174
175    /// Push the given SQL identifier on the end of the query being constructed.
176    ///
177    /// The identifier will be quoted using the rules specific to the backend
178    /// the query is being constructed for.
179    pub fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> {
180        match self.internals {
181            AstPassInternals::ToSql(ref mut builder) => builder.push_identifier(identifier)?,
182            AstPassInternals::IsNoop(ref mut result) => **result = false,
183            _ => {}
184        }
185        Ok(())
186    }
187
188    /// Push a value onto the given query to be sent separate from the SQL
189    ///
190    /// This method affects multiple AST passes. It should be called at the
191    /// point in the query where you'd want the parameter placeholder (`$1` on
192    /// PG, `?` on other backends) to be inserted.
193    pub fn push_bind_param<T, U>(&mut self, bind: &U) -> QueryResult<()>
194    where
195        DB: HasSqlType<T>,
196        U: ToSql<T, DB>,
197    {
198        use self::AstPassInternals::*;
199        match self.internals {
200            ToSql(ref mut out) => out.push_bind_param(),
201            CollectBinds {
202                ref mut collector,
203                metadata_lookup,
204            } => collector.push_bound_value(bind, metadata_lookup)?,
205            DebugBinds(ref mut f) => {
206                f.entry(bind);
207            }
208            IsNoop(ref mut result) => **result = false,
209            _ => {}
210        }
211        Ok(())
212    }
213
214    #[doc(hidden)]
215    pub fn push_bind_param_value_only<T, U>(&mut self, bind: &U) -> QueryResult<()>
216    where
217        DB: HasSqlType<T>,
218        U: ToSql<T, DB>,
219    {
220        use self::AstPassInternals::*;
221        match self.internals {
222            CollectBinds { .. } | DebugBinds(..) => self.push_bind_param(bind)?,
223            _ => {}
224        }
225        Ok(())
226    }
227}
228
229#[allow(missing_debug_implementations)]
230/// This is separate from the struct to cause the enum to be opaque, forcing
231/// usage of the methods provided rather than matching on the enum directly.
232/// This essentially mimics the capabilities that would be available if
233/// `AstPass` were a trait.
234enum AstPassInternals<'a, DB>
235where
236    DB: Backend,
237    DB::QueryBuilder: 'a,
238    DB::BindCollector: 'a,
239    DB::MetadataLookup: 'a,
240{
241    ToSql(&'a mut DB::QueryBuilder),
242    CollectBinds {
243        collector: &'a mut DB::BindCollector,
244        metadata_lookup: &'a DB::MetadataLookup,
245    },
246    IsSafeToCachePrepared(&'a mut bool),
247    DebugBinds(&'a mut fmt::DebugList<'a, 'a>),
248    IsNoop(&'a mut bool),
249}