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 StatusWrapper statusWrapper{client};
143
144 auto tpbBuilder = buildTpb(master, statusWrapper, options);
145 const auto tpbBuffer = tpbBuilder->getBuffer(&statusWrapper);
146 const auto tpbBufferLen = tpbBuilder->getBufferLength(&statusWrapper);
147
148 handle.reset(attachment.getHandle()->startTransaction(&statusWrapper, tpbBufferLen, tpbBuffer));
149}
150
151Transaction::Transaction(Attachment& attachment, std::string_view setTransactionCmd)
152 : client{attachment.getClient()}
153{
154 assert(attachment.isValid());
155
156 StatusWrapper statusWrapper{client};
157
158 handle.reset(
159 attachment.getHandle()->execute(&statusWrapper, nullptr, static_cast<unsigned>(setTransactionCmd.length()),
160 setTransactionCmd.data(), SQL_DIALECT_V6, nullptr, nullptr, nullptr, nullptr));
161}
162
163Transaction::Transaction(std::span<std::reference_wrapper<Attachment>> attachments, const TransactionOptions& options)
164 : client{attachments[0].get().getClient()},
165 isMultiDatabase{true}
166{
167 assert(!attachments.empty());
168
169 // Validate all attachments use the same Client
170 for (const auto& attachment : attachments)
171 {
172 assert(attachment.get().isValid());
173
174 if (&attachment.get().getClient() != &client)
175 throw std::invalid_argument("All attachments must use the same Client for multi-database transactions");
176 }
177
178 const auto master = client.getMaster();
179
180 StatusWrapper statusWrapper{client};
181
182 auto tpbBuilder = buildTpb(master, statusWrapper, options);
183 const auto tpbBuffer = tpbBuilder->getBuffer(&statusWrapper);
184 const auto tpbBufferLen = tpbBuilder->getBufferLength(&statusWrapper);
185
186 auto dtcInterface = master->getDtc();
187 auto dtcStart = fbUnique(dtcInterface->startBuilder(&statusWrapper));
188
189 // Add each attachment with the same TPB
190 for (const auto& attachment : attachments)
191 dtcStart->addWithTpb(&statusWrapper, attachment.get().getHandle().get(), tpbBufferLen, tpbBuffer);
192
193 // Start the multi-database transaction, which disposes the IDtcStart instance
194 handle.reset(dtcStart->start(&statusWrapper));
195 dtcStart.release();
196}
197
199{
200 assert(isValid());
201 assert(state == TransactionState::ACTIVE || state == TransactionState::PREPARED);
202
203 StatusWrapper statusWrapper{client};
204
205 handle->rollback(&statusWrapper);
206 handle.reset();
208}
209
211{
212 assert(isValid());
213 assert(state == TransactionState::ACTIVE || state == TransactionState::PREPARED);
214
215 StatusWrapper statusWrapper{client};
216
217 handle->commit(&statusWrapper);
218 handle.reset();
220}
221
223{
224 assert(isValid());
225 assert(state == TransactionState::ACTIVE);
226
227 StatusWrapper statusWrapper{client};
228
229 handle->commitRetaining(&statusWrapper);
230}
231
233{
234 assert(isValid());
235 assert(state == TransactionState::ACTIVE);
236
237 StatusWrapper statusWrapper{client};
238
239 handle->rollbackRetaining(&statusWrapper);
240}
241
243{
244 prepare(std::span<const std::uint8_t>{});
245}
246
247void Transaction::prepare(std::span<const std::uint8_t> message)
248{
249 assert(isValid());
250 assert(state == TransactionState::ACTIVE);
251
252 StatusWrapper statusWrapper{client};
253
254 handle->prepare(&statusWrapper, static_cast<unsigned>(message.size()), message.data());
256}
257
258void Transaction::prepare(std::string_view message)
259{
260 const auto messageBytes = reinterpret_cast<const std::uint8_t*>(message.data());
261 prepare(std::span<const std::uint8_t>{messageBytes, message.size()});
262}
Represents a connection to a Firebird database.
Definition Attachment.h:213
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:289
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:273
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