Skip to content

Commit 8522727

Browse files
authored
Add convenience statement methods in Attachment class (#55)
* Add Attachment::execute methods * Add Attachment::queryRowSet methods * Add Attachment::queryScalar methods * Add Attachment::queryFirstRowAs methods * Refactor * EXECUTE PROCEDURE do not return more than 1 row
1 parent 386c062 commit 8522727

8 files changed

Lines changed: 1070 additions & 174 deletions

File tree

src/fb-cpp/Attachment.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#include "Attachment.h"
2626
#include "Client.h"
2727
#include "Exception.h"
28+
#include "RowSet.h"
29+
#include "Statement.h"
30+
#include "Transaction.h"
2831

2932
using namespace fbcpp;
3033
using namespace fbcpp::impl;
@@ -93,3 +96,39 @@ void Attachment::dropDatabase()
9396
{
9497
disconnectOrDrop(true);
9598
}
99+
100+
bool Attachment::execute(Transaction& transaction, std::string_view sql, const StatementOptions& options)
101+
{
102+
Statement statement{*this, transaction, sql, options};
103+
return statement.execute(transaction);
104+
}
105+
106+
RowSet Attachment::queryRowSet(
107+
Transaction& transaction, std::string_view sql, unsigned maxRows, const StatementOptions& options)
108+
{
109+
Statement statement{*this, transaction, sql, options};
110+
return queryPreparedRowSet(statement, transaction, maxRows);
111+
}
112+
113+
RowSet Attachment::queryPreparedRowSet(Statement& statement, Transaction& transaction, unsigned maxRows)
114+
{
115+
switch (statement.getType())
116+
{
117+
case StatementType::SELECT:
118+
case StatementType::SELECT_FOR_UPDATE:
119+
break;
120+
121+
case StatementType::EXEC_PROCEDURE:
122+
if (!statement.getOutputDescriptors().empty())
123+
break;
124+
125+
throw FbCppException("Cannot use procedure without output columns with Attachment::queryRowSet");
126+
127+
default:
128+
throw FbCppException("Cannot use non-query SQL with Attachment::queryRowSet");
129+
}
130+
131+
const auto hasRow = statement.execute(transaction);
132+
const auto effectiveMaxRows = statement.getType() == StatementType::EXEC_PROCEDURE ? 1u : maxRows;
133+
return RowSet{statement, hasRow ? effectiveMaxRows : 0u, hasRow};
134+
}

src/fb-cpp/Attachment.h

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
#define FBCPP_ATTACHMENT_H
2727

2828
#include "fb-api.h"
29+
#include "RowSet.h"
2930
#include "SmartPtrs.h"
31+
#include "StatementOptions.h"
32+
#include <cstddef>
3033
#include <cstdint>
3134
#include <memory>
3235
#include <optional>
3336
#include <string>
37+
#include <string_view>
3438
#include <vector>
35-
#include <cstddef>
3639

3740

3841
///
@@ -41,6 +44,8 @@
4144
namespace fbcpp
4245
{
4346
class Client;
47+
class Statement;
48+
class Transaction;
4449

4550
///
4651
/// Represents options used when creating an Attachment object.
@@ -301,14 +306,204 @@ namespace fbcpp
301306
///
302307
void dropDatabase();
303308

309+
///
310+
/// Prepares and executes an SQL statement using the supplied transaction.
311+
///
312+
bool execute(Transaction& transaction, std::string_view sql, const StatementOptions& options = {});
313+
314+
///
315+
/// Prepares, binds parameters, and executes an SQL statement using the supplied transaction.
316+
///
317+
template <typename Params>
318+
bool execute(Transaction& transaction, std::string_view sql, const Params& params);
319+
320+
///
321+
/// Prepares, binds parameters, and executes an SQL statement using the supplied transaction and statement
322+
/// options.
323+
///
324+
template <typename Params>
325+
bool execute(
326+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params);
327+
328+
///
329+
/// Prepares and executes a query using the supplied transaction and returns up to maxRows rows.
330+
///
331+
RowSet queryRowSet(
332+
Transaction& transaction, std::string_view sql, unsigned maxRows, const StatementOptions& options = {});
333+
334+
///
335+
/// Prepares, binds parameters, and executes a query using the supplied transaction and returns up to maxRows
336+
/// rows.
337+
///
338+
template <typename Params>
339+
RowSet queryRowSet(Transaction& transaction, std::string_view sql, unsigned maxRows, const Params& params);
340+
341+
///
342+
/// Prepares, binds parameters, and executes a query using the supplied transaction and statement options and
343+
/// returns up to maxRows rows.
344+
///
345+
template <typename Params>
346+
RowSet queryRowSet(Transaction& transaction, std::string_view sql, unsigned maxRows,
347+
const StatementOptions& options, const Params& params);
348+
349+
///
350+
/// Prepares and executes a query using the supplied transaction and returns the first column of the first row.
351+
///
352+
template <typename T>
353+
std::optional<T> queryScalar(
354+
Transaction& transaction, std::string_view sql, const StatementOptions& options = {});
355+
356+
///
357+
/// Prepares, binds parameters, and executes a query using the supplied transaction and returns the first column
358+
/// of the first row.
359+
///
360+
template <typename T, typename Params>
361+
std::optional<T> queryScalar(Transaction& transaction, std::string_view sql, const Params& params);
362+
363+
///
364+
/// Prepares, binds parameters, and executes a query using the supplied transaction and statement options and
365+
/// returns the first column of the first row.
366+
///
367+
template <typename T, typename Params>
368+
std::optional<T> queryScalar(
369+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params);
370+
371+
///
372+
/// Prepares and executes a query using the supplied transaction and returns the first row mapped as T.
373+
///
374+
template <typename T>
375+
std::optional<T> queryFirstRowAs(
376+
Transaction& transaction, std::string_view sql, const StatementOptions& options = {});
377+
378+
///
379+
/// Prepares, binds parameters, and executes a query using the supplied transaction and returns the first row
380+
/// mapped as T.
381+
///
382+
template <typename T, typename Params>
383+
std::optional<T> queryFirstRowAs(Transaction& transaction, std::string_view sql, const Params& params);
384+
385+
///
386+
/// Prepares, binds parameters, and executes a query using the supplied transaction and statement options and
387+
/// returns the first row mapped as T.
388+
///
389+
template <typename T, typename Params>
390+
std::optional<T> queryFirstRowAs(
391+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params);
392+
304393
private:
305394
void disconnectOrDrop(bool drop);
395+
RowSet queryPreparedRowSet(Statement& statement, Transaction& transaction, unsigned maxRows);
306396

307397
private:
308398
Client* client;
309399
FbRef<fb::IAttachment> handle;
310400
};
401+
402+
template <typename T>
403+
std::optional<T> Attachment::queryScalar(
404+
Transaction& transaction, std::string_view sql, const StatementOptions& options)
405+
{
406+
auto rowSet = queryRowSet(transaction, sql, 1u, options);
407+
408+
if (rowSet.getCount() == 0u)
409+
return std::nullopt;
410+
411+
return rowSet.getRow(0).get<std::optional<T>>(0);
412+
}
413+
414+
template <typename T>
415+
std::optional<T> Attachment::queryFirstRowAs(
416+
Transaction& transaction, std::string_view sql, const StatementOptions& options)
417+
{
418+
auto rowSet = queryRowSet(transaction, sql, 1u, options);
419+
420+
if (rowSet.getCount() == 0u)
421+
return std::nullopt;
422+
423+
return rowSet.getRow(0).get<T>();
424+
}
311425
} // namespace fbcpp
312426

427+
#include "Statement.h"
428+
429+
namespace fbcpp
430+
{
431+
template <typename Params>
432+
bool Attachment::execute(Transaction& transaction, std::string_view sql, const Params& params)
433+
{
434+
return execute(transaction, sql, StatementOptions{}, params);
435+
}
436+
437+
template <typename Params>
438+
bool Attachment::execute(
439+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params)
440+
{
441+
Statement statement{*this, transaction, sql, options};
442+
statement.set(params);
443+
return statement.execute(transaction);
444+
}
445+
446+
template <typename Params>
447+
RowSet Attachment::queryRowSet(
448+
Transaction& transaction, std::string_view sql, unsigned maxRows, const Params& params)
449+
{
450+
return queryRowSet(transaction, sql, maxRows, StatementOptions{}, params);
451+
}
452+
453+
template <typename Params>
454+
RowSet Attachment::queryRowSet(Transaction& transaction, std::string_view sql, unsigned maxRows,
455+
const StatementOptions& options, const Params& params)
456+
{
457+
Statement statement{*this, transaction, sql, options};
458+
statement.set(params);
459+
return queryPreparedRowSet(statement, transaction, maxRows);
460+
}
461+
462+
template <typename T, typename Params>
463+
std::optional<T> Attachment::queryScalar(Transaction& transaction, std::string_view sql, const Params& params)
464+
{
465+
auto rowSet = queryRowSet(transaction, sql, 1u, params);
466+
467+
if (rowSet.getCount() == 0u)
468+
return std::nullopt;
469+
470+
return rowSet.getRow(0).template get<std::optional<T>>(0);
471+
}
472+
473+
template <typename T, typename Params>
474+
std::optional<T> Attachment::queryScalar(
475+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params)
476+
{
477+
auto rowSet = queryRowSet(transaction, sql, 1u, options, params);
478+
479+
if (rowSet.getCount() == 0u)
480+
return std::nullopt;
481+
482+
return rowSet.getRow(0).template get<std::optional<T>>(0);
483+
}
484+
485+
template <typename T, typename Params>
486+
std::optional<T> Attachment::queryFirstRowAs(Transaction& transaction, std::string_view sql, const Params& params)
487+
{
488+
auto rowSet = queryRowSet(transaction, sql, 1u, params);
489+
490+
if (rowSet.getCount() == 0u)
491+
return std::nullopt;
492+
493+
return rowSet.getRow(0).template get<T>();
494+
}
495+
496+
template <typename T, typename Params>
497+
std::optional<T> Attachment::queryFirstRowAs(
498+
Transaction& transaction, std::string_view sql, const StatementOptions& options, const Params& params)
499+
{
500+
auto rowSet = queryRowSet(transaction, sql, 1u, options, params);
501+
502+
if (rowSet.getCount() == 0u)
503+
return std::nullopt;
504+
505+
return rowSet.getRow(0).template get<T>();
506+
}
507+
} // namespace fbcpp
313508

314509
#endif // FBCPP_ATTACHMENT_H

src/fb-cpp/RowSet.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,28 @@
2323
*/
2424

2525
#include "RowSet.h"
26+
#include "Attachment.h"
2627
#include "Client.h"
2728
#include "Statement.h"
29+
#include <algorithm>
2830

2931
using namespace fbcpp;
3032
using namespace fbcpp::impl;
3133

3234

3335
RowSet::RowSet(Statement& statement, unsigned maxRows)
36+
: RowSet{statement, maxRows, false}
37+
{
38+
}
39+
40+
RowSet::RowSet(Statement& statement, unsigned maxRows, bool includeCurrentRow)
3441
: client{&statement.getAttachment().getClient()},
3542
statusWrapper{statement.getAttachment().getClient()},
3643
numericConverter{statement.getAttachment().getClient()},
3744
calendarConverter{statement.getAttachment().getClient()}
3845
{
3946
assert(statement.isValid());
40-
assert(statement.getResultSetHandle());
47+
assert(includeCurrentRow || statement.getResultSetHandle());
4148

4249
descriptors = statement.getOutputDescriptors();
4350

@@ -49,7 +56,22 @@ RowSet::RowSet(Statement& statement, unsigned maxRows)
4956
auto resultSet = statement.getResultSetHandle();
5057
auto* dest = buffer.data();
5158

52-
for (unsigned i = 0; i < maxRows; ++i)
59+
if (includeCurrentRow && maxRows > 0u)
60+
{
61+
auto& currentRow = statement.getOutputMessage();
62+
assert(currentRow.size() == messageLength);
63+
std::copy(currentRow.begin(), currentRow.end(), dest);
64+
dest += messageLength;
65+
++count;
66+
}
67+
68+
if (!resultSet)
69+
{
70+
buffer.resize(static_cast<std::size_t>(dest - buffer.data()));
71+
return;
72+
}
73+
74+
for (unsigned i = count; i < maxRows; ++i)
5375
{
5476
if (resultSet->fetchNext(&statusWrapper, dest) != fb::IStatus::RESULT_OK)
5577
break;

src/fb-cpp/RowSet.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ namespace fbcpp
6969
///
7070
explicit RowSet(Statement& statement, unsigned maxRows);
7171

72+
///
73+
/// @brief Fetches up to `maxRows` rows from the current result set of `statement`.
74+
///
75+
/// When `includeCurrentRow` is true, the current output message already fetched by `statement.execute()` is
76+
/// copied as the first row before fetching the remaining rows from the result set.
77+
///
78+
explicit RowSet(Statement& statement, unsigned maxRows, bool includeCurrentRow);
79+
7280
RowSet(RowSet&& o) noexcept
7381
: client{o.client},
7482
count{o.count},

0 commit comments

Comments
 (0)