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
59 type = static_cast<StatementType>(statementHandle->getType(&statusWrapper));
60
61 switch (type)
62 {
64 free();
65 throw FbCppException("Cannot use SET TRANSACTION command with Statement class. Use Transaction class");
66
68 free();
69 throw FbCppException(
70 "Cannot use COMMIT command with Statement class. Use the commit method from the Transaction class");
71
73 free();
74 throw FbCppException(
75 "Cannot use ROLLBACK command with Statement class. Use the rollback method from the Transaction class");
76
79 throw FbCppException("Unsupported statement type: BLOB segment operations");
80
81 default:
82 break;
83 }
84
85 const auto processMetadata = [&](FbRef<fb::IMessageMetadata>& metadata, std::vector<Descriptor>& descriptors,
86 std::vector<std::byte>& message)
87 {
88 if (!metadata)
89 return;
90
91 message.resize(metadata->getMessageLength(&statusWrapper));
92
94
95 const auto count = metadata->getCount(&statusWrapper);
96 descriptors.reserve(count);
97
98 for (unsigned index = 0u; index < count; ++index)
99 {
100 Descriptor descriptor{
101 .originalType = static_cast<DescriptorOriginalType>(metadata->getType(&statusWrapper, index)),
102 .adjustedType = static_cast<DescriptorAdjustedType>(metadata->getType(&statusWrapper, index)),
103 .scale = metadata->getScale(&statusWrapper, index),
104 .length = metadata->getLength(&statusWrapper, index),
105 .offset = 0,
106 .nullOffset = 0,
107 .isNullable = static_cast<bool>(metadata->isNullable(&statusWrapper, index)),
108 };
109
110 switch (descriptor.originalType)
111 {
113 if (!builder)
114 builder.reset(metadata->getBuilder(&statusWrapper));
115
116 builder->setType(&statusWrapper, index, SQL_VARYING);
117 descriptor.adjustedType = DescriptorAdjustedType::STRING;
118 break;
119
121 if (!builder)
122 builder.reset(metadata->getBuilder(&statusWrapper));
123
124 builder->setType(&statusWrapper, index, SQL_TIME_TZ);
125 descriptor.adjustedType = DescriptorAdjustedType::TIME_TZ;
126 break;
127
129 if (!builder)
130 builder.reset(metadata->getBuilder(&statusWrapper));
131
132 builder->setType(&statusWrapper, index, SQL_TIMESTAMP_TZ);
133 descriptor.adjustedType = DescriptorAdjustedType::TIMESTAMP_TZ;
134 break;
135
136 default:
137 break;
138 }
139
140 if (!builder)
141 {
142 descriptor.offset = metadata->getOffset(&statusWrapper, index);
143 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
144
145 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
146 }
147
148 descriptors.push_back(descriptor);
149 }
150
151 if (builder)
152 {
153 metadata.reset(builder->getMetadata(&statusWrapper));
154 message.resize(metadata->getMessageLength(&statusWrapper));
155
156 for (unsigned index = 0u; index < count; ++index)
157 {
158 auto& descriptor = descriptors[index];
159 descriptor.offset = metadata->getOffset(&statusWrapper, index);
160 descriptor.nullOffset = metadata->getNullOffset(&statusWrapper, index);
161
162 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
163 }
164 }
165 };
166
167 inMetadata.reset(statementHandle->getInputMetadata(&statusWrapper));
168 processMetadata(inMetadata, inDescriptors, inMessage);
169
170 outMetadata.reset(statementHandle->getOutputMetadata(&statusWrapper));
171 processMetadata(outMetadata, outDescriptors, outMessage);
172}
173
175{
176 assert(isValid());
177
178 if (resultSetHandle)
179 {
180 resultSetHandle->close(&statusWrapper);
181 resultSetHandle.reset();
182 }
183
184 statementHandle->free(&statusWrapper);
185 statementHandle.reset();
186}
187
189{
190 assert(isValid());
191
192 return statementHandle->getPlan(&statusWrapper, false);
193}
194
196{
197 assert(isValid());
198
199 return statementHandle->getPlan(&statusWrapper, true);
200}
201
203{
204 assert(isValid());
205 assert(transaction.isValid());
206
207 if (resultSetHandle)
208 {
209 resultSetHandle->close(&statusWrapper);
210 resultSetHandle.reset();
211 }
212
213 const auto outMessageData = outMessage.data();
214
215 if (outMessageData)
216 {
217 for (const auto& descriptor : outDescriptors)
218 *reinterpret_cast<std::int16_t*>(&outMessageData[descriptor.nullOffset]) = FB_TRUE;
219 }
220
221 switch (type)
222 {
225 resultSetHandle.reset(statementHandle->openCursor(&statusWrapper, transaction.getHandle().get(),
226 inMetadata.get(), inMessage.data(), outMetadata.get(), 0));
227 return resultSetHandle->fetchNext(&statusWrapper, outMessageData) == fb::IStatus::RESULT_OK;
228
229 default:
230 statementHandle->execute(&statusWrapper, transaction.getHandle().get(), inMetadata.get(), inMessage.data(),
231 outMetadata.get(), outMessageData);
232 return true;
233 }
234}
235
237{
238 assert(isValid());
239
240 return resultSetHandle && resultSetHandle->fetchNext(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
241}
242
244{
245 assert(isValid());
246
247 return resultSetHandle && resultSetHandle->fetchPrior(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
248}
249
251{
252 assert(isValid());
253
254 return resultSetHandle && resultSetHandle->fetchFirst(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
255}
256
258{
259 assert(isValid());
260
261 return resultSetHandle && resultSetHandle->fetchLast(&statusWrapper, outMessage.data()) == fb::IStatus::RESULT_OK;
262}
263
264bool Statement::fetchAbsolute(unsigned position)
265{
266 assert(isValid());
267
268 return resultSetHandle &&
269 resultSetHandle->fetchAbsolute(&statusWrapper, static_cast<int>(position), outMessage.data()) ==
270 fb::IStatus::RESULT_OK;
271}
272
274{
275 assert(isValid());
276
277 return resultSetHandle &&
278 resultSetHandle->fetchRelative(&statusWrapper, offset, outMessage.data()) == fb::IStatus::RESULT_OK;
279}
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:238
bool isValid() noexcept
Returns whether the Attachment object is valid.
Definition Attachment.h:222
Base exception class for all fb-cpp exceptions.
Definition Exception.h:183
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:73
const std::optional< std::string > & getCursorName() const
Returns the cursor name to be set for the statement.
Definition Statement.h:116
bool getPrefetchPlan() const
Reports whether the structured plan should be prefetched during prepare.
Definition Statement.h:97
bool getPrefetchLegacyPlan() const
Reports whether the legacy textual plan should be prefetched during prepare.
Definition Statement.h:78
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:270
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
DescriptorAdjustedType
Descriptor adjusted type.
Definition Descriptor.h:146
@ 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:142
@ 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:40
@ 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:247
DescriptorOriginalType originalType
Original SQL type as reported by Firebird.
Definition Descriptor.h:251