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 = "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        // we need to have separate impls for Sized values and str/[u8] as otherwise
848        // we need separate impls for `Sized` and `str`/`[u8]` here as
849        // we cannot use `Any::downcast_ref` otherwise (which implies `Sized`)
850        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            // we use an option here to initialize an "empty"
923            // as part of the `BindCollector` impl below
924            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                        // nulls are special and need a special handling because
1002                        // there is a wildcard `ToSql` impl in diesel. That means we won't
1003                        // set the `inner` field of `BindValue` to something for the `None`
1004                        // case. Therefore we need to handle that explicitly here.
1005                        //
1006                        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            // no on conflict support is also the default
1665            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}