Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2023 Carl Zeiss Microscopy GmbH | ||
2 | // | ||
3 | // SPDX-License-Identifier: MIT | ||
4 | |||
5 | #pragma once | ||
6 | |||
7 | #include <functional> | ||
8 | #include <utility> | ||
9 | #include <memory> | ||
10 | |||
11 | /// A utility in order to wrap a piece of code into a database-transaction. | ||
12 | /// | ||
13 | /// \tparam t_return_value Type of the return value. | ||
14 | template <typename t_return_value> | ||
15 | class TransactionHelper | ||
16 | { | ||
17 | private: | ||
18 | std::function< t_return_value()> action_; | ||
19 | std::shared_ptr<IDbConnection> database_connection_; | ||
20 | public: | ||
21 | 41918 | TransactionHelper( | |
22 | std::shared_ptr<IDbConnection> database_connection, | ||
23 | std::function<t_return_value()> action) : | ||
24 | 41918 | action_(std::move(action)), | |
25 | 41918 | database_connection_(std::move(database_connection)) | |
26 | 41918 | {} | |
27 | |||
28 | /// Execute the action guarded with Begin-/End-Transaction. Or, in more detail, what this is about | ||
29 | /// is: | ||
30 | /// First thing to be aware of is that the DbConnection-objection is taking care of maintaing a "transaction state", | ||
31 | /// i. e. whether currently we are inside a transaction. Background here is that nested transactions are not supported | ||
32 | /// with SQLite (https://www.sqlite.org/lang_transaction.html), so a transaction is a "global state". | ||
33 | /// So, what we do here, is | ||
34 | /// - we query the DbConnection-object if there is a transaction pending | ||
35 | /// - if this is the case, we execute the action right away | ||
36 | /// - if not, we initiate a transaction, then call the action, then end the transaction | ||
37 | /// In other words - if there is no pending transaction, we wrap the action into a Begin-/End-Transaction. | ||
38 | /// If the action is throwing an execption, we end the transaction with a rollback (i.e. again, only if we | ||
39 | /// initiated the transaction). | ||
40 | /// \returns {t_return_value} The return value of the action. | ||
41 | 41918 | t_return_value Execute() | |
42 | { | ||
43 | 41918 | bool transaction_initiated = false; | |
44 |
2/2✓ Branch 2 taken 41914 times.
✓ Branch 3 taken 4 times.
|
41918 | if (!this->database_connection_->IsTransactionPending()) |
45 | { | ||
46 | 41914 | this->database_connection_->BeginTransaction(); | |
47 | 41914 | transaction_initiated = true; | |
48 | } | ||
49 | |||
50 | try | ||
51 | { | ||
52 |
1/2✓ Branch 1 taken 41918 times.
✗ Branch 2 not taken.
|
41918 | t_return_value return_value = this->action_(); |
53 | |||
54 |
2/2✓ Branch 0 taken 41914 times.
✓ Branch 1 taken 4 times.
|
41918 | if (transaction_initiated) |
55 | { | ||
56 | // TODO(JBL): I guess we need to think about how to deal with "exception from the next line" | ||
57 |
1/2✓ Branch 2 taken 41914 times.
✗ Branch 3 not taken.
|
41914 | this->database_connection_->EndTransaction(true); |
58 | } | ||
59 | |||
60 | 41918 | return return_value; | |
61 | } | ||
62 | ✗ | catch (...) | |
63 | { | ||
64 | ✗ | if (transaction_initiated) | |
65 | { | ||
66 | ✗ | this->database_connection_->EndTransaction(false); | |
67 | } | ||
68 | |||
69 | ✗ | throw; | |
70 | } | ||
71 | } | ||
72 | }; | ||
73 |