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 status{attachment.getClient().newStatus()},
38 statusWrapper{attachment.getClient(), status.get()},
39 calendarConverter{attachment.getClient(), &statusWrapper},
40 numericConverter{attachment.getClient(), &statusWrapper}
41{
42 assert(attachment.isValid());
43 assert(transaction.isValid());
44
45 unsigned flags = fb::IStatement::PREPARE_PREFETCH_METADATA;
46
47 if (options.getPrefetchLegacyPlan())
48 flags |= fb::IStatement::PREPARE_PREFETCH_LEGACY_PLAN;
49
50 if (options.getPrefetchPlan())
51 flags |= fb::IStatement::PREPARE_PREFETCH_DETAILED_PLAN;
52
53 statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(),
54 static_cast<unsigned>(sql.length()), sql.data(), SQL_DIALECT_CURRENT, flags));
55
56 if (options.getCursorName().has_value())
57 statementHandle->setCursorName(&statusWrapper, options.getCursorName()->c_str());
58
60 cursorFlags = fb::IStatement::CURSOR_TYPE_SCROLLABLE;
61
62 type = static_cast<StatementType>(statementHandle->getType(&statusWrapper));
63
64 switch (type)
65 {
67 free();
68 throw FbCppException("Cannot use SET TRANSACTION command with Statement class. Use Transaction class");
69
71 free();
72 throw FbCppException(
73 "Cannot use COMMIT command with Statement class. Use the commit method from the Transaction class");
74
76 free();
77 throw FbCppException(
78 "Cannot use ROLLBACK command with Statement class. Use the rollback method from the Transaction class");
79
82 throw FbCppException("Unsupported statement type: BLOB segment operations");
83
84 default:
85 break;
86 }
87
88 const auto processMetadata = [&](FbRef<fb::IMessageMetadata>& metadata, std::vector<Descriptor>& descriptors,
89 std::vector<std::byte>& message)
90 {
91 if (!metadata)
92 return;
93
94 message.resize(metadata->getMessageLength(&statusWrapper));
95
97
98 const auto count = metadata->getCount(&statusWrapper);
99 descriptors.reserve(count);
100
101 for (unsigned index = 0u; index < count; ++index)
102 {
103 Descriptor descriptor{
104 .originalType = static_cast<DescriptorOriginalType>(metadata->getType(&statusWrapper, index)),
105 .adjustedType = static_cast<DescriptorAdjustedType>(metadata->getType(&statusWrapper, index)),
106 .scale = metadata->getScale(&statusWrapper, index),
107 .length = metadata->getLength(&statusWrapper, index),
108 .offset = 0,
109 .nullOffset = 0,
110 .isNullable = static_cast<bool>(metadata->isNullable(&statusWrapper, index)),
111 .name = metadata->getField(&statusWrapper, index),
112 .relation = metadata->getRelation(&statusWrapper, index),
113 .alias = metadata->getAlias(&statusWrapper, index),
114 .owner = metadata->getOwner(&statusWrapper, index),
115 .charSetId = metadata->getCharSet(&statusWrapper, index),
116 .subType = metadata->getSubType(&statusWrapper, index),
117 };
118
119 switch (descriptor.originalType)
120 {
122 if (!builder)
123 builder.reset(metadata->getBuilder(&statusWrapper));
124
125 builder->setType(&statusWrapper, index, SQL_VARYING);
126 descriptor.adjustedType = DescriptorAdjustedType::STRING;
127 break;
128
130 if (!builder)
131 builder.reset(metadata->getBuilder(&statusWrapper));
132
133 builder->setType(&statusWrapper, index, SQL_TIME_TZ);
134 descriptor.adjustedType = DescriptorAdjustedType::TIME_TZ;
135 break;
136
138 if (!builder)
139 builder.reset(metadata->getBuilder(&statusWrapper));
140
141 builder->setType(&statusWrapper, index, SQL_TIMESTAMP_TZ);
142 descriptor.adjustedType = DescriptorAdjustedType::TIMESTAMP_TZ;
143 break;
144
145 default:
146 break;
147 }
148
149 if (!builder)
150 {
151 descriptor.offset = metadata->getOffset(&statusWrapper, index);
152 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
153
154 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
155 }
156
157 descriptors.push_back(descriptor);
158 }
159
160 if (builder)
161 {
162 metadata.reset(builder->getMetadata(&statusWrapper));
163 message.resize(metadata->getMessageLength(&statusWrapper));
164
165 for (unsigned index = 0u; index < count; ++index)
166 {
167 auto& descriptor = descriptors[index];
168 descriptor.offset = metadata->getOffset(&statusWrapper, index);
169 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
170
171 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
172 }
173 }
174 };
175
176 inMetadata.reset(statementHandle->getInputMetadata(&statusWrapper));
177 processMetadata(inMetadata, inDescriptors, inMessage);
178
179 outMetadata.reset(statementHandle->getOutputMetadata(&statusWrapper));
180 processMetadata(outMetadata, outDescriptors, outMessage);
181}
182
184{
185 assert(isValid());
186
187 if (resultSetHandle)
188 {
189 resultSetHandle->close(&statusWrapper);
190 resultSetHandle.reset();
191 }
192
193 statementHandle->free(&statusWrapper);
194 statementHandle.reset();
195}
196
198{
199 assert(isValid());
200
201 return statementHandle->getPlan(&statusWrapper, false);
202}
203
205{
206 assert(isValid());
207
208 return statementHandle->getPlan(&statusWrapper, true);
209}
210
212{
213 assert(isValid());
214 assert(transaction.isValid());
215
216 if (resultSetHandle)
217 {
218 resultSetHandle->close(&statusWrapper);
219 resultSetHandle.reset();
220 }
221
222 const auto outMessageData = outMessage.data();
223
224 if (outMessageData)
225 {
226 for (const auto& descriptor : outDescriptors)
227 *reinterpret_cast<std::int16_t*>(&outMessageData[descriptor.nullOffset]) = FB_TRUE;
228 }
229
230 switch (type)
231 {
234 resultSetHandle.reset(statementHandle->openCursor(&statusWrapper, transaction.getHandle().get(),
235 inMetadata.get(), inMessage.data(), outMetadata.get(), cursorFlags));
236 return resultSetHandle->fetchNext(&statusWrapper, outMessageData) == fb::IStatus::RESULT_OK;
237
238 default:
239 statementHandle->execute(&statusWrapper, transaction.getHandle().get(), inMetadata.get(), inMessage.data(),
240 outMetadata.get(), outMessageData);
241 return true;
242 }
243}
244
246{
247 assert(isValid());
248
249 return resultSetHandle && resultSetHandle->fetchNext(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
250}
251
253{
254 assert(isValid());
255
256 return resultSetHandle && resultSetHandle->fetchPrior(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
257}
258
260{
261 assert(isValid());
262
263 return resultSetHandle && resultSetHandle->fetchFirst(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
264}
265
267{
268 assert(isValid());
269
270 return resultSetHandle && resultSetHandle->fetchLast(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
271}
272
273bool Statement::fetchAbsolute(unsigned position)
274{
275 assert(isValid());
276
277 return resultSetHandle &&
278 resultSetHandle->fetchAbsolute(&statusWrapper, static_cast<int>(position), outMessage.data()) ==
279 fb::IStatus::RESULT_OK;
280}
281
283{
284 assert(isValid());
285
286 return resultSetHandle &&
287 resultSetHandle->fetchRelative(&statusWrapper, offset, outMessage.data()) == fb::IStatus::RESULT_OK;
288}
Represents a connection to a Firebird database.
Definition Attachment.h:177
FbRef< fb::IAttachment > getHandle() noexcept
Returns the internal Firebird IAttachment handle.
Definition Attachment.h:253
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:237
Base exception class for all fb-cpp exceptions.
Definition Exception.h:184
Reference-counted smart pointer for Firebird objects using addRef/release semantics.
Definition SmartPtrs.h:70
Represents options used when preparing a Statement.
Definition Statement.h:89
const std::optional< std::string > & getCursorName() const
Returns the cursor name to be set for the statement.
Definition Statement.h:132
bool getPrefetchPlan() const
Reports whether the structured plan should be prefetched during prepare.
Definition Statement.h:113
bool getPrefetchLegacyPlan() const
Reports whether the legacy textual plan should be prefetched during prepare.
Definition Statement.h:94
CursorType getCursorType() const
Returns the cursor type to be used when opening a result set.
Definition Statement.h:151
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:336
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:42
@ 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:178
@ 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