From 5b80b251a6fa9b9b50d4229883b9ed2d80584e93 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 24 Feb 2016 22:29:09 -0800 Subject: [PATCH] Finish up transaction config --- src/lib.rs | 18 +++++++++++------- src/transaction.rs | 20 ++++++++++++++++++++ tests/test.rs | 40 ++++++++++++++++++++++++++++++++++------ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3385a0ce..7ce1b89d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1162,11 +1162,18 @@ impl Connection { /// trans.commit().unwrap(); /// ``` pub fn transaction<'a>(&'a self) -> Result> { + self.transaction_with(&transaction::Config::new()) + } + + /// Begins a new transaction with the specified configuration. + pub fn transaction_with<'a>(&'a self, config: &transaction::Config) -> Result> { let mut conn = self.conn.borrow_mut(); check_desync!(conn); assert!(conn.trans_depth == 0, "`transaction` must be called on the active transaction"); - try!(conn.quick_query("BEGIN")); + let mut query = "BEGIN".to_owned(); + config.build_command(&mut query); + try!(conn.quick_query(&query)); conn.trans_depth += 1; Ok(Transaction::new(self, 1)) } @@ -1230,17 +1237,14 @@ impl Connection { IsolationLevel::parse(result[0][0].as_ref().unwrap()) } - /// Sets the isolation level which will be used for future transactions. + /// # Deprecated /// - /// This is a simple wrapper around `SET TRANSACTION ISOLATION LEVEL ...`. - /// - /// # Note - /// - /// This will not change the behavior of an active transaction. + /// Use `Connection::set_transaction_config` instead. pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> { self.set_transaction_config(transaction::Config::new().isolation_level(level)) } + /// Sets the configuration that will be used for future transactions. pub fn set_transaction_config(&self, config: &transaction::Config) -> Result<()> { let mut command = "SET SESSION CHARACTERISTICS AS TRANSACTION".to_owned(); config.build_command(&mut command); diff --git a/src/transaction.rs b/src/transaction.rs index e6d892c6..3023c969 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -8,6 +8,7 @@ use stmt::Statement; use rows::Rows; use types::ToSql; +/// Configuration of a transaction. #[derive(Debug)] pub struct Config { isolation_level: Option, @@ -51,6 +52,7 @@ impl ConfigInternals for Config { } impl Config { + /// Creates a new `Config` with no configuration overrides. pub fn new() -> Config { Config { isolation_level: None, @@ -59,16 +61,27 @@ impl Config { } } + /// Sets the isolation level of the configuration. pub fn isolation_level(&mut self, isolation_level: IsolationLevel) -> &mut Config { self.isolation_level = Some(isolation_level); self } + /// Sets the read-only property of a transaction. + /// + /// If enabled, a transaction will be unable to modify any persistent + /// database state. pub fn read_only(&mut self, read_only: bool) -> &mut Config { self.read_only = Some(read_only); self } + /// Sets the deferrable property of a transaction. + /// + /// If enabled in a read only, serializable transaction, the transaction may + /// block when created, after which it will run without the normal overhead + /// of a serializable transaction and will not be forced to roll back due + /// to serialization failures. pub fn deferrable(&mut self, deferrable: bool) -> &mut Config { self.deferrable = Some(deferrable); self @@ -193,6 +206,13 @@ impl<'conn> Transaction<'conn> { self.conn.conn.borrow().trans_depth == self.depth } + /// Alters the configuration of the active transaction. + pub fn set_config(&self, config: &Config) -> Result<()> { + let mut command = "SET TRANSACTION".to_owned(); + config.build_command(&mut command); + self.batch_execute(&command) + } + /// Determines if the transaction is currently set to commit or roll back. pub fn will_commit(&self) -> bool { self.commit.get() diff --git a/tests/test.rs b/tests/test.rs index c70fa0b1..f1da0329 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -13,12 +13,8 @@ use std::io; use std::io::prelude::*; use std::time::Duration; -use postgres::{HandleNotice, - Connection, - GenericConnection, - SslMode, - IntoConnectParams, - IsolationLevel}; +use postgres::{HandleNotice, Connection, GenericConnection, SslMode, IntoConnectParams, + IsolationLevel, transaction}; use postgres::error::{Error, ConnectError, DbError}; use postgres::types::{Oid, Type, Kind, WrongType}; use postgres::error::SqlState::{SyntaxError, @@ -1020,3 +1016,35 @@ fn test_conn_query() { .collect::>(); assert_eq!(ids, [1, 2, 3]); } + +#[test] +fn transaction_config() { + let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); + let mut config = transaction::Config::new(); + config.isolation_level(IsolationLevel::Serializable) + .read_only(true) + .deferrable(true); + conn.set_transaction_config(&config).unwrap(); +} + +#[test] +fn transaction_with() { + let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); + let mut config = transaction::Config::new(); + config.isolation_level(IsolationLevel::Serializable) + .read_only(true) + .deferrable(true); + conn.transaction_with(&config).unwrap().finish().unwrap(); +} + +#[test] +fn transaction_set_config() { + let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); + let trans = conn.transaction().unwrap(); + let mut config = transaction::Config::new(); + config.isolation_level(IsolationLevel::Serializable) + .read_only(true) + .deferrable(true); + trans.set_config(&config).unwrap(); + trans.finish().unwrap(); +}