fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Batch.cpp
1/*
2 * MIT License
3 *
4 * Copyright (c) 2026 F.D.Castel
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 "Batch.h"
26#include "Attachment.h"
27#include "Client.h"
28#include "Statement.h"
29#include "Transaction.h"
30
31using namespace fbcpp;
32using namespace fbcpp::impl;
33
34
35// --- BatchCompletionState ---
36
38 : client{&client},
39 status{client.newStatus()},
40 statusWrapper{client, status.get()},
41 handle{std::move(handle)}
42{
43}
44
46 : client{o.client},
47 status{std::move(o.status)},
48 statusWrapper{std::move(o.statusWrapper)},
49 handle{std::move(o.handle)}
50{
51}
52
54{
55 return handle->getSize(&statusWrapper);
56}
57
59{
60 return handle->getState(&statusWrapper, pos);
61}
62
63std::optional<unsigned> BatchCompletionState::findError(unsigned pos)
64{
65 const auto result = handle->findError(&statusWrapper, pos);
66
67 if (result == fb::IBatchCompletionState::NO_MORE_ERRORS)
68 return std::nullopt;
69
70 return result;
71}
72
73std::vector<std::intptr_t> BatchCompletionState::getStatus(unsigned pos)
74{
75 auto tempStatus = client->newStatus();
76
77 handle->getStatus(&statusWrapper, tempStatus.get(), pos);
78
79 std::vector<std::intptr_t> result;
80 const auto* errors = tempStatus->getErrors();
81
82 if (errors)
83 {
84 const auto* p = errors;
85
86 while (*p != isc_arg_end)
87 {
88 result.push_back(*p++);
89 result.push_back(*p++);
90 }
91
92 result.push_back(isc_arg_end);
93 }
94
95 return result;
96}
97
98
99// --- Batch ---
100
101Batch::Batch(Statement& statement, Transaction& transaction, const BatchOptions& options)
102 : client{&statement.getAttachment().getClient()},
103 transaction{&transaction},
104 statement{&statement},
105 status{client->newStatus()},
106 statusWrapper{*client, status.get()}
107{
108 assert(statement.isValid());
109 assert(transaction.isValid());
110
111 const auto parBlock = buildParametersBlock(*client, options);
112
113 handle.reset(statement.getStatementHandle()->createBatch(
114 &statusWrapper, statement.getInputMetadata().get(), static_cast<unsigned>(parBlock.size()), parBlock.data()));
115}
116
117Batch::Batch(Attachment& attachment, Transaction& transaction, std::string_view sql, unsigned dialect,
118 const BatchOptions& options)
119 : client{&attachment.getClient()},
120 transaction{&transaction},
121 status{client->newStatus()},
122 statusWrapper{*client, status.get()}
123{
124 assert(attachment.isValid());
125 assert(transaction.isValid());
126
127 const auto parBlock = buildParametersBlock(*client, options);
128
129 handle.reset(attachment.getHandle()->createBatch(&statusWrapper, transaction.getHandle().get(),
130 static_cast<unsigned>(sql.length()), sql.data(), dialect, nullptr, static_cast<unsigned>(parBlock.size()),
131 parBlock.data()));
132}
133
134Batch::Batch(Batch&& o) noexcept
135 : client{o.client},
136 transaction{o.transaction},
137 statement{o.statement},
138 status{std::move(o.status)},
139 statusWrapper{std::move(o.statusWrapper)},
140 handle{std::move(o.handle)}
141{
142}
143
144
145// --- Adding messages ---
146
147void Batch::add(unsigned count, const void* inBuffer)
148{
149 assert(isValid());
150 handle->add(&statusWrapper, count, inBuffer);
151}
152
154{
155 assert(isValid());
156 assert(statement);
157 handle->add(&statusWrapper, 1, statement->getInputMessage().data());
158}
159
160
161// --- Blob support ---
162
163BlobId Batch::addBlob(std::span<const std::byte> data, const BlobOptions& bpb)
164{
165 assert(isValid());
166
167 const auto preparedBpb = prepareBpb(*client, bpb);
168
169 BlobId blobId;
170 handle->addBlob(&statusWrapper, static_cast<unsigned>(data.size()), data.data(), &blobId.id,
171 static_cast<unsigned>(preparedBpb.size()), preparedBpb.data());
172
173 return blobId;
174}
175
176void Batch::appendBlobData(std::span<const std::byte> data)
177{
178 assert(isValid());
179 handle->appendBlobData(&statusWrapper, static_cast<unsigned>(data.size()), data.data());
180}
181
182void Batch::addBlobStream(std::span<const std::byte> data)
183{
184 assert(isValid());
185 handle->addBlobStream(&statusWrapper, static_cast<unsigned>(data.size()), data.data());
186}
187
189{
190 assert(isValid());
191
192 BlobId batchId;
193 handle->registerBlob(&statusWrapper, &existingBlob.id, &batchId.id);
194
195 return batchId;
196}
197
199{
200 assert(isValid());
201
202 const auto preparedBpb = prepareBpb(*client, bpb);
203 handle->setDefaultBpb(&statusWrapper, static_cast<unsigned>(preparedBpb.size()), preparedBpb.data());
204}
205
207{
208 assert(isValid());
209 return handle->getBlobAlignment(&statusWrapper);
210}
211
212
213// --- Execution ---
214
216{
217 assert(isValid());
218
219 auto completionState = fbUnique(handle->execute(&statusWrapper, transaction->getHandle().get()));
220
221 return BatchCompletionState{*client, std::move(completionState)};
222}
223
225{
226 assert(isValid());
227 handle->cancel(&statusWrapper);
228 handle.reset();
229}
230
232{
233 assert(isValid());
234 handle->close(&statusWrapper);
235 handle.reset();
236}
237
239{
240 assert(isValid());
241
243 metadata.reset(handle->getMetadata(&statusWrapper));
244
245 return metadata;
246}
247
248const std::vector<Descriptor>& Batch::getInputDescriptors()
249{
250 assert(isValid());
251
252 if (inputDescriptors.empty())
253 buildInputDescriptors();
254
255 return inputDescriptors;
256}
257
258
259// --- Internal helpers ---
260
261std::vector<std::uint8_t> Batch::buildParametersBlock(Client& client, const BatchOptions& options)
262{
263 auto builder = fbUnique(client.getUtil()->getXpbBuilder(&statusWrapper, fb::IXpbBuilder::BATCH, nullptr, 0));
264
265 if (options.getMultiError())
266 builder->insertInt(&statusWrapper, fb::IBatch::TAG_MULTIERROR, 1);
267
268 if (options.getRecordCounts())
269 builder->insertInt(&statusWrapper, fb::IBatch::TAG_RECORD_COUNTS, 1);
270
271 if (const auto bufferSize = options.getBufferBytesSize(); bufferSize.has_value())
272 builder->insertInt(&statusWrapper, fb::IBatch::TAG_BUFFER_BYTES_SIZE, static_cast<int>(bufferSize.value()));
273
274 if (options.getBlobPolicy() != BlobPolicy::NONE)
275 builder->insertInt(&statusWrapper, fb::IBatch::TAG_BLOB_POLICY, static_cast<int>(options.getBlobPolicy()));
276
277 if (options.getDetailedErrors() != 64)
278 {
279 builder->insertInt(
280 &statusWrapper, fb::IBatch::TAG_DETAILED_ERRORS, static_cast<int>(options.getDetailedErrors()));
281 }
282
283 const auto buffer = builder->getBuffer(&statusWrapper);
284 const auto length = builder->getBufferLength(&statusWrapper);
285
286 return {buffer, buffer + length};
287}
288
289std::vector<std::uint8_t> Batch::prepareBpb(Client& client, const BlobOptions& bpb)
290{
291 auto builder = fbUnique(client.getUtil()->getXpbBuilder(&statusWrapper, fb::IXpbBuilder::BPB,
292 reinterpret_cast<const std::uint8_t*>(bpb.getBpb().data()), static_cast<unsigned>(bpb.getBpb().size())));
293
294 if (const auto type = bpb.getType(); type.has_value())
295 builder->insertInt(&statusWrapper, isc_bpb_type, static_cast<int>(type.value()));
296
297 if (const auto storage = bpb.getStorage(); storage.has_value())
298 builder->insertInt(&statusWrapper, isc_bpb_storage, static_cast<int>(storage.value()));
299
300 const auto buffer = builder->getBuffer(&statusWrapper);
301 const auto length = builder->getBufferLength(&statusWrapper);
302
303 return {buffer, buffer + length};
304}
305
306void Batch::buildInputDescriptors()
307{
308 auto metadata = getInputMetadata();
309 const auto count = metadata->getCount(&statusWrapper);
310
311 inputDescriptors.reserve(count);
312
313 for (unsigned index = 0u; index < count; ++index)
314 {
315 inputDescriptors.push_back(Descriptor{
316 .originalType = static_cast<DescriptorOriginalType>(metadata->getType(&statusWrapper, index)),
317 .adjustedType = static_cast<DescriptorAdjustedType>(metadata->getType(&statusWrapper, index)),
318 .scale = metadata->getScale(&statusWrapper, index),
319 .length = metadata->getLength(&statusWrapper, index),
320 .offset = metadata->getOffset(&statusWrapper, index),
321 .nullOffset = metadata->getNullOffset(&statusWrapper, index),
322 .isNullable = static_cast<bool>(metadata->isNullable(&statusWrapper, index)),
323 .name = metadata->getField(&statusWrapper, index),
324 .relation = metadata->getRelation(&statusWrapper, index),
325 .alias = metadata->getAlias(&statusWrapper, index),
326 .owner = metadata->getOwner(&statusWrapper, index),
327 .charSetId = metadata->getCharSet(&statusWrapper, index),
328 .subType = metadata->getSubType(&statusWrapper, index),
329 });
330 }
331}
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
Wraps IBatchCompletionState to provide RAII-safe access to batch execution results.
Definition Batch.h:186
std::optional< unsigned > findError(unsigned pos)
Finds the next error at or after the given position.
Definition Batch.cpp:63
int getState(unsigned pos)
Returns the per-message result at the given position.
Definition Batch.cpp:58
std::vector< std::intptr_t > getStatus(unsigned pos)
Returns the detailed error status vector for the given position.
Definition Batch.cpp:73
BatchCompletionState(Client &client, FbUniquePtr< fb::IBatchCompletionState > handle) noexcept
Constructs a BatchCompletionState from a Firebird completion state handle.
Definition Batch.cpp:37
unsigned getSize()
Returns the number of messages processed.
Definition Batch.cpp:53
Configuration options for creating a Batch.
Definition Batch.h:85
std::optional< unsigned > getBufferBytesSize() const
Returns the batch buffer size in bytes, or nullopt for the server default.
Definition Batch.h:124
bool getRecordCounts() const
Returns whether per-message affected row counts are reported.
Definition Batch.h:107
unsigned getDetailedErrors() const
Returns the maximum number of detailed error statuses to collect.
Definition Batch.h:158
BlobPolicy getBlobPolicy() const
Returns the blob handling policy.
Definition Batch.h:141
bool getMultiError() const
Returns whether multiple errors are collected per execution.
Definition Batch.h:90
Wraps the Firebird IBatch interface for bulk DML operations.
Definition Batch.h:275
void close()
Closes the batch handle and releases resources.
Definition Batch.cpp:231
void addBlobStream(std::span< const std::byte > data)
Adds blob data in stream mode (BlobPolicy::STREAM only).
Definition Batch.cpp:182
bool isValid() const noexcept
Returns whether the batch handle is valid.
Definition Batch.h:332
void appendBlobData(std::span< const std::byte > data)
Appends more data to the last blob added with addBlob().
Definition Batch.cpp:176
void addMessage()
Adds the Statement's current input-message buffer as one message.
Definition Batch.cpp:153
void add(unsigned count, const void *inBuffer)
Adds one or more raw messages to the batch buffer.
Definition Batch.cpp:147
const std::vector< Descriptor > & getInputDescriptors()
Returns cached input parameter descriptors for this batch.
Definition Batch.cpp:248
FbRef< fb::IMessageMetadata > getInputMetadata()
Returns the input metadata for this batch.
Definition Batch.cpp:238
BlobId registerBlob(const BlobId &existingBlob)
Registers an existing blob (created via the normal Blob class) for use in the batch,...
Definition Batch.cpp:188
void setDefaultBpb(const BlobOptions &bpb)
Sets the default BPB (Blob Parameter Block) for blobs in this batch.
Definition Batch.cpp:198
BlobId addBlob(std::span< const std::byte > data, const BlobOptions &bpb={})
Adds an inline blob and returns its batch-local ID.
Definition Batch.cpp:163
BatchCompletionState execute()
Executes all queued messages and returns the completion state.
Definition Batch.cpp:215
Batch(Statement &statement, Transaction &transaction, const BatchOptions &options={})
Creates a Batch from a prepared Statement.
Definition Batch.cpp:101
void cancel()
Cancels the batch, discarding all queued messages.
Definition Batch.cpp:224
unsigned getBlobAlignment()
Returns the blob alignment requirement for this batch.
Definition Batch.cpp:206
Represents a Firebird blob identifier.
Definition Blob.h:52
ISC_QUAD id
Stores the raw Firebird blob identifier value.
Definition Blob.h:64
Additional options used when creating or opening blobs.
Definition Blob.h:103
const std::optional< BlobType > getType() const
Retrieves the blob type to be used for blob operations.
Definition Blob.h:134
const std::optional< BlobStorage > getStorage() const
Retrieves the blob storage mode.
Definition Blob.h:219
const std::vector< std::uint8_t > & getBpb() const noexcept
Retrieves the blob parameter block (BPB) used during blob operations.
Definition Blob.h:108
Represents a Firebird client library instance.
Definition Client.h:53
fb::IUtil * getUtil()
Returns a Firebird IUtil interface.
Definition Client.h:144
FbUniquePtr< fb::IStatus > newStatus()
Creates and returns a Firebird IStatus instance.
Definition Client.h:199
Reference-counted smart pointer for Firebird objects using addRef/release semantics.
Definition SmartPtrs.h:70
Prepares, executes, and fetches SQL statements against a Firebird attachment.
Definition Statement.h:261
bool isValid() noexcept
Returns whether the Statement object is valid.
Definition Statement.h:364
FbRef< fb::IStatement > getStatementHandle() noexcept
Provides direct access to the underlying Firebird statement handle.
Definition Statement.h:373
FbRef< fb::IMessageMetadata > getInputMetadata() noexcept
Returns the metadata describing prepared input parameters.
Definition Statement.h:390
std::vector< std::byte > & getInputMessage() noexcept
Provides direct access to the raw input message buffer.
Definition Statement.h:398
Represents a transaction in one or more Firebird databases.
bool isValid() noexcept
Returns whether the Transaction object is valid.
FbRef< fb::ITransaction > getHandle() noexcept
Returns the internal Firebird ITransaction handle.
fb-cpp namespace.
Definition Attachment.h:42
DescriptorAdjustedType
Descriptor adjusted type.
Definition Descriptor.h:147
DescriptorOriginalType
Descriptor original type.
Definition Descriptor.h:41
FbUniquePtr< T > fbUnique(T *obj) noexcept
Creates a unique pointer for a Firebird disposable object.
Definition SmartPtrs.h:59
@ NONE
Blobs are not allowed in the batch.
std::unique_ptr< T, impl::FbDisposeDeleter > FbUniquePtr
Unique pointer type for Firebird disposable objects.
Definition SmartPtrs.h:53
Describes a parameter or column.
Definition Descriptor.h:248
DescriptorOriginalType originalType
Original SQL type as reported by Firebird.
Definition Descriptor.h:252