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
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 {
102 Descriptor descriptor{
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);
125 descriptor.adjustedType = DescriptorAdjustedType::STRING;
126 break;
127
129 if (!builder)
130 builder.reset(metadata->getBuilder(&statusWrapper));
131
132 builder->setType(&statusWrapper, index, SQL_TIME_TZ);
133 descriptor.adjustedType = DescriptorAdjustedType::TIME_TZ;
134 break;
135
137 if (!builder)
138 builder.reset(metadata->getBuilder(&statusWrapper));
139
140 builder->setType(&statusWrapper, index, SQL_TIMESTAMP_TZ);
141 descriptor.adjustedType = DescriptorAdjustedType::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{
186 assert(isValid());
187
188 if (resultSetHandle)
189 {
190 resultSetHandle->close(&statusWrapper);
191 resultSetHandle.reset();
192 }
193
194 statementHandle->free(&statusWrapper);
195 statementHandle.reset();
196}
197
199{
200 assert(isValid());
201
202 return statementHandle->getPlan(&statusWrapper, false);
203}
204
206{
207 assert(isValid());
208
209 return statementHandle->getPlan(&statusWrapper, true);
210}
211
213{
214 assert(isValid());
215 assert(transaction.isValid());
216
217 if (resultSetHandle)
218 {
219 resultSetHandle->close(&statusWrapper);
220 resultSetHandle.reset();
221 }
222
223 const auto outMessageData = outMessage.data();
224
225 if (outMessageData)
226 {
227 for (const auto& descriptor : outDescriptors)
228 *reinterpret_cast<std::int16_t*>(&outMessageData[descriptor.nullOffset]) = FB_TRUE;
229 }
230
231 switch (type)
232 {
235 resultSetHandle.reset(statementHandle->openCursor(&statusWrapper, transaction.getHandle().get(),
236 inMetadata.get(), inMessage.data(), outMetadata.get(), cursorFlags));
237 return resultSetHandle->fetchNext(&statusWrapper, outMessageData) == fb::IStatus::RESULT_OK;
238
239 default:
240 statementHandle->execute(&statusWrapper, transaction.getHandle().get(), inMetadata.get(), inMessage.data(),
241 outMetadata.get(), outMessageData);
242 return true;
243 }
244}
245
247{
248 assert(isValid());
249
250 return resultSetHandle && resultSetHandle->fetchNext(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
251}
252
254{
255 assert(isValid());
256
257 return resultSetHandle && resultSetHandle->fetchPrior(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
258}
259
261{
262 assert(isValid());
263
264 return resultSetHandle && resultSetHandle->fetchFirst(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
265}
266
268{
269 assert(isValid());
270
271 return resultSetHandle && resultSetHandle->fetchLast(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
272}
273
274bool Statement::fetchAbsolute(unsigned position)
275{
276 assert(isValid());
277
278 return resultSetHandle &&
279 resultSetHandle->fetchAbsolute(&statusWrapper, static_cast<int>(position), outMessage.data()) ==
280 fb::IStatus::RESULT_OK;
281}
282
284{
285 assert(isValid());
286
287 return resultSetHandle &&
288 resultSetHandle->fetchRelative(&statusWrapper, offset, outMessage.data()) == fb::IStatus::RESULT_OK;
289}
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
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.
Definition Statement.h:90
const std::optional< std::string > & getCursorName() const
Returns the cursor name to be set for the statement.
Definition Statement.h:133
unsigned getDialect() const
Returns the SQL dialect used when preparing the statement.
Definition Statement.h:171
bool getPrefetchPlan() const
Reports whether the structured plan should be prefetched during prepare.
Definition Statement.h:114
bool getPrefetchLegacyPlan() const
Reports whether the legacy textual plan should be prefetched during prepare.
Definition Statement.h:95
CursorType getCursorType() const
Returns the cursor type to be used when opening a result set.
Definition Statement.h:152
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:368
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:199
@ 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