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