| 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 |