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