1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
//! Types related to database connections
mod statement_cache;
mod transaction_manager;
use std::fmt::Debug;
use backend::Backend;
use deserialize::{Queryable, QueryableByName};
use query_builder::{AsQuery, QueryFragment, QueryId};
use result::*;
use sql_types::HasSqlType;
#[doc(hidden)]
pub use self::statement_cache::{MaybeCached, StatementCache, StatementCacheKey};
pub use self::transaction_manager::{AnsiTransactionManager, TransactionManager};
/// Perform simple operations on a backend.
///
/// You should likely use [`Connection`](trait.Connection.html) instead.
pub trait SimpleConnection {
/// Execute multiple SQL statements within the same string.
///
/// This function is used to execute migrations,
/// which may contain more than one SQL statement.
fn batch_execute(&self, query: &str) -> QueryResult<()>;
}
/// A connection to a database
pub trait Connection: SimpleConnection + Sized + Send {
/// The backend this type connects to
type Backend: Backend;
#[doc(hidden)]
type TransactionManager: TransactionManager<Self>;
/// Establishes a new connection to the database
///
/// The argument to this method varies by backend.
/// See the documentation for that backend's connection class
/// for details about what it accepts.
fn establish(database_url: &str) -> ConnectionResult<Self>;
/// Executes the given function inside of a database transaction
///
/// If there is already an open transaction,
/// savepoints will be used instead.
///
/// If the transaction fails to commit due to a `SerializationFailure`, a rollback will be attempted.
/// If the rollback succeeds, the original error will be returned, otherwise the error generated by
/// the rollback will be returned. In the second case the connection should be considered broken
/// as it contains a uncommitted unabortable open transaction.
/// # Example
///
/// ```rust
/// # #[macro_use] extern crate diesel;
/// # include!("../doctest_setup.rs");
/// use diesel::result::Error;
///
/// # fn main() {
/// # run_test().unwrap();
/// # }
/// #
/// # fn run_test() -> QueryResult<()> {
/// # use schema::users::dsl::*;
/// # let conn = establish_connection();
/// conn.transaction::<_, Error, _>(|| {
/// diesel::insert_into(users)
/// .values(name.eq("Ruby"))
/// .execute(&conn)?;
///
/// let all_names = users.select(name).load::<String>(&conn)?;
/// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
///
/// Ok(())
/// })?;
///
/// conn.transaction::<(), _, _>(|| {
/// diesel::insert_into(users)
/// .values(name.eq("Pascal"))
/// .execute(&conn)?;
///
/// let all_names = users.select(name).load::<String>(&conn)?;
/// assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names);
///
/// // If we want to roll back the transaction, but don't have an
/// // actual error to return, we can return `RollbackTransaction`.
/// Err(Error::RollbackTransaction)
/// });
///
/// let all_names = users.select(name).load::<String>(&conn)?;
/// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
/// # Ok(())
/// # }
/// ```
fn transaction<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
E: From<Error>,
{
let transaction_manager = self.transaction_manager();
transaction_manager.begin_transaction(self)?;
match f() {
Ok(value) => {
transaction_manager.commit_transaction(self)?;
Ok(value)
}
Err(e) => {
transaction_manager.rollback_transaction(self)?;
Err(e)
}
}
}
/// Creates a transaction that will never be committed. This is useful for
/// tests. Panics if called while inside of a transaction.
fn begin_test_transaction(&self) -> QueryResult<()> {
let transaction_manager = self.transaction_manager();
assert_eq!(transaction_manager.get_transaction_depth(), 0);
transaction_manager.begin_transaction(self)
}
/// Executes the given function inside a transaction, but does not commit
/// it. Panics if the given function returns an error.
///
/// # Example
///
/// ```rust
/// # #[macro_use] extern crate diesel;
/// # include!("../doctest_setup.rs");
/// use diesel::result::Error;
///
/// # fn main() {
/// # run_test().unwrap();
/// # }
/// #
/// # fn run_test() -> QueryResult<()> {
/// # use schema::users::dsl::*;
/// # let conn = establish_connection();
/// conn.test_transaction::<_, Error, _>(|| {
/// diesel::insert_into(users)
/// .values(name.eq("Ruby"))
/// .execute(&conn)?;
///
/// let all_names = users.select(name).load::<String>(&conn)?;
/// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
///
/// Ok(())
/// });
///
/// // Even though we returned `Ok`, the transaction wasn't committed.
/// let all_names = users.select(name).load::<String>(&conn)?;
/// assert_eq!(vec!["Sean", "Tess"], all_names);
/// # Ok(())
/// # }
/// ```
fn test_transaction<T, E, F>(&self, f: F) -> T
where
F: FnOnce() -> Result<T, E>,
E: Debug,
{
let mut user_result = None;
let _ = self.transaction::<(), _, _>(|| {
user_result = f().ok();
Err(Error::RollbackTransaction)
});
user_result.expect("Transaction did not succeed")
}
#[doc(hidden)]
fn execute(&self, query: &str) -> QueryResult<usize>;
#[doc(hidden)]
fn query_by_index<T, U>(&self, source: T) -> QueryResult<Vec<U>>
where
T: AsQuery,
T::Query: QueryFragment<Self::Backend> + QueryId,
Self::Backend: HasSqlType<T::SqlType>,
U: Queryable<T::SqlType, Self::Backend>;
#[doc(hidden)]
fn query_by_name<T, U>(&self, source: &T) -> QueryResult<Vec<U>>
where
T: QueryFragment<Self::Backend> + QueryId,
U: QueryableByName<Self::Backend>;
#[doc(hidden)]
fn execute_returning_count<T>(&self, source: &T) -> QueryResult<usize>
where
T: QueryFragment<Self::Backend> + QueryId;
#[doc(hidden)]
fn transaction_manager(&self) -> &Self::TransactionManager;
}