From cfa36aa5466d4c342c7de0f6cea7b8386984fd36 Mon Sep 17 00:00:00 2001 From: randomdan Date: Wed, 9 Feb 2011 01:33:33 +0000 Subject: 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 --- libpqpp/Jamfile.jam | 25 ++++++++ libpqpp/column.cpp | 73 +++++++++++++++++++++++ libpqpp/column.h | 23 +++++++ libpqpp/command.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++++++ libpqpp/command.h | 46 ++++++++++++++ libpqpp/connection.cpp | 96 +++++++++++++++++++++++++++++ libpqpp/connection.h | 36 +++++++++++ libpqpp/error.cpp | 28 +++++++++ libpqpp/error.h | 24 ++++++++ libpqpp/modifycommand.cpp | 26 ++++++++ libpqpp/modifycommand.h | 21 +++++++ libpqpp/selectcommand.cpp | 94 +++++++++++++++++++++++++++++ libpqpp/selectcommand.h | 36 +++++++++++ 13 files changed, 677 insertions(+) create mode 100644 libpqpp/Jamfile.jam create mode 100644 libpqpp/column.cpp create mode 100644 libpqpp/column.h create mode 100644 libpqpp/command.cpp create mode 100644 libpqpp/command.h create mode 100644 libpqpp/connection.cpp create mode 100644 libpqpp/connection.h create mode 100644 libpqpp/error.cpp create mode 100644 libpqpp/error.h create mode 100644 libpqpp/modifycommand.cpp create mode 100644 libpqpp/modifycommand.h create mode 100644 libpqpp/selectcommand.cpp create mode 100644 libpqpp/selectcommand.h 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 : : : : + "`pkg-config --cflags glibmm-2.4`" + "`pkg-config --libs glibmm-2.4`" + ; + +lib pq ; + +alias libpq : : : : + "-I`pg_config --includedir`" + "-L`pg_config --libdir`" + pq + ; + + +lib pqpp : + [ glob *.cpp ] : + -fPIC + glibmm + libpq + : : + . + "-I`pg_config --includedir`" + glibmm + ../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 + +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 + +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 +#include + +static std::string addrStr(void * p, unsigned int no) { + std::string r; + r.resize(30); + r.resize(snprintf(const_cast(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::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(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 +#include + +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 values; + std::vector lengths; + std::vector 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 + +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 + +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 +#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::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::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 +#include + +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 fields; + std::map fieldsName; + int nTuples, tuple; + PGresult * execRes; + + friend class Column; + }; +} + +#endif + + -- cgit v1.2.3