@@ -23,20 +23,26 @@ use tokio::io::{AsyncRead, AsyncWrite};
23
23
/// transaction. Transactions can be nested, with inner transactions implemented via safepoints.
24
24
pub struct Transaction < ' a > {
25
25
client : & ' a mut Client ,
26
- depth : u32 ,
26
+ savepoint : Option < Savepoint > ,
27
27
done : bool ,
28
28
}
29
29
30
+ /// A representation of a PostgreSQL database savepoint.
31
+ struct Savepoint {
32
+ name : String ,
33
+ depth : u32 ,
34
+ }
35
+
30
36
impl < ' a > Drop for Transaction < ' a > {
31
37
fn drop ( & mut self ) {
32
38
if self . done {
33
39
return ;
34
40
}
35
41
36
- let query = if self . depth == 0 {
37
- "ROLLBACK" . to_string ( )
42
+ let query = if let Some ( sp ) = self . savepoint . as_ref ( ) {
43
+ format ! ( "ROLLBACK TO {}" , sp . name )
38
44
} else {
39
- format ! ( "ROLLBACK TO sp{}" , self . depth )
45
+ "ROLLBACK" . to_string ( )
40
46
} ;
41
47
let buf = self . client . inner ( ) . with_buf ( |buf| {
42
48
frontend:: query ( & query, buf) . unwrap ( ) ;
@@ -53,18 +59,18 @@ impl<'a> Transaction<'a> {
53
59
pub ( crate ) fn new ( client : & ' a mut Client ) -> Transaction < ' a > {
54
60
Transaction {
55
61
client,
56
- depth : 0 ,
62
+ savepoint : None ,
57
63
done : false ,
58
64
}
59
65
}
60
66
61
67
/// Consumes the transaction, committing all changes made within it.
62
68
pub async fn commit ( mut self ) -> Result < ( ) , Error > {
63
69
self . done = true ;
64
- let query = if self . depth == 0 {
65
- "COMMIT" . to_string ( )
70
+ let query = if let Some ( sp ) = self . savepoint . as_ref ( ) {
71
+ format ! ( "RELEASE {}" , sp . name )
66
72
} else {
67
- format ! ( "RELEASE sp{}" , self . depth )
73
+ "COMMIT" . to_string ( )
68
74
} ;
69
75
self . client . batch_execute ( & query) . await
70
76
}
@@ -74,10 +80,10 @@ impl<'a> Transaction<'a> {
74
80
/// This is equivalent to `Transaction`'s `Drop` implementation, but provides any error encountered to the caller.
75
81
pub async fn rollback ( mut self ) -> Result < ( ) , Error > {
76
82
self . done = true ;
77
- let query = if self . depth == 0 {
78
- "ROLLBACK" . to_string ( )
83
+ let query = if let Some ( sp ) = self . savepoint . as_ref ( ) {
84
+ format ! ( "ROLLBACK TO {}" , sp . name )
79
85
} else {
80
- format ! ( "ROLLBACK TO sp{}" , self . depth )
86
+ "ROLLBACK" . to_string ( )
81
87
} ;
82
88
self . client . batch_execute ( & query) . await
83
89
}
@@ -272,15 +278,28 @@ impl<'a> Transaction<'a> {
272
278
self . client . cancel_query_raw ( stream, tls) . await
273
279
}
274
280
275
- /// Like `Client::transaction`.
281
+ /// Like `Client::transaction`, but creates a nested transaction via a savepoint .
276
282
pub async fn transaction ( & mut self ) -> Result < Transaction < ' _ > , Error > {
277
- let depth = self . depth + 1 ;
278
- let query = format ! ( "SAVEPOINT sp{}" , depth) ;
283
+ self . _savepoint ( None ) . await
284
+ }
285
+
286
+ /// Like `Client::transaction`, but creates a nested transaction via a savepoint with the specified name.
287
+ pub async fn savepoint < I > ( & mut self , name : I ) -> Result < Transaction < ' _ > , Error >
288
+ where
289
+ I : Into < String > ,
290
+ {
291
+ self . _savepoint ( Some ( name. into ( ) ) ) . await
292
+ }
293
+
294
+ async fn _savepoint ( & mut self , name : Option < String > ) -> Result < Transaction < ' _ > , Error > {
295
+ let depth = self . savepoint . as_ref ( ) . map_or ( 0 , |sp| sp. depth ) + 1 ;
296
+ let name = name. unwrap_or_else ( || format ! ( "sp_{}" , depth) ) ;
297
+ let query = format ! ( "SAVEPOINT {}" , name) ;
279
298
self . batch_execute ( & query) . await ?;
280
299
281
300
Ok ( Transaction {
282
301
client : self . client ,
283
- depth,
302
+ savepoint : Some ( Savepoint { name , depth } ) ,
284
303
done : false ,
285
304
} )
286
305
}
0 commit comments