fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Statement.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
14 * all 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 "Statement.h"
26#include "Transaction.h"
27#include "Attachment.h"
28#include "Client.h"
29
30using namespace fbcpp;
31using namespace fbcpp::impl;
32
33
35 Attachment& attachment, Transaction& transaction, std::string_view sql, const StatementOptions& options)
36 : attachment{&attachment},
37 statusWrapper{attachment.getClient()},
38 calendarConverter{attachment.getClient()},
39 numericConverter{attachment.getClient()}
40{
41 assert(attachment.isValid());
42 assert(transaction.isValid());
43
44 unsigned flags = fb::IStatement::PREPARE_PREFETCH_METADATA;
45
46 if (options.getPrefetchLegacyPlan())
47 flags |= fb::IStatement::PREPARE_PREFETCH_LEGACY_PLAN;
48
49 if (options.getPrefetchPlan())
50 flags |= fb::IStatement::PREPARE_PREFETCH_DETAILED_PLAN;
51
52 statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(),
53 static_cast<unsigned>(sql.length()), sql.data(), options.getDialect(), flags));
54
55 if (options.getCursorName().has_value())
56 statementHandle->setCursorName(&statusWrapper, options.getCursorName()->c_str());
57
58 if (options.getCursorType() == CursorType::SCROLLABLE)
59 cursorFlags = fb::IStatement::CURSOR_TYPE_SCROLLABLE;
60
61 type = static_cast<StatementType>(statementHandle->getType(&statusWrapper));
62
63 switch (type)
64 {
66 free();
67 throw FbCppException("Cannot use SET TRANSACTION command with Statement class. Use Transaction class");
68
70 free();
71 throw FbCppException(
72 "Cannot use COMMIT command with Statement class. Use the commit method from the Transaction class");
73
75 free();
76 throw FbCppException(
77 "Cannot use ROLLBACK command with Statement class. Use the rollback method from the Transaction class");
78
81 throw FbCppException("Unsupported statement type: BLOB segment operations");
82
83 default:
84 break;
85 }
86
87 const auto processMetadata = [&](FbRef<fb::IMessageMetadata>& metadata, std::vector<Descriptor>& descriptors,
88 std::vector<std::byte>& message)
89 {
90 if (!metadata)
91 return;
92
93 message.resize(metadata->getMessageLength(&statusWrapper));
94
96
97 const auto count = metadata->getCount(&statusWrapper);
98 descriptors.reserve(count);
99
100 for (unsigned index = 0u; index < count; ++index)
101 {
103 .originalType = static_cast<DescriptorOriginalType>(metadata->getType(&statusWrapper, index)),
104 .adjustedType = static_cast<DescriptorAdjustedType>(metadata->getType(&statusWrapper, index)),
105 .scale = metadata->getScale(&statusWrapper, index),
106 .length = metadata->getLength(&statusWrapper, index),
107 .offset = 0,
108 .nullOffset = 0,
109 .isNullable = static_cast<bool>(metadata->isNullable(&statusWrapper, index)),
110 .name = metadata->getField(&statusWrapper, index),
111 .relation = metadata->getRelation(&statusWrapper, index),
112 .alias = metadata->getAlias(&statusWrapper, index),
113 .owner = metadata->getOwner(&statusWrapper, index),
114 .charSetId = metadata->getCharSet(&statusWrapper, index),
115 .subType = metadata->getSubType(&statusWrapper, index),
116 };
117
118 switch (descriptor.originalType)
119 {
121 if (!builder)
122 builder.reset(metadata->getBuilder(&statusWrapper));
123
124 builder->setType(&statusWrapper, index, SQL_VARYING);
126 break;
127
129 if (!builder)
130 builder.reset(metadata->getBuilder(&statusWrapper));
131
132 builder->setType(&statusWrapper, index, SQL_TIME_TZ);
134 break;
135
137 if (!builder)
138 builder.reset(metadata->getBuilder(&statusWrapper));
139
140 builder->setType(&statusWrapper, index, SQL_TIMESTAMP_TZ);
142 break;
143
144 default:
145 break;
146 }
147
148 if (!builder)
149 {
150 descriptor.offset = metadata->getOffset(&statusWrapper, index);
151 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
152
153 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
154 }
155
156 descriptors.push_back(descriptor);
157 }
158
159 if (builder)
160 {
161 metadata.reset(builder->getMetadata(&statusWrapper));
162 message.resize(metadata->getMessageLength(&statusWrapper));
163
164 for (unsigned index = 0u; index < count; ++index)
165 {
166 auto& descriptor = descriptors[index];
167 descriptor.offset = metadata->getOffset(&statusWrapper, index);
168 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
169
170 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
171 }
172 }
173 };
174
175 inMetadata.reset(statementHandle->getInputMetadata(&statusWrapper));
176 processMetadata(inMetadata, inDescriptors, inMessage);
177
178 outMetadata.reset(statementHandle->getOutputMetadata(&statusWrapper));
179 processMetadata(outMetadata, outDescriptors, outMessage);
180
181 outRow = std::make_unique<Row>(attachment.getClient(), outDescriptors, std::span{outMessage});
182}
183
185 : attachment{o.attachment},
186 statusWrapper{std::move(o.statusWrapper)},
187 calendarConverter{std::move(o.calendarConverter)},
188 numericConverter{std::move(o.numericConverter)},
189 statementHandle{std::move(o.statementHandle)},
190 resultSetHandle{std::move(o.resultSetHandle)},
191 inMetadata{std::move(o.inMetadata)},
192 inDescriptors{std::move(o.inDescriptors)},
193 inMessage{std::move(o.inMessage)},
194 outMetadata{std::move(o.outMetadata)},
195 outDescriptors{std::move(o.outDescriptors)},
196 outMessage{std::move(o.outMessage)},
197 outRow{std::make_unique<Row>(attachment->getClient(), outDescriptors, std::span{outMessage})},
198 type{o.type},
199 cursorFlags{o.cursorFlags}
200{
201 o.outRow.reset();
202}
203
205{
206 if (this != &o)
207 {
208 attachment = o.attachment;
209 statusWrapper = std::move(o.statusWrapper);
210 calendarConverter = std::move(o.calendarConverter);
211 numericConverter = std::move(o.numericConverter);
212 statementHandle = std::move(o.statementHandle);
213 resultSetHandle = std::move(o.resultSetHandle);
214 inMetadata = std::move(o.inMetadata);
215 inDescriptors = std::move(o.inDescriptors);
216 inMessage = std::move(o.inMessage);
217 outMetadata = std::move(o.outMetadata);
218 outDescriptors = std::move(o.outDescriptors);
219 outMessage = std::move(o.outMessage);
220 outRow = std::make_unique<Row>(attachment->getClient(), outDescriptors, std::span{outMessage});
221 type = o.type;
222 cursorFlags = o.cursorFlags;
223
224 o.outRow.reset();
225 }
226
227 return *this;
228}
229
231{
232 assert(isValid());
233
234 if (resultSetHandle)
235 {
236 resultSetHandle->close(&statusWrapper);
237 resultSetHandle.reset();
238 }
239
240 statementHandle->free(&statusWrapper);
241 statementHandle.reset();
242}
243
245{
246 assert(isValid());
247
248 return statementHandle->getPlan(&statusWrapper, false);
249}
250
252{
253 assert(isValid());
254
255 return statementHandle->getPlan(&statusWrapper, true);
256}
257
259{
260 assert(isValid());
261 assert(transaction.isValid());
262
263 if (resultSetHandle)
264 {
265 resultSetHandle->close(&statusWrapper);
266 resultSetHandle.reset();
267 }
268
269 const auto outMessageData = outMessage.data();
270
271 if (outMessageData)
272 {
273 for (const auto& descriptor : outDescriptors)
274 *reinterpret_cast<std::int16_t*>(&outMessageData[descriptor.nullOffset]) = FB_TRUE;
275 }
276
277 switch (type)
278 {
281 resultSetHandle.reset(statementHandle->openCursor(&statusWrapper, transaction.getHandle().get(),
282 inMetadata.get(), inMessage.data(), outMetadata.get(), cursorFlags));
283 return resultSetHandle->fetchNext(&statusWrapper, outMessageData) == fb::IStatus::RESULT_OK;
284
285 default:
286 statementHandle->execute(&statusWrapper, transaction.getHandle().get(), inMetadata.get(), inMessage.data(),
287 outMetadata.get(), outMessageData);
288 return true;
289 }
290}
291
292Client& Statement::getClient() noexcept
293{
294 return attachment->getClient();
295}
296
298{
299 assert(isValid());
300
301 return resultSetHandle && resultSetHandle->fetchNext(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
302}
303
305{
306 assert(isValid());
307
308 return resultSetHandle && resultSetHandle->fetchPrior(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
309}
310
312{
313 assert(isValid());
314
315 return resultSetHandle && resultSetHandle->fetchFirst(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
316}
317
319{
320 assert(isValid());
321
322 return resultSetHandle && resultSetHandle->fetchLast(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
323}
324
325bool Statement::fetchAbsolute(unsigned position)
326{
327 assert(isValid());
328
329 return resultSetHandle &&
330 resultSetHandle->fetchAbsolute(&statusWrapper, static_cast<int>(position), outMessage.data()) ==
331 fb::IStatus::RESULT_OK;
332}
333
335{
336 assert(isValid());
337
338 return resultSetHandle &&
339 resultSetHandle->fetchRelative(&statusWrapper, offset, outMessage.data()) == fb::IStatus::RESULT_OK;
340}
Represents a connection to a Firebird database.
Definition Attachment.h:218
Client & getClient() noexcept
Returns the Client object reference used to create this Attachment object.
Definition Attachment.h:286
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:294
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:278
Represents a Firebird client library instance.
Definition Client.h:53
Base exception class for all fb-cpp exceptions.
Definition Exception.h:230
Reference-counted smart pointer for Firebird objects using addRef/release semantics.
Definition SmartPtrs.h:70
Represents options used when preparing a Statement.
Prepares, executes, and fetches SQL statements against a Firebird attachment.
Definition Statement.h:138
bool fetchAbsolute(unsigned position)
Positions the cursor on the given absolute row number.
bool fetchRelative(int offset)
Moves the cursor by the requested relative offset.
std::string getLegacyPlan()
Retrieves the textual legacy plan if the server produced one.
void free()
Releases the prepared handle and any associated result set.
bool isValid() noexcept
Returns whether the Statement object is valid.
Definition Statement.h:202
T get()
Retrieves all output columns into a user-defined aggregate struct.
Definition Statement.h:1734
Statement & operator=(Statement &&o) noexcept
Transfers ownership of another prepared statement into this one.
std::string getPlan()
Retrieves the structured textual plan if the server produced one.
bool fetchNext()
Fetches the next row in the current result set.
bool fetchLast()
Positions the cursor on the last row.
bool execute(Transaction &transaction)
Executes a prepared statement using the supplied transaction.
Statement(Attachment &attachment, Transaction &transaction, std::string_view sql, const StatementOptions &options={})
Prepares an SQL statement.
Definition Statement.cpp:34
bool fetchPrior()
Fetches the previous row in the current result set.
bool fetchFirst()
Positions the cursor on the first row.
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:45
@ SCROLLABLE
Allows bidirectional traversal and absolute/relative positioning.
DescriptorAdjustedType
Descriptor adjusted type.
Definition Descriptor.h:147
@ TIME_TZ
Time of day with time zone.
@ STRING
String type (variable-length).
@ TIMESTAMP_TZ
Timestamp with time zone.
StatementType
Distinguishes the semantic category of the prepared SQL statement.
Definition Statement.h:75
@ PUT_SEGMENT
Statement writes a blob segment - legacy feature.
@ COMMIT
Statement commits a transaction.
@ ROLLBACK
Statement rolls back a transaction.
@ GET_SEGMENT
Statement reads a blob segment - legacy feature.
@ SELECT
Server classified the statement as a SELECT.
@ START_TRANSACTION
Statement starts a new transaction.
@ SELECT_FOR_UPDATE
Cursor-based SELECT that allows updates.
DescriptorOriginalType
Descriptor original type.
Definition Descriptor.h:41
@ TIMESTAMP_TZ_EX
Extended timestamp with time zone.
@ TEXT
Fixed-length text.
@ TIME_TZ_EX
Extended time of day with time zone.
Describes a parameter or column.
Definition Descriptor.h:248
DescriptorOriginalType originalType
Original SQL type as reported by Firebird.
Definition Descriptor.h:252