summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2017-06-05 16:47:06 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2017-06-05 17:17:09 +0100
commitb9aaad1d4d8be6604c84aa11651b99ac932794af (patch)
tree907695b54295aaf3b25240c8c0cb20d56ecc14c6
parentFix memory leak in unit test (diff)
downloadlibdbpp-postgresql-b9aaad1d4d8be6604c84aa11651b99ac932794af.tar.bz2
libdbpp-postgresql-b9aaad1d4d8be6604c84aa11651b99ac932794af.tar.xz
libdbpp-postgresql-b9aaad1d4d8be6604c84aa11651b99ac932794af.zip
Add support for bytea / blob type data
-rw-r--r--libpqpp/pq-bulkselectcommand.cpp2
-rw-r--r--libpqpp/pq-column.cpp20
-rw-r--r--libpqpp/pq-column.h3
-rw-r--r--libpqpp/pq-command.cpp19
-rw-r--r--libpqpp/pq-command.h3
-rw-r--r--libpqpp/pq-cursorselectcommand.cpp2
-rw-r--r--libpqpp/pq-modifycommand.cpp2
-rw-r--r--libpqpp/unittests/pqschema.sql3
-rw-r--r--libpqpp/unittests/testpq.cpp24
9 files changed, 69 insertions, 9 deletions
diff --git a/libpqpp/pq-bulkselectcommand.cpp b/libpqpp/pq-bulkselectcommand.cpp
index 503d535..ab44df5 100644
--- a/libpqpp/pq-bulkselectcommand.cpp
+++ b/libpqpp/pq-bulkselectcommand.cpp
@@ -16,7 +16,7 @@ PQ::BulkSelectCommand::execute()
{
if (!executed) {
execRes = c->checkResult(
- PQexecPrepared(c->conn, prepare(), values.size(), &values.front(), &lengths.front(), NULL, 0),
+ PQexecPrepared(c->conn, prepare(), values.size(), &values.front(), &lengths.front(), &formats.front(), 0),
PGRES_TUPLES_OK);
nTuples = PQntuples(execRes);
tuple = -1;
diff --git a/libpqpp/pq-column.cpp b/libpqpp/pq-column.cpp
index bb09c45..92107e8 100644
--- a/libpqpp/pq-column.cpp
+++ b/libpqpp/pq-column.cpp
@@ -7,10 +7,18 @@
PQ::Column::Column(const SelectBase * s, unsigned int i) :
DB::Column(PQfname(s->execRes, i), i),
sc(s),
- oid(PQftype(sc->execRes, colNo))
+ oid(PQftype(sc->execRes, colNo)),
+ buf(nullptr)
{
}
+PQ::Column::~Column()
+{
+ if (buf) {
+ PQfreemem(buf);
+ }
+}
+
bool
PQ::Column::isNull() const
{
@@ -67,6 +75,16 @@ PQ::Column::apply(DB::HandleField & h) const
case 1184: //TIMESTAMPTZOID:
h.timestamp(boost::posix_time::time_from_string(PQgetvalue(sc->execRes, sc->tuple, colNo)));
break;
+ case 17: //BYTEAOID
+ {
+ if (buf) {
+ PQfreemem(buf);
+ }
+ size_t len;
+ buf = PQunescapeBytea((unsigned char *)PQgetvalue(sc->execRes, sc->tuple, colNo), &len);
+ h.blob(DB::Blob(buf, len));
+ break;
+ }
default:
h.string(PQgetvalue(sc->execRes, sc->tuple, colNo), PQgetlength(sc->execRes, sc->tuple, colNo));
}
diff --git a/libpqpp/pq-column.h b/libpqpp/pq-column.h
index f01ee9a..08e66a6 100644
--- a/libpqpp/pq-column.h
+++ b/libpqpp/pq-column.h
@@ -9,6 +9,7 @@ namespace PQ {
class Column : public DB::Column {
public:
Column(const SelectBase *, unsigned int field);
+ ~Column();
bool isNull() const override;
void apply(DB::HandleField &) const override;
@@ -16,6 +17,8 @@ namespace PQ {
protected:
const SelectBase * sc;
const Oid oid;
+ // Buffer for PQunescapeBytea
+ mutable unsigned char * buf;
};
}
diff --git a/libpqpp/pq-command.cpp b/libpqpp/pq-command.cpp
index f31d78a..5476393 100644
--- a/libpqpp/pq-command.cpp
+++ b/libpqpp/pq-command.cpp
@@ -23,7 +23,7 @@ PQ::Command::~Command()
if (bufs[i]) {
delete bufs[i];
}
- else {
+ else if (formats[i] == 0) {
free(values[i]);
}
}
@@ -75,6 +75,7 @@ PQ::Command::paramsAtLeast(unsigned int n)
if (values.size() <= n) {
values.resize(n + 1, NULL);
lengths.resize(n + 1, 0);
+ formats.resize(n + 1, 0);
bufs.resize(n + 1, NULL);
}
else {
@@ -82,10 +83,11 @@ PQ::Command::paramsAtLeast(unsigned int n)
delete bufs[n];
bufs[n] = nullptr;
}
- else {
+ else if (formats[n] == 0) {
free(values[n]);
}
values[n] = NULL;
+ formats[n] = 0;
}
}
@@ -95,15 +97,12 @@ PQ::Command::paramSet(unsigned int n, const char * fmt, const T & ... v)
{
paramsAtLeast(n);
lengths[n] = asprintf(&values[n], fmt, v...);
- delete bufs[n];
- bufs[n] = nullptr;
}
void
PQ::Command::paramSet(unsigned int n, const std::string & b)
{
paramsAtLeast(n);
- delete bufs[n];
bufs[n] = new std::string(b);
lengths[n] = b.length();
values[n] = const_cast<char *>(bufs[n]->data());
@@ -170,6 +169,16 @@ PQ::Command::bindParamT(unsigned int n, const boost::posix_time::ptime & v)
paramSet(n, boost::posix_time::to_iso_extended_string(v));
}
void
+PQ::Command::bindParamBLOB(unsigned int n, const DB::Blob & v)
+{
+ paramsAtLeast(n);
+ lengths[n] = v.len;
+ formats[n] = 1;
+ values[n] = reinterpret_cast<char *>(const_cast<void *>(v.data));
+ delete bufs[n];
+ bufs[n] = nullptr;
+}
+void
PQ::Command::bindNull(unsigned int n)
{
paramsAtLeast(n);
diff --git a/libpqpp/pq-command.h b/libpqpp/pq-command.h
index 7612262..989844c 100644
--- a/libpqpp/pq-command.h
+++ b/libpqpp/pq-command.h
@@ -43,6 +43,8 @@ namespace PQ {
void bindParamT(unsigned int, const boost::posix_time::time_duration &) override;
void bindParamT(unsigned int, const boost::posix_time::ptime &) override;
+ void bindParamBLOB(unsigned int, const DB::Blob &) override;
+
void bindNull(unsigned int) override;
protected:
void prepareSql(std::stringstream & psql, const std::string & sql) const;
@@ -56,6 +58,7 @@ namespace PQ {
void paramSet(unsigned int, const std::string &);
std::vector<char *> values;
std::vector<int> lengths;
+ std::vector<int> formats;
std::vector<std::string *> bufs;
};
}
diff --git a/libpqpp/pq-cursorselectcommand.cpp b/libpqpp/pq-cursorselectcommand.cpp
index d754680..54b843f 100644
--- a/libpqpp/pq-cursorselectcommand.cpp
+++ b/libpqpp/pq-cursorselectcommand.cpp
@@ -50,7 +50,7 @@ PQ::CursorSelectCommand::execute()
s_declare = mkdeclare();
}
c->checkResultFree(
- PQexecParams(c->conn, s_declare.c_str(), values.size(), NULL, &values.front(), &lengths.front(), NULL, 0),
+ PQexecParams(c->conn, s_declare.c_str(), values.size(), NULL, &values.front(), &lengths.front(), &formats.front(), 0),
PGRES_COMMAND_OK);
fetchTuples();
createColumns(execRes);
diff --git a/libpqpp/pq-modifycommand.cpp b/libpqpp/pq-modifycommand.cpp
index 7629f00..b2cf626 100644
--- a/libpqpp/pq-modifycommand.cpp
+++ b/libpqpp/pq-modifycommand.cpp
@@ -17,7 +17,7 @@ PQ::ModifyCommand::~ModifyCommand()
unsigned int
PQ::ModifyCommand::execute(bool anc)
{
- PGresult * res = PQexecPrepared(c->conn, prepare(), values.size(), &values.front(), &lengths.front(), NULL, 0);
+ PGresult * res = PQexecPrepared(c->conn, prepare(), values.size(), &values.front(), &lengths.front(), &formats.front(), 0);
c->checkResult(res, PGRES_COMMAND_OK, PGRES_TUPLES_OK);
unsigned int rows = atoi(PQcmdTuples(res));
PQclear(res);
diff --git a/libpqpp/unittests/pqschema.sql b/libpqpp/unittests/pqschema.sql
index 2a7dd09..a79b678 100644
--- a/libpqpp/unittests/pqschema.sql
+++ b/libpqpp/unittests/pqschema.sql
@@ -38,4 +38,7 @@ CREATE TABLE bulktest(
CREATE TABLE idtest(
id serial,
foo int);
+CREATE TABLE blobtest(
+ data bytea,
+ md5 text);
diff --git a/libpqpp/unittests/testpq.cpp b/libpqpp/unittests/testpq.cpp
index 87992ca..97dc22d 100644
--- a/libpqpp/unittests/testpq.cpp
+++ b/libpqpp/unittests/testpq.cpp
@@ -383,6 +383,30 @@ BOOST_AUTO_TEST_CASE( closeOnError )
ro->commitTx();
}
+BOOST_AUTO_TEST_CASE( blobs )
+{
+ auto ro = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("PQmock"));
+ std::vector<char> buf(29);
+ memcpy(&buf[0], "This is some binary text data", 29);
+ auto ins = ro->modify("INSERT INTO blobtest(data) VALUES(?)");
+ ins->bindParamBLOB(0, buf);
+ ins->execute();
+
+ ro->execute("UPDATE blobtest SET md5 = md5(data)");
+
+ auto sel = ro->select("SELECT data, md5, length(data) FROM blobtest");
+ for (const auto & r : sel->as<DB::Blob, std::string, int64_t>()) {
+ // Assert the DB understood the insert
+ BOOST_REQUIRE_EQUAL(r.value<2>(), buf.size());
+ BOOST_REQUIRE_EQUAL(r.value<1>(), "37c7c3737f93e8d17e845deff8fa74d2");
+ // Assert the fetch of the data is correct
+ BOOST_REQUIRE_EQUAL(r.value<0>().len, buf.size());
+ std::string str((const char *)r.value<0>().data, r.value<0>().len);
+ BOOST_REQUIRE_EQUAL(str, "This is some binary text data");
+ BOOST_REQUIRE(memcmp(r.value<0>().data, &buf[0], 29) == 0);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_CASE( connfail )