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)]
10pub 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 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 #[allow(clippy::explicit_auto_deref)] 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 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 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 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 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 #[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 #[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 #[cfg_attr(
286 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
287 doc(hidden)
288 )] #[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 #[cfg_attr(
299 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
300 doc(hidden)
301 )] #[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)]
316enum 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)]
343pub(crate) struct AstPassToSqlOptions {
346 skip_from: bool,
347}
348
349pub trait AstPassHelper<'a, 'b, DB>
352where
353 DB: Backend,
354 DB::QueryBuilder: 'a,
355 DB::MetadataLookup: 'a,
356 'b: 'a,
357{
358 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 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}