fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Array.cpp
1/*
2 * MIT License
3 *
4 * Copyright (c) 2026 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 "Array.h"
26#include "Attachment.h"
27#include "Exception.h"
28#include "Transaction.h"
29#include "firebird/impl/blr.h"
30#include <array>
31#include <cassert>
32#include <cstdint>
33#include <cstring>
34#include <limits>
35
36using namespace fbcpp;
37using namespace fbcpp::impl;
38
39
40static unsigned char arrayElementType(DescriptorOriginalType type)
41{
42 switch (type)
43 {
44 case DescriptorOriginalType::TEXT:
45 return blr_text;
46
47 case DescriptorOriginalType::VARYING:
48 return blr_varying;
49
50 case DescriptorOriginalType::SHORT:
51 return blr_short;
52
53 case DescriptorOriginalType::LONG:
54 return blr_long;
55
56 case DescriptorOriginalType::FLOAT:
57 return blr_float;
58
59 case DescriptorOriginalType::DOUBLE:
60 return blr_double;
61
62 case DescriptorOriginalType::TIMESTAMP:
63 return blr_timestamp;
64
65 case DescriptorOriginalType::TIME:
66 return blr_sql_time;
67
68 case DescriptorOriginalType::DATE:
69 return blr_sql_date;
70
71 case DescriptorOriginalType::INT64:
72 return blr_int64;
73
74 case DescriptorOriginalType::TIMESTAMP_TZ:
75 return blr_timestamp_tz;
76
77 case DescriptorOriginalType::TIMESTAMP_TZ_EX:
78 return blr_ex_timestamp_tz;
79
80 case DescriptorOriginalType::TIME_TZ:
81 return blr_sql_time_tz;
82
83 case DescriptorOriginalType::TIME_TZ_EX:
84 return blr_ex_time_tz;
85
86 case DescriptorOriginalType::INT128:
87 return blr_int128;
88
89 case DescriptorOriginalType::DEC16:
90 return blr_dec64;
91
92 case DescriptorOriginalType::DEC34:
93 return blr_dec128;
94
95 case DescriptorOriginalType::BOOLEAN:
96 return blr_bool;
97
98 default:
99 throw FbCppException("Unsupported Firebird array element type");
100 }
101}
102
103
104Array::Array(Attachment& attachment, Transaction& transaction, ArrayDescriptor descriptor, ArrayId id)
105 : attachment{attachment},
106 transaction{transaction},
107 descriptor{std::move(descriptor)},
108 id{id}
109{
110 assert(attachment.isValid());
111 assert(transaction.isValid());
112 buildIscDescriptor();
113}
114
115std::size_t Array::getSliceLength() const
116{
117 std::size_t result = descriptor.elementLength;
118
119 for (const auto& bound : descriptor.bounds)
120 {
121 const auto length = static_cast<unsigned>(bound.upper) - static_cast<unsigned>(bound.lower) + 1u;
122
123 if (result > std::numeric_limits<std::size_t>::max() / length)
124 throw FbCppException("Firebird array slice is too large");
125
126 result *= length;
127 }
128
129 return result;
130}
131
132unsigned Array::read(std::span<std::byte> buffer)
133{
134 validateBuffer(buffer.size());
135
136 std::array<ISC_UCHAR, 1024> sdl{};
137 ISC_SHORT sdlBufferLength = static_cast<ISC_SHORT>(sdl.size());
138 ISC_SHORT sdlLength = 0;
139 const auto iscDescriptor = buildIscDescriptor();
140 std::array<ISC_STATUS, 20> statusVector{};
141 const auto status =
142 isc_array_gen_sdl(statusVector.data(), &iscDescriptor, &sdlBufferLength, sdl.data(), &sdlLength);
143
144 if (status != 0)
145 throw FbCppException("Cannot generate Firebird array SDL: " + std::to_string(status));
146
147 StatusWrapper statusWrapper{attachment.getClient()};
148 const auto result = attachment.getHandle()->getSlice(&statusWrapper, transaction.getHandle().get(), &id.id,
149 static_cast<unsigned>(sdlLength), sdl.data(), 0, nullptr, static_cast<int>(buffer.size()),
150 reinterpret_cast<unsigned char*>(buffer.data()));
151
152 return static_cast<unsigned>(result);
153}
154
155void Array::write(std::span<const std::byte> buffer)
156{
157 validateBuffer(buffer.size());
158
159 std::array<ISC_UCHAR, 1024> sdl{};
160 ISC_SHORT sdlBufferLength = static_cast<ISC_SHORT>(sdl.size());
161 ISC_SHORT sdlLength = 0;
162 const auto iscDescriptor = buildIscDescriptor();
163 std::array<ISC_STATUS, 20> statusVector{};
164 const auto status =
165 isc_array_gen_sdl(statusVector.data(), &iscDescriptor, &sdlBufferLength, sdl.data(), &sdlLength);
166
167 if (status != 0)
168 throw FbCppException("Cannot generate Firebird array SDL: " + std::to_string(status));
169
170 StatusWrapper statusWrapper{attachment.getClient()};
171 attachment.getHandle()->putSlice(&statusWrapper, transaction.getHandle().get(), &id.id,
172 static_cast<unsigned>(sdlLength), sdl.data(), 0, nullptr, static_cast<int>(buffer.size()),
173 reinterpret_cast<unsigned char*>(const_cast<std::byte*>(buffer.data())));
174}
175
176void Array::validateBuffer(std::size_t size) const
177{
178 if (size != getSliceLength())
179 throw FbCppException("Firebird array buffer size does not match the complete slice length");
180}
181
182ISC_ARRAY_DESC Array::buildIscDescriptor() const
183{
184 if (descriptor.bounds.empty() || descriptor.bounds.size() > 16u)
185 throw FbCppException("Firebird arrays must have between 1 and 16 dimensions");
186
187 if (descriptor.elementLength == 0 || descriptor.elementLength > std::numeric_limits<unsigned short>::max())
188 throw FbCppException("Invalid Firebird array element length");
189
190 if (descriptor.scale < std::numeric_limits<ISC_SCHAR>::min() ||
191 descriptor.scale > std::numeric_limits<ISC_SCHAR>::max())
192 {
193 throw FbCppException("Invalid Firebird array element scale");
194 }
195
196 ISC_ARRAY_DESC result{};
197
198 if (descriptor.relation.size() >= sizeof(result.array_desc_relation_name) ||
199 descriptor.field.size() >= sizeof(result.array_desc_field_name))
200 {
201 throw FbCppException("Firebird array relation or field name is too long");
202 }
203
204 std::memcpy(result.array_desc_relation_name, descriptor.relation.data(), descriptor.relation.size());
205 std::memcpy(result.array_desc_field_name, descriptor.field.data(), descriptor.field.size());
206 result.array_desc_dtype = arrayElementType(descriptor.elementType);
207 result.array_desc_scale = static_cast<ISC_SCHAR>(descriptor.scale);
208 result.array_desc_length = static_cast<unsigned short>(descriptor.elementLength);
209 result.array_desc_dimensions = static_cast<short>(descriptor.bounds.size());
210
211 for (std::size_t index = 0; index < descriptor.bounds.size(); ++index)
212 {
213 const auto& bound = descriptor.bounds[index];
214
215 if (bound.lower > bound.upper)
216 throw FbCppException("Firebird array lower bound exceeds upper bound");
217
218 result.array_desc_bounds[index] = {bound.lower, bound.upper};
219 }
220
221 return result;
222}
Represents a Firebird array identifier.
Definition Array.h:51
Array(Attachment &attachment, Transaction &transaction, ArrayDescriptor descriptor, ArrayId id={})
Creates an array helper for a new array or an existing array identifier.
Definition Array.cpp:104
std::size_t getSliceLength() const
Returns the exact number of bytes in a complete array slice.
Definition Array.cpp:115
unsigned read(std::span< std::byte > buffer)
Reads the complete array slice into Firebird-format bytes.
Definition Array.cpp:132
void write(std::span< const std::byte > buffer)
Writes the complete array slice from Firebird-format bytes.
Definition Array.cpp:155
Represents a connection to a Firebird database.
Definition Attachment.h:220
Client & getClient() noexcept
Returns the Client object reference used to create this Attachment object.
Definition Attachment.h:288
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:296
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:280
Base exception class for all fb-cpp exceptions.
Definition Exception.h:231
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 Array.h:43
DescriptorOriginalType
Descriptor original type.
Definition Descriptor.h:50
Defines the element representation and bounds of a Firebird array.
Definition Array.h:80
std::string field
Array field name; required when creating a new array.
Definition Array.h:85
int scale
Decimal scale of each array element.
Definition Array.h:91
DescriptorOriginalType elementType
SQL type of each array element.
Definition Array.h:88
unsigned elementLength
Size in bytes of each array element in Firebird's wire representation.
Definition Array.h:94
std::vector< ArrayBound > bounds
Inclusive bounds for each dimension, from outermost to innermost.
Definition Array.h:97
std::string relation
Relation containing this array field; required when creating a new array.
Definition Array.h:82