@@ -2,152 +2,200 @@ use crate::{pg_sys, PgMemoryContexts, SpiClient};
22use std:: fmt:: Debug ;
33use std:: ops:: Deref ;
44
5- /// Sub-transaction
6- ///
7- /// Can be created by calling `SpiClient::sub_transaction`, `SubTransaction<Parent>::sub_transaction`
8- /// or any other implementation of `SubTransactionExt` and obtaining it as an argument to the provided closure.
9- ///
10- /// Unless rolled back or committed explicitly, it'll commit if `COMMIT` generic parameter is `true`
11- /// (default) or roll back if it is `false`.
12- #[ derive( Debug ) ]
13- pub struct SubTransaction < Parent : SubTransactionExt , const COMMIT : bool = true> {
5+ /// Releases a sub-transaction on Drop
6+ pub trait ReleaseOnDrop { }
7+
8+ /// Sub-transaction's contextual information
9+ #[ derive( Clone , Copy ) ]
10+ pub struct Context {
1411 memory_context : pg_sys:: MemoryContext ,
1512 // Resource ownership before the transaction
1613 //
1714 // Based on information from src/backend/utils/resowner/README
1815 // as well as practical use of it in src/pl/plpython/plpy_spi.c
1916 resource_owner : pg_sys:: ResourceOwner ,
20- // Should the transaction be released, or was it already committed or rolled back?
21- //
22- // The reason we are not calling this `released` as we're also using this flag when
23- // we convert between commit_on_drop and rollback_on_drop to ensure it doesn't get released
24- // on the drop of the original value.
25- should_release : bool ,
26- parent : Option < Parent > ,
2717}
2818
29- impl < Parent : SubTransactionExt , const COMMIT : bool > SubTransaction < Parent , COMMIT > {
30- /// Create a new sub-transaction.
31- fn new ( parent : Parent ) -> Self {
19+ impl Context {
20+ /// Captures the context
21+ fn capture ( ) -> Self {
3222 // Remember the memory context before starting the sub-transaction
33- let ctx = PgMemoryContexts :: CurrentMemoryContext . value ( ) ;
23+ let memory_context = PgMemoryContexts :: CurrentMemoryContext . value ( ) ;
3424 // Remember resource owner before starting the sub-transaction
3525 let resource_owner = unsafe { pg_sys:: CurrentResourceOwner } ;
36- unsafe {
37- pg_sys:: BeginInternalSubTransaction ( std:: ptr:: null ( ) /* [no] transaction name */ ) ;
38- }
39- // Switch to the outer memory context so that all allocations remain
40- // there instead of the sub-transaction's context
41- PgMemoryContexts :: For ( ctx) . set_as_current ( ) ;
42- Self { memory_context : ctx, should_release : true , resource_owner, parent : Some ( parent) }
26+ Self { memory_context, resource_owner }
4327 }
28+ }
4429
45- /// Commit the transaction, returning its parent
46- pub fn commit ( mut self ) -> Parent {
47- self . internal_commit ( ) ;
48- self . should_release = false ;
49- self . parent . take ( ) . unwrap ( )
30+ impl From < Context > for CommitOnDrop {
31+ fn from ( context : Context ) -> Self {
32+ CommitOnDrop ( context)
5033 }
34+ }
5135
52- /// Rollback the transaction, returning its parent
53- pub fn rollback ( mut self ) -> Parent {
54- self . internal_rollback ( ) ;
55- self . should_release = false ;
56- self . parent . take ( ) . unwrap ( )
36+ impl From < Context > for RollbackOnDrop {
37+ fn from ( context : Context ) -> Self {
38+ RollbackOnDrop ( context)
5739 }
40+ }
5841
59- fn internal_rollback ( & self ) {
42+ /// Commits a sub-transaction on Drop
43+ pub struct CommitOnDrop ( Context ) ;
44+
45+ impl Drop for CommitOnDrop {
46+ fn drop ( & mut self ) {
6047 unsafe {
61- pg_sys:: RollbackAndReleaseCurrentSubTransaction ( ) ;
62- pg_sys:: CurrentResourceOwner = self . resource_owner ;
48+ pg_sys:: ReleaseCurrentSubTransaction ( ) ;
49+ pg_sys:: CurrentResourceOwner = self . 0 . resource_owner ;
6350 }
64- PgMemoryContexts :: For ( self . memory_context ) . set_as_current ( ) ;
51+ PgMemoryContexts :: For ( self . 0 . memory_context ) . set_as_current ( ) ;
6552 }
53+ }
6654
67- fn internal_commit ( & self ) {
55+ impl ReleaseOnDrop for CommitOnDrop { }
56+
57+ /// Rolls back a sub-transaction on Drop
58+ pub struct RollbackOnDrop ( Context ) ;
59+
60+ impl Drop for RollbackOnDrop {
61+ fn drop ( & mut self ) {
6862 unsafe {
69- pg_sys:: ReleaseCurrentSubTransaction ( ) ;
70- pg_sys:: CurrentResourceOwner = self . resource_owner ;
63+ pg_sys:: RollbackAndReleaseCurrentSubTransaction ( ) ;
64+ pg_sys:: CurrentResourceOwner = self . 0 . resource_owner ;
7165 }
72- PgMemoryContexts :: For ( self . memory_context ) . set_as_current ( ) ;
66+ PgMemoryContexts :: For ( self . 0 . memory_context ) . set_as_current ( ) ;
7367 }
7468}
7569
76- impl < Parent : SubTransactionExt > SubTransaction < Parent , true > {
77- /// Make this sub-transaction roll back on drop
78- pub fn rollback_on_drop ( self ) -> SubTransaction < Parent , false > {
79- self . into ( )
70+ impl ReleaseOnDrop for RollbackOnDrop { }
71+
72+ impl Into < RollbackOnDrop > for CommitOnDrop {
73+ fn into ( self ) -> RollbackOnDrop {
74+ let result = RollbackOnDrop ( self . 0 ) ;
75+ // IMPORTANT: avoid running Drop (that would commit)
76+ std:: mem:: forget ( self ) ;
77+ result
8078 }
8179}
8280
83- impl < Parent : SubTransactionExt > SubTransaction < Parent , false > {
84- /// Make this sub-transaction commit on drop
85- pub fn commit_on_drop ( self ) -> SubTransaction < Parent , true > {
86- self . into ( )
81+ impl Into < CommitOnDrop > for RollbackOnDrop {
82+ fn into ( self ) -> CommitOnDrop {
83+ let result = CommitOnDrop ( self . 0 ) ;
84+ // IMPORTANT: avoid running Drop (that would roll back)
85+ std:: mem:: forget ( self ) ;
86+ result
8787 }
8888}
8989
90- impl < Parent : SubTransactionExt > Into < SubTransaction < Parent , false > >
91- for SubTransaction < Parent , true >
90+ struct NoOpOnDrop ;
91+
92+ impl ReleaseOnDrop for NoOpOnDrop { }
93+
94+ /// Sub-transaction
95+ ///
96+ /// Can be created by calling `SpiClient::sub_transaction`, `SubTransaction<Parent>::sub_transaction`
97+ /// or any other implementation of `SubTransactionExt` and obtaining it as an argument to the provided closure.
98+ ///
99+ /// Unless rolled back or committed explicitly, it'll commit if `Release` generic parameter is `CommitOnDrop`
100+ /// (default) or roll back if it is `RollbackOnDrop`.
101+ #[ derive( Debug ) ]
102+ pub struct SubTransaction < Parent : SubTransactionExt , Release : ReleaseOnDrop = CommitOnDrop > {
103+ release : Release ,
104+ parent : Parent ,
105+ }
106+
107+ impl < Parent : SubTransactionExt , Release : ReleaseOnDrop > SubTransaction < Parent , Release >
108+ where
109+ Release : From < Context > ,
92110{
93- fn into ( mut self ) -> SubTransaction < Parent , false > {
94- let result = SubTransaction {
95- memory_context : self . memory_context ,
96- resource_owner : self . resource_owner ,
97- should_release : self . should_release ,
98- parent : self . parent . take ( ) ,
99- } ;
100- // Make sure original sub-transaction won't commit
101- self . should_release = false ;
102- result
111+ /// Create a new sub-transaction.
112+ fn new ( parent : Parent ) -> Self {
113+ let context = Context :: capture ( ) ;
114+ let memory_context = context. memory_context ;
115+ let release = context. into ( ) ;
116+ unsafe {
117+ pg_sys:: BeginInternalSubTransaction ( std:: ptr:: null ( ) /* [no] transaction name */ ) ;
118+ }
119+ // Switch to the outer memory context so that all allocations remain
120+ // there instead of the sub-transaction's context
121+ PgMemoryContexts :: For ( memory_context) . set_as_current ( ) ;
122+ Self { release, parent }
103123 }
104124}
105125
106- impl < Parent : SubTransactionExt > Into < SubTransaction < Parent , true > >
107- for SubTransaction < Parent , false >
108- {
109- fn into ( mut self ) -> SubTransaction < Parent , true > {
110- let result = SubTransaction {
111- memory_context : self . memory_context ,
112- resource_owner : self . resource_owner ,
113- should_release : self . should_release ,
114- parent : self . parent . take ( ) ,
115- } ;
116- // Make sure original sub-transaction won't roll back
117- self . should_release = false ;
118- result
126+ impl < Parent : SubTransactionExt > SubTransaction < Parent , CommitOnDrop > {
127+ /// Commit the transaction, returning its parent
128+ pub fn commit ( self ) -> Parent {
129+ // `Self::do_nothing_on_drop()` will commit as `Release` is `CommitOnDrop`
130+ self . do_nothing_on_drop ( ) . parent
119131 }
120132}
121133
122- impl < Parent : SubTransactionExt , const COMMIT : bool > Drop for SubTransaction < Parent , COMMIT > {
123- fn drop ( & mut self ) {
124- if self . should_release {
125- if COMMIT {
126- self . internal_commit ( ) ;
127- } else {
128- self . internal_rollback ( ) ;
129- }
130- }
134+ impl < Parent : SubTransactionExt > SubTransaction < Parent , RollbackOnDrop > {
135+ /// Commit the transaction, returning its parent
136+ pub fn commit ( self ) -> Parent {
137+ // Make sub-transaction commit on drop and then use `commit`
138+ self . commit_on_drop ( ) . commit ( )
139+ }
140+ }
141+
142+ impl < Parent : SubTransactionExt > SubTransaction < Parent , RollbackOnDrop > {
143+ /// Rollback the transaction, returning its parent
144+ pub fn rollback ( self ) -> Parent {
145+ // `Self::do_nothing_on_drop()` will roll back as `Release` is `RollbackOnDrop`
146+ self . do_nothing_on_drop ( ) . parent
147+ }
148+ }
149+
150+ impl < Parent : SubTransactionExt > SubTransaction < Parent , CommitOnDrop > {
151+ /// Rollback the transaction, returning its parent
152+ pub fn rollback ( self ) -> Parent {
153+ // Make sub-transaction roll back on drop and then use `rollback`
154+ self . rollback_on_drop ( ) . rollback ( )
155+ }
156+ }
157+
158+ impl < Parent : SubTransactionExt > SubTransaction < Parent , CommitOnDrop > {
159+ /// Make this sub-transaction roll back on drop
160+ pub fn rollback_on_drop ( self ) -> SubTransaction < Parent , RollbackOnDrop > {
161+ SubTransaction { parent : self . parent , release : self . release . into ( ) }
162+ }
163+ }
164+
165+ impl < Parent : SubTransactionExt > SubTransaction < Parent , RollbackOnDrop > {
166+ /// Make this sub-transaction commit on drop
167+ pub fn commit_on_drop ( self ) -> SubTransaction < Parent , CommitOnDrop > {
168+ SubTransaction { parent : self . parent , release : self . release . into ( ) }
169+ }
170+ }
171+
172+ impl < Parent : SubTransactionExt , Release : ReleaseOnDrop > SubTransaction < Parent , Release > {
173+ /// Make this sub-transaction do nothing on drop
174+ ///
175+ /// Releases the sub-transaction based on `Release` generic parameter. Further
176+ /// dropping of the sub-transaction will not do anything.
177+ fn do_nothing_on_drop ( self ) -> SubTransaction < Parent , NoOpOnDrop > {
178+ SubTransaction { parent : self . parent , release : NoOpOnDrop }
131179 }
132180}
133181
134182// This allows SubTransaction to be de-referenced to SpiClient
135- impl < ' conn , const COMMIT : bool > Deref for SubTransaction < SpiClient < ' conn > , COMMIT > {
183+ impl < ' conn , Release : ReleaseOnDrop > Deref for SubTransaction < SpiClient < ' conn > , Release > {
136184 type Target = SpiClient < ' conn > ;
137185
138186 fn deref ( & self ) -> & Self :: Target {
139- self . parent . as_ref ( ) . unwrap ( )
187+ & self . parent
140188 }
141189}
142190
143191// This allows a SubTransaction of a SubTransaction to be de-referenced to SpiClient
144- impl < Parent : SubTransactionExt , const COMMIT : bool > Deref
145- for SubTransaction < SubTransaction < Parent > , COMMIT >
192+ impl < Parent : SubTransactionExt , Release : ReleaseOnDrop > Deref
193+ for SubTransaction < SubTransaction < Parent > , Release >
146194{
147195 type Target = Parent ;
148196
149197 fn deref ( & self ) -> & Self :: Target {
150- self . parent . as_ref ( ) . and_then ( |p| p . parent . as_ref ( ) ) . unwrap ( )
198+ & self . parent . parent
151199 }
152200}
153201
0 commit comments