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 #[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 #[allow(clippy::explicit_auto_deref)] 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 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 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 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 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 #[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 #[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 #[cfg_attr(
294 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
295 doc(hidden)
296 )] #[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 #[cfg_attr(
307 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
308 doc(hidden)
309 )] #[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)]
324enum 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)]
351pub(crate) struct AstPassToSqlOptions {
354 skip_from: bool,
355}
356
357pub trait AstPassHelper<'a, 'b, DB>
360where
361 DB: Backend,
362 DB::QueryBuilder: 'a,
363 DB::MetadataLookup: 'a,
364 'b: 'a,
365{
366 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 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}