diesel_derives/
multiconnection.rs

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            // we use untyped here as this does not really matter
338            // + that type is supported for all backends
339            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        // we need to have separate impls for Sized values and str/[u8] as otherwise
836        // we need separate impls for `Sized` and `str`/`[u8]` here as
837        // we cannot use `Any::downcast_ref` otherwise (which implies `Sized`)
838        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            // we use an option here to initialize an "empty"
911            // as part of the `BindCollector` impl below
912            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                        // nulls are special and need a special handling because
990                        // there is a wildcard `ToSql` impl in diesel. That means we won't
991                        // set the `inner` field of `BindValue` to something for the `None`
992                        // case. Therefore we need to handle that explicitly here.
993                        //
994                        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            // no on conflict support is also the default
1644            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}