Skip to content

Commit 812d852

Browse files
committed
Add Attachment::queryScalar methods
1 parent 34c01d2 commit 812d852

2 files changed

Lines changed: 135 additions & 1 deletion

File tree

src/fb-cpp/Attachment.h

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "fb-api.h"
2929
#include "SmartPtrs.h"
30+
#include "RowSet.h"
3031
#include <cstdint>
3132
#include <memory>
3233
#include <optional>
@@ -42,7 +43,6 @@
4243
namespace fbcpp
4344
{
4445
class Client;
45-
class RowSet;
4646
class StatementOptions;
4747
class Transaction;
4848

@@ -327,13 +327,49 @@ namespace fbcpp
327327
RowSet queryRowSet(
328328
Transaction& transaction, std::string_view sql, unsigned maxRows, const StatementOptions& options);
329329

330+
///
331+
/// Prepares and executes a query using the supplied transaction and returns the first column of the first row.
332+
///
333+
template <typename T>
334+
std::optional<T> queryScalar(Transaction& transaction, std::string_view sql);
335+
336+
///
337+
/// Prepares and executes a query using the supplied transaction and statement options and returns the first
338+
/// column of the first row.
339+
///
340+
template <typename T>
341+
std::optional<T> queryScalar(Transaction& transaction, std::string_view sql, const StatementOptions& options);
342+
330343
private:
331344
void disconnectOrDrop(bool drop);
332345

333346
private:
334347
Client* client;
335348
FbRef<fb::IAttachment> handle;
336349
};
350+
351+
template <typename T>
352+
std::optional<T> Attachment::queryScalar(Transaction& transaction, std::string_view sql)
353+
{
354+
auto rowSet = queryRowSet(transaction, sql, 1u);
355+
356+
if (rowSet.getCount() == 0u)
357+
return std::nullopt;
358+
359+
return rowSet.getRow(0).get<std::optional<T>>(0);
360+
}
361+
362+
template <typename T>
363+
std::optional<T> Attachment::queryScalar(
364+
Transaction& transaction, std::string_view sql, const StatementOptions& options)
365+
{
366+
auto rowSet = queryRowSet(transaction, sql, 1u, options);
367+
368+
if (rowSet.getCount() == 0u)
369+
return std::nullopt;
370+
371+
return rowSet.getRow(0).get<std::optional<T>>(0);
372+
}
337373
} // namespace fbcpp
338374

339375

src/test/Attachment.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,104 @@ BOOST_AUTO_TEST_CASE(queryRowSetRejectsProcedureWithoutOutputColumns)
244244
transaction.commit();
245245
}
246246

247+
BOOST_AUTO_TEST_CASE(queryScalarReturnsFirstColumnOfFirstRow)
248+
{
249+
const auto database = getTempFile("Attachment-queryScalarReturnsFirstColumnOfFirstRow.fdb");
250+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
251+
FbDropDatabase attachmentDrop{attachment};
252+
253+
Transaction transaction{attachment};
254+
BOOST_REQUIRE(
255+
attachment.execute(transaction, "create table t (id integer not null primary key, name varchar(20))"));
256+
transaction.commitRetaining();
257+
258+
BOOST_REQUIRE(attachment.execute(transaction, "insert into t (id, name) values (1, 'one')"));
259+
BOOST_REQUIRE(attachment.execute(transaction, "insert into t (id, name) values (2, 'two')"));
260+
261+
const auto value = attachment.queryScalar<std::string>(transaction, "select name, id from t order by id");
262+
263+
BOOST_REQUIRE(value.has_value());
264+
BOOST_CHECK_EQUAL(*value, "one");
265+
266+
transaction.commit();
267+
}
268+
269+
BOOST_AUTO_TEST_CASE(queryScalarReturnsNulloptForNoRows)
270+
{
271+
const auto database = getTempFile("Attachment-queryScalarReturnsNulloptForNoRows.fdb");
272+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
273+
FbDropDatabase attachmentDrop{attachment};
274+
275+
Transaction transaction{attachment};
276+
const auto value = attachment.queryScalar<std::int32_t>(transaction, "select 1 from rdb$database where 1 = 0");
277+
278+
BOOST_CHECK(!value.has_value());
279+
280+
transaction.commit();
281+
}
282+
283+
BOOST_AUTO_TEST_CASE(queryScalarReturnsNulloptForNullColumn)
284+
{
285+
const auto database = getTempFile("Attachment-queryScalarReturnsNulloptForNullColumn.fdb");
286+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
287+
FbDropDatabase attachmentDrop{attachment};
288+
289+
Transaction transaction{attachment};
290+
const auto value =
291+
attachment.queryScalar<std::int32_t>(transaction, "select cast(null as integer) from rdb$database");
292+
293+
BOOST_CHECK(!value.has_value());
294+
295+
transaction.commit();
296+
}
297+
298+
BOOST_AUTO_TEST_CASE(queryScalarSupportsStatementOptions)
299+
{
300+
const auto database = getTempFile("Attachment-queryScalarSupportsStatementOptions.fdb");
301+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
302+
FbDropDatabase attachmentDrop{attachment};
303+
304+
Transaction transaction{attachment};
305+
const auto value = attachment.queryScalar<std::int32_t>(
306+
transaction, "select 1 from rdb$database", StatementOptions().setDialect(3u));
307+
308+
BOOST_REQUIRE(value.has_value());
309+
BOOST_CHECK_EQUAL(*value, 1);
310+
311+
transaction.commit();
312+
}
313+
314+
BOOST_AUTO_TEST_CASE(queryScalarSupportsProcedureWithOutputColumns)
315+
{
316+
const auto database = getTempFile("Attachment-queryScalarSupportsProcedureWithOutputColumns.fdb");
317+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
318+
FbDropDatabase attachmentDrop{attachment};
319+
320+
Transaction transaction{attachment};
321+
BOOST_REQUIRE(attachment.execute(transaction,
322+
"create procedure p returns (id integer, name varchar(20)) as begin id = 42; name = 'answer'; suspend; end"));
323+
transaction.commitRetaining();
324+
325+
const auto value = attachment.queryScalar<std::int32_t>(transaction, "execute procedure p");
326+
327+
BOOST_REQUIRE(value.has_value());
328+
BOOST_CHECK_EQUAL(*value, 42);
329+
330+
transaction.commit();
331+
}
332+
333+
BOOST_AUTO_TEST_CASE(queryScalarThrowsForNonQueryStatement)
334+
{
335+
const auto database = getTempFile("Attachment-queryScalarThrowsForNonQueryStatement.fdb");
336+
Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setForcedWrites(false)};
337+
FbDropDatabase attachmentDrop{attachment};
338+
339+
Transaction transaction{attachment};
340+
BOOST_CHECK_THROW(attachment.queryScalar<std::int32_t>(transaction, "create table t (id integer)"), FbCppException);
341+
342+
transaction.commit();
343+
}
344+
247345
BOOST_AUTO_TEST_CASE(isNotValidAfterMove)
248346
{
249347
const auto database = getTempFile("Attachment-isNotValidAfterMove.fdb");

0 commit comments

Comments
 (0)