fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Blob.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 do so, subject to the
11 * 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 "Blob.h"
26#include "Attachment.h"
27#include "Client.h"
28#include "Transaction.h"
29#include "firebird/impl/inf_pub.h"
30#include <cstdint>
31#include <cstring>
32#include <limits>
33#include <vector>
34
35using namespace fbcpp;
36using namespace fbcpp::impl;
37
38Blob::Blob(Attachment& attachment, Transaction& transaction, const BlobOptions& options)
39 : attachment{attachment},
40 transaction{transaction},
41 statusWrapper{attachment.getClient()}
42{
43 assert(attachment.isValid());
44 assert(transaction.isValid());
45
46 const auto preparedBpb = prepareBpb(options);
47
48 handle.reset(attachment.getHandle()->createBlob(&statusWrapper, transaction.getHandle().get(), &id.id,
49 static_cast<unsigned>(preparedBpb.size()), preparedBpb.data()));
50}
51
52Blob::Blob(Attachment& attachment, Transaction& transaction, const BlobId& blobId, const BlobOptions& options)
53 : attachment{attachment},
54 transaction{transaction},
55 id{blobId},
56 statusWrapper{attachment.getClient()}
57{
58 assert(attachment.isValid());
59 assert(transaction.isValid());
60
61 const auto preparedBpb = prepareBpb(options);
62
63 handle.reset(attachment.getHandle()->openBlob(&statusWrapper, transaction.getHandle().get(), &id.id,
64 static_cast<unsigned>(preparedBpb.size()), preparedBpb.data()));
65}
66
67std::vector<std::uint8_t> Blob::prepareBpb(const BlobOptions& options)
68{
69 const auto util = attachment.getClient().getUtil();
70
71 auto builder = fbUnique(util->getXpbBuilder(&statusWrapper, fb::IXpbBuilder::BPB,
72 reinterpret_cast<const std::uint8_t*>(options.getBpb().data()),
73 static_cast<unsigned>(options.getBpb().size())));
74
75 if (const auto type = options.getType(); type.has_value())
76 builder->insertInt(&statusWrapper, isc_bpb_type, static_cast<int>(type.value()));
77
78 if (const auto sourceType = options.getSourceType(); sourceType.has_value())
79 builder->insertInt(&statusWrapper, isc_bpb_source_type, static_cast<int>(sourceType.value()));
80
81 if (const auto targetType = options.getTargetType(); targetType.has_value())
82 builder->insertInt(&statusWrapper, isc_bpb_target_type, static_cast<int>(targetType.value()));
83
84 if (const auto sourceCharSet = options.getSourceCharSet(); sourceCharSet.has_value())
85 builder->insertInt(&statusWrapper, isc_bpb_source_interp, static_cast<int>(sourceCharSet.value()));
86
87 if (const auto targetCharSet = options.getTargetCharSet(); targetCharSet.has_value())
88 builder->insertInt(&statusWrapper, isc_bpb_target_interp, static_cast<int>(targetCharSet.value()));
89
90 if (const auto storage = options.getStorage(); storage.has_value())
91 builder->insertInt(&statusWrapper, isc_bpb_storage, static_cast<int>(storage.value()));
92
93 const auto buffer = builder->getBuffer(&statusWrapper);
94 const auto length = builder->getBufferLength(&statusWrapper);
95
96 std::vector<std::uint8_t> bpb(length);
97
98 if (length != 0)
99 std::memcpy(bpb.data(), buffer, length);
100
101 return bpb;
102}
103
105{
106 assert(isValid());
107
108 const std::uint8_t items[] = {isc_info_blob_total_length};
109 std::uint8_t buffer[16]{};
110
111 handle->getInfo(&statusWrapper, sizeof(items), items, sizeof(buffer), buffer);
112
113 const auto* ptr = buffer;
114 const auto* end = buffer + sizeof(buffer);
115
116 while (ptr < end)
117 {
118 const auto item = *ptr++;
119
120 if (item == isc_info_end)
121 break;
122
123 if (item == isc_info_truncated)
124 throw FbCppException("Blob::getLength truncated response");
125
126 if (item == isc_info_error)
127 throw FbCppException("Blob::getLength error response");
128
129 if (ptr + 2 > end)
130 throw FbCppException("Blob::getLength malformed response");
131
132 const auto itemLength = static_cast<std::uint16_t>((ptr[0]) | (ptr[1] << 8));
133 ptr += 2;
134
135 if (ptr + itemLength > end)
136 throw FbCppException("Blob::getLength invalid length");
137
138 if (item == isc_info_blob_total_length)
139 {
140 unsigned result = 0;
141
142 for (std::uint16_t i = 0; i < itemLength; ++i)
143 result |= static_cast<unsigned>(ptr[i]) << (8u * i);
144
145 return result;
146 }
147
148 ptr += itemLength;
149 }
150
151 throw FbCppException("Blob::getLength value not found");
152}
153
154unsigned Blob::read(std::span<std::byte> buffer)
155{
156 assert(isValid());
157
158 unsigned totalRead = 0;
159 const unsigned maxChunkSize = std::numeric_limits<std::uint16_t>::max();
160
161 while (!buffer.empty())
162 {
163 const auto chunkSize = buffer.size() < maxChunkSize ? buffer.size() : maxChunkSize;
164 const auto chunk = buffer.first(chunkSize);
165 const auto readNow = readSegment(chunk);
166
167 if (readNow == 0)
168 break;
169
170 totalRead += readNow;
171 buffer = buffer.subspan(readNow);
172 }
173
174 return totalRead;
175}
176
177unsigned Blob::readSegment(std::span<std::byte> buffer)
178{
179 assert(isValid());
180
181 if (buffer.empty())
182 return 0;
183
184 unsigned segmentLength = 0;
185 const auto result =
186 handle->getSegment(&statusWrapper, static_cast<unsigned>(buffer.size()), buffer.data(), &segmentLength);
187
188 switch (result)
189 {
190 case fb::IStatus::RESULT_OK:
191 case fb::IStatus::RESULT_SEGMENT:
192 return segmentLength;
193
194 case fb::IStatus::RESULT_NO_DATA:
195 return 0;
196
197 default:
198 return 0;
199 }
200}
201
202void Blob::write(std::span<const std::byte> buffer)
203{
204 assert(isValid());
205
206 const unsigned maxChunkSize = std::numeric_limits<std::uint16_t>::max();
207
208 while (!buffer.empty())
209 {
210 const auto chunkSize = buffer.size() < maxChunkSize ? buffer.size() : maxChunkSize;
211 const auto chunk = buffer.first(chunkSize);
212 writeSegment(chunk);
213 buffer = buffer.subspan(chunkSize);
214 }
215}
216
217void Blob::writeSegment(std::span<const std::byte> buffer)
218{
219 assert(isValid());
220
221 if (buffer.empty())
222 return;
223
224 if (buffer.size() > std::numeric_limits<unsigned>::max())
225 throw FbCppException("Segment too large");
226
227 handle->putSegment(&statusWrapper, static_cast<unsigned>(buffer.size()), buffer.data());
228}
229
230int Blob::seek(BlobSeekMode mode, int offset)
231{
232 assert(isValid());
233 return handle->seek(&statusWrapper, static_cast<int>(mode), offset);
234}
235
237{
238 assert(isValid());
239
240 handle->cancel(&statusWrapper);
241 handle.reset();
242}
243
245{
246 assert(isValid());
247
248 handle->close(&statusWrapper);
249 handle.reset();
250}
Represents a connection to a Firebird database.
Definition Attachment.h:213
Client & getClient() noexcept
Returns the Client object reference used to create this Attachment object.
Definition Attachment.h:281
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
Represents a Firebird blob identifier.
Definition Blob.h:52
Additional options used when creating or opening blobs.
Definition Blob.h:103
const std::optional< BlobType > getSourceType() const
Retrieves the source blob subtype.
Definition Blob.h:151
const std::optional< BlobType > getType() const
Retrieves the blob type to be used for blob operations.
Definition Blob.h:134
const std::optional< BlobType > getTargetType() const
Retrieves the target blob subtype.
Definition Blob.h:168
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
const std::optional< std::int16_t > getTargetCharSet() const
Retrieves the target character set identifier.
Definition Blob.h:202
const std::optional< std::int16_t > getSourceCharSet() const
Retrieves the source character set identifier.
Definition Blob.h:185
bool isValid() const noexcept
Returns whether the blob handle is valid.
Definition Blob.h:329
unsigned read(std::span< std::byte > buffer)
Reads data from the blob into the provided buffer.
Definition Blob.cpp:154
unsigned getLength()
Retrieves the length of the blob in bytes.
Definition Blob.cpp:104
void cancel()
Cancels any changes performed on the blob and releases the handle.
Definition Blob.cpp:236
void write(std::span< const std::byte > buffer)
Writes data from the buffer into the blob.
Definition Blob.cpp:202
int seek(BlobSeekMode mode, int offset)
Repositions the blob read/write cursor.
Definition Blob.cpp:230
unsigned readSegment(std::span< std::byte > buffer)
Reads a single segment from the blob into the provided buffer.
Definition Blob.cpp:177
Blob(Attachment &attachment, Transaction &transaction, const BlobOptions &options={})
Creates and opens a new blob for writing.
Definition Blob.cpp:38
void writeSegment(std::span< const std::byte > buffer)
Writes a single segment from the buffer into the blob.
Definition Blob.cpp:217
void close()
Closes the blob and finalizes any pending changes.
Definition Blob.cpp:244
fb::IUtil * getUtil()
Returns a Firebird IUtil interface.
Definition Client.h:144
Base exception class for all fb-cpp exceptions.
Definition Exception.h:230
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
BlobSeekMode
Defines the origin used when repositioning a blob.
Definition Blob.h:247
FbUniquePtr< T > fbUnique(T *obj) noexcept
Creates a unique pointer for a Firebird disposable object.
Definition SmartPtrs.h:59