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 = "numeric") {
649 to_sql_impls.push((
650 quote::quote!(diesel::sql_types::Numeric),
651 quote::quote!(diesel::internal::derives::multiconnection::bigdecimal::BigDecimal),
652 ));
653 }
654 if cfg!(feature = "chrono") {
655 to_sql_impls.push((
656 quote::quote!(diesel::sql_types::Timestamp),
657 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
658 ));
659 to_sql_impls.push((
660 quote::quote!(diesel::sql_types::Date),
661 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
662 ));
663 to_sql_impls.push((
664 quote::quote!(diesel::sql_types::Time),
665 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
666 ));
667 }
668 if cfg!(feature = "time") {
669 to_sql_impls.push((
670 quote::quote!(diesel::sql_types::Timestamp),
671 quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
672 ));
673 to_sql_impls.push((
674 quote::quote!(diesel::sql_types::Time),
675 quote::quote!(diesel::internal::derives::multiconnection::time::Time),
676 ));
677 to_sql_impls.push((
678 quote::quote!(diesel::sql_types::Date),
679 quote::quote!(diesel::internal::derives::multiconnection::time::Date),
680 ));
681 }
682 let to_sql_impls = to_sql_impls
683 .into_iter()
684 .map(|t| generate_to_sql_impls(t, connection_types));
685
686 let mut from_sql_impls = vec![
687 (
688 quote::quote!(diesel::sql_types::SmallInt),
689 quote::quote!(i16),
690 ),
691 (
692 quote::quote!(diesel::sql_types::Integer),
693 quote::quote!(i32),
694 ),
695 (quote::quote!(diesel::sql_types::BigInt), quote::quote!(i64)),
696 (quote::quote!(diesel::sql_types::Double), quote::quote!(f64)),
697 (quote::quote!(diesel::sql_types::Float), quote::quote!(f32)),
698 (
699 quote::quote!(diesel::sql_types::Text),
700 quote::quote!(String),
701 ),
702 (
703 quote::quote!(diesel::sql_types::Binary),
704 quote::quote!(Vec<u8>),
705 ),
706 (quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
707 ];
708 if cfg!(feature = "numeric") {
709 from_sql_impls.push((
710 quote::quote!(diesel::sql_types::Numeric),
711 quote::quote!(diesel::internal::derives::multiconnection::bigdecimal::BigDecimal),
712 ));
713 }
714 if cfg!(feature = "chrono") {
715 from_sql_impls.push((
716 quote::quote!(diesel::sql_types::Timestamp),
717 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
718 ));
719 from_sql_impls.push((
720 quote::quote!(diesel::sql_types::Date),
721 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
722 ));
723 from_sql_impls.push((
724 quote::quote!(diesel::sql_types::Time),
725 quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
726 ));
727 }
728 if cfg!(feature = "time") {
729 from_sql_impls.push((
730 quote::quote!(diesel::sql_types::Timestamp),
731 quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
732 ));
733 from_sql_impls.push((
734 quote::quote!(diesel::sql_types::Time),
735 quote::quote!(diesel::internal::derives::multiconnection::time::Time),
736 ));
737 from_sql_impls.push((
738 quote::quote!(diesel::sql_types::Date),
739 quote::quote!(diesel::internal::derives::multiconnection::time::Date),
740 ));
741 }
742 let from_sql_impls = from_sql_impls.into_iter().map(generate_from_sql_impls);
743
744 let into_bind_value_bounds = connection_types.iter().map(|c| {
745 let ty = c.ty;
746 quote::quote! {
747 diesel::serialize::ToSql<ST, <#ty as diesel::connection::Connection>::Backend>
748 }
749 });
750
751 let has_sql_type_bounds = connection_types.iter().map(|c| {
752 let ty = c.ty;
753 quote::quote! {
754 <#ty as diesel::connection::Connection>::Backend: diesel::sql_types::HasSqlType<ST>
755 }
756 });
757
758 let multi_bind_collector_variants = connection_types.iter().map(|c| {
759 let ident = c.name;
760 let ty = c.ty;
761 quote::quote! {
762 #ident(<<#ty as diesel::connection::Connection>::Backend as diesel::backend::Backend>::BindCollector<'a>)
763 }
764 });
765
766 let multi_bind_collector_accessor = connection_types.iter().map(|c| {
767 let ident = c.name;
768 let lower_ident = syn::Ident::new(&c.name.to_string().to_lowercase(), c.name.span());
769 let ty = c.ty;
770 quote::quote! {
771 pub(super) fn #lower_ident(
772 &mut self,
773 ) -> &mut <<#ty as diesel::connection::Connection>::Backend as diesel::backend::Backend>::BindCollector<'a> {
774 match self {
775 Self::#ident(bc) => bc,
776 _ => unreachable!(),
777 }
778 }
779
780 }
781
782 });
783
784 let push_to_inner_collector = connection_types.iter().map(|c| {
785 let ident = c.name;
786 let ty = c.ty;
787 quote::quote! {
788 Self::#ident(ref mut bc) => {
789 let out = out.inner.expect("This inner value is set via our custom `ToSql` impls");
790 let callback = out.push_bound_value_to_collector;
791 let value = out.value;
792 <_ as PushBoundValueToCollectorDB<<#ty as diesel::Connection>::Backend>>::push_bound_value(
793 callback,
794 value,
795 bc,
796 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(metadata_lookup)
797 .expect("We can downcast the metadata lookup to the right type")
798 )?
799 }
800 }
801 });
802
803 let push_null_to_inner_collector = connection_types
804 .iter()
805 .map(|c| {
806 let ident = c.name;
807 quote::quote! {
808 (Self::#ident(ref mut bc), super::backend::MultiTypeMetadata{ #ident: Some(metadata), .. }) => {
809 bc.push_null_value(metadata)?;
810 }
811 }
812 })
813 .collect::<Vec<_>>();
814
815 let push_bound_value_super_traits = connection_types
816 .iter()
817 .map(|c| {
818 let ty = c.ty;
819 quote::quote! {
820 PushBoundValueToCollectorDB<<#ty as diesel::Connection>::Backend>
821 }
822 })
823 .collect::<Vec<_>>();
824
825 quote::quote! {
826 pub enum MultiBindCollector<'a> {
827 #(#multi_bind_collector_variants,)*
828 }
829
830 impl<'a> MultiBindCollector<'a> {
831 #(#multi_bind_collector_accessor)*
832 }
833
834 trait PushBoundValueToCollectorDB<DB: diesel::backend::Backend> {
835 fn push_bound_value<'a: 'b, 'b>(
836 &self,
837 v: InnerBindValueKind<'a>,
838 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
839 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
840 ) -> diesel::result::QueryResult<()>;
841 }
842
843 struct PushBoundValueToCollectorImpl<ST, T: ?Sized> {
844 p: std::marker::PhantomData<(ST, T)>
845 }
846
847 impl<ST, T, DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<ST, T>
851 where DB: diesel::backend::Backend
852 + diesel::sql_types::HasSqlType<ST>,
853 T: diesel::serialize::ToSql<ST, DB> + 'static,
854 Option<T>: diesel::serialize::ToSql<diesel::sql_types::Nullable<ST>, DB> + 'static,
855 ST: diesel::sql_types::SqlType,
856 {
857 fn push_bound_value<'a: 'b, 'b>(
858 &self,
859 v: InnerBindValueKind<'a>,
860 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
861 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
862 ) -> diesel::result::QueryResult<()> {
863 use diesel::query_builder::BindCollector;
864 match v {
865 InnerBindValueKind::Sized(v) => {
866 let v = v.downcast_ref::<T>().expect("We know the type statically here");
867 collector.push_bound_value::<ST, T>(v, lookup)
868 }
869 InnerBindValueKind::Null => {
870 collector.push_bound_value::<diesel::sql_types::Nullable<ST>, Option<T>>(&None, lookup)
871 },
872 _ => unreachable!("We set the value to `InnerBindValueKind::Sized` or `InnerBindValueKind::Null`")
873 }
874 }
875 }
876
877 impl<DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<diesel::sql_types::Text, str>
878 where DB: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Text>,
879 str: diesel::serialize::ToSql<diesel::sql_types::Text, DB> + 'static,
880 {
881 fn push_bound_value<'a: 'b, 'b>(
882 &self,
883 v: InnerBindValueKind<'a>,
884 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
885 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
886 ) -> diesel::result::QueryResult<()> {
887 use diesel::query_builder::BindCollector;
888 if let InnerBindValueKind::Str(v) = v {
889 collector.push_bound_value::<diesel::sql_types::Text, str>(v, lookup)
890 } else {
891 unreachable!("We set the value to `InnerBindValueKind::Str`")
892 }
893 }
894 }
895
896 impl<DB> PushBoundValueToCollectorDB<DB> for PushBoundValueToCollectorImpl<diesel::sql_types::Binary, [u8]>
897 where DB: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Binary>,
898 [u8]: diesel::serialize::ToSql<diesel::sql_types::Binary, DB> + 'static,
899 {
900 fn push_bound_value<'a: 'b, 'b>(
901 &self,
902 v: InnerBindValueKind<'a>,
903 collector: &mut <DB as diesel::backend::Backend>::BindCollector<'b>,
904 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
905 ) -> diesel::result::QueryResult<()> {
906 use diesel::query_builder::BindCollector;
907 if let InnerBindValueKind::Bytes(v) = v {
908 collector.push_bound_value::<diesel::sql_types::Binary, [u8]>(v, lookup)
909 } else {
910 unreachable!("We set the value to `InnerBindValueKind::Binary`")
911 }
912 }
913 }
914
915 trait PushBoundValueToCollector: #(#push_bound_value_super_traits +)* {}
916
917 impl<T> PushBoundValueToCollector for T
918 where T: #(#push_bound_value_super_traits + )* {}
919
920 #[derive(Default)]
921 pub struct BindValue<'a> {
922 inner: Option<InnerBindValue<'a>>
925 }
926
927 struct InnerBindValue<'a> {
928 value: InnerBindValueKind<'a>,
929 push_bound_value_to_collector: &'static dyn PushBoundValueToCollector
930 }
931
932 enum InnerBindValueKind<'a> {
933 Sized(&'a (dyn std::any::Any + std::marker::Send + std::marker::Sync)),
934 Str(&'a str),
935 Bytes(&'a [u8]),
936 Null,
937 }
938
939 impl<'a> From<(diesel::sql_types::Text, &'a str)> for BindValue<'a> {
940 fn from((_, v): (diesel::sql_types::Text, &'a str)) -> Self {
941 Self {
942 inner: Some(InnerBindValue{
943 value: InnerBindValueKind::Str(v),
944 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
945 p: std::marker::PhantomData::<(diesel::sql_types::Text, str)>
946 }
947 })
948 }
949 }
950 }
951
952 impl<'a> From<(diesel::sql_types::Binary, &'a [u8])> for BindValue<'a> {
953 fn from((_, v): (diesel::sql_types::Binary, &'a [u8])) -> Self {
954 Self {
955 inner: Some(InnerBindValue {
956 value: InnerBindValueKind::Bytes(v),
957 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
958 p: std::marker::PhantomData::<(diesel::sql_types::Binary, [u8])>
959 }
960 })
961 }
962 }
963 }
964
965 impl<'a, T, ST> From<(ST, &'a T)> for BindValue<'a>
966 where
967 T: std::any::Any #(+ #into_bind_value_bounds)* + Send + Sync + 'static,
968 ST: Send + diesel::sql_types::SqlType<IsNull = diesel::sql_types::is_nullable::NotNull> + 'static,
969 #(#has_sql_type_bounds,)*
970 {
971 fn from((_, v): (ST, &'a T)) -> Self {
972 Self {
973 inner: Some(InnerBindValue{
974 value: InnerBindValueKind::Sized(v),
975 push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
976 p: std::marker::PhantomData::<(ST, T)>
977 }
978 })
979 }
980 }
981 }
982
983 impl<'a> diesel::query_builder::BindCollector<'a, MultiBackend> for MultiBindCollector<'a> {
984 type Buffer = multi_connection_impl::bind_collector::BindValue<'a>;
985
986 fn push_bound_value<T, U>(
987 &mut self,
988 bind: &'a U,
989 metadata_lookup: &mut (dyn std::any::Any + 'static),
990 ) -> diesel::QueryResult<()>
991 where
992 MultiBackend: diesel::sql_types::HasSqlType<T>,
993 U: diesel::serialize::ToSql<T, MultiBackend> + ?Sized + 'a,
994 {
995 let out = {
996 let out = multi_connection_impl::bind_collector::BindValue::default();
997 let mut out =
998 diesel::serialize::Output::<MultiBackend>::new(out, metadata_lookup);
999 let bind_is_null = bind.to_sql(&mut out).map_err(diesel::result::Error::SerializationError)?;
1000 if matches!(bind_is_null, diesel::serialize::IsNull::Yes) {
1001 let metadata = <MultiBackend as diesel::sql_types::HasSqlType<T>>::metadata(metadata_lookup);
1007 match (self, metadata) {
1008 #(#push_null_to_inner_collector)*
1009 _ => {
1010 unreachable!("We have matching metadata")
1011 },
1012 }
1013 return Ok(());
1014 } else {
1015 out.into_inner()
1016 }
1017 };
1018 match self {
1019 #(#push_to_inner_collector)*
1020 }
1021
1022 Ok(())
1023 }
1024
1025 fn push_null_value(&mut self, metadata: super::backend::MultiTypeMetadata) -> diesel::QueryResult<()> {
1026 match (self, metadata) {
1027 #(#push_null_to_inner_collector)*
1028 _ => unreachable!("We have matching metadata"),
1029 }
1030 Ok(())
1031 }
1032 }
1033
1034 #(#to_sql_impls)*
1035 #(#from_sql_impls)*
1036
1037 }
1038}
1039
1040fn generate_has_sql_type_impls(sql_type: TokenStream) -> TokenStream {
1041 quote::quote! {
1042 impl diesel::sql_types::HasSqlType<#sql_type> for super::MultiBackend {
1043 fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata {
1044 Self::lookup_sql_type::<#sql_type>(lookup)
1045 }
1046 }
1047 }
1048}
1049
1050fn generate_from_sql_impls((sql_type, tpe): (TokenStream, TokenStream)) -> TokenStream {
1051 quote::quote! {
1052 impl diesel::deserialize::FromSql<#sql_type, super::MultiBackend> for #tpe {
1053 fn from_sql(
1054 bytes: <super::MultiBackend as diesel::backend::Backend>::RawValue<'_>,
1055 ) -> diesel::deserialize::Result<Self> {
1056 bytes.from_sql::<Self, #sql_type>()
1057 }
1058 }
1059
1060 }
1061}
1062
1063fn generate_to_sql_impls(
1064 (sql_type, tpe): (TokenStream, TokenStream),
1065 _connection_types: &[ConnectionVariant],
1066) -> TokenStream {
1067 quote::quote! {
1068 impl diesel::serialize::ToSql<#sql_type, super::MultiBackend> for #tpe {
1069 fn to_sql<'b>(
1070 &'b self,
1071 out: &mut diesel::serialize::Output<'b, '_, super::MultiBackend>,
1072 ) -> diesel::serialize::Result {
1073 out.set_value((#sql_type, self));
1074 Ok(diesel::serialize::IsNull::No)
1075 }
1076 }
1077 }
1078}
1079
1080fn generate_queryfragment_impls(
1081 trait_def: TokenStream,
1082 query_fragment_bounds: &[TokenStream],
1083) -> TokenStream {
1084 quote::quote! {
1085 impl #trait_def
1086 where
1087 Self: #(#query_fragment_bounds+)*
1088 {
1089 fn walk_ast<'b>(
1090 &'b self,
1091 pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1092 ) -> diesel::QueryResult<()> {
1093 super::backend::MultiBackend::walk_variant_ast(self, pass)
1094 }
1095 }
1096 }
1097}
1098
1099fn generate_querybuilder(connection_types: &[ConnectionVariant]) -> TokenStream {
1100 let variants = connection_types.iter().map(|c| {
1101 let ident = c.name;
1102 let ty = c.ty;
1103 quote::quote! {
1104 #ident(<<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::QueryBuilder)
1105 }
1106 });
1107
1108 let push_sql_impl = connection_types.iter().map(|c| {
1109 let ident = c.name;
1110 quote::quote! {
1111 Self::#ident(q) => q.push_sql(sql)
1112 }
1113 });
1114
1115 let push_identifier_impl = connection_types.iter().map(|c| {
1116 let ident = c.name;
1117 quote::quote! {
1118 Self::#ident(q) => q.push_identifier(identifier)
1119 }
1120 });
1121
1122 let push_bind_param_impl = connection_types.iter().map(|c| {
1123 let ident = c.name;
1124 quote::quote! {
1125 Self::#ident(q) => q.push_bind_param()
1126 }
1127 });
1128
1129 let finish_impl = connection_types.iter().map(|c| {
1130 let ident = c.name;
1131 quote::quote! {
1132 Self::#ident(q) => q.finish()
1133 }
1134 });
1135
1136 let into_variant_functions = connection_types.iter().map(|c|{
1137 let ty = c.ty;
1138 let ident = c.name;
1139 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), ident.span());
1140 quote::quote! {
1141 pub(super) fn #lower_ident(&mut self) -> &mut <<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::QueryBuilder {
1142 match self {
1143 Self::#ident(qb) => qb,
1144 _ => unreachable!(),
1145 }
1146 }
1147 }
1148 });
1149
1150 let query_fragment_bounds = connection_types.iter().map(|c| {
1151 let ty = c.ty;
1152 quote::quote! {
1153 diesel::query_builder::QueryFragment<<#ty as diesel::connection::Connection>::Backend>
1154 }
1155 }).collect::<Vec<_>>();
1156
1157 let duplicate_query_builder = connection_types.iter().map(|c| {
1158 let ident = c.name;
1159 quote::quote! {
1160 Self::#ident(_) => Self::#ident(Default::default())
1161 }
1162 });
1163
1164 let query_fragment = quote::quote! {
1165 diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1166 };
1167
1168 let query_fragment_impls = IntoIterator::into_iter([
1169 quote::quote!{
1170 <L, O> #query_fragment for diesel::internal::derives::multiconnection::LimitOffsetClause<L, O>
1171 },
1172 quote::quote! {
1173 <L, R> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiConcatClauseSyntax>
1174 for diesel::internal::derives::multiconnection::Concat<L, R>
1175 },
1176 quote::quote! {
1177 <T, U> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1178 for diesel::internal::derives::multiconnection::array_comparison::In<T, U>
1179 },
1180 quote::quote! {
1181 <T, U> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1182 for diesel::internal::derives::multiconnection::array_comparison::NotIn<T, U>
1183 },
1184 quote::quote! {
1185 <ST, I> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiArrayComparisonSyntax>
1186 for diesel::internal::derives::multiconnection::array_comparison::Many<ST, I>
1187 },
1188 quote::quote! {
1189 <T> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiExistsSyntax>
1190 for diesel::internal::derives::multiconnection::Exists<T>
1191 },
1192 quote::quote! {
1193 diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiEmptyFromClauseSyntax>
1194 for diesel::internal::derives::multiconnection::NoFromClause
1195 },
1196 quote::quote! {
1197 diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiDefaultValueClauseForInsert>
1198 for diesel::internal::derives::multiconnection::DefaultValues
1199 },
1200 quote::quote! {
1201 <Expr> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiReturningClause>
1202 for diesel::internal::derives::multiconnection::ReturningClause<Expr>
1203 },
1204 quote::quote! {
1205 <Expr> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiInsertWithDefaultKeyword>
1206 for diesel::insertable::DefaultableColumnInsertValue<Expr>
1207 },
1208 quote::quote! {
1209 <Tab, V, QId, const HAS_STATIC_QUERY_ID: bool> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiBatchInsertSupport>
1210 for diesel::internal::derives::multiconnection::BatchInsert<V, Tab, QId, HAS_STATIC_QUERY_ID>
1211 },
1212 quote::quote! {
1213 <S> diesel::query_builder::QueryFragment<super::backend::MultiBackend, super::backend::MultiAliasSyntax>
1214 for diesel::query_source::Alias<S>
1215 },
1216 ])
1217 .map(|t| generate_queryfragment_impls(t, &query_fragment_bounds));
1218
1219 let insert_values_impl_variants = connection_types.iter().map(|c| {
1220 let ident = c.name;
1221 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), c.name.span());
1222 let ty = c.ty;
1223 quote::quote! {
1224 super::backend::MultiBackend::#ident(_) => {
1225 <Self as diesel::insertable::InsertValues<<#ty as diesel::connection::Connection>::Backend, Col::Table>>::column_names(
1226 &self,
1227 out.cast_database(
1228 super::bind_collector::MultiBindCollector::#lower_ident,
1229 super::query_builder::MultiQueryBuilder::#lower_ident,
1230 super::backend::MultiBackend::#lower_ident,
1231 |l| {
1232 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l)
1233 .expect("It's possible to downcast the metadata lookup type to the correct type")
1234 },
1235 ),
1236 )
1237 }
1238 }
1239 });
1240
1241 let insert_values_backend_bounds = connection_types.iter().map(|c| {
1242 let ty = c.ty;
1243 quote::quote! {
1244 diesel::insertable::DefaultableColumnInsertValue<diesel::insertable::ColumnInsertValue<Col, Expr>>: diesel::insertable::InsertValues<<#ty as diesel::connection::Connection>::Backend, Col::Table>
1245 }
1246 });
1247
1248 quote::quote! {
1249 pub enum MultiQueryBuilder {
1250 #(#variants,)*
1251 }
1252
1253 impl MultiQueryBuilder {
1254 pub(super) fn duplicate(&self) -> Self {
1255 match self {
1256 #(#duplicate_query_builder,)*
1257 }
1258 }
1259 }
1260
1261 impl MultiQueryBuilder {
1262 #(#into_variant_functions)*
1263 }
1264
1265 impl diesel::query_builder::QueryBuilder<super::MultiBackend> for MultiQueryBuilder {
1266 fn push_sql(&mut self, sql: &str) {
1267 match self {
1268 #(#push_sql_impl,)*
1269 }
1270 }
1271
1272 fn push_identifier(&mut self, identifier: &str) -> diesel::QueryResult<()> {
1273 match self {
1274 #(#push_identifier_impl,)*
1275 }
1276 }
1277
1278 fn push_bind_param(&mut self) {
1279 match self {
1280 #(#push_bind_param_impl,)*
1281 }
1282 }
1283
1284 fn finish(self) -> String {
1285 match self {
1286 #(#finish_impl,)*
1287 }
1288 }
1289 }
1290
1291 #(#query_fragment_impls)*
1292
1293 impl<F, S, D, W, O, LOf, G, H, LC>
1294 diesel::query_builder::QueryFragment<
1295 super::backend::MultiBackend,
1296 super::backend::MultiSelectStatementSyntax,
1297 >
1298 for diesel::internal::derives::multiconnection::SelectStatement<
1299 F,
1300 S,
1301 D,
1302 W,
1303 O,
1304 LOf,
1305 G,
1306 H,
1307 LC,
1308 >
1309 where
1310 S: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1311 F: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1312 D: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1313 W: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1314 O: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1315 LOf: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1316 G: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1317 H: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1318 LC: diesel::query_builder::QueryFragment<super::backend::MultiBackend>,
1319 {
1320 fn walk_ast<'b>(
1321 &'b self,
1322 mut out: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1323 ) -> diesel::QueryResult<()> {
1324 use diesel::internal::derives::multiconnection::SelectStatementAccessor;
1325
1326 out.push_sql("SELECT ");
1327 self.distinct_clause().walk_ast(out.reborrow())?;
1328 self.select_clause().walk_ast(out.reborrow())?;
1329 self.from_clause().walk_ast(out.reborrow())?;
1330 self.where_clause().walk_ast(out.reborrow())?;
1331 self.group_by_clause().walk_ast(out.reborrow())?;
1332 self.having_clause().walk_ast(out.reborrow())?;
1333 self.order_clause().walk_ast(out.reborrow())?;
1334 self.limit_offset_clause().walk_ast(out.reborrow())?;
1335 self.locking_clause().walk_ast(out.reborrow())?;
1336 Ok(())
1337 }
1338 }
1339
1340 impl<'a, ST, QS, GB>
1341 diesel::query_builder::QueryFragment<
1342 super::backend::MultiBackend,
1343 super::backend::MultiSelectStatementSyntax,
1344 >
1345 for diesel::internal::derives::multiconnection::BoxedSelectStatement<
1346 'a,
1347 ST,
1348 QS,
1349 super::backend::MultiBackend,
1350 GB,
1351 >
1352 where
1353 QS: diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1354 {
1355 fn walk_ast<'b>(
1356 &'b self,
1357 pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1358 ) -> diesel::QueryResult<()> {
1359 use diesel::internal::derives::multiconnection::BoxedQueryHelper;
1360 self.build_query(pass, |where_clause, pass| where_clause.walk_ast(pass))
1361 }
1362 }
1363
1364 impl diesel::query_builder::QueryFragment<super::backend::MultiBackend>
1365 for diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<
1366 '_,
1367 super::backend::MultiBackend,
1368 >
1369 {
1370 fn walk_ast<'b>(
1371 &'b self,
1372 mut pass: diesel::query_builder::AstPass<'_, 'b, MultiBackend>,
1373 ) -> diesel::QueryResult<()> {
1374 if let Some(ref limit) = self.limit {
1375 limit.walk_ast(pass.reborrow())?;
1376 }
1377 if let Some(ref offset) = self.offset {
1378 offset.walk_ast(pass.reborrow())?;
1379 }
1380 Ok(())
1381 }
1382 }
1383
1384 impl<'a> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1385 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::NoLimitClause, diesel::internal::derives::multiconnection::NoOffsetClause>
1386 {
1387 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1388
1389 fn into_boxed(self) -> Self::BoxedClause {
1390 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1391 limit: None,
1392 offset: None,
1393 }
1394 }
1395 }
1396 impl<'a, L> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1397 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::LimitClause<L>, diesel::internal::derives::multiconnection::NoOffsetClause>
1398 where diesel::internal::derives::multiconnection::LimitClause<L>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1399 {
1400 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1401 fn into_boxed(self) -> Self::BoxedClause {
1402 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1403 limit: Some(Box::new(self.limit_clause)),
1404 offset: None,
1405 }
1406 }
1407 }
1408 impl<'a, O> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1409 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::NoLimitClause, diesel::internal::derives::multiconnection::OffsetClause<O>>
1410 where diesel::internal::derives::multiconnection::OffsetClause<O>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1411
1412 {
1413 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1414 fn into_boxed(self) -> Self::BoxedClause {
1415 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1416 limit: None,
1417 offset: Some(Box::new(self.offset_clause)),
1418 }
1419 }
1420 }
1421 impl<'a, L, O> diesel::query_builder::IntoBoxedClause<'a, super::multi_connection_impl::backend::MultiBackend>
1422 for diesel::internal::derives::multiconnection::LimitOffsetClause<diesel::internal::derives::multiconnection::LimitClause<L>, diesel::internal::derives::multiconnection::OffsetClause<O>>
1423 where diesel::internal::derives::multiconnection::LimitClause<L>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1424 diesel::internal::derives::multiconnection::OffsetClause<O>: diesel::query_builder::QueryFragment<super::backend::MultiBackend> + Send + 'static,
1425 {
1426 type BoxedClause = diesel::internal::derives::multiconnection::BoxedLimitOffsetClause<'a, super::multi_connection_impl::backend::MultiBackend>;
1427 fn into_boxed(self) -> Self::BoxedClause {
1428 diesel::internal::derives::multiconnection::BoxedLimitOffsetClause {
1429 limit: Some(Box::new(self.limit_clause)),
1430 offset: Some(Box::new(self.offset_clause)),
1431 }
1432 }
1433 }
1434
1435 impl<Col, Expr> diesel::insertable::InsertValues<super::multi_connection_impl::backend::MultiBackend, Col::Table>
1436 for diesel::insertable::DefaultableColumnInsertValue<diesel::insertable::ColumnInsertValue<Col, Expr>>
1437 where
1438 Col: diesel::prelude::Column,
1439 Expr: diesel::prelude::Expression<SqlType = Col::SqlType>,
1440 Expr: diesel::prelude::AppearsOnTable<diesel::internal::derives::multiconnection::NoFromClause>,
1441 Self: diesel::query_builder::QueryFragment<super::multi_connection_impl::backend::MultiBackend>,
1442 #(#insert_values_backend_bounds,)*
1443 {
1444 fn column_names(
1445 &self,
1446 mut out: diesel::query_builder::AstPass<'_, '_, super::multi_connection_impl::backend::MultiBackend>
1447 ) -> diesel::QueryResult<()> {
1448 use diesel::internal::derives::multiconnection::AstPassHelper;
1449
1450 match out.backend() {
1451 #(#insert_values_impl_variants,)*
1452 }
1453 }
1454 }
1455 }
1456}
1457
1458fn generate_backend(connection_types: &[ConnectionVariant]) -> TokenStream {
1459 let backend_variants = connection_types.iter().map(|c| {
1460 let ident = c.name;
1461 let ty = c.ty;
1462 quote::quote! {
1463 #ident(<#ty as diesel::Connection>::Backend)
1464 }
1465 });
1466
1467 let value_variants = connection_types.iter().map(|c| {
1468 let ident = c.name;
1469 let ty = c.ty;
1470 quote::quote! {
1471 #ident(<<#ty as diesel::Connection>::Backend as diesel::backend::Backend>::RawValue<'a>)
1472 }
1473 });
1474
1475 let type_metadata_variants = connection_types.iter().map(|c| {
1476 let ident = c.name;
1477 let ty = c.ty;
1478 quote::quote! {
1479 pub(super) #ident: Option<<<#ty as diesel::Connection>::Backend as diesel::sql_types::TypeMetadata>::TypeMetadata>
1480 }
1481 });
1482
1483 let mut has_sql_type_impls = vec![
1484 quote::quote!(diesel::sql_types::SmallInt),
1485 quote::quote!(diesel::sql_types::Integer),
1486 quote::quote!(diesel::sql_types::BigInt),
1487 quote::quote!(diesel::sql_types::Double),
1488 quote::quote!(diesel::sql_types::Float),
1489 quote::quote!(diesel::sql_types::Text),
1490 quote::quote!(diesel::sql_types::Binary),
1491 quote::quote!(diesel::sql_types::Date),
1492 quote::quote!(diesel::sql_types::Time),
1493 quote::quote!(diesel::sql_types::Timestamp),
1494 quote::quote!(diesel::sql_types::Bool),
1495 ];
1496 if cfg!(feature = "numeric") {
1497 has_sql_type_impls.push(quote::quote!(diesel::sql_types::Numeric))
1498 }
1499 let has_sql_type_impls = has_sql_type_impls
1500 .into_iter()
1501 .map(generate_has_sql_type_impls);
1502
1503 let into_variant_functions = connection_types.iter().map(|c| {
1504 let ty = c.ty;
1505 let ident = c.name;
1506 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), ident.span());
1507 quote::quote! {
1508 pub(super) fn #lower_ident(&self) -> &<#ty as diesel::Connection>::Backend {
1509 match self {
1510 Self::#ident(b) => b,
1511 _ => unreachable!(),
1512 }
1513 }
1514 }
1515 });
1516
1517 let from_sql_match_arms = connection_types.iter().map(|v| {
1518 let ident = v.name;
1519 let ty = v.ty;
1520 quote::quote!{
1521 Self::#ident(b) => {
1522 <T as diesel::deserialize::FromSql<ST, <#ty as diesel::Connection>::Backend>>::from_sql(b)
1523 }
1524 }
1525 });
1526
1527 let backend_from_sql_bounds = connection_types.iter().map(|v| {
1528 let ty = v.ty;
1529 quote::quote! {
1530 T: diesel::deserialize::FromSql<ST, <#ty as diesel::Connection>::Backend>
1531 }
1532 });
1533
1534 let query_fragment_impl_variants = connection_types.iter().map(|c| {
1535 let ident = c.name;
1536 let lower_ident = syn::Ident::new(&ident.to_string().to_lowercase(), c.name.span());
1537 let ty = c.ty;
1538 quote::quote! {
1539 super::backend::MultiBackend::#ident(_) => {
1540 <T as diesel::query_builder::QueryFragment<<#ty as diesel::connection::Connection>::Backend>>::walk_ast(
1541 ast_node,
1542 pass.cast_database(
1543 super::bind_collector::MultiBindCollector::#lower_ident,
1544 super::query_builder::MultiQueryBuilder::#lower_ident,
1545 super::backend::MultiBackend::#lower_ident,
1546 |l| {
1547 <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l)
1548 .expect("It's possible to downcast the metadata lookup type to the correct type")
1549 },
1550 ),
1551 )
1552 }
1553 }
1554 });
1555
1556 let query_fragment_impl_bounds = connection_types.iter().map(|c| {
1557 let ty = c.ty;
1558
1559 quote::quote! {
1560 T: diesel::query_builder::QueryFragment<<#ty as diesel::Connection>::Backend>
1561 }
1562 });
1563
1564 let lookup_impl = connection_types.iter().map(|v| {
1565 let name = v.name;
1566 let ty = v.ty;
1567
1568 quote::quote!{
1569 if let Some(lookup) = <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(lookup) {
1570 ret.#name = Some(<<#ty as diesel::Connection>::Backend as diesel::sql_types::HasSqlType<ST>>::metadata(lookup));
1571 }
1572 }
1573
1574 });
1575
1576 let lookup_sql_type_bounds = connection_types.iter().map(|c| {
1577 let ty = c.ty;
1578 quote::quote! {
1579 <#ty as diesel::Connection>::Backend: diesel::sql_types::HasSqlType<ST>
1580 }
1581 });
1582
1583 quote::quote! {
1584 pub enum MultiBackend {
1585 #(#backend_variants,)*
1586 }
1587
1588 impl MultiBackend {
1589 #(#into_variant_functions)*
1590
1591 pub fn lookup_sql_type<ST>(lookup: &mut dyn std::any::Any) -> MultiTypeMetadata
1592 where #(#lookup_sql_type_bounds,)*
1593 {
1594 let mut ret = MultiTypeMetadata::default();
1595 #(#lookup_impl)*
1596 ret
1597 }
1598 }
1599
1600 impl MultiBackend {
1601 pub fn walk_variant_ast<'b, T>(
1602 ast_node: &'b T,
1603 pass: diesel::query_builder::AstPass<'_, 'b, Self>,
1604 ) -> diesel::QueryResult<()>
1605 where #(#query_fragment_impl_bounds,)*
1606 {
1607 use diesel::internal::derives::multiconnection::AstPassHelper;
1608 match pass.backend() {
1609 #(#query_fragment_impl_variants,)*
1610 }
1611 }
1612 }
1613
1614 pub enum MultiRawValue<'a> {
1615 #(#value_variants,)*
1616 }
1617
1618 impl MultiRawValue<'_> {
1619 pub fn from_sql<T, ST>(self) -> diesel::deserialize::Result<T>
1620 where #(#backend_from_sql_bounds,)*
1621 {
1622 match self {
1623 #(#from_sql_match_arms,)*
1624 }
1625 }
1626 }
1627
1628 impl diesel::backend::Backend for MultiBackend {
1629 type QueryBuilder = super::query_builder::MultiQueryBuilder;
1630 type RawValue<'a> = MultiRawValue<'a>;
1631 type BindCollector<'a> = super::bind_collector::MultiBindCollector<'a>;
1632 }
1633
1634 #[derive(Default)]
1635 #[allow(non_snake_case)]
1636 pub struct MultiTypeMetadata {
1637 #(#type_metadata_variants,)*
1638 }
1639
1640 impl diesel::sql_types::TypeMetadata for MultiBackend {
1641 type TypeMetadata = MultiTypeMetadata;
1642
1643 type MetadataLookup = dyn std::any::Any;
1644 }
1645
1646 pub struct MultiReturningClause;
1647 pub struct MultiInsertWithDefaultKeyword;
1648 pub struct MultiBatchInsertSupport;
1649 pub struct MultiDefaultValueClauseForInsert;
1650 pub struct MultiEmptyFromClauseSyntax;
1651 pub struct MultiExistsSyntax;
1652 pub struct MultiArrayComparisonSyntax;
1653 pub struct MultiConcatClauseSyntax;
1654 pub struct MultiSelectStatementSyntax;
1655 pub struct MultiAliasSyntax;
1656
1657 pub struct MultiWindowFrameClauseGroupSupport;
1658 pub struct MultiWindowFrameExclusionSupport;
1659 pub struct MultiAggregateFunctionExpressions;
1660 pub struct MultiBuiltInWindowFunctionRequireOrder;
1661
1662 impl diesel::backend::SqlDialect for MultiBackend {
1663 type ReturningClause = MultiReturningClause;
1664 type OnConflictClause = diesel::internal::derives::multiconnection::sql_dialect::on_conflict_clause::DoesNotSupportOnConflictClause;
1666 type InsertWithDefaultKeyword = MultiInsertWithDefaultKeyword;
1667 type BatchInsertSupport = MultiBatchInsertSupport;
1668 type DefaultValueClauseForInsert = MultiDefaultValueClauseForInsert;
1669 type EmptyFromClauseSyntax = MultiEmptyFromClauseSyntax;
1670 type ExistsSyntax = MultiExistsSyntax;
1671 type ArrayComparison = MultiArrayComparisonSyntax;
1672 type ConcatClause = MultiConcatClauseSyntax;
1673 type SelectStatementSyntax = MultiSelectStatementSyntax;
1674 type AliasSyntax = MultiAliasSyntax;
1675 type WindowFrameClauseGroupSupport = MultiWindowFrameClauseGroupSupport;
1676 type WindowFrameExclusionSupport = MultiWindowFrameExclusionSupport;
1677 type AggregateFunctionExpressions = MultiAggregateFunctionExpressions;
1678 type BuiltInWindowFunctionRequireOrder = MultiBuiltInWindowFunctionRequireOrder;
1679 }
1680
1681 impl diesel::internal::derives::multiconnection::TrustedBackend for MultiBackend {}
1682 impl diesel::internal::derives::multiconnection::DieselReserveSpecialization for MultiBackend {}
1683
1684 #(#has_sql_type_impls)*
1685 }
1686}