Skip to content

Commit cf5bd66

Browse files
committed
Add nested transaction support
1 parent c81c207 commit cf5bd66

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ do conn.in_transaction |trans| {
138138
}
139139
}
140140
```
141-
A transaction will commit by default. Transactions cannot currently be nested.
141+
A transaction will commit by default. Nested transactions are supported via
142+
savepoints.
142143

143144
Lazy Queries
144145
------------

src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ impl PostgresConnection {
328328
329329
let trans = PostgresTransaction {
330330
conn: self,
331+
next_savepoint_id: Cell::new(0),
331332
commit: Cell::new(true)
332333
};
333334
// If this fails, Postgres will rollback when the connection closes
@@ -378,6 +379,7 @@ impl PostgresConnection {
378379
379380
pub struct PostgresTransaction<'self> {
380381
priv conn: &'self PostgresConnection,
382+
priv next_savepoint_id: Cell<uint>,
381383
priv commit: Cell<bool>
382384
}
383385
@@ -403,6 +405,30 @@ impl<'self> PostgresTransaction<'self> {
403405
self.conn.try_update(query, params)
404406
}
405407
408+
pub fn in_transaction<T>(&self, blk: &fn(&PostgresTransaction) -> T) -> T {
409+
let id = self.next_savepoint_id.take();
410+
let savepoint = fmt!("savepoint_%u", id);
411+
self.next_savepoint_id.put_back(id + 1);
412+
413+
self.conn.quick_query(fmt!("SAVEPOINT %s", savepoint));
414+
415+
let nested_trans = PostgresTransaction {
416+
conn: self.conn,
417+
next_savepoint_id: Cell::new(id + 1),
418+
commit: Cell::new(true)
419+
};
420+
421+
let ret = blk(&nested_trans);
422+
423+
if nested_trans.commit.take() {
424+
self.conn.quick_query(fmt!("RELEASE %s", savepoint));
425+
} else {
426+
self.conn.quick_query(fmt!("ROLLBACK TO %s", savepoint));
427+
}
428+
429+
ret
430+
}
431+
406432
pub fn will_commit(&self) -> bool {
407433
let commit = self.commit.take();
408434
self.commit.put_back(commit);

src/test.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,48 @@ fn test_transaction_rollback() {
5656
assert_eq!(~[1i32], result.map(|row| { row[0] }).collect());
5757
}
5858

59+
#[test]
60+
fn test_nested_transactions() {
61+
let conn = PostgresConnection::connect("postgres://postgres@127.0.0.1:5432");
62+
conn.update("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", []);
63+
64+
conn.update("INSERT INTO foo (id) VALUES (1)", []);
65+
66+
do conn.in_transaction |trans1| {
67+
trans1.update("INSERT INTO foo (id) VALUES (2)", []);
68+
69+
do trans1.in_transaction |trans2| {
70+
trans2.update("INSERT INTO foo (id) VALUES (3)", []);
71+
trans2.set_rollback();
72+
}
73+
74+
do trans1.in_transaction |trans2| {
75+
trans2.update("INSERT INTO foo (id) VALUES (4)", []);
76+
77+
do trans2.in_transaction |trans3| {
78+
trans3.update("INSERT INTO foo (id) VALUES (5)", []);
79+
trans3.set_rollback();
80+
}
81+
82+
do trans2.in_transaction |trans3| {
83+
trans3.update("INSERT INTO foo (id) VALUES (6)", []);
84+
}
85+
}
86+
87+
let stmt = conn.prepare("SELECT * FROM foo ORDER BY id");
88+
let result = stmt.query([]);
89+
90+
assert_eq!(~[1i32, 2, 4, 6], result.map(|row| { row[0] }).collect());
91+
92+
trans1.set_rollback();
93+
}
94+
95+
let stmt = conn.prepare("SELECT * FROM foo ORDER BY id");
96+
let result = stmt.query([]);
97+
98+
assert_eq!(~[1i32], result.map(|row| { row[0] }).collect());
99+
}
100+
59101
#[test]
60102
fn test_query() {
61103
let conn = PostgresConnection::connect("postgres://postgres@127.0.0.1:5432");

0 commit comments

Comments
 (0)