Skip to content

Commit c044dfa

Browse files
committed
Add Attachment::queryFirstRowAs methods
1 parent 812d852 commit c044dfa

2 files changed

Lines changed: 202 additions & 0 deletions

File tree

src/fb-cpp/Attachment.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,20 @@ namespace fbcpp
340340
template <typename T>
341341
std::optional<T> queryScalar(Transaction& transaction, std::string_view sql, const StatementOptions& options);
342342

343+
///
344+
/// Prepares and executes a query using the supplied transaction and returns the first row mapped as T.
345+
///
346+
template <typename T>
347+
std::optional<T> queryFirstRowAs(Transaction& transaction, std::string_view sql);
348+
349+
///
350+
/// Prepares and executes a query using the supplied transaction and statement options and returns the first row
351+
/// mapped as T.
352+
///
353+
template <typename T>
354+
std::optional<T> queryFirstRowAs(
355+
Transaction& transaction, std::string_view sql, const StatementOptions& options);
356+
343357
private:
344358
void disconnectOrDrop(bool drop);
345359

@@ -370,6 +384,29 @@ namespace fbcpp
370384

371385
return rowSet.getRow(0).get<std::optional<T>>(0);
372386
}
387+
388+
template <typename T>
389+
std::optional<T> Attachment::queryFirstRowAs(Transaction& transaction, std::string_view sql)
390+
{
391+
auto rowSet = queryRowSet(transaction, sql, 1u);
392+
393+
if (rowSet.getCount() == 0u)
394+
return std::nullopt;
395+
396+
return rowSet.getRow(0).get<T>();
397+
}
398+
399+
template <typename T>
400+
std::optional<T> Attachment::queryFirstRowAs(
401+
Transaction& transaction, std::string_view sql, const StatementOptions& options)
402+
{
403+
auto rowSet = queryRowSet(transaction, sql, 1u, options);
404+
405+
if (rowSet.getCount() == 0u)
406+
return std::nullopt;
407+
408+
return rowSet.getRow(0).get<T>();
409+
}
373410
} // namespace fbcpp
374411

375412

src/test/Attachment.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,171 @@ BOOST_AUTO_TEST_CASE(queryScalarThrowsForNonQueryStatement)
342342
transaction.commit();
343343
}
344344

345+
BOOST_AUTO_TEST_CASE(queryFirstRowAsReturnsStructFromFirstRow)
346+
{
347+
struct Result
348+
{
349+
std::int32_t id;
350+
std::optional<std::string> name;
351+
};
352+
353+
const auto database = getTempFile("Attachment-queryFirstRowAsReturnsStructFromFirstRow.fdb");
354+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
355+
FbDropDatabase attachmentDrop{attachment};
356+
357+
Transaction transaction{attachment};
358+
BOOST_REQUIRE(
359+
attachment.execute(transaction, "create table t (id integer not null primary key, name varchar(20))"));
360+
transaction.commitRetaining();
361+
362+
BOOST_REQUIRE(attachment.execute(transaction, "insert into t (id, name) values (1, 'one')"));
363+
BOOST_REQUIRE(attachment.execute(transaction, "insert into t (id, name) values (2, 'two')"));
364+
365+
const auto value = attachment.queryFirstRowAs<Result>(transaction, "select id, name from t order by id");
366+
367+
BOOST_REQUIRE(value.has_value());
368+
BOOST_CHECK_EQUAL(value->id, 1);
369+
BOOST_REQUIRE(value->name.has_value());
370+
BOOST_CHECK_EQUAL(*value->name, "one");
371+
372+
transaction.commit();
373+
}
374+
375+
BOOST_AUTO_TEST_CASE(queryFirstRowAsReturnsTupleFromFirstRow)
376+
{
377+
using Result = std::tuple<std::int32_t, std::optional<std::string>>;
378+
379+
const auto database = getTempFile("Attachment-queryFirstRowAsReturnsTupleFromFirstRow.fdb");
380+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
381+
FbDropDatabase attachmentDrop{attachment};
382+
383+
Transaction transaction{attachment};
384+
const auto value = attachment.queryFirstRowAs<Result>(transaction, "select 42, 'answer' from rdb$database");
385+
386+
BOOST_REQUIRE(value.has_value());
387+
BOOST_CHECK_EQUAL(std::get<0>(*value), 42);
388+
BOOST_REQUIRE(std::get<1>(*value).has_value());
389+
BOOST_CHECK_EQUAL(*std::get<1>(*value), "answer");
390+
391+
transaction.commit();
392+
}
393+
394+
BOOST_AUTO_TEST_CASE(queryFirstRowAsReturnsNulloptForNoRows)
395+
{
396+
struct Result
397+
{
398+
std::int32_t id;
399+
};
400+
401+
const auto database = getTempFile("Attachment-queryFirstRowAsReturnsNulloptForNoRows.fdb");
402+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
403+
FbDropDatabase attachmentDrop{attachment};
404+
405+
Transaction transaction{attachment};
406+
const auto value = attachment.queryFirstRowAs<Result>(transaction, "select 1 from rdb$database where 1 = 0");
407+
408+
BOOST_CHECK(!value.has_value());
409+
410+
transaction.commit();
411+
}
412+
413+
BOOST_AUTO_TEST_CASE(queryFirstRowAsSupportsStatementOptions)
414+
{
415+
using Result = std::tuple<std::int32_t>;
416+
417+
const auto database = getTempFile("Attachment-queryFirstRowAsSupportsStatementOptions.fdb");
418+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
419+
FbDropDatabase attachmentDrop{attachment};
420+
421+
Transaction transaction{attachment};
422+
const auto value = attachment.queryFirstRowAs<Result>(
423+
transaction, "select 1 from rdb$database", StatementOptions().setDialect(3u));
424+
425+
BOOST_REQUIRE(value.has_value());
426+
BOOST_CHECK_EQUAL(std::get<0>(*value), 1);
427+
428+
transaction.commit();
429+
}
430+
431+
BOOST_AUTO_TEST_CASE(queryFirstRowAsSupportsProcedureWithOutputColumns)
432+
{
433+
struct Result
434+
{
435+
std::int32_t id;
436+
std::optional<std::string> name;
437+
};
438+
439+
const auto database = getTempFile("Attachment-queryFirstRowAsSupportsProcedureWithOutputColumns.fdb");
440+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
441+
FbDropDatabase attachmentDrop{attachment};
442+
443+
Transaction transaction{attachment};
444+
BOOST_REQUIRE(attachment.execute(transaction,
445+
"create procedure p returns (id integer, name varchar(20)) as begin id = 42; name = 'answer'; suspend; end"));
446+
transaction.commitRetaining();
447+
448+
const auto value = attachment.queryFirstRowAs<Result>(transaction, "execute procedure p");
449+
450+
BOOST_REQUIRE(value.has_value());
451+
BOOST_CHECK_EQUAL(value->id, 42);
452+
BOOST_REQUIRE(value->name.has_value());
453+
BOOST_CHECK_EQUAL(*value->name, "answer");
454+
455+
transaction.commit();
456+
}
457+
458+
BOOST_AUTO_TEST_CASE(queryFirstRowAsThrowsForFieldCountMismatch)
459+
{
460+
struct Result
461+
{
462+
std::int32_t id;
463+
};
464+
465+
const auto database = getTempFile("Attachment-queryFirstRowAsThrowsForFieldCountMismatch.fdb");
466+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
467+
FbDropDatabase attachmentDrop{attachment};
468+
469+
Transaction transaction{attachment};
470+
BOOST_CHECK_THROW(attachment.queryFirstRowAs<Result>(transaction, "select 1, 2 from rdb$database"), FbCppException);
471+
472+
transaction.commit();
473+
}
474+
475+
BOOST_AUTO_TEST_CASE(queryFirstRowAsThrowsForNullIntoNonOptionalField)
476+
{
477+
struct Result
478+
{
479+
std::int32_t id;
480+
};
481+
482+
const auto database = getTempFile("Attachment-queryFirstRowAsThrowsForNullIntoNonOptionalField.fdb");
483+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
484+
FbDropDatabase attachmentDrop{attachment};
485+
486+
Transaction transaction{attachment};
487+
BOOST_CHECK_THROW(attachment.queryFirstRowAs<Result>(transaction, "select cast(null as integer) from rdb$database"),
488+
FbCppException);
489+
490+
transaction.commit();
491+
}
492+
493+
BOOST_AUTO_TEST_CASE(queryFirstRowAsThrowsForNonQueryStatement)
494+
{
495+
struct Result
496+
{
497+
std::int32_t id;
498+
};
499+
500+
const auto database = getTempFile("Attachment-queryFirstRowAsThrowsForNonQueryStatement.fdb");
501+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
502+
FbDropDatabase attachmentDrop{attachment};
503+
504+
Transaction transaction{attachment};
505+
BOOST_CHECK_THROW(attachment.queryFirstRowAs<Result>(transaction, "create table t (id integer)"), FbCppException);
506+
507+
transaction.commit();
508+
}
509+
345510
BOOST_AUTO_TEST_CASE(isNotValidAfterMove)
346511
{
347512
const auto database = getTempFile("Attachment-isNotValidAfterMove.fdb");

0 commit comments

Comments
 (0)