summaryrefslogtreecommitdiff
path: root/libpqpp
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2011-02-09 01:33:33 +0000
committerrandomdan <randomdan@localhost>2011-02-09 01:33:33 +0000
commitcfa36aa5466d4c342c7de0f6cea7b8386984fd36 (patch)
tree17a13c13b3588101a146705839d5da6f280bddb9 /libpqpp
parentCentralise the ICE splicer (diff)
downloadlibdbpp-postgresql-cfa36aa5466d4c342c7de0f6cea7b8386984fd36.tar.bz2
libdbpp-postgresql-cfa36aa5466d4c342c7de0f6cea7b8386984fd36.tar.xz
libdbpp-postgresql-cfa36aa5466d4c342c7de0f6cea7b8386984fd36.zip
Fix the build system to do dependencies properly
Break down libodbcpp into a set of base classes; libdbpp Add a native PostgreSQL implementation of libdbpp; libpqpp Extend project2 rdbms stuff to work with generic connectors Update datasources to specify connector type Build libmisc as .so
Diffstat (limited to 'libpqpp')
-rw-r--r--libpqpp/Jamfile.jam25
-rw-r--r--libpqpp/column.cpp73
-rw-r--r--libpqpp/column.h23
-rw-r--r--libpqpp/command.cpp149
-rw-r--r--libpqpp/command.h46
-rw-r--r--libpqpp/connection.cpp96
-rw-r--r--libpqpp/connection.h36
-rw-r--r--libpqpp/error.cpp28
-rw-r--r--libpqpp/error.h24
-rw-r--r--libpqpp/modifycommand.cpp26
-rw-r--r--libpqpp/modifycommand.h21
-rw-r--r--libpqpp/selectcommand.cpp94
-rw-r--r--libpqpp/selectcommand.h36
13 files changed, 677 insertions, 0 deletions
diff --git a/libpqpp/Jamfile.jam b/libpqpp/Jamfile.jam
new file mode 100644
index 0000000..0305236
--- /dev/null
+++ b/libpqpp/Jamfile.jam
@@ -0,0 +1,25 @@
+alias glibmm : : : :
+ <cflags>"`pkg-config --cflags glibmm-2.4`"
+ <linkflags>"`pkg-config --libs glibmm-2.4`"
+ ;
+
+lib pq ;
+
+alias libpq : : : :
+ <cflags>"-I`pg_config --includedir`"
+ <linkflags>"-L`pg_config --libdir`"
+ <library>pq
+ ;
+
+
+lib pqpp :
+ [ glob *.cpp ] :
+ <cflags>-fPIC
+ <library>glibmm
+ <library>libpq
+ : :
+ <include>.
+ <cflags>"-I`pg_config --includedir`"
+ <library>glibmm
+ <library>../libdbpp
+ ;
diff --git a/libpqpp/column.cpp b/libpqpp/column.cpp
new file mode 100644
index 0000000..1bd212a
--- /dev/null
+++ b/libpqpp/column.cpp
@@ -0,0 +1,73 @@
+#include "column.h"
+#include "selectcommand.h"
+#include "error.h"
+#include <string.h>
+
+PQ::Column::Column(const SelectCommand * s, unsigned int i) :
+ DB::Column(PQfname(s->execRes, i), i),
+ sc(s),
+ oid(PQftype(sc->execRes, colNo))
+{
+}
+
+bool
+PQ::Column::isNull() const
+{
+ return PQgetisnull(sc->execRes, sc->tuple, colNo);
+}
+
+void
+PQ::Column::apply(DB::HandleField & h) const
+{
+ if (isNull()) {
+ h.null();
+ return;
+ }
+ struct tm tm;
+ switch (oid) {
+ case 18: //CHAROID:
+ case 25: //TEXTOID:
+ case 142: //XMLOID:
+ h.string(PQgetvalue(sc->execRes, sc->tuple, colNo), PQgetlength(sc->execRes, sc->tuple, colNo));
+ break;
+ case 16: //BOOLOID:
+ h.integer(PQgetvalue(sc->execRes, sc->tuple, colNo)[0] == 't' ? 1 : 0);
+ break;
+ case 21: //INT2OID:
+ case 23: //INT4OID:
+ case 20: //INT8OID:
+ h.integer(atol(PQgetvalue(sc->execRes, sc->tuple, colNo)));
+ break;
+ case 1700: //NUMERICOID:
+ case 700: //FLOAT4OID:
+ case 701: //FLOAT8OID:
+ h.floatingpoint(atof(PQgetvalue(sc->execRes, sc->tuple, colNo)));
+ break;
+ case 1083: //TIMEOID:
+ memset(&tm, 0, sizeof(tm));
+ strptime(PQgetvalue(sc->execRes, sc->tuple, colNo), "%T", &tm);
+ h.timestamp(tm);
+ break;
+ case 1082: //DATEOID:
+ memset(&tm, 0, sizeof(tm));
+ strptime(PQgetvalue(sc->execRes, sc->tuple, colNo), "%F", &tm);
+ h.timestamp(tm);
+ break;
+ case 702: //ABSTIMEOID:
+ case 1114: //TIMESTAMPOID:
+ case 1184: //TIMESTAMPTZOID:
+ strptime(PQgetvalue(sc->execRes, sc->tuple, colNo), "%F %T", &tm);
+ h.timestamp(tm);
+ break;
+ default:
+ fprintf(stderr, "Unknown Oid %d\n", oid);
+ throw Error("Unknown Oid");
+ }
+}
+
+void
+PQ::Column::rebind(DB::Command *, unsigned int) const
+{
+ throw Error("Not supported");
+}
+
diff --git a/libpqpp/column.h b/libpqpp/column.h
new file mode 100644
index 0000000..22ab755
--- /dev/null
+++ b/libpqpp/column.h
@@ -0,0 +1,23 @@
+#ifndef PG_COLUMN_H
+#define PG_COLUMN_H
+
+#include "../libdbpp/column.h"
+#include <libpq-fe.h>
+
+namespace PQ {
+ class SelectCommand;
+ class Column : public DB::Column {
+ public:
+ Column(const SelectCommand *, unsigned int field);
+
+ bool isNull() const;
+ void apply(DB::HandleField &) const;
+ void rebind(DB::Command *, unsigned int) const;
+ protected:
+ const SelectCommand * sc;
+ const Oid oid;
+ };
+}
+
+#endif
+
diff --git a/libpqpp/command.cpp b/libpqpp/command.cpp
new file mode 100644
index 0000000..28d4f24
--- /dev/null
+++ b/libpqpp/command.cpp
@@ -0,0 +1,149 @@
+#include "command.h"
+#include "connection.h"
+#include <stdlib.h>
+#include <string.h>
+
+static std::string addrStr(void * p, unsigned int no) {
+ std::string r;
+ r.resize(30);
+ r.resize(snprintf(const_cast<char *>(r.c_str()), 30, "pStatement-%u-%p", no, p));
+ return r;
+}
+
+PQ::Command::Command(const Connection * conn, const std::string & sql, unsigned int no) :
+ DB::Command(sql),
+ stmntName(addrStr(this, no)),
+ prepared(false),
+ c(conn)
+{
+}
+
+PQ::Command::~Command()
+{
+ for (std::vector<char *>::const_iterator i = values.begin(); i != values.end(); i++) {
+ free(*i);
+ }
+}
+
+void
+PQ::Command::prepare() const
+{
+ if (!prepared) {
+ std::string psql;
+ psql.reserve(sql.length() + 20);
+ char buf[4];
+ int p = 1;
+ for(std::string::const_iterator i = sql.begin(); i != sql.end(); i++) {
+ if (*i == '?') {
+ snprintf(buf, 4, "$%d", p++);
+ psql += buf;
+ }
+ else {
+ psql += *i;
+ }
+ }
+ c->checkResultFree(PQprepare(
+ c->conn, stmntName.c_str(), psql.c_str(), values.size(), NULL), PGRES_COMMAND_OK, __PRETTY_FUNCTION__);
+ prepared = true;
+ }
+}
+
+void
+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);
+ }
+ else {
+ free(values[n]);
+ }
+}
+
+void
+PQ::Command::bindParamI(unsigned int n, int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%d", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamI(unsigned int n, long int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%ld", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamI(unsigned int n, long long int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%lld", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamI(unsigned int n, unsigned int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%u", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamI(unsigned int n, long unsigned int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%lu", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamI(unsigned int n, long long unsigned int v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%llu", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamF(unsigned int n, double v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%f", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamF(unsigned int n, float v)
+{
+ paramsAtLeast(n);
+ lengths[n] = asprintf(&values[n], "%f", v);
+ formats[n] = 0;
+}
+void
+PQ::Command::bindParamS(unsigned int n, const Glib::ustring & s)
+{
+ paramsAtLeast(n);
+ values[n] = strndup(s.c_str(), s.length());
+ formats[n] = 0;
+ lengths[n] = s.length();
+}
+void
+PQ::Command::bindParamT(unsigned int n, const tm * v)
+{
+ paramsAtLeast(n);
+ values[n] = static_cast<char *>(malloc(19));
+ formats[n] = 0;
+ strftime(values[n], 19, "%F %T", v);
+ lengths[n] = 19;
+}
+void
+PQ::Command::bindParamT(unsigned int n, time_t v)
+{
+ struct tm t;
+ gmtime_r(&v, &t);
+ bindParamT(n, &t);
+}
+void
+PQ::Command::bindNull(unsigned int n)
+{
+ paramsAtLeast(n);
+}
+
diff --git a/libpqpp/command.h b/libpqpp/command.h
new file mode 100644
index 0000000..697516a
--- /dev/null
+++ b/libpqpp/command.h
@@ -0,0 +1,46 @@
+#ifndef PQ_COMMAND_H
+#define PQ_COMMAND_H
+
+#include "../libdbpp/command.h"
+#include <libpq-fe.h>
+#include <vector>
+
+namespace PQ {
+ class Connection;
+ class Command : public virtual DB::Command {
+ public:
+ Command(const Connection *, const std::string & sql, unsigned int no);
+ virtual ~Command() = 0;
+
+ void bindParamI(unsigned int, int);
+ void bindParamI(unsigned int, long int);
+ void bindParamI(unsigned int, long long int);
+ void bindParamI(unsigned int, unsigned int);
+ void bindParamI(unsigned int, long unsigned int);
+ void bindParamI(unsigned int, long long unsigned int);
+
+ void bindParamF(unsigned int, double);
+ void bindParamF(unsigned int, float);
+
+ void bindParamS(unsigned int, const Glib::ustring&);
+
+ void bindParamT(unsigned int, const tm*);
+ void bindParamT(unsigned int, time_t);
+
+ void bindNull(unsigned int);
+ protected:
+ void prepare() const;
+ const std::string stmntName;
+ mutable bool prepared;
+ const Connection * c;
+
+ void paramsAtLeast(unsigned int);
+ std::vector<char *> values;
+ std::vector<int> lengths;
+ std::vector<int> formats;
+ };
+}
+
+#endif
+
+
diff --git a/libpqpp/connection.cpp b/libpqpp/connection.cpp
new file mode 100644
index 0000000..4330b3f
--- /dev/null
+++ b/libpqpp/connection.cpp
@@ -0,0 +1,96 @@
+#include "connection.h"
+#include "error.h"
+#include "selectcommand.h"
+#include "modifycommand.h"
+
+PQ::Connection::Connection(const std::string & info) :
+ conn(PQconnectdb(info.c_str())),
+ txDepth(0),
+ pstmntNo(0)
+{
+ if (PQstatus(conn) != CONNECTION_OK) {
+ throw ConnectionError();
+ }
+}
+
+PQ::Connection::~Connection()
+{
+ PQfinish(conn);
+}
+
+int
+PQ::Connection::beginTx() const
+{
+ if (txDepth == 0) {
+ checkResultFree(PQexec(conn, "BEGIN"), PGRES_COMMAND_OK, __PRETTY_FUNCTION__);
+ }
+ return ++txDepth;
+}
+
+int
+PQ::Connection::commitTx() const
+{
+ if (--txDepth == 0) {
+ checkResultFree(PQexec(conn, "COMMIT"), PGRES_COMMAND_OK, __PRETTY_FUNCTION__);
+ }
+ return txDepth;
+}
+
+int
+PQ::Connection::rollbackTx() const
+{
+ if (--txDepth == 0) {
+ checkResultFree(PQexec(conn, "ROLLBACK"), PGRES_COMMAND_OK, __PRETTY_FUNCTION__);
+ }
+ return txDepth;
+}
+
+bool
+PQ::Connection::inTx() const
+{
+ return txDepth;
+}
+
+void
+PQ::Connection::ping() const
+{
+}
+
+
+DB::SelectCommand *
+PQ::Connection::newSelectCommand(const std::string & sql) const
+{
+ return new SelectCommand(this, sql, pstmntNo++);
+}
+
+DB::ModifyCommand *
+PQ::Connection::newModifyCommand(const std::string & sql) const
+{
+ return new ModifyCommand(this, sql, pstmntNo++);
+}
+
+bool
+PQ::Connection::checkResultInt(PGresult * res, int expected)
+{
+ return (PQresultStatus(res) == expected);
+}
+
+void
+PQ::Connection::checkResult(PGresult * res, int expected, const char * doing) const
+{
+ if (!checkResultInt(res, expected)) {
+ PQclear(res);
+ throw Error(PQerrorMessage(conn));
+ }
+}
+
+void
+PQ::Connection::checkResultFree(PGresult * res, int expected, const char * doing) const
+{
+ if (!checkResultInt(res, expected)) {
+ PQclear(res);
+ throw Error(PQerrorMessage(conn));
+ }
+ PQclear(res);
+}
+
diff --git a/libpqpp/connection.h b/libpqpp/connection.h
new file mode 100644
index 0000000..da13e6c
--- /dev/null
+++ b/libpqpp/connection.h
@@ -0,0 +1,36 @@
+#ifndef PQ_CONNECTION_H
+#define PQ_CONNECTION_H
+
+#include "../libdbpp/connection.h"
+#include <libpq-fe.h>
+
+namespace PQ {
+ class Connection : public DB::Connection {
+ public:
+ Connection(const std::string & info);
+ ~Connection();
+
+ int beginTx() const;
+ int commitTx() const;
+ int rollbackTx() const;
+ bool inTx() const;
+ void ping() const;
+
+ DB::SelectCommand * newSelectCommand(const std::string & sql) const;
+ DB::ModifyCommand * newModifyCommand(const std::string & sql) const;
+
+ void checkResult(PGresult * res, int expected, const char * doing) const;
+ void checkResultFree(PGresult * res, int expected, const char * doing) const;
+
+ PGconn * conn;
+
+ private:
+ static bool checkResultInt(PGresult * res, int expected);
+
+ mutable unsigned int txDepth;
+ mutable unsigned int pstmntNo;
+ };
+}
+
+#endif
+
diff --git a/libpqpp/error.cpp b/libpqpp/error.cpp
new file mode 100644
index 0000000..0886238
--- /dev/null
+++ b/libpqpp/error.cpp
@@ -0,0 +1,28 @@
+#include "error.h"
+#include <string.h>
+
+PQ::Error::Error() :
+ msg(NULL)
+{
+}
+
+PQ::Error::Error(const PQ::Error & e) :
+ msg(e.msg ? strdup(e.msg) : NULL)
+{
+}
+
+PQ::Error::Error(const char * e) :
+ msg(e ? strdup(e) : NULL)
+{
+}
+
+PQ::Error::~Error() throw()
+{
+}
+
+const char *
+PQ::Error::what() const throw()
+{
+ return msg ? msg : "No message";
+}
+
diff --git a/libpqpp/error.h b/libpqpp/error.h
new file mode 100644
index 0000000..e8aea56
--- /dev/null
+++ b/libpqpp/error.h
@@ -0,0 +1,24 @@
+#ifndef PQ_ERROR_H
+#define PQ_ERROR_H
+
+#include "../libdbpp/error.h"
+
+namespace PQ {
+ class Error : public DB::Error {
+ public:
+ Error();
+ Error(const Error &);
+ Error(const char *);
+ ~Error() throw();
+
+ const char * what() const throw();
+
+ private:
+ char * msg;
+ };
+ class ConnectionError : public Error, public virtual DB::ConnectionError {
+ };
+}
+
+#endif
+
diff --git a/libpqpp/modifycommand.cpp b/libpqpp/modifycommand.cpp
new file mode 100644
index 0000000..14bfb16
--- /dev/null
+++ b/libpqpp/modifycommand.cpp
@@ -0,0 +1,26 @@
+#include "modifycommand.h"
+#include <stdlib.h>
+#include "connection.h"
+
+PQ::ModifyCommand::ModifyCommand(const Connection * conn, const std::string & sql, unsigned int no) :
+ DB::Command(sql),
+ DB::ModifyCommand(sql),
+ PQ::Command(conn, sql, no)
+{
+}
+
+PQ::ModifyCommand::~ModifyCommand()
+{
+}
+
+unsigned int
+PQ::ModifyCommand::execute(bool anc)
+{
+ prepare();
+ PGresult * res = PQexecPrepared(c->conn, stmntName.c_str(), values.size(), &values.front(), &lengths.front(), &formats.front(), 0);
+ c->checkResult(res, PGRES_COMMAND_OK, __PRETTY_FUNCTION__);
+ unsigned int rows = atoi(PQcmdTuples(res));
+ PQclear(res);
+ return rows;
+}
+
diff --git a/libpqpp/modifycommand.h b/libpqpp/modifycommand.h
new file mode 100644
index 0000000..a9cdbef
--- /dev/null
+++ b/libpqpp/modifycommand.h
@@ -0,0 +1,21 @@
+#ifndef PQ_MODIFYCOMMAND_H
+#define PQ_MODIFYCOMMAND_H
+
+#include "../libdbpp/modifycommand.h"
+#include "command.h"
+
+namespace PQ {
+ class Connection;
+ class ModifyCommand : public DB::ModifyCommand, public Command {
+ public:
+ ModifyCommand(const Connection *, const std::string & sql, unsigned int no);
+ virtual ~ModifyCommand();
+
+ unsigned int execute(bool);
+ };
+}
+
+#endif
+
+
+
diff --git a/libpqpp/selectcommand.cpp b/libpqpp/selectcommand.cpp
new file mode 100644
index 0000000..603cffd
--- /dev/null
+++ b/libpqpp/selectcommand.cpp
@@ -0,0 +1,94 @@
+#include "selectcommand.h"
+#include "connection.h"
+#include "column.h"
+#include "error.h"
+
+PQ::SelectCommand::SelectCommand(const Connection * conn, const std::string & sql, unsigned int no) :
+ DB::Command(sql),
+ DB::SelectCommand(sql),
+ PQ::Command(conn, sql, no),
+ executed(false),
+ nTuples(0),
+ tuple(0),
+ execRes(NULL)
+{
+}
+
+PQ::SelectCommand::~SelectCommand()
+{
+ if (execRes) {
+ PQclear(execRes);
+ }
+ for (unsigned int f = 0; f < fields.size(); f += 1) {
+ delete fields[f];
+ }
+}
+
+void
+PQ::SelectCommand::execute()
+{
+ if (!executed) {
+ prepare();
+ execRes = PQexecPrepared(c->conn, stmntName.c_str(), values.size(), &values.front(), &lengths.front(), &formats.front(), 0);
+ c->checkResult(execRes, PGRES_TUPLES_OK, __PRETTY_FUNCTION__);
+ unsigned int nFields = PQnfields(execRes);
+ fields.resize(nFields);
+ for (unsigned int f = 0; f < nFields; f += 1) {
+ Column * c = new Column(this, f);
+ fields[f] = c;
+ fieldsName[c->name] = c;
+ }
+ nTuples = PQntuples(execRes);
+ tuple = -1;
+ executed = true;
+ }
+}
+
+bool
+PQ::SelectCommand::fetch()
+{
+ execute();
+ if (tuple++ < (nTuples - 1)) {
+ return true;
+ }
+ else {
+ executed = false;
+ return false;
+ }
+}
+
+const DB::Column&
+PQ::SelectCommand::operator[](unsigned int n) const
+{
+ if (n < fields.size()) {
+ return *fields[n];
+ }
+ throw Error();
+}
+
+const DB::Column&
+PQ::SelectCommand::operator[](const Glib::ustring & n) const
+{
+ std::map<Glib::ustring, Column *>::const_iterator i = fieldsName.find(n);
+ if (i != fieldsName.end()) {
+ return *i->second;
+ }
+ throw Error();
+}
+
+unsigned int
+PQ::SelectCommand::getOrdinal(const Glib::ustring & n) const
+{
+ std::map<Glib::ustring, Column *>::const_iterator i = fieldsName.find(n);
+ if (i != fieldsName.end()) {
+ return i->second->colNo;
+ }
+ throw Error();
+}
+
+unsigned int
+PQ::SelectCommand::columnCount() const
+{
+ return fields.size();
+}
+
diff --git a/libpqpp/selectcommand.h b/libpqpp/selectcommand.h
new file mode 100644
index 0000000..932717d
--- /dev/null
+++ b/libpqpp/selectcommand.h
@@ -0,0 +1,36 @@
+#ifndef PQ_SELECTCOMMAND_H
+#define PQ_SELECTCOMMAND_H
+
+#include "../libdbpp/selectcommand.h"
+#include "command.h"
+#include <vector>
+#include <map>
+
+namespace PQ {
+ class Connection;
+ class Column;
+ class SelectCommand : public DB::SelectCommand, public Command {
+ public:
+ SelectCommand(const Connection *, const std::string & sql, unsigned int no);
+ virtual ~SelectCommand();
+
+ bool fetch();
+ void execute();
+ const DB::Column& operator[](unsigned int) const;
+ const DB::Column& operator[](const Glib::ustring&) const;
+ unsigned int columnCount() const;
+ unsigned int getOrdinal(const Glib::ustring&) const;
+ private:
+ mutable bool executed;
+ std::vector<Column *> fields;
+ std::map<Glib::ustring, Column *> fieldsName;
+ int nTuples, tuple;
+ PGresult * execRes;
+
+ friend class Column;
+ };
+}
+
+#endif
+
+