1use proc_macro2::TokenStream;
2use syn::DeriveInput;
3
4struct ConnectionVariant<'a> {
5 ty: &'a syn::Type,
6 name: &'a syn::Ident,
7}
8
9pub fn derive(item: DeriveInput) -> TokenStream {
10 if let syn::Data::Enum(e) = item.data {
11 let connection_types = e
12 .variants
13 .iter()
14 .map(|v| match &v.fields {
15 syn::Fields::Unnamed(f) if f.unnamed.len() == 1 => ConnectionVariant {
16 ty: &f.unnamed.first().unwrap().ty,
17 name: &v.ident,
18 },
19 _ => panic!("Only enums with on field per variant are supported"),
20 })
21 .collect::<Vec<_>>();
22 let backend = generate_backend(&connection_types);
23 let query_builder = generate_querybuilder(&connection_types);
24 let bind_collector = generate_bind_collector(&connection_types);
25 let row = generate_row(&connection_types);
26 let connection = generate_connection_impl(&connection_types, &item.ident);
27
28 quote::quote! {
29 mod multi_connection_impl {
30 use super::*;
31
32 mod backend {
33 use super::*;
34 #backend
35 }
36
37 mod query_builder {
38 use super::*;
39 #query_builder
40 }
41
42 mod bind_collector {
43 use super::*;
44 #bind_collector
45 }
46
47 mod row {
48 use super::*;
49 #row
50 }
51
52 mod connection {
53 use super::*;
54 #connection
55 }
56
57 pub use self::backend::{MultiBackend, MultiRawValue};
58 pub use self::row::{MultiRow, MultiField};
59 }
60
61 pub use self::multi_connection_impl::{MultiBackend, MultiRow, MultiRawValue, MultiField};
62 }
63 } else {
64 panic!("Only enums are supported as multiconnection type");
65 }
66}
67
68fn generate_connection_impl(
69 connection_types: &[ConnectionVariant],
70 ident: &syn::Ident,
71) -> TokenStream {
72 let batch_execute_impl = connection_types.iter().map(|c| {
73 let ident = c.name;
74 quote::quote! {
75 Self::#ident(conn) => conn.batch_execute(query)
76 }
77 });
78
79 let execute_returning_count_impl = connection_types.iter().map(|c| {
80 let ident = c.name;
81 let ty = c.ty;
82 quote::quote! {
83 Self::#ident(conn) => {
84 let query = SerializedQuery {
85 inner: source,
86 backend: MultiBackend::#ident(Default::default()),
87 query_builder: super::query_builder::MultiQueryBuilder::#ident(Default::default()),
88 p: std::marker::PhantomData::<#ty>,
89 };
90 conn.execute_returning_count(&query)
91 }
92 }
93 });
94
95 let load_impl = connection_types.iter().map(|c| {
96 let variant_ident = c.name;
97 let ty = &c.ty;
98 quote::quote! {
99 #ident::#variant_ident(conn) => {
100 let query = SerializedQuery {
101 inner: source,
102 backend: MultiBackend::#variant_ident(Default::default()),
103 query_builder: super::query_builder::MultiQueryBuilder::#variant_ident(Default::default()),
104 p: std::marker::PhantomData::<#ty>,
105 };
106 let r = <#ty as diesel::connection::LoadConnection>::load(conn, query)?;
107 Ok(super::row::MultiCursor::#variant_ident(r))
108 }
109 }
110 });
111
112 let instrumentation_impl = connection_types.iter().map(|c| {
113 let variant_ident = c.name;
114 quote::quote! {
115 #ident::#variant_ident(conn) => {
116 diesel::connection::Connection::set_instrumentation(conn, instrumentation);
117 }
118 }
119 });
120
121 let set_cache_impl = connection_types.iter().map(|c| {
122 let variant_ident = c.name;
123 quote::quote! {
124 #ident::#variant_ident(conn) => {
125 diesel::connection::Connection::set_prepared_statement_cache_size(conn, size);
126 }
127 }
128 });
129
130 let get_instrumentation_impl = connection_types.iter().map(|c| {
131 let variant_ident = c.name;
132 quote::quote! {
133 #ident::#variant_ident(conn) => {
134 diesel::connection::Connection::instrumentation(conn)
135 }
136 }
137 });
138
139 let establish_impls = connection_types.iter().map(|c| {
140 let ident = c.name;
141 let ty = c.ty;
142 quote::quote! {
143 if let Ok(conn) = #ty::establish(database_url) {
144 return Ok(Self::#ident(conn));
145 }
146 }
147 });
148
149 let begin_transaction_impl = connection_types.iter().map(|c| {
150 let ident = c.name;
151 let ty = c.ty;
152 quote::quote! {
153 Self::#ident(conn) => <#ty as Connection>::TransactionManager::begin_transaction(conn)
154 }
155 });
156
157 let commit_transaction_impl = connection_types.iter().map(|c| {
158 let ident = c.name;
159 let ty = c.ty;
160 quote::quote! {
161 Self::#ident(conn) => <#ty as Connection>::TransactionManager::commit_transaction(conn)
162 }
163 });
164
165 let rollback_transaction_impl = connection_types.iter().map(|c| {
166 let ident = c.name;
167 let ty = c.ty;
168 quote::quote! {
169 Self::#ident(conn) => <#ty as Connection>::TransactionManager::rollback_transaction(conn)
170 }
171 });
172
173 let is_broken_transaction_manager_impl = connection_types.iter().map(|c| {
174 let ident = c.name;
175 let ty = c.ty;
176 quote::quote! {
177 Self::#ident(conn) => <#ty as Connection>::TransactionManager::is_broken_transaction_manager(conn)
178 }
179 });
180
181 let transaction_manager_status_mut_impl = connection_types.iter().map(|c| {
182 let ident = c.name;
183 let ty = c.ty;
184 quote::quote! {
185 Self::#ident(conn) => <#ty as Connection>::TransactionManager::transaction_manager_status_mut(conn)
186 }
187 });
188
189 let bind_param_helper_impl = connection_types.iter().map(|c| {
190 let ident = c.name;
191 let ty = c.ty;
192 quote::quote! {
193 impl BindParamHelper for #ty {
194 fn handle_inner_pass<'a, 'b: 'a>(
195 outer_collector: &mut <Self::Backend as diesel::backend::Backend>::BindCollector<'a>,
196 lookup: &mut <Self::Backend as diesel::sql_types::TypeMetadata>::MetadataLookup,
197 backend: &'b MultiBackend,
198 q: &'b impl diesel::query_builder::QueryFragment<MultiBackend>,
199 ) -> diesel::QueryResult<()> {
200 use diesel::internal::derives::multiconnection::MultiConnectionHelper;
201
202 let mut collector = super::bind_collector::MultiBindCollector::#ident(Default::default());
203 let lookup = Self::to_any(lookup);
204 q.collect_binds(&mut collector, lookup, backend)?;
205 if let super::bind_collector::MultiBindCollector::#ident(collector) = collector {
206 *outer_collector = collector;
207 }
208 Ok(())
209 }
210 }
211 }
212 });
213
214 let impl_migration_connection = connection_types.iter().map(|c| {
215 let ident = c.name;
216 quote::quote! {
217 Self::#ident(conn) => {
218 use diesel::migration::MigrationConnection;
219 conn.setup()
220 }
221 }
222 });
223
224 let impl_begin_test_transaction = connection_types.iter().map(|c| {
225 let ident = c.name;
226 quote::quote! {
227 Self::#ident(conn) => conn.begin_test_transaction()
228 }
229 });
230
231 let r2d2_impl = if cfg!(feature = "r2d2") {
232 let impl_ping_r2d2 = connection_types.iter().map(|c| {
233 let ident = c.name;
234 quote::quote! {
235 Self::#ident(conn) => conn.ping()
236 }
237 });
238
239 let impl_is_broken_r2d2 = connection_types.iter().map(|c| {
240 let ident = c.name;
241 quote::quote! {
242 Self::#ident(conn) => conn.is_broken()
243 }
244 });
245 Some(quote::quote! {
246 impl diesel::r2d2::R2D2Connection for MultiConnection {
247 fn ping(&mut self) -> diesel::QueryResult<()> {
248 use diesel::r2d2::R2D2Connection;
249 match self {
250 #(#impl_ping_r2d2,)*
251 }
252 }
253
254 fn is_broken(&mut self) -> bool {
255 use diesel::r2d2::R2D2Connection;
256 match self {
257 #(#impl_is_broken_r2d2,)*
258 }
259 }
260 }
261 })
262 } else {
263 None
264 };
265
266 quote::quote! {
267 use diesel::connection::*;
268 pub(super) use super::#ident as MultiConnection;
269
270 impl SimpleConnection for MultiConnection {
271 fn batch_execute(&mut self, query: &str) -> diesel::result::QueryResult<()> {
272 match self {
273 #(#batch_execute_impl,)*
274 }
275 }
276 }
277
278 impl diesel::internal::derives::multiconnection::ConnectionSealed for MultiConnection {}
279
280 struct SerializedQuery<T, C> {
281 inner: T,
282 backend: MultiBackend,
283 query_builder: super::query_builder::MultiQueryBuilder,
284 p: std::marker::PhantomData<C>,
285 }
286
287 trait BindParamHelper: Connection {
288 fn handle_inner_pass<'a, 'b: 'a>(
289 collector: &mut <Self::Backend as diesel::backend::Backend>::BindCollector<'a>,
290 lookup: &mut <Self::Backend as diesel::sql_types::TypeMetadata>::MetadataLookup,
291 backend: &'b MultiBackend,
292 q: &'b impl diesel::query_builder::QueryFragment<MultiBackend>,
293 ) -> diesel::QueryResult<()>;
294 }
295
296 #(#bind_param_helper_impl)*
297
298 impl<T, DB, C> diesel::query_builder::QueryFragment<DB> for SerializedQuery<T, C>
299 where
300 DB: diesel::backend::Backend + 'static,
301 T: diesel::query_builder::QueryFragment<MultiBackend>,
302 C: diesel::connection::Connection<Backend = DB> + BindParamHelper + diesel::internal::derives::multiconnection::MultiConnectionHelper,
303 {
304 fn walk_ast<'b>(
305 &'b self,
306 mut pass: diesel::query_builder::AstPass<'_, 'b, DB>,
307 ) -> diesel::QueryResult<()> {
308 use diesel::query_builder::QueryBuilder;
309 use diesel::internal::derives::multiconnection::AstPassHelper;
310
311 let mut query_builder = self.query_builder.duplicate();
312 self.inner.to_sql(&mut query_builder, &self.backend)?;
313 pass.push_sql(&query_builder.finish());
314 if !self.inner.is_safe_to_cache_prepared(&self.backend)? {
315 pass.unsafe_to_cache_prepared();
316 }
317 if let Some((outer_collector, lookup)) = pass.bind_collector() {
318 C::handle_inner_pass(outer_collector, lookup, &self.backend, &self.inner)?;
319 }
320 Ok(())
321 }
322 }
323
324 impl<T, C> diesel::query_builder::QueryId for SerializedQuery<T, C>
325 where
326 T: diesel::query_builder::QueryId,
327 {
328 type QueryId = <T as diesel::query_builder::QueryId>::QueryId;
329
330 const HAS_STATIC_QUERY_ID: bool = <T as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID;
331 }
332
333 impl<T, C> diesel::query_builder::Query for SerializedQuery<T, C>
334 where
335 T: diesel::query_builder::Query
336 {
337 type SqlType = diesel::sql_types::Untyped;
340 }
341
342 impl Connection for MultiConnection {
343 type Backend = super::MultiBackend;
344
345 type TransactionManager = Self;
346
347 fn establish(database_url: &str) -> diesel::ConnectionResult<Self> {
348 #(#establish_impls)*
349 Err(diesel::ConnectionError::BadConnection("Invalid connection url for multiconnection".into()))
350 }
351
352 fn execute_returning_count<T>(&mut self, source: &T) -> diesel::result::QueryResult<usize>
353 where
354 T: diesel::query_builder::QueryFragment<Self::Backend> + diesel::query_builder::QueryId,
355 {
356 match self {
357 #(#execute_returning_count_impl,)*
358 }
359 }
360
361 fn transaction_state(
362 &mut self,
363 ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData {
364 self
365 }
366
367 fn instrumentation(&mut self) -> &mut dyn diesel::connection::Instrumentation {
368 match self {
369 #(#get_instrumentation_impl,)*
370 }
371 }
372
373 fn set_instrumentation(&mut self, instrumentation: impl diesel::connection::Instrumentation) {
374 match self {
375 #(#instrumentation_impl,)*
376 }
377 }
378
379 fn set_prepared_statement_cache_size(&mut self, size: diesel::connection::CacheSize) {
380 match self {
381 #(#set_cache_impl,)*
382 }
383 }
384
385 fn begin_test_transaction(&mut self) -> diesel::QueryResult<()> {
386 match self {
387 #(#impl_begin_test_transaction,)*
388 }
389 }
390 }
391
392 impl LoadConnection for MultiConnection
393 {
394 type Cursor<'conn, 'query> = super::row::MultiCursor<'conn, 'query>;
395 type Row<'conn, 'query> = super::MultiRow<'conn, 'query>;
396
397 fn load<'conn, 'query, T>(
398 &'conn mut self,
399 source: T,
400 ) -> diesel::result::QueryResult<Self::Cursor<'conn, 'query>>
401 where
402 T: diesel::query_builder::Query + diesel::query_builder::QueryFragment<Self::Backend> + diesel::query_builder::QueryId + 'query,
403 Self::Backend: diesel::expression::QueryMetadata<T::SqlType>,
404 {
405 match self {
406 #(#load_impl,)*
407 }
408 }
409 }
410
411 impl TransactionManager<MultiConnection> for MultiConnection {
412 type TransactionStateData = Self;
413
414 fn begin_transaction(conn: &mut MultiConnection) -> diesel::QueryResult<()> {
415 match conn {
416 #(#begin_transaction_impl,)*
417 }
418 }
419
420 fn rollback_transaction(conn: &mut MultiConnection) -> diesel::QueryResult<()> {
421 match conn {
422 #(#rollback_transaction_impl,)*
423 }
424 }
425
426 fn commit_transaction(conn: &mut MultiConnection) -> diesel::QueryResult<()> {
427 match conn {
428 #(#commit_transaction_impl,)*
429 }
430 }
431
432 fn transaction_manager_status_mut(conn: &mut MultiConnection) -> &mut diesel::connection::TransactionManagerStatus {
433 match conn {
434 #(#transaction_manager_status_mut_impl,)*
435 }
436 }
437
438 fn is_broken_transaction_manager(conn: &mut MultiConnection) -> bool {
439 match conn {
440 #(#is_broken_transaction_manager_impl,)*
441 }
442 }
443 }
444
445 impl diesel::migration::MigrationConnection for MultiConnection {
446 fn setup(&mut self) -> diesel::QueryResult<usize> {
447 match self {
448 #(#impl_migration_connection,)*
449 }
450 }
451 }
452
453 #r2d2_impl
454 }
455}
456
457fn generate_row(connection_types: &[ConnectionVariant]) -> TokenStream {
458 let row_variants = connection_types.iter().map(|c| {
459 let ident = c.name;
460 let ty = c.ty;
461 quote::quote! {
462 #ident(<#ty as diesel::connection::LoadConnection>::Row<'conn, 'query>)
463 }
464 });
465
466 let field_variants = connection_types.iter().map(|c| {
467 let ident = c.name;
468 let ty = c.ty;
469 quote::quote! {
470 #ident(<<#ty as diesel::connection::LoadConnection>::Row<'conn, 'query> as diesel::row::Row<'conn, <#ty as diesel::connection::Connection>::Backend>>::Field<'query>)
471 }
472 });
473
474 let field_name_impl = connection_types.iter().map(|c| {
475 let ident = c.name;
476 quote::quote! {
477 Self::#ident(f) => f.field_name()
478 }
479 });
480
481 let field_value_impl = connection_types.iter().map(|c| {
482 let ident = c.name;
483 quote::quote! {
484 Self::#ident(f) => f.value().map(super::MultiRawValue::#ident)
485 }
486 });
487
488 let row_index_impl = connection_types
489 .iter()
490 .map(|c| {
491 let ident = c.name;
492 quote::quote! {
493 Self::#ident(r) => r.idx(idx)
494 }
495 })
496 .collect::<Vec<_>>();
497 let row_index_impl = &row_index_impl;
498
499 let cursor_variants = connection_types.iter().map(|c| {
500 let ident = c.name;
501 let ty = c.ty;
502 quote::quote! {
503 #ident(<#ty as diesel::connection::LoadConnection>::Cursor<'conn, 'query>)
504 }
505 });
506
507 let iterator_impl = connection_types.iter().map(|c| {
508 let ident = c.name;
509 quote::quote! {
510 Self::#ident(r) => Some(r.next()?.map(MultiRow::#ident))
511 }
512 });
513
514 let field_count_impl = connection_types.iter().map(|c| {
515 let ident = c.name;
516 quote::quote! {
517 Self::#ident(r) => r.field_count()
518 }
519 });
520
521 let get_field_impl = connection_types.iter().map(|c| {
522 let ident = c.name;
523 quote::quote! {
524 Self::#ident(r) => r.get(idx).map(MultiField::#ident)
525 }
526 });
527
528 quote::quote! {
529
530 pub enum MultiRow<'conn, 'query> {
531 #(#row_variants,)*
532
533 }
534
535 impl<'conn, 'query> diesel::internal::derives::multiconnection::RowSealed for MultiRow<'conn, 'query> {}
536
537 pub enum MultiField<'conn: 'query, 'query> {
538 #(#field_variants,)*
539 }
540
541 impl<'conn, 'query> diesel::row::Field<'conn, super::MultiBackend> for MultiField<'conn, 'query> {
542 fn field_name(&self) -> Option<&str> {
543 use diesel::row::Field;
544
545 match self {
546 #(#field_name_impl,)*
547 }
548 }
549
550 fn value(&self) -> Option<<super::MultiBackend as diesel::backend::Backend>::RawValue<'_>> {
551 use diesel::row::Field;
552
553 match self {
554 #(#field_value_impl,)*
555 }
556 }
557 }
558
559 impl<'conn, 'query, 'c> diesel::row::RowIndex<&'c str> for MultiRow<'conn, 'query> {
560 fn idx(&self, idx: &'c str) -> Option<usize> {
561 use diesel::row::RowIndex;
562
563 match self {
564 #(#row_index_impl,)*
565 }
566 }
567 }
568
569 impl<'conn, 'query> diesel::row::RowIndex<usize> for MultiRow<'conn, 'query> {
570 fn idx(&self, idx: usize) -> Option<usize> {
571 use diesel::row::RowIndex;
572
573 match self {
574 #(#row_index_impl,)*
575 }
576 }
577 }
578
579 impl<'conn, 'query> diesel::row::Row<'conn, super::MultiBackend> for MultiRow<'conn, 'query> {
580 type Field<'a> = MultiField<'a, 'a> where 'conn: 'a, Self: 'a;
581 type InnerPartialRow = Self;
582
583 fn field_count(&self) -> usize {
584 use diesel::row::Row;
585 match self {
586 #(#field_count_impl,)*
587 }
588 }
589
590 fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
591 where
592 'conn: 'b,
593 Self: diesel::row::RowIndex<I>,
594 {
595 use diesel::row::{RowIndex, Row};
596 let idx = self.idx(idx)?;
597
598 match self {
599 #(#get_field_impl,)*
600 }
601 }
602
603 fn partial_row(
604 &self,
605 range: std::ops::Range<usize>,
606 ) -> diesel::internal::derives::multiconnection::PartialRow<'_, Self::InnerPartialRow> {
607 diesel::internal::derives::multiconnection::PartialRow::new(self, range)
608 }
609 }
610
611 pub enum MultiCursor<'conn, 'query> {
612 #(#cursor_variants,)*
613 }
614
615 impl<'conn, 'query> Iterator for MultiCursor<'conn, 'query> {
616 type Item = diesel::QueryResult<MultiRow<'conn, 'query>>;
617
618 fn next(&mut self) -> Option<Self::Item> {
619 match self {
620 #(#iterator_impl,)*
621 }
622 }
623 }
624
625 }
626}
627
628fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStream {
629 let mut to_sql_impls = vec![
630 (
631 quote::quote!(diesel::sql_types::SmallInt),
632 quote::quote!(i16),
633 ),
634 (
635 quote::quote!(diesel::sql_types::Integer),
636 quote::quote!(i32),
637 ),
638 (quote::quote!(diesel::sql_types::BigInt), quote::quote!(i64)),
639 (quote::quote!(diesel::sql_types::Double), quote::quote!(f64)),
640 (quote::quote!(diesel::sql_types::Float), quote::quote!(f32)),
641 (quote::quote!(diesel::sql_types::Text), quote::quote!(str)),
642 (
643 quote::quote!(diesel::sql_types::Binary),
644 quote::quote!([u8]),
645 ),
646 (quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
647 ];
648 if cfg!(feature = "chrono") {
649 to_sql_impls.push((
650 quote::quote!(diesel::sql_types::Timestamp),
651 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
652 ));
653 to_sql_impls.push((
654 quote::quote!(diesel::sql_types::Date),
655 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
656 ));
657 to_sql_impls.push((
658 quote::quote!(diesel::sql_types::Time),
659 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
660 ));
661 }
662 if cfg!(feature = "time") {
663 to_sql_impls.push((
664 quote::quote!(diesel::sql_types::Timestamp),
665 quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
666 ));
667 to_sql_impls.push((
668 quote::quote!(diesel::sql_types::Time),
669 quote::quote!(diesel::internal::derives::multiconnection::time::Time),
670 ));
671 to_sql_impls.push((
672 quote::quote!(diesel::sql_types::Date),
673 quote::quote!(diesel::internal::derives::multiconnection::time::Date),
674 ));
675 }
676 let to_sql_impls = to_sql_impls
677 .into_iter()
678 .map(|t| generate_to_sql_impls(t, connection_types));
679
680 let mut from_sql_impls = vec![
681 (
682 quote::quote!(diesel::sql_types::SmallInt),
683 quote::quote!(i16),
684 ),
685 (
686 quote::quote!(diesel::sql_types::Integer),
687 quote::quote!(i32),
688 ),
689 (quote::quote!(diesel::sql_types::BigInt), quote::quote!(i64)),
690 (quote::quote!(diesel::sql_types::Double), quote::quote!(f64)),
691 (quote::quote!(diesel::sql_types::Float), quote::quote!(f32)),
692 (
693 quote::quote!(diesel::sql_types::Text),
694 quote::quote!(String),
695 ),
696 (
697 quote::quote!(diesel::sql_types::Binary),
698 quote::quote!(Vec<u8>),
699 ),
700 (quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
701 ];
702 if cfg!(feature = "chrono") {
703 from_sql_impls.push((
704 quote::quote!(diesel::sql_types::Timestamp),
705 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
706 ));
707 from_sql_impls.push((
708 quote::quote!(diesel::sql_types::Date),
709 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
710 ));
711 from_sql_impls.push((
712 quote::quote!(diesel::sql_types::Time),
713 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
714 ));
715 }
716 if cfg!(feature = "time") {
717 from_sql_impls.push((
718 quote::quote!(diesel::sql_types::Timestamp),
719 quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
720 ));
721 from_sql_impls.push((
722 quote::quote!(diesel::sql_types::Time),
723 quote::quote!(diesel::internal::derives::multiconnection::time::Time),
724 ));
725 from_sql_impls.push((
726 quote::quote!(diesel::sql_types::Date),
727 quote::quote!(diesel::internal::derives::multiconnection::time::Date),
728 ));
729 }
730 let from_sql_impls = from_sql_impls.into_iter().map(generate_from_sql_impls);
731
732 let into_bind_value_bounds = connection_types.iter().map(|c| {
733 let ty = c.ty;
734 quote::quote! {
735 diesel::serialize::ToSql<ST, <#ty as diesel::connection::Connection>::Backend>
736 }
737 });
738
739 let has_sql_type_bounds = connection_types.iter().map(|c| {
740 let ty = c.ty;
741 quote::quote! {
742 <#ty as diesel::connection::Connection>::Backend: diesel::sql_types::HasSqlType<ST>
743 }
744 });
745
746 let multi_bind_collector_variants = connection_types.iter().map(|c| {
747 let ident = c.name;
748 let ty = c.ty;
749 quote::quote! {
750 #ident(<<#ty as diesel::connection::Connection>::Backend as diesel::backend::Backend>::BindCollector<'a>)
751 }
752 });
753
754 let multi_bind_collector_accessor = connection_types.iter().map(|c| {
755 let ident = c.name;
756 let lower_ident = syn::Ident::new(&c.name.to_string().to_lowercase(), c.name.span());
757 let ty = c.ty;
758 quote::quote! {
759 pub(super) fn #lower_ident(
760 &mut self,
761 ) -> &mut <<#ty as diesel::connection::Connection>::Backend as diesel::backend::Backend>::BindCollector<'a> {
762 match self {
763 Self::#ident(bc) => bc,
764 _ => unreachable!(),
765 }
766 }
767
768 }
769
770 });
771
772 let push_to_inner_collector = connection_types.iter().map(|c| {
773 let ident = c.name;
774 let ty = c.ty;
775 quote::quote! {
776 Self::#ident(ref mut bc) => {
777 let out = out.inner.expect("This inner value is set via our custom `ToSql` impls");
778 let callback = out.push_bound_value_to_collector;
779 let value = out.value;
780 <_ as PushBoundValueToCollectorDB<<#ty as diesel::Connection>::Backend>>::push_bound_value(
781 callback,
782 value,
783 bc,
784 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(metadata_lookup)
785 .expect("We can downcast the metadata lookup to the right type")
786 )?
787 }
788 }
789 });
790
791 let push_null_to_inner_collector = connection_types
792 .iter()
793 .map(|c| {
794 let ident = c.name;
795 quote::quote! {
796 (Self::#ident(ref mut bc), super::backend::MultiTypeMetadata{ #ident: Some(metadata), .. }) => {
797 bc.push_null_value(metadata)?;
798 }
799 }
800 })
801 .collect::<Vec<_>>();
802
803 let push_bound_value_super_traits = connection_types
804 .iter()
805 .map(|c| {
806 let ty = c.ty;
807 quote::quote! {
808 PushBoundValueToCollectorDB<<#ty as diesel::Connection>::Backend>
809 }
810 })
811 .collect::<Vec<_>>();
812
813 quote::quote! {
814 pub enum MultiBindCollector<'a> {
815 #(#multi_bind_collector_variants,)*
816 }
817
818 impl<'a> MultiBindCollector<'a> {
819 #(#multi_bind_collector_accessor)*
820 }
821
822 trait PushBoundValueToCollectorDB<DB: diesel::backend::Backend> {
823 fn push_bound_value<'a: 'b, 'b>(
824 &self,
825 v: InnerBindValueKind<'a>,
826 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
827 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
828 ) -> diesel::result::QueryResult<()>;
829 }
830
831 struct PushBoundValueToCollectorImpl<ST, T: ?Sized> {
832 p: std::marker::PhantomData<(ST, T)>
833 }
834
835 impl<ST, T, DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<ST, T>
839 where DB: diesel::backend::Backend
840 + diesel::sql_types::HasSqlType<ST>,
841 T: diesel::serialize::ToSql<ST, DB> + 'static,
842 Option<T>: diesel::serialize::ToSql<diesel::sql_types::Nullable<ST>, DB> + 'static,
843 ST: diesel::sql_types::SqlType,
844 {
845 fn push_bound_value<'a: 'b, 'b>(
846 &self,
847 v: InnerBindValueKind<'a>,
848 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
849 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
850 ) -> diesel::result::QueryResult<()> {
851 use diesel::query_builder::BindCollector;
852 match v {
853 InnerBindValueKind::Sized(v) => {
854 let v = v.downcast_ref::<T>().expect("We know the type statically here");
855 collector.push_bound_value::<ST, T>(v, lookup)
856 }
857 InnerBindValueKind::Null => {
858 collector.push_bound_value::<diesel::sql_types::Nullable<ST>, Option<T>>(&None, lookup)
859 },
860 _ => unreachable!("We set the value to `InnerBindValueKind::Sized` or `InnerBindValueKind::Null`")
861 }
862 }
863 }
864
865 impl<DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<diesel::sql_types::Text, str>
866 where DB: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Text>,
867 str: diesel::serialize::ToSql<diesel::sql_types::Text, DB> + 'static,
868 {
869 fn push_bound_value<'a: 'b, 'b>(
870 &self,
871 v: InnerBindValueKind<'a>,
872 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
873 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
874 ) -> diesel::result::QueryResult<()> {
875 use diesel::query_builder::BindCollector;
876 if let InnerBindValueKind::Str(v) = v {
877 collector.push_bound_value::<diesel::sql_types::Text, str>(v, lookup)
878 } else {
879 unreachable!("We set the value to `InnerBindValueKind::Str`")
880 }
881 }
882 }
883
884 impl<DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<diesel::sql_types::Binary, [u8]>
885 where DB: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Binary>,
886 [u8]: diesel::serialize::ToSql<diesel::sql_types::Binary, DB> + 'static,
887 {
888 fn push_bound_value<'a: 'b, 'b>(
889 &self,
890 v: InnerBindValueKind<'a>,
891 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
892 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
893 ) -> diesel::result::QueryResult<()> {
894 use diesel::query_builder::BindCollector;
895 if let InnerBindValueKind::Bytes(v) = v {
896 collector.push_bound_value::<diesel::sql_types::Binary, [u8]>(v, lookup)
897 } else {
898 unreachable!("We set the value to `InnerBindValueKind::Binary`")
899 }
900 }
901 }
902
903 trait PushBoundValueToCollector: #(#push_bound_value_super_traits +)* {}
904
905 impl<T> PushBoundValueToCollector for T
906 where T: #(#push_bound_value_super_traits + )* {}
907
908 #[derive(Default)]
909 pub struct BindValue<'a> {
910 inner: Option<InnerBindValue<'a>>
913 }
914
915 struct InnerBindValue<'a> {
916 value: InnerBindValueKind<'a>,
917 push_bound_value_to_collector: &'static dyn PushBoundValueToCollector
918 }
919
920 enum InnerBindValueKind<'a> {
921 Sized(&'a (dyn std::any::Any + std::marker::Send + std::marker::Sync)),
922 Str(&'a str),
923 Bytes(&'a [u8]),
924 Null,
925 }
926
927 impl<'a> From<(diesel::sql_types::Text, &'a str)> for BindValue<'a> {
928 fn from((_, v): (diesel::sql_types::Text, &'a str)) -> Self {
929 Self {
930 inner: Some(InnerBindValue{
931 value: InnerBindValueKind::Str(v),
932 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
933 p: std::marker::PhantomData::<(diesel::sql_types::Text, str)>
934 }
935 })
936 }
937 }
938 }
939
940 impl<'a> From<(diesel::sql_types::Binary, &'a [u8])> for BindValue<'a> {
941 fn from((_, v): (diesel::sql_types::Binary, &'a [u8])) -> Self {
942 Self {
943 inner: Some(InnerBindValue {
944 value: InnerBindValueKind::Bytes(v),
945 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
946 p: std::marker::PhantomData::<(diesel::sql_types::Binary, [u8])>
947 }
948 })
949 }
950 }
951 }
952
953 impl<'a, T, ST> From<(ST, &'a T)> for BindValue<'a>
954 where
955 T: std::any::Any #(+ #into_bind_value_bounds)* + Send + Sync + 'static,
956 ST: Send + diesel::sql_types::SqlType<IsNull = diesel::sql_types::is_nullable::NotNull> + 'static,
957 #(#has_sql_type_bounds,)*
958 {
959 fn from((_, v): (ST, &'a T)) -> Self {
960 Self {
961 inner: Some(InnerBindValue{
962 value: InnerBindValueKind::Sized(v),
963 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
964 p: std::marker::PhantomData::<(ST, T)>
965 }
966 })
967 }
968 }
969 }
970
971 impl<'a> diesel::query_builder::BindCollector<'a, MultiBackend> for MultiBindCollector<'a> {
972 type Buffer = multi_connection_impl::bind_collector::BindValue<'a>;
973
974 fn push_bound_value<T, U>(
975 &mut self,
976 bind: &'a U,
977 metadata_lookup: &mut (dyn std::any::Any + 'static),
978 ) -> diesel::QueryResult<()>
979 where
980 MultiBackend: diesel::sql_types::HasSqlType<T>,
981 U: diesel::serialize::ToSql<T, MultiBackend> + ?Sized + 'a,
982 {
983 let out = {
984 let out = multi_connection_impl::bind_collector::BindValue::default();
985 let mut out =
986 diesel::serialize::Output::<MultiBackend>::new(out, metadata_lookup);
987 let bind_is_null = bind.to_sql(&mut out).map_err(diesel::result::Error::SerializationError)?;
988 if matches!(bind_is_null, diesel::serialize::IsNull::Yes) {
989 let metadata = <MultiBackend as diesel::sql_types::HasSqlType<T>>::metadata(metadata_lookup);
995 match (self, metadata) {
996 #(#push_null_to_inner_collector)*
997 _ => {
998 unreachable!("We have matching metadata")
999 },
1000 }
1001 return Ok(());
1002 } else {
1003 out.into_inner()
1004 }
1005 };
1006 match self {
1007 #(#push_to_inner_collector)*
1008 }
1009
1010 Ok(())
1011 }
1012
1013 fn push_null_value(&mut self, metadata: super::backend::MultiTypeMetadata) -> diesel::QueryResult<()> {
1014 match (self, metadata) {
1015 #(#push_null_to_inner_collector)*
1016 _ => unreachable!("We have matching metadata"),
1017 }
1018 Ok(())
1019 }
1020 }
1021
1022 #(#to_sql_impls)*
1023 #(#from_sql_impls)*
1024
1025 }
1026}
1027
1028fn generate_has_sql_type_impls(sql_type: TokenStream) -> TokenStream {
1029 quote::quote! {
1030 impl diesel::sql_types::HasSqlType<#sql_type> for super::MultiBackend {
1031 fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata {
1032 Self::lookup_sql_type::<#sql_type>(lookup)
1033 }
1034 }
1035 }
1036}
1037
1038fn generate_from_sql_impls((sql_type, tpe): (TokenStream, TokenStream)) -> TokenStream {
1039 quote::quote! {
1040 impl diesel::deserialize::FromSql<#sql_type, super::MultiBackend> for #tpe {
1041 fn from_sql(
1042 bytes: <super::MultiBackend as diesel::backend::Backend>::RawValue<'_>,
1043 ) -> diesel::deserialize::Result<Self> {
1044 bytes.from_sql::<Self, #sql_type>()
1045 }
1046 }
1047
1048 }
1049}
1050
1051fn generate_to_sql_impls(
1052 (sql_type, tpe): (TokenStream, TokenStream),
1053 _connection_types: &[ConnectionVariant],
1054) -> TokenStream {
1055 quote::quote! {
1056 impl diesel::serialize::ToSql<#sql_type, super::MultiBackend> for #tpe {
1057 fn to_sql<'b>(
1058 &'b self,
1059 out: &mut diesel::serialize::Output<'b, '_, super::MultiBackend>,
1060 ) -> diesel::serialize::Result {
1061 out.set_value((#sql_type, self));
1062 Ok(diesel::serialize::IsNull::No)
1063 }
1064 }
1065 }
1066}
1067
1068fn generate_queryfragment_impls(
1069 trait_def: TokenStream,
1070 query_fragment_bounds: &[TokenStream],
1071) -> TokenStream {
1072 quote::quote! {
1073 impl #trait_def
1074 where
1075 Self: #(#query_fragment_bounds+)*
1076 {
1077 fn walk_ast<'b>(
1078 &'b self,
1079 pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1080 ) -> diesel::QueryResult<()> {
1081 super::backend::MultiBackend::walk_variant_ast(self, pass)
1082 }
1083 }
1084 }
1085}
1086
1087fn generate_querybuilder(connection_types: &[ConnectionVariant]) -> TokenStream {
1088 let variants = connection_types.iter().map(|c| {
1089 let ident = c.name;
1090 let ty = c.ty;
1091 quote::quote! {
1092 #ident(<<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::QueryBuilder)
1093 }
1094 });
1095
1096 let push_sql_impl = connection_types.iter().map(|c| {
1097 let ident = c.name;
1098 quote::quote! {
1099 Self::#ident(q) => q.push_sql(sql)
1100 }
1101 });
1102
1103 let push_identifier_impl = connection_types.iter().map(|c| {
1104 let ident = c.name;
1105 quote::quote! {
1106 Self::#ident(q) => q.push_identifier(identifier)
1107 }
1108 });
1109
1110 let push_bind_param_impl = connection_types.iter().map(|c| {
1111 let ident = c.name;
1112 quote::quote! {
1113 Self::#ident(q) => q.push_bind_param()
1114 }
1115 });
1116
1117 let finish_impl = connection_types.iter().map(|c| {
1118 let ident = c.name;
1119 quote::quote! {
1120 Self::#ident(q) => q.finish()
1121 }
1122 });
1123
1124 let into_variant_functions = connection_types.iter().map(|c|{
1125 let ty = c.ty;
1126 let ident = c.name;
1127 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), ident.span());
1128 quote::quote! {
1129 pub(super) fn #lower_ident(&mut self) -> &mut <<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::QueryBuilder {
1130 match self {
1131 Self::#ident(qb) => qb,
1132 _ => unreachable!(),
1133 }
1134 }
1135 }
1136 });
1137
1138 let query_fragment_bounds = connection_types.iter().map(|c| {
1139 let ty = c.ty;
1140 quote::quote! {
1141 diesel::query_builder::QueryFragment<<#ty as diesel::connection::Connection>::Backend>
1142 }
1143 }).collect::<Vec<_>>();
1144
1145 let duplicate_query_builder = connection_types.iter().map(|c| {
1146 let ident = c.name;
1147 quote::quote! {
1148 Self::#ident(_) => Self::#ident(Default::default())
1149 }
1150 });
1151
1152 let query_fragment = quote::quote! {
1153 diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1154 };
1155
1156 let query_fragment_impls = IntoIterator::into_iter([
1157 quote::quote!{
1158 <L, O> #query_fragment for diesel::internal::derives::multiconnection::LimitOffsetClause<L, O>
1159 },
1160 quote::quote! {
1161 <L, R> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiConcatClauseSyntax>
1162 for diesel::internal::derives::multiconnection::Concat<L, R>
1163 },
1164 quote::quote! {
1165 <T, U> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1166 for diesel::internal::derives::multiconnection::array_comparison::In<T, U>
1167 },
1168 quote::quote! {
1169 <T, U> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1170 for diesel::internal::derives::multiconnection::array_comparison::NotIn<T, U>
1171 },
1172 quote::quote! {
1173 <ST, I> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1174 for diesel::internal::derives::multiconnection::array_comparison::Many<ST, I>
1175 },
1176 quote::quote! {
1177 <T> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiExistsSyntax>
1178 for diesel::internal::derives::multiconnection::Exists<T>
1179 },
1180 quote::quote! {
1181 diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiEmptyFromClauseSyntax>
1182 for diesel::internal::derives::multiconnection::NoFromClause
1183 },
1184 quote::quote! {
1185 diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiDefaultValueClauseForInsert>
1186 for diesel::internal::derives::multiconnection::DefaultValues
1187 },
1188 quote::quote! {
1189 <Expr> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiReturningClause>
1190 for diesel::internal::derives::multiconnection::ReturningClause<Expr>
1191 },
1192 quote::quote! {
1193 <Expr> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiInsertWithDefaultKeyword>
1194 for diesel::insertable::DefaultableColumnInsertValue<Expr>
1195 },
1196 quote::quote! {
1197 <Tab, V, QId, const HAS_STATIC_QUERY_ID: bool> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiBatchInsertSupport>
1198 for diesel::internal::derives::multiconnection::BatchInsert<V, Tab, QId, HAS_STATIC_QUERY_ID>
1199 },
1200 quote::quote! {
1201 <S> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiAliasSyntax>
1202 for diesel::query_source::Alias<S>
1203 }
1204 ])
1205 .map(|t| generate_queryfragment_impls(t, &query_fragment_bounds));
1206
1207 let insert_values_impl_variants = connection_types.iter().map(|c| {
1208 let ident = c.name;
1209 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), c.name.span());
1210 let ty = c.ty;
1211 quote::quote! {
1212 super::backend::MultiBackend::#ident(_) => {
1213 <Self as diesel::insertable::InsertValues<<#ty as diesel::connection::Connection>::Backend, Col::Table>>::column_names(
1214 &self,
1215 out.cast_database(
1216 super::bind_collector::MultiBindCollector::#lower_ident,
1217 super::query_builder::MultiQueryBuilder::#lower_ident,
1218 super::backend::MultiBackend::#lower_ident,
1219 |l| {
1220 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l)
1221 .expect("It's possible to downcast the metadata lookup type to the correct type")
1222 },
1223 ),
1224 )
1225 }
1226 }
1227 });
1228
1229 let insert_values_backend_bounds = connection_types.iter().map(|c| {
1230 let ty = c.ty;
1231 quote::quote! {
1232 diesel::insertable::DefaultableColumnInsertValue<diesel::insertable::ColumnInsertValue<Col, Expr>>: diesel::insertable::InsertValues<<#ty as diesel::connection::Connection>::Backend, Col::Table>
1233 }
1234 });
1235
1236 quote::quote! {
1237 pub enum MultiQueryBuilder {
1238 #(#variants,)*
1239 }
1240
1241 impl MultiQueryBuilder {
1242 pub(super) fn duplicate(&self) -> Self {
1243 match self {
1244 #(#duplicate_query_builder,)*
1245 }
1246 }
1247 }
1248
1249 impl MultiQueryBuilder {
1250 #(#into_variant_functions)*
1251 }
1252
1253 impl diesel::query_builder::QueryBuilder<super::MultiBackend> for MultiQueryBuilder {
1254 fn push_sql(&mut self, sql: &str) {
1255 match self {
1256 #(#push_sql_impl,)*
1257 }
1258 }
1259
1260 fn push_identifier(&mut self, identifier: &str) -> diesel::QueryResult<()> {
1261 match self {
1262 #(#push_identifier_impl,)*
1263 }
1264 }
1265
1266 fn push_bind_param(&mut self) {
1267 match self {
1268 #(#push_bind_param_impl,)*
1269 }
1270 }
1271
1272 fn finish(self) -> String {
1273 match self {
1274 #(#finish_impl,)*
1275 }
1276 }
1277 }
1278
1279 #(#query_fragment_impls)*
1280
1281 impl<F, S, D, W, O, LOf, G, H, LC>
1282 diesel::query_builder::QueryFragment<
1283 super::backend::MultiBackend,
1284 super::backend::MultiSelectStatementSyntax,
1285 >
1286 for diesel::internal::derives::multiconnection::SelectStatement<
1287 F,
1288 S,
1289 D,
1290 W,
1291 O,
1292 LOf,
1293 G,
1294 H,
1295 LC,
1296 >
1297 where
1298 S: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1299 F: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1300 D: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1301 W: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1302 O: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1303 LOf: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1304 G: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1305 H: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1306 LC: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1307 {
1308 fn walk_ast<'b>(
1309 &'b self,
1310 mut out: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1311 ) -> diesel::QueryResult<()> {
1312 use diesel::internal::derives::multiconnection::SelectStatementAccessor;
1313
1314 out.push_sql("SELECT ");
1315 self.distinct_clause().walk_ast(out.reborrow())?;
1316 self.select_clause().walk_ast(out.reborrow())?;
1317 self.from_clause().walk_ast(out.reborrow())?;
1318 self.where_clause().walk_ast(out.reborrow())?;
1319 self.group_by_clause().walk_ast(out.reborrow())?;
1320 self.having_clause().walk_ast(out.reborrow())?;
1321 self.order_clause().walk_ast(out.reborrow())?;
1322 self.limit_offset_clause().walk_ast(out.reborrow())?;
1323 self.locking_clause().walk_ast(out.reborrow())?;
1324 Ok(())
1325 }
1326 }
1327
1328 impl<'a, ST, QS, GB>
1329 diesel::query_builder::QueryFragment<
1330 super::backend::MultiBackend,
1331 super::backend::MultiSelectStatementSyntax,
1332 >
1333 for diesel::internal::derives::multiconnection::BoxedSelectStatement<
1334 'a,
1335 ST,
1336 QS,
1337 super::backend::MultiBackend,
1338 GB,
1339 >
1340 where
1341 QS: diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1342 {
1343 fn walk_ast<'b>(
1344 &'b self,
1345 pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1346 ) -> diesel::QueryResult<()> {
1347 use diesel::internal::derives::multiconnection::BoxedQueryHelper;
1348 self.build_query(pass, |where_clause, pass| where_clause.walk_ast(pass))
1349 }
1350 }
1351
1352 impl diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1353 for diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<
1354 '_,
1355 super::backend::MultiBackend,
1356 >
1357 {
1358 fn walk_ast<'b>(
1359 &'b self,
1360 mut pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1361 ) -> diesel::QueryResult<()> {
1362 if let Some(ref limit) = self.limit {
1363 limit.walk_ast(pass.reborrow())?;
1364 }
1365 if let Some(ref offset) = self.offset {
1366 offset.walk_ast(pass.reborrow())?;
1367 }
1368 Ok(())
1369 }
1370 }
1371
1372 impl<'a> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1373 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::NoLimitClause, diesel::internal::derives::multiconnection::NoOffsetClause>
1374 {
1375 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1376
1377 fn into_boxed(self) -> Self::BoxedClause {
1378 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1379 limit: None,
1380 offset: None,
1381 }
1382 }
1383 }
1384 impl<'a, L> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1385 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::LimitClause<L>, diesel::internal::derives::multiconnection::NoOffsetClause>
1386 where diesel::internal::derives::multiconnection::LimitClause<L>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1387 {
1388 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1389 fn into_boxed(self) -> Self::BoxedClause {
1390 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1391 limit: Some(Box::new(self.limit_clause)),
1392 offset: None,
1393 }
1394 }
1395 }
1396 impl<'a, O> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1397 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::NoLimitClause, diesel::internal::derives::multiconnection::OffsetClause<O>>
1398 where diesel::internal::derives::multiconnection::OffsetClause<O>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1399
1400 {
1401 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1402 fn into_boxed(self) -> Self::BoxedClause {
1403 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1404 limit: None,
1405 offset: Some(Box::new(self.offset_clause)),
1406 }
1407 }
1408 }
1409 impl<'a, L, O> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1410 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::LimitClause<L>, diesel::internal::derives::multiconnection::OffsetClause<O>>
1411 where diesel::internal::derives::multiconnection::LimitClause<L>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1412 diesel::internal::derives::multiconnection::OffsetClause<O>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1413 {
1414 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1415 fn into_boxed(self) -> Self::BoxedClause {
1416 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1417 limit: Some(Box::new(self.limit_clause)),
1418 offset: Some(Box::new(self.offset_clause)),
1419 }
1420 }
1421 }
1422
1423 impl<Col, Expr> diesel::insertable::InsertValues<super::multi_connection_impl::backend::MultiBackend, Col::Table>
1424 for diesel::insertable::DefaultableColumnInsertValue<diesel::insertable::ColumnInsertValue<Col, Expr>>
1425 where
1426 Col: diesel::prelude::Column,
1427 Expr: diesel::prelude::Expression<SqlType = Col::SqlType>,
1428 Expr: diesel::prelude::AppearsOnTable<diesel::internal::derives::multiconnection::NoFromClause>,
1429 Self: diesel::query_builder::QueryFragment<super::multi_connection_impl::backend::MultiBackend>,
1430 #(#insert_values_backend_bounds,)*
1431 {
1432 fn column_names(
1433 &self,
1434 mut out: diesel::query_builder::AstPass<'_, '_, super::multi_connection_impl::backend::MultiBackend>
1435 ) -> QueryResult<()> {
1436 use diesel::internal::derives::multiconnection::AstPassHelper;
1437
1438 match out.backend() {
1439 #(#insert_values_impl_variants,)*
1440 }
1441 }
1442 }
1443 }
1444}
1445
1446fn generate_backend(connection_types: &[ConnectionVariant]) -> TokenStream {
1447 let backend_variants = connection_types.iter().map(|c| {
1448 let ident = c.name;
1449 let ty = c.ty;
1450 quote::quote! {
1451 #ident(<#ty as diesel::Connection>::Backend)
1452 }
1453 });
1454
1455 let value_variants = connection_types.iter().map(|c| {
1456 let ident = c.name;
1457 let ty = c.ty;
1458 quote::quote! {
1459 #ident(<<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::RawValue<'a>)
1460 }
1461 });
1462
1463 let type_metadata_variants = connection_types.iter().map(|c| {
1464 let ident = c.name;
1465 let ty = c.ty;
1466 quote::quote! {
1467 pub(super) #ident: Option<<<#ty as diesel::Connection>::Backend as diesel::sql_types::TypeMetadata>::TypeMetadata>
1468 }
1469 });
1470
1471 let has_sql_type_impls = vec![
1472 quote::quote!(diesel::sql_types::SmallInt),
1473 quote::quote!(diesel::sql_types::Integer),
1474 quote::quote!(diesel::sql_types::BigInt),
1475 quote::quote!(diesel::sql_types::Double),
1476 quote::quote!(diesel::sql_types::Float),
1477 quote::quote!(diesel::sql_types::Text),
1478 quote::quote!(diesel::sql_types::Binary),
1479 quote::quote!(diesel::sql_types::Date),
1480 quote::quote!(diesel::sql_types::Time),
1481 quote::quote!(diesel::sql_types::Timestamp),
1482 quote::quote!(diesel::sql_types::Bool),
1483 ]
1484 .into_iter()
1485 .map(generate_has_sql_type_impls);
1486
1487 let into_variant_functions = connection_types.iter().map(|c| {
1488 let ty = c.ty;
1489 let ident = c.name;
1490 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), ident.span());
1491 quote::quote! {
1492 pub(super) fn #lower_ident(&self) -> &<#ty as diesel::Connection>::Backend {
1493 match self {
1494 Self::#ident(b) => b,
1495 _ => unreachable!(),
1496 }
1497 }
1498 }
1499 });
1500
1501 let from_sql_match_arms = connection_types.iter().map(|v| {
1502 let ident = v.name;
1503 let ty = v.ty;
1504 quote::quote!{
1505 Self::#ident(b) => {
1506 <T as diesel::deserialize::FromSql<ST, <#ty as diesel::Connection>::Backend>>::from_sql(b)
1507 }
1508 }
1509 });
1510
1511 let backend_from_sql_bounds = connection_types.iter().map(|v| {
1512 let ty = v.ty;
1513 quote::quote! {
1514 T: diesel::deserialize::FromSql<ST, <#ty as diesel::Connection>::Backend>
1515 }
1516 });
1517
1518 let query_fragment_impl_variants = connection_types.iter().map(|c| {
1519 let ident = c.name;
1520 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), c.name.span());
1521 let ty = c.ty;
1522 quote::quote! {
1523 super::backend::MultiBackend::#ident(_) => {
1524 <T as diesel::query_builder::QueryFragment<<#ty as diesel::connection::Connection>::Backend>>::walk_ast(
1525 ast_node,
1526 pass.cast_database(
1527 super::bind_collector::MultiBindCollector::#lower_ident,
1528 super::query_builder::MultiQueryBuilder::#lower_ident,
1529 super::backend::MultiBackend::#lower_ident,
1530 |l| {
1531 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l)
1532 .expect("It's possible to downcast the metadata lookup type to the correct type")
1533 },
1534 ),
1535 )
1536 }
1537 }
1538 });
1539
1540 let query_fragment_impl_bounds = connection_types.iter().map(|c| {
1541 let ty = c.ty;
1542
1543 quote::quote! {
1544 T: diesel::query_builder::QueryFragment<<#ty as diesel::Connection>::Backend>
1545 }
1546 });
1547
1548 let lookup_impl = connection_types.iter().map(|v| {
1549 let name = v.name;
1550 let ty = v.ty;
1551
1552 quote::quote!{
1553 if let Some(lookup) = <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(lookup) {
1554 ret.#name = Some(<<#ty as diesel::Connection>::Backend as diesel::sql_types::HasSqlType<ST>>::metadata(lookup));
1555 }
1556 }
1557
1558 });
1559
1560 let lookup_sql_type_bounds = connection_types.iter().map(|c| {
1561 let ty = c.ty;
1562 quote::quote! {
1563 <#ty as diesel::Connection>::Backend: diesel::sql_types::HasSqlType<ST>
1564 }
1565 });
1566
1567 quote::quote! {
1568 pub enum MultiBackend {
1569 #(#backend_variants,)*
1570 }
1571
1572 impl MultiBackend {
1573 #(#into_variant_functions)*
1574
1575 pub fn lookup_sql_type<ST>(lookup: &mut dyn std::any::Any) -> MultiTypeMetadata
1576 where #(#lookup_sql_type_bounds,)*
1577 {
1578 let mut ret = MultiTypeMetadata::default();
1579 #(#lookup_impl)*
1580 ret
1581 }
1582 }
1583
1584 impl MultiBackend {
1585 pub fn walk_variant_ast<'b, T>(
1586 ast_node: &'b T,
1587 pass: diesel::query_builder::AstPass<'_, 'b, Self>,
1588 ) -> diesel::QueryResult<()>
1589 where #(#query_fragment_impl_bounds,)*
1590 {
1591 use diesel::internal::derives::multiconnection::AstPassHelper;
1592 match pass.backend() {
1593 #(#query_fragment_impl_variants,)*
1594 }
1595 }
1596 }
1597
1598 pub enum MultiRawValue<'a> {
1599 #(#value_variants,)*
1600 }
1601
1602 impl MultiRawValue<'_> {
1603 pub fn from_sql<T, ST>(self) -> diesel::deserialize::Result<T>
1604 where #(#backend_from_sql_bounds,)*
1605 {
1606 match self {
1607 #(#from_sql_match_arms,)*
1608 }
1609 }
1610 }
1611
1612 impl diesel::backend::Backend for MultiBackend {
1613 type QueryBuilder = super::query_builder::MultiQueryBuilder;
1614 type RawValue<'a> = MultiRawValue<'a>;
1615 type BindCollector<'a> = super::bind_collector::MultiBindCollector<'a>;
1616 }
1617
1618 #[derive(Default)]
1619 #[allow(non_snake_case)]
1620 pub struct MultiTypeMetadata {
1621 #(#type_metadata_variants,)*
1622 }
1623
1624 impl diesel::sql_types::TypeMetadata for MultiBackend {
1625 type TypeMetadata = MultiTypeMetadata;
1626
1627 type MetadataLookup = dyn std::any::Any;
1628 }
1629
1630 pub struct MultiReturningClause;
1631 pub struct MultiInsertWithDefaultKeyword;
1632 pub struct MultiBatchInsertSupport;
1633 pub struct MultiDefaultValueClauseForInsert;
1634 pub struct MultiEmptyFromClauseSyntax;
1635 pub struct MultiExistsSyntax;
1636 pub struct MultiArrayComparisonSyntax;
1637 pub struct MultiConcatClauseSyntax;
1638 pub struct MultiSelectStatementSyntax;
1639 pub struct MultiAliasSyntax;
1640
1641 impl diesel::backend::SqlDialect for MultiBackend {
1642 type ReturningClause = MultiReturningClause;
1643 type OnConflictClause = diesel::internal::derives::multiconnection::sql_dialect::on_conflict_clause::DoesNotSupportOnConflictClause;
1645 type InsertWithDefaultKeyword = MultiInsertWithDefaultKeyword;
1646 type BatchInsertSupport = MultiBatchInsertSupport;
1647 type DefaultValueClauseForInsert = MultiDefaultValueClauseForInsert;
1648 type EmptyFromClauseSyntax = MultiEmptyFromClauseSyntax;
1649 type ExistsSyntax = MultiExistsSyntax;
1650 type ArrayComparison = MultiArrayComparisonSyntax;
1651 type ConcatClause = MultiConcatClauseSyntax;
1652 type SelectStatementSyntax = MultiSelectStatementSyntax;
1653 type AliasSyntax = MultiAliasSyntax;
1654 }
1655
1656 impl diesel::internal::derives::multiconnection::TrustedBackend for MultiBackend {}
1657 impl diesel::internal::derives::multiconnection::DieselReserveSpecialization for MultiBackend {}
1658
1659 #(#has_sql_type_impls)*
1660 }
1661}