Add methods to access the session transaction isolation level

Closes #115
This commit is contained in:
Steven Fackler 2015-04-22 22:03:09 -07:00
parent 4a0067af6f
commit 8b43f0e3cf
2 changed files with 87 additions and 0 deletions

View File

@ -59,6 +59,7 @@ use debug_builders::DebugStruct;
use openssl::crypto::hash::{self, Hasher};
use openssl::ssl::{SslContext, MaybeSslStream};
use serialize::hex::ToHex;
use std::ascii::AsciiExt;
use std::borrow::{ToOwned, Cow};
use std::cell::{Cell, RefCell};
use std::collections::{VecDeque, HashMap};
@ -399,6 +400,60 @@ pub fn cancel_query<T>(params: T, ssl: &SslMode, data: CancelData)
Ok(())
}
/// An enumeration of transaction isolation levels.
///
/// See the (Postgres documentation)[http://www.postgresql.org/docs/9.4/static/transaction-iso.html]
/// for full details on the semantics of each level.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsolationLevel {
/// The "read uncommitted" level.
///
/// In current versions of Postgres, this behaves identically to
/// `ReadCommitted`.
ReadUncommitted,
/// The "read committed" level.
///
/// This is the default isolation level in Postgres.
ReadCommitted,
/// The "repeatable read" level.
RepeatableRead,
/// The "serializable" level.
Serializable,
}
impl IsolationLevel {
fn to_set_query(&self) -> &'static str {
match *self {
IsolationLevel::ReadUncommitted => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
}
IsolationLevel::ReadCommitted => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"
}
IsolationLevel::RepeatableRead => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ"
}
IsolationLevel::Serializable => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE"
}
}
}
fn parse(raw: &str) -> Result<IsolationLevel> {
if raw.eq_ignore_ascii_case("READ UNCOMMITTED") {
Ok(IsolationLevel::ReadUncommitted)
} else if raw.eq_ignore_ascii_case("READ COMMITTED") {
Ok(IsolationLevel::ReadCommitted)
} else if raw.eq_ignore_ascii_case("REPEATABLE READ") {
Ok(IsolationLevel::RepeatableRead)
} else if raw.eq_ignore_ascii_case("SERIALIZABLE") {
Ok(IsolationLevel::Serializable)
} else {
Err(Error::BadResponse)
}
}
}
#[derive(Clone)]
struct CachedStatement {
name: String,
@ -1062,6 +1117,23 @@ impl Connection {
})
}
/// Sets the isolation level which will be used for future transactions.
///
/// ## Note
///
/// This will not change the behavior of an active transaction.
pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> {
self.batch_execute(level.to_set_query())
}
/// Returns the isolation level which will be used for future transactions.
pub fn get_transaction_isolation(&self) -> Result<IsolationLevel> {
let mut conn = self.conn.borrow_mut();
check_desync!(conn);
let result = try!(conn.quick_query("SHOW TRANSACTION ISOLATION LEVEL"));
IsolationLevel::parse(result[0][0].as_ref().unwrap())
}
/// A convenience function for queries that are only run once.
///
/// If an error is returned, it could have come from either the preparation

View File

@ -18,6 +18,7 @@ use postgres::{HandleNotice,
ConnectError,
DbError,
IntoConnectParams,
IsolationLevel,
VecStreamIterator};
use postgres::SqlState::{SyntaxError,
QueryCanceled,
@ -944,3 +945,17 @@ fn url_encoded_password() {
assert_eq!("username{|", &params.user.as_ref().unwrap().user[..]);
assert_eq!("password{|", &params.user.as_ref().unwrap().password.as_ref().unwrap()[..]);
}
#[test]
fn test_transaction_isolation_level() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", &SslMode::None));
assert_eq!(IsolationLevel::ReadCommitted, or_panic!(conn.get_transaction_isolation()));
or_panic!(conn.set_transaction_isolation(IsolationLevel::ReadUncommitted));
assert_eq!(IsolationLevel::ReadUncommitted, or_panic!(conn.get_transaction_isolation()));
or_panic!(conn.set_transaction_isolation(IsolationLevel::RepeatableRead));
assert_eq!(IsolationLevel::RepeatableRead, or_panic!(conn.get_transaction_isolation()));
or_panic!(conn.set_transaction_isolation(IsolationLevel::Serializable));
assert_eq!(IsolationLevel::Serializable, or_panic!(conn.get_transaction_isolation()));
or_panic!(conn.set_transaction_isolation(IsolationLevel::ReadCommitted));
assert_eq!(IsolationLevel::ReadCommitted, or_panic!(conn.get_transaction_isolation()));
}