From cf3ead046e8a104bfd1c434ebed08d0238123639 Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Wed, 25 May 2016 16:30:53 -0400 Subject: [PATCH 1/2] Add savepoint method to Transaction This change creates a `Transaction.savepoint` method, which is equivalent to `Transaction.transaction`, but takes a custom name for the nested transaction's savepoint name. --- src/transaction.rs | 34 +++++++++++++++++++++++----------- tests/test.rs | 23 ++++++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index d606b06f..b0462d60 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -151,6 +151,7 @@ impl Config { pub struct Transaction<'conn> { conn: &'conn Connection, depth: u32, + savepoint_name: Option<&'conn str>, commit: Cell, finished: bool, } @@ -177,6 +178,7 @@ impl<'conn> TransactionInternals<'conn> for Transaction<'conn> { Transaction { conn: conn, depth: depth, + savepoint_name: None, commit: Cell::new(false), finished: false, } @@ -195,14 +197,13 @@ impl<'conn> Transaction<'conn> { fn finish_inner(&mut self) -> Result<()> { let mut conn = self.conn.conn.borrow_mut(); debug_assert!(self.depth == conn.trans_depth); - let query = match (self.commit.get(), self.depth != 1) { - (false, true) => "ROLLBACK TO sp", - (false, false) => "ROLLBACK", - (true, true) => "RELEASE sp", - (true, false) => "COMMIT", - }; conn.trans_depth -= 1; - conn.quick_query(query).map(|_| ()) + match (self.commit.get(), self.savepoint_name) { + (false, Some(savepoint_name)) => conn.quick_query(&format!("ROLLBACK TO {}", savepoint_name)), + (false, None) => conn.quick_query("ROLLBACK"), + (true, Some(savepoint_name)) => conn.quick_query(&format!("RELEASE {}", savepoint_name)), + (true, None) => conn.quick_query("COMMIT"), + }.map(|_| ()) } /// Like `Connection::prepare`. @@ -233,22 +234,33 @@ impl<'conn> Transaction<'conn> { self.conn.batch_execute(query) } - /// Like `Connection::transaction`. + /// Like `Connection::transaction`, but creates a nested transaction. /// /// # Panics /// /// Panics if there is an active nested transaction. pub fn transaction<'a>(&'a self) -> Result> { + self.savepoint("sp") + } + + /// Like `Connection::transaction`, but creates a nested transaction + /// with the provided name. + /// + /// # Panics + /// + /// Panics if there is an active nested transaction. + pub fn savepoint<'a>(&'a self, name: &'a str) -> Result> { let mut conn = self.conn.conn.borrow_mut(); check_desync!(conn); assert!(conn.trans_depth == self.depth, - "`transaction` may only be called on the active transaction"); - try!(conn.quick_query("SAVEPOINT sp")); + "`savepoint` may only be called on the active transaction"); + try!(conn.quick_query(&format!("SAVEPOINT {}", name))); conn.trans_depth += 1; Ok(Transaction { conn: self.conn, - commit: Cell::new(false), depth: self.depth + 1, + savepoint_name: Some(name), + commit: Cell::new(false), finished: false, }) } diff --git a/tests/test.rs b/tests/test.rs index fd600ba3..4c040a94 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -202,9 +202,9 @@ fn test_nested_transactions() { } { - let trans3 = or_panic!(trans2.transaction()); - or_panic!(trans3.execute("INSERT INTO foo (id) VALUES (6)", &[])); - assert!(trans3.commit().is_ok()); + let sp = or_panic!(trans2.savepoint("custom")); + or_panic!(sp.execute("INSERT INTO foo (id) VALUES (6)", &[])); + assert!(sp.commit().is_ok()); } assert!(trans2.commit().is_ok()); @@ -250,10 +250,10 @@ fn test_nested_transactions_finish() { } { - let trans3 = or_panic!(trans2.transaction()); - or_panic!(trans3.execute("INSERT INTO foo (id) VALUES (6)", &[])); - trans3.set_commit(); - assert!(trans3.finish().is_ok()); + let sp = or_panic!(trans2.savepoint("custom")); + or_panic!(sp.execute("INSERT INTO foo (id) VALUES (6)", &[])); + sp.set_commit(); + assert!(sp.finish().is_ok()); } trans2.set_commit(); @@ -294,6 +294,15 @@ fn test_trans_with_nested_trans() { trans.transaction().unwrap(); } +#[test] +#[should_panic(expected = "active transaction")] +fn test_trans_with_savepoints() { + let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); + let trans = or_panic!(conn.transaction()); + let _sp = or_panic!(trans.savepoint("custom")); + trans.savepoint("custom2").unwrap(); +} + #[test] fn test_stmt_execute_after_transaction() { let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); From c60964af17f77f7f12f966b775f3a20fa1546bbc Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Fri, 27 May 2016 00:05:09 -0400 Subject: [PATCH 2/2] Replace Option<&'conn str> with Option --- src/transaction.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index b0462d60..b12b1662 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -151,7 +151,7 @@ impl Config { pub struct Transaction<'conn> { conn: &'conn Connection, depth: u32, - savepoint_name: Option<&'conn str>, + savepoint_name: Option, commit: Cell, finished: bool, } @@ -198,11 +198,11 @@ impl<'conn> Transaction<'conn> { let mut conn = self.conn.conn.borrow_mut(); debug_assert!(self.depth == conn.trans_depth); conn.trans_depth -= 1; - match (self.commit.get(), self.savepoint_name) { - (false, Some(savepoint_name)) => conn.quick_query(&format!("ROLLBACK TO {}", savepoint_name)), - (false, None) => conn.quick_query("ROLLBACK"), - (true, Some(savepoint_name)) => conn.quick_query(&format!("RELEASE {}", savepoint_name)), - (true, None) => conn.quick_query("COMMIT"), + match (self.commit.get(), &self.savepoint_name) { + (false, &Some(ref savepoint_name)) => conn.quick_query(&format!("ROLLBACK TO {}", savepoint_name)), + (false, &None) => conn.quick_query("ROLLBACK"), + (true, &Some(ref savepoint_name)) => conn.quick_query(&format!("RELEASE {}", savepoint_name)), + (true, &None) => conn.quick_query("COMMIT"), }.map(|_| ()) } @@ -249,7 +249,7 @@ impl<'conn> Transaction<'conn> { /// # Panics /// /// Panics if there is an active nested transaction. - pub fn savepoint<'a>(&'a self, name: &'a str) -> Result> { + pub fn savepoint<'a>(&'a self, name: &str) -> Result> { let mut conn = self.conn.conn.borrow_mut(); check_desync!(conn); assert!(conn.trans_depth == self.depth, @@ -259,7 +259,7 @@ impl<'conn> Transaction<'conn> { Ok(Transaction { conn: self.conn, depth: self.depth + 1, - savepoint_name: Some(name), + savepoint_name: Some(name.to_owned()), commit: Cell::new(false), finished: false, })