diesel/connection/mod.rs
1//! Types related to database connections
2
3pub(crate) mod instrumentation;
4#[cfg(all(
5 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
6 any(
7 feature = "__sqlite-shared",
8 feature = "postgres",
9 feature = "mysql",
10 feature = "mariadb"
11 )
12))]
13pub(crate) mod statement_cache;
14#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
15pub mod statement_cache;
16mod transaction_manager;
17
18use crate::backend::Backend;
19use crate::expression::QueryMetadata;
20use crate::query_builder::{Query, QueryFragment, QueryId};
21use crate::result::*;
22use crate::sql_types::TypeMetadata;
23use core::fmt::Debug;
24
25#[cfg(feature = "std")]
26#[doc(inline)]
27pub use self::instrumentation::set_default_instrumentation;
28#[doc(inline)]
29pub use self::instrumentation::{
30 DebugQuery, Instrumentation, InstrumentationEvent, get_default_instrumentation,
31};
32#[doc(inline)]
33pub use self::transaction_manager::{
34 AnsiTransactionManager, InTransactionStatus, TransactionDepthChange, TransactionManager,
35 TransactionManagerStatus, ValidTransactionManagerStatus,
36};
37
38#[diesel_derives::__diesel_public_if(
39 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
40)]
41pub(crate) use self::private::ConnectionSealed;
42
43#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
44pub use self::private::MultiConnectionHelper;
45
46#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
47pub use self::instrumentation::{DynInstrumentation, StrQueryHelper};
48
49#[cfg(all(
50 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
51 any(
52 feature = "__sqlite-shared",
53 feature = "postgres",
54 feature = "mysql",
55 feature = "mariadb"
56 )
57))]
58pub(crate) use self::private::MultiConnectionHelper;
59
60/// Set cache size for a connection
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum CacheSize {
64 /// Caches all queries if possible
65 Unbounded,
66 /// Disable statement cache
67 Disabled,
68}
69
70/// Perform simple operations on a backend.
71///
72/// You should likely use [`Connection`] instead.
73pub trait SimpleConnection {
74 /// Execute multiple SQL statements within the same string.
75 ///
76 /// This function is used to execute migrations,
77 /// which may contain more than one SQL statement.
78 fn batch_execute(&mut self, query: &str) -> QueryResult<()>;
79}
80
81#[doc(hidden)]
82#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
83#[deprecated(note = "Directly use `LoadConnection::Cursor` instead")]
84pub type LoadRowIter<'conn, 'query, C, DB, B = DefaultLoadingMode> =
85 <C as self::private::ConnectionHelperType<DB, B>>::Cursor<'conn, 'query>;
86
87/// A connection to a database
88///
89/// This trait represents a database connection. It can be used to query the database through
90/// the query dsl provided by diesel, custom extensions or raw sql queries.
91///
92/// # Implementing a custom connection
93///
94/// There are several reasons why you would want to implement a custom connection implementation:
95///
96/// * To wrap an existing connection for instrumentation purposes
97/// * To use a different underlying library to provide a connection implementation
98/// for already existing backends.
99/// * To add support for an unsupported database system
100///
101/// Implementing a `Connection` in a third party crate requires
102/// enabling the
103/// `i-implement-a-third-party-backend-and-opt-into-breaking-changes`
104/// crate feature which grants access to some of diesel's implementation details.
105///
106///
107/// ## Wrapping an existing connection impl
108///
109/// Wrapping an existing connection allows you to customize the implementation to
110/// add additional functionality, like for example instrumentation. For this use case
111/// you only need to implement `Connection`, [`LoadConnection`] and all super traits.
112/// You should forward any method call to the wrapped connection type.
113/// It is **important** to also forward any method where diesel provides a
114/// default implementation, as the wrapped connection implementation may
115/// contain a customized implementation.
116///
117/// To allow the integration of your new connection type with other diesel features
118#[cfg_attr(
119 feature = "r2d2",
120 doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)"
121)]
122#[cfg_attr(
123 not(feature = "r2d2"),
124 doc = "it may be useful to also implement `R2D2Connection`"
125)]
126/// and [`MigrationConnection`](crate::migration::MigrationConnection).
127///
128/// ## Provide a new connection implementation for an existing backend
129///
130/// Implementing a new connection based on an existing backend can enable the usage of
131/// other methods to connect to the database. One example here would be to replace
132/// the official diesel provided connection implementations with an implementation
133/// based on a pure rust connection crate.
134///
135/// **It's important to use prepared statements to implement the following methods:**
136/// * [`LoadConnection::load`]
137/// * [`Connection::execute_returning_count`]
138///
139/// For performance reasons it may also be meaningful to cache already prepared statements.
140#[cfg_attr(
141 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
142 doc = "See [`StatementCache`](self::statement_cache::StatementCache)"
143)]
144#[cfg_attr(
145 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
146 doc = "See `StatementCache`"
147)]
148/// for a helper type to implement prepared statement caching.
149#[cfg_attr(
150 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
151 doc = "The [statement_cache](self::statement_cache)"
152)]
153#[cfg_attr(
154 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
155 doc = "The statement_cache"
156)]
157/// module documentation contains details about efficient prepared statement caching
158/// based on diesels query builder.
159///
160/// It is required to implement at least the following parts:
161///
162/// * A row type that describes how to receive values form a database row.
163/// This type needs to implement [`Row`](crate::row::Row)
164/// * A field type that describes a database field value.
165/// This type needs to implement [`Field`](crate::row::Field)
166/// * A connection type that wraps the connection +
167/// the necessary state management.
168/// * Maybe a [`TransactionManager`] implementation matching
169/// the interface provided by the database connection crate.
170/// Otherwise the implementation used by the corresponding
171/// `Connection` in diesel can be reused.
172///
173/// To allow the integration of your new connection type with other diesel features
174#[cfg_attr(
175 feature = "r2d2",
176 doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)"
177)]
178#[cfg_attr(
179 not(feature = "r2d2"),
180 doc = "it may be useful to also implement `R2D2Connection`"
181)]
182/// and [`MigrationConnection`](crate::migration::MigrationConnection).
183///
184/// The exact implementation of the `Connection` trait depends on the interface provided
185/// by the connection crate/library. A struct implementing `Connection` should
186#[cfg_attr(
187 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
188 doc = "likely contain a [`StatementCache`](self::statement_cache::StatementCache)"
189)]
190#[cfg_attr(
191 not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
192 doc = "likely contain a `StatementCache`"
193)]
194/// to cache prepared statements efficiently.
195///
196/// As implementations differ significantly between the supported backends
197/// we cannot give a one for all description here. Generally it's likely a
198/// good idea to follow the implementation of the corresponding connection
199/// in diesel at a high level to gain some idea how to implement your
200/// custom implementation.
201///
202/// ## Implement support for an unsupported database system
203///
204/// Additionally to anything mentioned in the previous section the following steps are required:
205///
206/// * Implement a custom backend type. See the documentation of [`Backend`] for details
207/// * Implement appropriate [`FromSql`](crate::deserialize::FromSql)/
208/// [`ToSql`](crate::serialize::ToSql) conversions.
209/// At least the following impls should be considered:
210/// * `i16`: `FromSql<SmallInt, YourBackend>`
211/// * `i32`: `FromSql<Integer, YourBackend>`
212/// * `i64`: `FromSql<BigInt, YourBackend>`
213/// * `f32`: `FromSql<Float, YourBackend>`
214/// * `f64`: `FromSql<Double, YourBackend>`
215/// * `bool`: `FromSql<Bool, YourBackend>`
216/// * `String`: `FromSql<Text, YourBackend>`
217/// * `Vec<u8>`: `FromSql<Binary, YourBackend>`
218/// * `i16`: `ToSql<SmallInt, YourBackend>`
219/// * `i32`: `ToSql<Integer, YourBackend>`
220/// * `i64`: `ToSql<BigInt, YourBackend>`
221/// * `f32`: `ToSql<Float, YourBackend>`
222/// * `f64`: `ToSql<Double, YourBackend>`
223/// * `bool`: `ToSql<Bool, YourBackend>`
224/// * `String`: `ToSql<Text, YourBackend>`
225/// * `Vec<u8>`: `ToSql<Binary, YourBackend>`
226/// * Maybe a [`TransactionManager`] implementation matching
227/// the interface provided by the database connection crate.
228/// Otherwise the implementation used by the corresponding
229/// `Connection` in diesel can be reused.
230///
231/// As these implementations will vary depending on the backend being used,
232/// we cannot give concrete examples here. We recommend looking at our existing
233/// implementations to see how you can implement your own connection.
234pub trait Connection: SimpleConnection + Sized + Send
235where
236 // This trait bound is there so that implementing a new connection is
237 // gated behind the `i-implement-a-third-party-backend-and-opt-into-breaking-changes`
238 // feature flag
239 Self: ConnectionSealed,
240{
241 /// The backend this type connects to
242 type Backend: Backend;
243
244 /// The transaction manager implementation used by this connection
245 #[diesel_derives::__diesel_public_if(
246 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
247 )]
248 type TransactionManager: TransactionManager<Self>;
249
250 /// Establishes a new connection to the database
251 ///
252 /// The argument to this method and the method's behavior varies by backend.
253 /// See the documentation for that backend's connection class
254 /// for details about what it accepts and how it behaves.
255 fn establish(database_url: &str) -> ConnectionResult<Self>;
256
257 /// Executes the given function inside of a database transaction
258 ///
259 /// This function executes the provided closure `f` inside a database
260 /// transaction. If there is already an open transaction for the current
261 /// connection savepoints will be used instead. The connection is committed if
262 /// the closure returns `Ok(_)`, it will be rolled back if it returns `Err(_)`.
263 /// For both cases the original result value will be returned from this function.
264 ///
265 /// If the transaction fails to commit due to a `SerializationFailure` or a
266 /// `ReadOnlyTransaction` a rollback will be attempted.
267 /// If the rollback fails, the error will be returned in a
268 /// [`Error::RollbackErrorOnCommit`],
269 /// from which you will be able to extract both the original commit error and
270 /// the rollback error.
271 /// In addition, the connection will be considered broken
272 /// as it contains a uncommitted unabortable open transaction. Any further
273 /// interaction with the transaction system will result in an returned error
274 /// in this case.
275 ///
276 /// If the closure returns an `Err(_)` and the rollback fails the function
277 /// will return that rollback error directly, and the transaction manager will
278 /// be marked as broken as it contains a uncommitted unabortable open transaction.
279 ///
280 /// If a nested transaction fails to release the corresponding savepoint
281 /// the error will be returned directly.
282 ///
283 /// # Example
284 ///
285 /// ```rust
286 /// # include!("../doctest_setup.rs");
287 /// use diesel::result::Error;
288 ///
289 /// # fn main() {
290 /// # run_test().unwrap();
291 /// # }
292 /// #
293 /// # fn run_test() -> QueryResult<()> {
294 /// # use schema::users::dsl::*;
295 /// # let conn = &mut establish_connection();
296 /// conn.transaction::<_, Error, _>(|conn| {
297 /// diesel::insert_into(users)
298 /// .values(name.eq("Ruby"))
299 /// .execute(conn)?;
300 ///
301 /// let all_names = users.select(name).load::<String>(conn)?;
302 /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
303 ///
304 /// Ok(())
305 /// })?;
306 ///
307 /// conn.transaction::<(), _, _>(|conn| {
308 /// diesel::insert_into(users)
309 /// .values(name.eq("Pascal"))
310 /// .execute(conn)?;
311 ///
312 /// let all_names = users.select(name).load::<String>(conn)?;
313 /// assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names);
314 ///
315 /// // If we want to roll back the transaction, but don't have an
316 /// // actual error to return, we can return `RollbackTransaction`.
317 /// Err(Error::RollbackTransaction)
318 /// });
319 ///
320 /// let all_names = users.select(name).load::<String>(conn)?;
321 /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
322 /// # Ok(())
323 /// # }
324 /// ```
325 fn transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
326 where
327 F: FnOnce(&mut Self) -> Result<T, E>,
328 E: From<Error>,
329 {
330 Self::TransactionManager::transaction(self, f)
331 }
332
333 /// Creates a transaction that will never be committed. This is useful for
334 /// tests. Panics if called while inside of a transaction or
335 /// if called with a connection containing a broken transaction
336 fn begin_test_transaction(&mut self) -> QueryResult<()> {
337 match Self::TransactionManager::transaction_manager_status_mut(self) {
338 TransactionManagerStatus::Valid(valid_status) => {
339 assert_eq!(None, valid_status.transaction_depth())
340 }
341 TransactionManagerStatus::InError => panic!("Transaction manager in error"),
342 };
343 Self::TransactionManager::begin_transaction(self)?;
344 // set the test transaction flag
345 // to prevent that this connection gets dropped in connection pools
346 // Tests commonly set the poolsize to 1 and use `begin_test_transaction`
347 // to prevent modifications to the schema
348 Self::TransactionManager::transaction_manager_status_mut(self).set_test_transaction_flag();
349 Ok(())
350 }
351
352 /// Executes the given function inside a transaction, but does not commit
353 /// it. Panics if the given function returns an error.
354 ///
355 /// # Example
356 ///
357 /// ```rust
358 /// # include!("../doctest_setup.rs");
359 /// use diesel::result::Error;
360 ///
361 /// # fn main() {
362 /// # run_test().unwrap();
363 /// # }
364 /// #
365 /// # fn run_test() -> QueryResult<()> {
366 /// # use schema::users::dsl::*;
367 /// # let conn = &mut establish_connection();
368 /// conn.test_transaction::<_, Error, _>(|conn| {
369 /// diesel::insert_into(users)
370 /// .values(name.eq("Ruby"))
371 /// .execute(conn)?;
372 ///
373 /// let all_names = users.select(name).load::<String>(conn)?;
374 /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
375 ///
376 /// Ok(())
377 /// });
378 ///
379 /// // Even though we returned `Ok`, the transaction wasn't committed.
380 /// let all_names = users.select(name).load::<String>(conn)?;
381 /// assert_eq!(vec!["Sean", "Tess"], all_names);
382 /// # Ok(())
383 /// # }
384 /// ```
385 fn test_transaction<T, E, F>(&mut self, f: F) -> T
386 where
387 F: FnOnce(&mut Self) -> Result<T, E>,
388 E: Debug,
389 {
390 let mut user_result = None;
391 let _ = self.transaction::<(), _, _>(|conn| {
392 user_result = Some(f(conn));
393 Err(Error::RollbackTransaction)
394 });
395 user_result
396 .expect("Transaction never executed")
397 .unwrap_or_else(|e| panic!("Transaction did not succeed: {:?}", e))
398 }
399
400 /// Execute a single SQL statements given by a query and return
401 /// number of affected rows
402 #[diesel_derives::__diesel_public_if(
403 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
404 )]
405 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
406 where
407 T: QueryFragment<Self::Backend> + QueryId;
408
409 /// Get access to the current transaction state of this connection
410 ///
411 /// This function should be used from [`TransactionManager`] to access
412 /// internally required state.
413 #[diesel_derives::__diesel_public_if(
414 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
415 )]
416 fn transaction_state(
417 &mut self,
418 ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData;
419
420 /// Get the instrumentation instance stored in this connection
421 #[diesel_derives::__diesel_public_if(
422 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
423 )]
424 fn instrumentation(&mut self) -> &mut dyn Instrumentation;
425
426 /// Set a specific [`Instrumentation`] implementation for this connection
427 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation);
428
429 /// Set the prepared statement cache size to [`CacheSize`] for this connection
430 fn set_prepared_statement_cache_size(&mut self, size: CacheSize);
431}
432
433/// The specific part of a [`Connection`] which actually loads data from the database
434///
435/// This is a separate trait to allow connection implementations to specify
436/// different loading modes via the generic parameter.
437pub trait LoadConnection<B = DefaultLoadingMode>: Connection {
438 /// The cursor type returned by [`LoadConnection::load`]
439 ///
440 /// Users should handle this as opaque type that implements [`Iterator`]
441 type Cursor<'conn, 'query>: Iterator<
442 Item = QueryResult<<Self as LoadConnection<B>>::Row<'conn, 'query>>,
443 >
444 where
445 Self: 'conn;
446
447 /// The row type used as [`Iterator::Item`] for the iterator implementation
448 /// of [`LoadConnection::Cursor`]
449 type Row<'conn, 'query>: crate::row::Row<'conn, Self::Backend>
450 where
451 Self: 'conn;
452
453 /// Executes a given query and returns any requested values
454 ///
455 /// This function executes a given query and returns the
456 /// query result as given by the database. **Normal users
457 /// should not use this function**. Use
458 /// [`QueryDsl::load`](crate::QueryDsl) instead.
459 ///
460 /// This function is useful for people trying to build an alternative
461 /// dsl on top of diesel. It returns an [`impl Iterator<Item = QueryResult<&impl Row<Self::Backend>>`](Iterator).
462 /// This type can be used to iterate over all rows returned by the database.
463 #[diesel_derives::__diesel_public_if(
464 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
465 )]
466 fn load<'conn, 'query, T>(
467 &'conn mut self,
468 source: T,
469 ) -> QueryResult<Self::Cursor<'conn, 'query>>
470 where
471 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
472 Self::Backend: QueryMetadata<T::SqlType>;
473}
474
475/// Describes a connection with an underlying [`crate::sql_types::TypeMetadata::MetadataLookup`]
476#[diesel_derives::__diesel_public_if(
477 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
478)]
479pub trait WithMetadataLookup: Connection {
480 /// Retrieves the underlying metadata lookup
481 fn metadata_lookup(&mut self) -> &mut <Self::Backend as TypeMetadata>::MetadataLookup;
482}
483
484/// A variant of the [`Connection`](trait.Connection.html) trait that is
485/// usable with dynamic dispatch
486///
487/// If you are looking for a way to use pass database connections
488/// for different database backends around in your application
489/// this trait won't help you much. Normally you should only
490/// need to use this trait if you are interacting with a connection
491/// passed to a [`Migration`](../migration/trait.Migration.html)
492pub trait BoxableConnection<DB: Backend>: SimpleConnection + core::any::Any {
493 /// Maps the current connection to `std::any::Any`
494 #[diesel_derives::__diesel_public_if(
495 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
496 )]
497 fn as_any(&self) -> &dyn core::any::Any;
498
499 #[doc(hidden)]
500 fn as_any_mut(&mut self) -> &mut dyn core::any::Any;
501}
502
503impl<C> BoxableConnection<C::Backend> for C
504where
505 C: Connection + core::any::Any,
506{
507 fn as_any(&self) -> &dyn core::any::Any {
508 self
509 }
510
511 fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
512 self
513 }
514}
515
516/// The default loading mode provided by a [`Connection`].
517///
518/// Checkout the documentation of concrete connection types for details about
519/// supported loading modes.
520///
521/// All types implementing [`Connection`] should provide at least
522/// a single [`LoadConnection<DefaultLoadingMode>`](self::LoadConnection)
523/// implementation.
524#[derive(Debug, Copy, Clone)]
525pub struct DefaultLoadingMode;
526
527impl<DB: Backend + 'static> dyn BoxableConnection<DB> {
528 /// Downcast the current connection to a specific connection
529 /// type.
530 ///
531 /// This will return `None` if the underlying
532 /// connection does not match the corresponding
533 /// type, otherwise a reference to the underlying connection is returned
534 pub fn downcast_ref<T>(&self) -> Option<&T>
535 where
536 T: Connection<Backend = DB> + 'static,
537 {
538 self.as_any().downcast_ref::<T>()
539 }
540
541 /// Downcast the current connection to a specific mutable connection
542 /// type.
543 ///
544 /// This will return `None` if the underlying
545 /// connection does not match the corresponding
546 /// type, otherwise a mutable reference to the underlying connection is returned
547 pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
548 where
549 T: Connection<Backend = DB> + 'static,
550 {
551 self.as_any_mut().downcast_mut::<T>()
552 }
553
554 /// Check if the current connection is
555 /// a specific connection type
556 pub fn is<T>(&self) -> bool
557 where
558 T: Connection<Backend = DB> + 'static,
559 {
560 self.as_any().is::<T>()
561 }
562}
563
564// These traits are considered private for different reasons:
565//
566// `ConnectionSealed` to control who can implement `Connection`,
567// so that we can later change the `Connection` trait
568//
569// `MultiConnectionHelper` is a workaround needed for the
570// `MultiConnection` derive. We might stabilize this trait with
571// the corresponding derive
572//
573// `ConnectionHelperType` as a workaround for the `LoadRowIter`
574// type def. That trait should not be used by any user outside of diesel,
575// it purely exists for backward compatibility reasons.
576pub(crate) mod private {
577
578 /// This trait restricts who can implement `Connection`
579 #[cfg_attr(
580 diesel_docsrs,
581 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
582 )]
583 pub trait ConnectionSealed {}
584
585 /// This trait provides helper methods to convert a database lookup type
586 /// to/from an `std::any::Any` reference. This is used internally by the `#[derive(MultiConnection)]`
587 /// implementation
588 #[cfg_attr(
589 diesel_docsrs,
590 doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
591 )]
592 pub trait MultiConnectionHelper: super::Connection {
593 /// Convert the lookup type to any
594 fn to_any<'a>(
595 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
596 ) -> &mut (dyn core::any::Any + 'a);
597
598 /// Get the lookup type from any
599 fn from_any(
600 lookup: &mut dyn core::any::Any,
601 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup>;
602 }
603
604 // These impls are only there for backward compatibility reasons
605 // Remove them on the next breaking release
606 #[allow(unreachable_pub)] // must be pub for the type def using this trait
607 #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
608 pub trait ConnectionHelperType<DB, B>: super::LoadConnection<B, Backend = DB> {
609 type Cursor<'conn, 'query>
610 where
611 Self: 'conn;
612 }
613 #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
614 impl<T, B> ConnectionHelperType<T::Backend, B> for T
615 where
616 T: super::LoadConnection<B>,
617 {
618 type Cursor<'conn, 'query>
619 = <T as super::LoadConnection<B>>::Cursor<'conn, 'query>
620 where
621 T: 'conn;
622 }
623}