Transaction.h
1/* Copyright (C) 2006-2026 by Tommi Maekitalo
2 Copyright (C) 2006-2026 by Marc Boris Duerner
3 SPDX-License-Identifier: LGPL-2.1-or-later WITH mif-exception
4*/
5
6#ifndef PT_DB_TRANSACTION_H
7#define PT_DB_TRANSACTION_H
8
9#include <Pt/Db/Api.h>
10#include <Pt/Db/Connection.h>
11#include <Pt/NonCopyable.h>
12#include <Pt/Signal.h>
13
14#if __cplusplus >= 202002L
15#include <Pt/Slot.h>
16#include <Pt/Coroutine.h>
17#endif
18
19namespace Pt {
20
21namespace Db {
22
23#if __cplusplus >= 202002L
24class AsyncBegin;
25class AsyncCommit;
26class AsyncRollback;
27#endif
28
73class Transaction : private NonCopyable
74{
75 private:
76 Connection& _connection;
77 bool _active;
78 Pt::Signal<> _startFinished;
79 Pt::Signal<> _commitFinished;
80 Pt::Signal<> _rollbackFinished;
81
82#if __cplusplus >= 202002L
83 ConnectionAwaiter* _awaiter = nullptr;
84#endif
85
86 public:
91 Transaction(Connection& conn, bool starttransaction = true)
92 : _connection(conn)
93 , _active(false)
94 {
95 if (starttransaction)
96 {
97 begin();
98 }
99 }
100
104 {
105#if __cplusplus >= 202002L
106 if(_awaiter)
107 {
108 _awaiter->cancel();
109 _awaiter->onDetach();
110 _awaiter = nullptr;
111 }
112#endif
113
114 if (_active)
115 {
116 try
117 {
118 rollback();
119 }
120 catch (const std::exception&)
121 {
122 }
123 }
124 }
125
129 { return _connection; }
130
134 { return _connection; }
135
140 void begin()
141 {
142 if (_active)
143 rollback();
144 _connection.startTransaction(onGetBeginSql());
145 _active = true;
146 }
147
152 void commit()
153 {
154 if (_active)
155 {
156 _connection.commitTransaction(onGetCommitSql());
157 _active = false;
158 }
159 }
160
165 void rollback()
166 {
167 if (_active)
168 {
169 _connection.rollbackTransaction(onGetRollbackSql());
170 _active = false;
171 }
172 }
173
177 {
178 if(_active)
179 rollback();
180 _connection.beginStartTransaction(*this, onGetBeginSql());
181 }
182
185 void endStart()
186 {
187 _connection.endStartTransaction();
188 _active = true;
189 }
190
194 { return _startFinished; }
195
199 {
200 _active = false;
201 _connection.beginCommitTransaction(*this, onGetCommitSql());
202 }
203
207 {
208 _connection.endCommitTransaction();
209 }
210
214 { return _commitFinished; }
215
216
220 {
221 _active = false;
222 _connection.beginRollbackTransaction(*this, onGetRollbackSql());
223 }
224
228 {
229 _connection.endRollbackTransaction();
230 }
231
235 { return _rollbackFinished; }
236
237#if __cplusplus >= 202002L
241
245
249#endif
250
251 protected:
254 virtual const char* onGetBeginSql()
255 { return nullptr; }
256
259 virtual const char* onGetCommitSql()
260 { return nullptr; }
261
264 virtual const char* onGetRollbackSql()
265 { return nullptr; }
266
267#if __cplusplus >= 202002L
268 private:
269 friend class AsyncBegin;
270 friend class AsyncCommit;
271 friend class AsyncRollback;
272
273 void attachAwaiter(ConnectionAwaiter& awaiter)
274 {
275 _awaiter = &awaiter;
276 }
277
278 void detachAwaiter(ConnectionAwaiter& awaiter)
279 {
280 if(_awaiter == &awaiter)
281 _awaiter = nullptr;
282 }
283#endif
284};
285
286
287#if __cplusplus >= 202002L
288
293class AsyncBegin : public ConnectionAwaiter
294{
295 public:
296 explicit AsyncBegin(Transaction& txn)
297 : ConnectionAwaiter(txn.getConnection())
298 , _txn(&txn)
299 {
300 txn.attachAwaiter(*this);
301 }
302
303 ~AsyncBegin()
304 {
305 if(_txn)
306 _txn->detachAwaiter(*this);
307 }
308
309 void onDetach() override
310 {
311 if(_txn)
312 {
313 _txn->detachAwaiter(*this);
314 _txn = nullptr;
315 }
316
317 ConnectionAwaiter::onDetach();
318 }
319
320 void await_resume()
321 {
322 transaction().endStart();
323 }
324
325 private:
326 Transaction& transaction()
327 {
328 if( ! _txn )
329 throw std::logic_error("invalid transaction");
330
331 return *_txn;
332 }
333
334 void onBegin() override
335 {
336 Transaction& txn = transaction();
337 txn.startFinished() += slot(*this, &AsyncBegin::setReady);
338 txn.beginStart();
339 }
340
341 Transaction* _txn;
342};
343
344
349class AsyncCommit : public ConnectionAwaiter
350{
351 public:
352 explicit AsyncCommit(Transaction& txn)
353 : ConnectionAwaiter(txn.getConnection())
354 , _txn(&txn)
355 {
356 txn.attachAwaiter(*this);
357 }
358
359 ~AsyncCommit()
360 {
361 if(_txn)
362 _txn->detachAwaiter(*this);
363 }
364
365 void onDetach() override
366 {
367 if(_txn)
368 {
369 _txn->detachAwaiter(*this);
370 _txn = nullptr;
371 }
372
373 ConnectionAwaiter::onDetach();
374 }
375
376 void await_resume()
377 {
378 transaction().endCommit();
379 }
380
381 private:
382 Transaction& transaction()
383 {
384 if( ! _txn )
385 throw std::logic_error("invalid transaction");
386
387 return *_txn;
388 }
389
390 void onBegin() override
391 {
392 Transaction& txn = transaction();
393 txn.commitFinished() += slot(*this, &AsyncCommit::setReady);
394 txn.beginCommit();
395 }
396
397 Transaction* _txn;
398};
399
400
405class AsyncRollback : public ConnectionAwaiter
406{
407 public:
408 explicit AsyncRollback(Transaction& txn)
409 : ConnectionAwaiter(txn.getConnection())
410 , _txn(&txn)
411 {
412 txn.attachAwaiter(*this);
413 }
414
415 ~AsyncRollback()
416 {
417 if(_txn)
418 _txn->detachAwaiter(*this);
419 }
420
421 void onDetach() override
422 {
423 if(_txn)
424 {
425 _txn->detachAwaiter(*this);
426 _txn = nullptr;
427 }
428
429 ConnectionAwaiter::onDetach();
430 }
431
432 void await_resume()
433 {
434 transaction().endRollback();
435 }
436
437 private:
438 Transaction& transaction()
439 {
440 if( ! _txn )
441 throw std::logic_error("invalid transaction");
442
443 return *_txn;
444 }
445
446 void onBegin() override
447 {
448 Transaction& txn = transaction();
450 txn.beginRollback();
451 }
452
453 Transaction* _txn;
454};
455
456
457inline AsyncBegin Transaction::beginAsync()
458{
459 return AsyncBegin(*this);
460}
461
462
463inline AsyncCommit Transaction::commitAsync()
464{
465 return AsyncCommit(*this);
466}
467
468
469inline AsyncRollback Transaction::rollbackAsync()
470{
471 return AsyncRollback(*this);
472}
473
474#endif // __cplusplus >= 202002L
475
476
487{
488 public:
491 SqliteTransaction(Connection& conn, bool start = true, bool immediate = false)
492 : Transaction(conn, false)
493 , _immediate(immediate)
494 {
495 if(start)
496 begin();
497 }
498
499 protected:
500 const char* onGetBeginSql() override
501 {
502 return _immediate ? "BEGIN IMMEDIATE TRANSACTION" : nullptr;
503 }
504
505 private:
506 bool _immediate;
507};
508
509} // namespace Db
510
511} // namespace Pt
512
513#endif // PT_DB_TRANSACTION_H
void setReady()
Resumes the waiting coroutine.
Definition Coroutine.h:162
Awaitable for asynchronous begin-transaction.
Definition Transaction.h:294
Awaitable for asynchronous commit.
Definition Transaction.h:350
Awaitable for asynchronous rollback.
Definition Transaction.h:406
Base awaiter for a pending connection operation.
Definition Connection.h:371
Database session for a registered driver.
Definition Connection.h:97
SqliteTransaction(Connection &conn, bool start=true, bool immediate=false)
Creates a SQLite transaction on conn.
Definition Transaction.h:491
const char * onGetBeginSql() override
Returns SQL for BEGIN, or a null pointer for the backend default.
Definition Transaction.h:500
Unit of work on a database connection.
Definition Transaction.h:74
AsyncCommit commitAsync()
Returns an awaitable that commits the transaction.
Definition Transaction.h:463
void rollback()
Rolls back the active transaction.
Definition Transaction.h:165
void endStart()
Completes an asynchronous begin-transaction.
Definition Transaction.h:185
void beginStart()
Starts an asynchronous begin-transaction.
Definition Transaction.h:176
void endCommit()
Completes an asynchronous commit.
Definition Transaction.h:206
virtual const char * onGetBeginSql()
Returns SQL for BEGIN, or a null pointer for the backend default.
Definition Transaction.h:254
Transaction(Connection &conn, bool starttransaction=true)
Creates a transaction on conn.
Definition Transaction.h:91
Pt::Signal & rollbackFinished()
Signal emitted when an asynchronous rollback completes.
Definition Transaction.h:234
AsyncRollback rollbackAsync()
Returns an awaitable that rolls back the transaction.
Definition Transaction.h:469
Pt::Signal & startFinished()
Signal emitted when an asynchronous begin completes.
Definition Transaction.h:193
~Transaction()
Rolls back an active transaction and destroys the object.
Definition Transaction.h:103
void beginRollback()
Starts an asynchronous rollback.
Definition Transaction.h:219
virtual const char * onGetCommitSql()
Returns SQL for COMMIT, or a null pointer for the backend default.
Definition Transaction.h:259
AsyncBegin beginAsync()
Returns an awaitable that begins the transaction.
Definition Transaction.h:457
virtual const char * onGetRollbackSql()
Returns SQL for ROLLBACK, or a null pointer for the backend default.
Definition Transaction.h:264
void endRollback()
Completes an asynchronous rollback.
Definition Transaction.h:227
Pt::Signal & commitFinished()
Signal emitted when an asynchronous commit completes.
Definition Transaction.h:213
void beginCommit()
Starts an asynchronous commit.
Definition Transaction.h:198
void begin()
Begins a deferred transaction.
Definition Transaction.h:140
Connection & getConnection()
Returns the connection this transaction uses.
Definition Transaction.h:133
void commit()
Commits the active transaction.
Definition Transaction.h:152
const Connection & getConnection() const
Returns the connection this transaction uses.
Definition Transaction.h:128
NonCopyable()
Default constructor.
Definition NonCopyable.h:63
Multicast Signal to call multiple slots.
Definition Signal.h:190
ConstMethodSlot< R, ClassT, As... > slot(ClassT &object, R(BaseT::*method)(As...) const)
Returns a slot object for the given object/member pair.
Definition ConstMethod.h:172
Portable SQL database access.
Core module.
Definition Allocator.h:33