From 4076ca685941ac433bc91130e29449cef7858fba Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 24 Apr 2016 15:47:33 +0100 Subject: Support bulk selects which don't use cursors, always use of RETURNING --- libpqpp/pq-bulkselectcommand.cpp | 53 ++++++++++++++++++++++++++++++++++++++++ libpqpp/pq-bulkselectcommand.h | 29 ++++++++++++++++++++++ libpqpp/pq-connection.cpp | 5 ++++ libpqpp/unittests/testpq.cpp | 31 +++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 libpqpp/pq-bulkselectcommand.cpp create mode 100644 libpqpp/pq-bulkselectcommand.h (limited to 'libpqpp') diff --git a/libpqpp/pq-bulkselectcommand.cpp b/libpqpp/pq-bulkselectcommand.cpp new file mode 100644 index 0000000..27674f5 --- /dev/null +++ b/libpqpp/pq-bulkselectcommand.cpp @@ -0,0 +1,53 @@ +#include "pq-bulkselectcommand.h" +#include "pq-connection.h" +#include "pq-column.h" +#include "pq-error.h" + +PQ::BulkSelectCommand::BulkSelectCommand(Connection * conn, const std::string & sql, unsigned int no) : + DB::Command(sql), + DB::SelectCommand(sql), + PQ::Command(conn, sql, no), + executed(false) +{ + prepareSql(preparedSql, sql); +} + +PQ::BulkSelectCommand::~BulkSelectCommand() +{ + if (execRes) { + PQclear(execRes); + } +} + +void +PQ::BulkSelectCommand::execute() +{ + if (!executed) { + execRes = c->checkResult( + PQexecParams(c->conn, preparedSql.c_str(), values.size(), NULL, &values.front(), &lengths.front(), NULL, 0), + PGRES_TUPLES_OK); + nTuples = PQntuples(execRes); + tuple = -1; + unsigned int nFields = PQnfields(execRes); + for (unsigned int f = 0; f < nFields; f += 1) { + insertColumn(DB::ColumnPtr(new Column(this, f))); + } + executed = true; + } +} + +bool +PQ::BulkSelectCommand::fetch() +{ + execute(); + if (++tuple < nTuples) { + return true; + } + else { + PQclear(execRes); + execRes = NULL; + executed = false; + return false; + } +} + diff --git a/libpqpp/pq-bulkselectcommand.h b/libpqpp/pq-bulkselectcommand.h new file mode 100644 index 0000000..aeda42d --- /dev/null +++ b/libpqpp/pq-bulkselectcommand.h @@ -0,0 +1,29 @@ +#ifndef PQ_BULKSELECTCOMMAND_H +#define PQ_BULKSELECTCOMMAND_H + +#include +#include "pq-selectbase.h" +#include "pq-command.h" +#include +#include + +namespace PQ { + class Connection; + class Column; + class BulkSelectCommand : public DB::SelectCommand, public SelectBase, public Command { + public: + BulkSelectCommand(Connection *, const std::string & sql, unsigned int no); + virtual ~BulkSelectCommand(); + + bool fetch() override; + void execute() override; + + private: + mutable bool executed; + std::string preparedSql; + }; +} + +#endif + + diff --git a/libpqpp/pq-connection.cpp b/libpqpp/pq-connection.cpp index 49867a4..078272e 100644 --- a/libpqpp/pq-connection.cpp +++ b/libpqpp/pq-connection.cpp @@ -1,5 +1,6 @@ #include "pq-connection.h" #include "pq-error.h" +#include "pq-bulkselectcommand.h" #include "pq-cursorselectcommand.h" #include "pq-modifycommand.h" #include @@ -97,6 +98,10 @@ PQ::Connection::ping() const DB::SelectCommand * PQ::Connection::newSelectCommand(const std::string & sql) { + // Yes, this is a hack + if (sql.find("libdbpp:no-cursor") != (std::string::size_type)-1) { + return new BulkSelectCommand(this, sql, pstmntNo++); + } return new CursorSelectCommand(this, sql, pstmntNo++); } diff --git a/libpqpp/unittests/testpq.cpp b/libpqpp/unittests/testpq.cpp index 64d60d9..f5cb760 100644 --- a/libpqpp/unittests/testpq.cpp +++ b/libpqpp/unittests/testpq.cpp @@ -287,6 +287,37 @@ BOOST_AUTO_TEST_CASE( statementReuse ) delete ro; } +BOOST_AUTO_TEST_CASE( bulkSelect ) +{ + auto ro = DB::MockDatabase::openConnectionTo("pqmock"); + auto sel = ro->newSelectCommand("SELECT * FROM test WHERE id > ? --libdbpp:no-cursor"); + sel->bindParamI(0, 1); + int totalInt = 0, count = 0; + sel->forEachRow([&totalInt, &count](auto i) { + totalInt += i; + count += 1; + }); + delete sel; + BOOST_REQUIRE_EQUAL(20, totalInt); + BOOST_REQUIRE_EQUAL(8, count); + delete ro; +} + +BOOST_AUTO_TEST_CASE( insertReturning ) +{ + auto ro = DB::MockDatabase::openConnectionTo("pqmock"); + auto sel = ro->newSelectCommand("INSERT INTO test(id, fl) VALUES(1, 3) RETURNING id + fl --libdbpp:no-cursor"); + int totalInt = 0, count = 0; + sel->forEachRow([&totalInt, &count](auto i) { + totalInt += i; + count += 1; + }); + delete sel; + BOOST_REQUIRE_EQUAL(4, totalInt); + BOOST_REQUIRE_EQUAL(1, count); + delete ro; +} + BOOST_AUTO_TEST_CASE( closeOnError ) { auto ro = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("pqmock")); -- cgit v1.2.3