Skip to content

Commit 8b43f0e

Browse files
committed
Add methods to access the session transaction isolation level
Closes sfackler#115
1 parent 4a0067a commit 8b43f0e

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/lib.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use debug_builders::DebugStruct;
5959
use openssl::crypto::hash::{self, Hasher};
6060
use openssl::ssl::{SslContext, MaybeSslStream};
6161
use serialize::hex::ToHex;
62+
use std::ascii::AsciiExt;
6263
use std::borrow::{ToOwned, Cow};
6364
use std::cell::{Cell, RefCell};
6465
use std::collections::{VecDeque, HashMap};
@@ -399,6 +400,60 @@ pub fn cancel_query<T>(params: T, ssl: &SslMode, data: CancelData)
399400
Ok(())
400401
}
401402

403+
/// An enumeration of transaction isolation levels.
404+
///
405+
/// See the (Postgres documentation)[http://www.postgresql.org/docs/9.4/static/transaction-iso.html]
406+
/// for full details on the semantics of each level.
407+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
408+
pub enum IsolationLevel {
409+
/// The "read uncommitted" level.
410+
///
411+
/// In current versions of Postgres, this behaves identically to
412+
/// `ReadCommitted`.
413+
ReadUncommitted,
414+
/// The "read committed" level.
415+
///
416+
/// This is the default isolation level in Postgres.
417+
ReadCommitted,
418+
/// The "repeatable read" level.
419+
RepeatableRead,
420+
/// The "serializable" level.
421+
Serializable,
422+
}
423+
424+
impl IsolationLevel {
425+
fn to_set_query(&self) -> &'static str {
426+
match *self {
427+
IsolationLevel::ReadUncommitted => {
428+
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
429+
}
430+
IsolationLevel::ReadCommitted => {
431+
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"
432+
}
433+
IsolationLevel::RepeatableRead => {
434+
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ"
435+
}
436+
IsolationLevel::Serializable => {
437+
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE"
438+
}
439+
}
440+
}
441+
442+
fn parse(raw: &str) -> Result<IsolationLevel> {
443+
if raw.eq_ignore_ascii_case("READ UNCOMMITTED") {
444+
Ok(IsolationLevel::ReadUncommitted)
445+
} else if raw.eq_ignore_ascii_case("READ COMMITTED") {
446+
Ok(IsolationLevel::ReadCommitted)
447+
} else if raw.eq_ignore_ascii_case("REPEATABLE READ") {
448+
Ok(IsolationLevel::RepeatableRead)
449+
} else if raw.eq_ignore_ascii_case("SERIALIZABLE") {
450+
Ok(IsolationLevel::Serializable)
451+
} else {
452+
Err(Error::BadResponse)
453+
}
454+
}
455+
}
456+
402457
#[derive(Clone)]
403458
struct CachedStatement {
404459
name: String,
@@ -1062,6 +1117,23 @@ impl Connection {
10621117
})
10631118
}
10641119

1120+
/// Sets the isolation level which will be used for future transactions.
1121+
///
1122+
/// ## Note
1123+
///
1124+
/// This will not change the behavior of an active transaction.
1125+
pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> {
1126+
self.batch_execute(level.to_set_query())
1127+
}
1128+
1129+
/// Returns the isolation level which will be used for future transactions.
1130+
pub fn get_transaction_isolation(&self) -> Result<IsolationLevel> {
1131+
let mut conn = self.conn.borrow_mut();
1132+
check_desync!(conn);
1133+
let result = try!(conn.quick_query("SHOW TRANSACTION ISOLATION LEVEL"));
1134+
IsolationLevel::parse(result[0][0].as_ref().unwrap())
1135+
}
1136+
10651137
/// A convenience function for queries that are only run once.
10661138
///
10671139
/// If an error is returned, it could have come from either the preparation

tests/test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use postgres::{HandleNotice,
1818
ConnectError,
1919
DbError,
2020
IntoConnectParams,
21+
IsolationLevel,
2122
VecStreamIterator};
2223
use postgres::SqlState::{SyntaxError,
2324
QueryCanceled,
@@ -944,3 +945,17 @@ fn url_encoded_password() {
944945
assert_eq!("username{|", &params.user.as_ref().unwrap().user[..]);
945946
assert_eq!("password{|", &params.user.as_ref().unwrap().password.as_ref().unwrap()[..]);
946947
}
948+
949+
#[test]
950+
fn test_transaction_isolation_level() {
951+
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", &SslMode::None));
952+
assert_eq!(IsolationLevel::ReadCommitted, or_panic!(conn.get_transaction_isolation()));
953+
or_panic!(conn.set_transaction_isolation(IsolationLevel::ReadUncommitted));
954+
assert_eq!(IsolationLevel::ReadUncommitted, or_panic!(conn.get_transaction_isolation()));
955+
or_panic!(conn.set_transaction_isolation(IsolationLevel::RepeatableRead));
956+
assert_eq!(IsolationLevel::RepeatableRead, or_panic!(conn.get_transaction_isolation()));
957+
or_panic!(conn.set_transaction_isolation(IsolationLevel::Serializable));
958+
assert_eq!(IsolationLevel::Serializable, or_panic!(conn.get_transaction_isolation()));
959+
or_panic!(conn.set_transaction_isolation(IsolationLevel::ReadCommitted));
960+
assert_eq!(IsolationLevel::ReadCommitted, or_panic!(conn.get_transaction_isolation()));
961+
}

0 commit comments

Comments
 (0)