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