fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Transaction.cpp
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Adriano dos Santos Fernandes
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include "Transaction.h"
26#include "Attachment.h"
27#include "Client.h"
28#include "Exception.h"
29#include <cassert>
30
31using namespace fbcpp;
32using namespace fbcpp::impl;
33
34
35static FbUniquePtr<fb::IXpbBuilder> buildTpb(
36 fb::IMaster* master, StatusWrapper& statusWrapper, const TransactionOptions& options)
37{
38 auto tpbBuilder = fbUnique(master->getUtilInterface()->getXpbBuilder(&statusWrapper, fb::IXpbBuilder::TPB,
39 reinterpret_cast<const std::uint8_t*>(options.getTpb().data()),
40 static_cast<unsigned>(options.getTpb().size())));
41
42 if (const auto accessMode = options.getAccessMode())
43 {
44 switch (accessMode.value())
45 {
46 case TransactionAccessMode::READ_ONLY:
47 tpbBuilder->insertTag(&statusWrapper, isc_tpb_read);
48 break;
49
50 case TransactionAccessMode::READ_WRITE:
51 tpbBuilder->insertTag(&statusWrapper, isc_tpb_write);
52 break;
53
54 default:
55 assert(false);
56 break;
57 }
58 }
59
60 if (const auto waitMode = options.getWaitMode())
61 {
62 switch (waitMode.value())
63 {
64 case TransactionWaitMode::NO_WAIT:
65 tpbBuilder->insertTag(&statusWrapper, isc_tpb_nowait);
66 break;
67
68 case TransactionWaitMode::WAIT:
69 tpbBuilder->insertTag(&statusWrapper, isc_tpb_wait);
70 break;
71
72 default:
73 assert(false);
74 break;
75 }
76 }
77
78 if (const auto isolationLevel = options.getIsolationLevel())
79 {
80 switch (isolationLevel.value())
81 {
82 case TransactionIsolationLevel::CONSISTENCY:
83 tpbBuilder->insertTag(&statusWrapper, isc_tpb_consistency);
84 break;
85
86 case TransactionIsolationLevel::SNAPSHOT:
87 tpbBuilder->insertTag(&statusWrapper, isc_tpb_concurrency);
88 break;
89
90 case TransactionIsolationLevel::READ_COMMITTED:
91 tpbBuilder->insertTag(&statusWrapper, isc_tpb_read_committed);
92
93 if (const auto readCommittedMode = options.getReadCommittedMode())
94 {
95 switch (readCommittedMode.value())
96 {
97 case TransactionReadCommittedMode::NO_RECORD_VERSION:
98 tpbBuilder->insertTag(&statusWrapper, isc_tpb_no_rec_version);
99 break;
100
101 case TransactionReadCommittedMode::RECORD_VERSION:
102 tpbBuilder->insertTag(&statusWrapper, isc_tpb_rec_version);
103 break;
104
105 default:
106 assert(false);
107 break;
108 }
109 }
110
111 break;
112
113 default:
114 assert(false);
115 break;
116 }
117 }
118
119 if (options.getNoAutoUndo())
120 tpbBuilder->insertTag(&statusWrapper, isc_tpb_no_auto_undo);
121
122 if (options.getIgnoreLimbo())
123 tpbBuilder->insertTag(&statusWrapper, isc_tpb_ignore_limbo);
124
125 if (options.getRestartRequests())
126 tpbBuilder->insertTag(&statusWrapper, isc_tpb_restart_requests);
127
128 if (options.getAutoCommit())
129 tpbBuilder->insertTag(&statusWrapper, isc_tpb_autocommit);
130
131 return tpbBuilder;
132}
133
134
136 : client{attachment.getClient()}
137{
138 assert(attachment.isValid());
139
140 const auto master = client.getMaster();
141
142 const auto status = client.newStatus();
143 StatusWrapper statusWrapper{client, status.get()};
144
145 auto tpbBuilder = buildTpb(master, statusWrapper, options);
146 const auto tpbBuffer = tpbBuilder->getBuffer(&statusWrapper);
147 const auto tpbBufferLen = tpbBuilder->getBufferLength(&statusWrapper);
148
149 handle.reset(attachment.getHandle()->startTransaction(&statusWrapper, tpbBufferLen, tpbBuffer));
150}
151
152Transaction::Transaction(Attachment& attachment, std::string_view setTransactionCmd)
153 : client{attachment.getClient()}
154{
155 assert(attachment.isValid());
156
157 const auto status = client.newStatus();
158 StatusWrapper statusWrapper{client, status.get()};
159
160 handle.reset(
161 attachment.getHandle()->execute(&statusWrapper, nullptr, static_cast<unsigned>(setTransactionCmd.length()),
162 setTransactionCmd.data(), SQL_DIALECT_V6, nullptr, nullptr, nullptr, nullptr));
163}
164
165Transaction::Transaction(std::span<std::reference_wrapper<Attachment>> attachments, const TransactionOptions& options)
166 : client{attachments[0].get().getClient()},
167 isMultiDatabase{true}
168{
169 assert(!attachments.empty());
170
171 // Validate all attachments use the same Client
172 for (const auto& attachment : attachments)
173 {
174 assert(attachment.get().isValid());
175
176 if (&attachment.get().getClient() != &client)
177 throw std::invalid_argument("All attachments must use the same Client for multi-database transactions");
178 }
179
180 const auto master = client.getMaster();
181
182 const auto status = client.newStatus();
183 StatusWrapper statusWrapper{client, status.get()};
184
185 auto tpbBuilder = buildTpb(master, statusWrapper, options);
186 const auto tpbBuffer = tpbBuilder->getBuffer(&statusWrapper);
187 const auto tpbBufferLen = tpbBuilder->getBufferLength(&statusWrapper);
188
189 auto dtcInterface = master->getDtc();
190 auto dtcStart = fbUnique(dtcInterface->startBuilder(&statusWrapper));
191
192 // Add each attachment with the same TPB
193 for (const auto& attachment : attachments)
194 dtcStart->addWithTpb(&statusWrapper, attachment.get().getHandle().get(), tpbBufferLen, tpbBuffer);
195
196 // Start the multi-database transaction, which disposes the IDtcStart instance
197 handle.reset(dtcStart->start(&statusWrapper));
198 dtcStart.release();
199}
200
202{
203 assert(isValid());
204 assert(state == TransactionState::ACTIVE || state == TransactionState::PREPARED);
205
206 const auto status = client.newStatus();
207 StatusWrapper statusWrapper{client, status.get()};
208
209 handle->rollback(&statusWrapper);
210 handle.reset();
212}
213
215{
216 assert(isValid());
217 assert(state == TransactionState::ACTIVE || state == TransactionState::PREPARED);
218
219 const auto status = client.newStatus();
220 StatusWrapper statusWrapper{client, status.get()};
221
222 handle->commit(&statusWrapper);
223 handle.reset();
225}
226
228{
229 assert(isValid());
230 assert(state == TransactionState::ACTIVE);
231
232 const auto status = client.newStatus();
233 StatusWrapper statusWrapper{client, status.get()};
234
235 handle->commitRetaining(&statusWrapper);
236}
237
239{
240 assert(isValid());
241 assert(state == TransactionState::ACTIVE);
242
243 const auto status = client.newStatus();
244 StatusWrapper statusWrapper{client, status.get()};
245
246 handle->rollbackRetaining(&statusWrapper);
247}
248
250{
251 prepare(std::span<const std::uint8_t>{});
252}
253
254void Transaction::prepare(std::span<const std::uint8_t> message)
255{
256 assert(isValid());
257 assert(state == TransactionState::ACTIVE);
258
259 const auto status = client.newStatus();
260 StatusWrapper statusWrapper{client, status.get()};
261
262 handle->prepare(&statusWrapper, static_cast<unsigned>(message.size()), message.data());
264}
265
266void Transaction::prepare(std::string_view message)
267{
268 const auto messageBytes = reinterpret_cast<const std::uint8_t*>(message.data());
269 prepare(std::span<const std::uint8_t>{messageBytes, message.size()});
270}
Represents a connection to a Firebird database.
Definition Attachment.h:177
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:238
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:222
FbUniquePtr< fb::IStatus > newStatus()
Creates and returns a Firebird IStatus instance.
Definition Client.h:199
fb::IMaster * getMaster() noexcept
Returns the Firebird IMaster interface.
Definition Client.h:136
Represents options used when creating a Transaction object.
bool getAutoCommit() const
Returns whether the transaction will be automatically committed.
const std::vector< std::uint8_t > & getTpb() const
Returns the TPB (Transaction Parameter Block) which will be used to start the transaction.
bool getRestartRequests() const
Returns whether the transaction will restart requests.
const std::optional< TransactionAccessMode > getAccessMode() const
Returns the transaction access mode.
bool getNoAutoUndo() const
Returns whether the transaction will not automatically undo changes in case of a deadlock or update c...
bool getIgnoreLimbo() const
Returns whether the transaction will ignore limbo transactions.
const std::optional< TransactionIsolationLevel > getIsolationLevel() const
Returns the transaction isolation level.
const std::optional< TransactionReadCommittedMode > getReadCommittedMode() const
Returns the read committed mode.
const std::optional< TransactionWaitMode > getWaitMode() const
Returns the transaction wait mode.
void rollback()
Rolls back the transaction.
void commit()
Commits the transaction.
void prepare()
Prepares the transaction for two-phase commit (2PC phase 1).
bool isValid() noexcept
Returns whether the Transaction object is valid.
void commitRetaining()
Commits the transaction while maintains it active.
void rollbackRetaining()
Rolls back the transaction while maintains it active.
Transaction(Attachment &attachment, const TransactionOptions &options={})
Constructs a Transaction object that starts a transaction in the specified Attachment using the speci...
fb-cpp namespace.
Definition Attachment.h:42
@ ACTIVE
Transaction is active and can execute statements.
@ COMMITTED
Transaction has been committed.
@ PREPARED
Transaction has been prepared (2PC phase 1).
@ ROLLED_BACK
Transaction has been rolled back.
FbUniquePtr< T > fbUnique(T *obj) noexcept
Creates a unique pointer for a Firebird disposable object.
Definition SmartPtrs.h:59
std::unique_ptr< T, impl::FbDisposeDeleter > FbUniquePtr
Unique pointer type for Firebird disposable objects.
Definition SmartPtrs.h:53