1use crate::error::{EFError, EFResult};
24use crate::provider::{IAsyncConnection, IsolationLevel};
25use crate::session::{Session, SessionConnection, SharedTxn};
26use std::future::Future;
27use std::pin::Pin;
28use std::sync::Arc;
29
30pub trait ITransaction: Send + Sync {
36 fn commit(self: Box<Self>) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'static>>;
38
39 fn rollback(self: Box<Self>) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'static>>;
41
42 fn create_point<'a>(
44 &'a mut self,
45 name: &'a str,
46 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>>;
47
48 fn release_point<'a>(
50 &'a mut self,
51 name: &'a str,
52 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>>;
53
54 fn rollback_point<'a>(
56 &'a mut self,
57 name: &'a str,
58 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>>;
59
60 fn set_isolation<'a>(
62 &'a mut self,
63 level: IsolationLevel,
64 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>>;
65
66 fn connection(&mut self) -> &mut (dyn IAsyncConnection + Send);
69}
70
71pub(crate) struct ConnectionGuard {
74 conn: Option<Box<dyn IAsyncConnection>>,
75}
76
77impl ConnectionGuard {
78 pub(crate) fn new(conn: Box<dyn IAsyncConnection>) -> Self {
79 Self { conn: Some(conn) }
80 }
81
82 pub(crate) fn as_mut(&mut self) -> &mut dyn IAsyncConnection {
83 self.conn
84 .as_mut()
85 .expect("connection guard already disarmed")
86 .as_mut()
87 }
88
89 pub(crate) fn disarm(mut self) -> Box<dyn IAsyncConnection> {
91 self.conn.take().expect("connection guard already disarmed")
92 }
93}
94
95impl Drop for ConnectionGuard {
96 fn drop(&mut self) {
97 if let Some(conn) = self.conn.take() {
98 conn.discard();
101 }
102 }
103}
104
105struct TxnState {
107 shared: Arc<SharedTxn>,
108 facade: SessionConnection,
109}
110
111pub struct DbTransaction {
113 state: Option<TxnState>,
114 session: Arc<Session>,
117 runtime: Option<tokio::runtime::Handle>,
120}
121
122impl DbTransaction {
123 pub(crate) fn new_shared(
124 shared: Arc<SharedTxn>,
125 facade: SessionConnection,
126 session: Arc<Session>,
127 ) -> Self {
128 Self {
129 state: Some(TxnState { shared, facade }),
130 session,
131 runtime: tokio::runtime::Handle::try_current().ok(),
132 }
133 }
134}
135
136impl ITransaction for DbTransaction {
137 fn commit(self: Box<Self>) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'static>> {
138 Box::pin(async move {
139 let mut this = self;
140 let Some(state) = this.state.take() else {
141 return Err(EFError::transaction("transaction already consumed"));
142 };
143 let result = match state.shared.take().await {
144 Ok(conn) => {
145 let mut guard = ConnectionGuard::new(conn);
146 match guard.as_mut().commit_transaction().await {
147 Ok(()) => {
148 drop(guard.disarm());
149 Ok(())
150 }
151 Err(e) => {
152 let _ = guard.as_mut().rollback_transaction().await;
156 Err(e)
157 }
158 }
159 }
160 Err(e) => Err(e),
161 };
162 this.session.clear().await;
163 result
164 })
165 }
166
167 fn rollback(self: Box<Self>) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'static>> {
168 Box::pin(async move {
169 let mut this = self;
170 let Some(state) = this.state.take() else {
171 return Err(EFError::transaction("transaction already consumed"));
172 };
173 let result = match state.shared.take().await {
174 Ok(conn) => {
175 let mut guard = ConnectionGuard::new(conn);
176 match guard.as_mut().rollback_transaction().await {
177 Ok(()) => {
178 drop(guard.disarm());
179 Ok(())
180 }
181 Err(e) => Err(e),
182 }
183 }
184 Err(e) => Err(e),
185 };
186 this.session.clear().await;
187 result
188 })
189 }
190
191 fn create_point<'a>(
192 &'a mut self,
193 name: &'a str,
194 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>> {
195 Box::pin(async move {
196 self.state
197 .as_mut()
198 .ok_or_else(|| EFError::transaction("transaction already consumed"))?
199 .facade
200 .create_savepoint(name)
201 .await
202 })
203 }
204
205 fn release_point<'a>(
206 &'a mut self,
207 name: &'a str,
208 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>> {
209 Box::pin(async move {
210 self.state
211 .as_mut()
212 .ok_or_else(|| EFError::transaction("transaction already consumed"))?
213 .facade
214 .release_savepoint(name)
215 .await
216 })
217 }
218
219 fn rollback_point<'a>(
220 &'a mut self,
221 name: &'a str,
222 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>> {
223 Box::pin(async move {
224 self.state
225 .as_mut()
226 .ok_or_else(|| EFError::transaction("transaction already consumed"))?
227 .facade
228 .rollback_to_savepoint(name)
229 .await
230 })
231 }
232
233 fn set_isolation<'a>(
234 &'a mut self,
235 level: IsolationLevel,
236 ) -> Pin<Box<dyn Future<Output = EFResult<()>> + Send + 'a>> {
237 Box::pin(async move {
238 self.state
239 .as_mut()
240 .ok_or_else(|| EFError::transaction("transaction already consumed"))?
241 .facade
242 .set_transaction_isolation(level)
243 .await
244 })
245 }
246
247 fn connection(&mut self) -> &mut (dyn IAsyncConnection + Send) {
248 &mut self
249 .state
250 .as_mut()
251 .expect("transaction already consumed")
252 .facade
253 }
254}
255
256impl Drop for DbTransaction {
257 fn drop(&mut self) {
258 let state = self.state.take();
259 self.session.clear_now();
260 let Some(state) = state else {
261 return;
262 };
263
264 if let Some(conn) = state.shared.try_take() {
267 conn.discard();
268 return;
269 }
270
271 let handle = self
275 .runtime
276 .clone()
277 .or_else(|| tokio::runtime::Handle::try_current().ok());
278 let Some(handle) = handle else {
279 return;
280 };
281 let session = Arc::clone(&self.session);
282 handle.spawn(async move {
283 if let Ok(conn) = state.shared.take().await {
284 let mut guard = ConnectionGuard::new(conn);
285 let _ = guard.as_mut().rollback_transaction().await;
286 }
288 session.clear().await;
289 });
290 }
291}