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