summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2016-04-24 15:47:33 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2016-04-24 15:47:33 +0100
commit4076ca685941ac433bc91130e29449cef7858fba (patch)
tree5b219bd316ca89c72a0fa361da849214f312699a
parentIntroduce select base for different kinds of select (diff)
downloadlibdbpp-postgresql-4076ca685941ac433bc91130e29449cef7858fba.tar.bz2
libdbpp-postgresql-4076ca685941ac433bc91130e29449cef7858fba.tar.xz
libdbpp-postgresql-4076ca685941ac433bc91130e29449cef7858fba.zip
Support bulk selects which don't use cursors, always use of RETURNING
-rw-r--r--libpqpp/pq-bulkselectcommand.cpp53
-rw-r--r--libpqpp/pq-bulkselectcommand.h29
-rw-r--r--libpqpp/pq-connection.cpp5
-rw-r--r--libpqpp/unittests/testpq.cpp31
4 files changed, 118 insertions, 0 deletions
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 <selectcommand.h>
+#include "pq-selectbase.h"
+#include "pq-command.h"
+#include <vector>
+#include <map>
+
+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 <unistd.h>
@@ -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<int64_t>([&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<int64_t>([&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"));