From 55dc8fa7bef00c0d382860b2f9b0245731db2bcb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 24 Dec 2015 04:00:01 +0000 Subject: PostgreSQL files prefixed with pq- --- libpqpp/Jamfile.jam | 2 +- libpqpp/column.cpp | 83 ----------------- libpqpp/column.h | 23 ----- libpqpp/command.cpp | 156 -------------------------------- libpqpp/command.h | 47 ---------- libpqpp/connection.cpp | 211 ------------------------------------------- libpqpp/connection.h | 48 ---------- libpqpp/error.cpp | 34 ------- libpqpp/error.h | 28 ------ libpqpp/mock.cpp | 33 ------- libpqpp/mock.h | 22 ----- libpqpp/modifycommand.cpp | 44 --------- libpqpp/modifycommand.h | 25 ----- libpqpp/pq-column.cpp | 83 +++++++++++++++++ libpqpp/pq-column.h | 23 +++++ libpqpp/pq-command.cpp | 156 ++++++++++++++++++++++++++++++++ libpqpp/pq-command.h | 47 ++++++++++ libpqpp/pq-connection.cpp | 211 +++++++++++++++++++++++++++++++++++++++++++ libpqpp/pq-connection.h | 48 ++++++++++ libpqpp/pq-error.cpp | 34 +++++++ libpqpp/pq-error.h | 28 ++++++ libpqpp/pq-mock.cpp | 33 +++++++ libpqpp/pq-mock.h | 22 +++++ libpqpp/pq-modifycommand.cpp | 44 +++++++++ libpqpp/pq-modifycommand.h | 25 +++++ libpqpp/pq-selectcommand.cpp | 113 +++++++++++++++++++++++ libpqpp/pq-selectcommand.h | 40 ++++++++ libpqpp/selectcommand.cpp | 113 ----------------------- libpqpp/selectcommand.h | 40 -------- libpqpp/unittests/testpq.cpp | 6 +- 30 files changed, 911 insertions(+), 911 deletions(-) delete mode 100644 libpqpp/column.cpp delete mode 100644 libpqpp/column.h delete mode 100644 libpqpp/command.cpp delete mode 100644 libpqpp/command.h delete mode 100644 libpqpp/connection.cpp delete mode 100644 libpqpp/connection.h delete mode 100644 libpqpp/error.cpp delete mode 100644 libpqpp/error.h delete mode 100644 libpqpp/mock.cpp delete mode 100644 libpqpp/mock.h delete mode 100644 libpqpp/modifycommand.cpp delete mode 100644 libpqpp/modifycommand.h create mode 100644 libpqpp/pq-column.cpp create mode 100644 libpqpp/pq-column.h create mode 100644 libpqpp/pq-command.cpp create mode 100644 libpqpp/pq-command.h create mode 100644 libpqpp/pq-connection.cpp create mode 100644 libpqpp/pq-connection.h create mode 100644 libpqpp/pq-error.cpp create mode 100644 libpqpp/pq-error.h create mode 100644 libpqpp/pq-mock.cpp create mode 100644 libpqpp/pq-mock.h create mode 100644 libpqpp/pq-modifycommand.cpp create mode 100644 libpqpp/pq-modifycommand.h create mode 100644 libpqpp/pq-selectcommand.cpp create mode 100644 libpqpp/pq-selectcommand.h delete mode 100644 libpqpp/selectcommand.cpp delete mode 100644 libpqpp/selectcommand.h diff --git a/libpqpp/Jamfile.jam b/libpqpp/Jamfile.jam index 86f5736..359edbc 100644 --- a/libpqpp/Jamfile.jam +++ b/libpqpp/Jamfile.jam @@ -30,5 +30,5 @@ lib dbpp-postgresql : build-project unittests ; -package.install install : . : : dbpp-postgresql : [ glob *.h ] ; +package.install install : . : : dbpp-postgresql : [ glob pq-*.h ] ; diff --git a/libpqpp/column.cpp b/libpqpp/column.cpp deleted file mode 100644 index c43e4ab..0000000 --- a/libpqpp/column.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "column.h" -#include "selectcommand.h" -#include "error.h" -#include -#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; - } - switch (oid) { - case 18: //CHAROID: - case 1043: //VARCHAROID: - 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.boolean(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 704: //TINTERVALOID - case 1083: //TIMEOID: - case 1186: //INTERVALOID - { - int days = 0, hours = 0, minutes = 0, seconds = 0, fractions = 0, flen1 = 0, flen2 = 0; - const char * val = PQgetvalue(sc->execRes, sc->tuple, colNo); - if (sscanf(val, "%d days %d:%d:%d.%n%d%n", &days, &hours, &minutes, &seconds, &flen1, &fractions, &flen2) >= 4) { - h.interval(boost::posix_time::time_duration((24 * days) + hours, minutes, seconds, fractions * pow(10, boost::posix_time::time_res_traits::num_fractional_digits() + flen1 - flen2))); - } - else if (sscanf(val, "%d day %d:%d:%d.%n%d%n", &days, &hours, &minutes, &seconds, &flen1, &fractions, &flen2) >= 4) { - h.interval(boost::posix_time::time_duration((24 * days) + hours, minutes, seconds, fractions * pow(10, boost::posix_time::time_res_traits::num_fractional_digits() + flen1 - flen2))); - } - else { - h.interval(boost::posix_time::duration_from_string(PQgetvalue(sc->execRes, sc->tuple, colNo))); - } - break; - } - case 1082: //DATEOID: - h.timestamp(boost::posix_time::ptime( - boost::gregorian::from_string(PQgetvalue(sc->execRes, sc->tuple, colNo)))); - break; - case 702: //ABSTIMEOID: - case 1114: //TIMESTAMPOID: - case 1184: //TIMESTAMPTZOID: - h.timestamp(boost::posix_time::time_from_string(PQgetvalue(sc->execRes, sc->tuple, colNo))); - break; - default: - h.string(PQgetvalue(sc->execRes, sc->tuple, colNo), PQgetlength(sc->execRes, sc->tuple, colNo)); - } -} - -void -PQ::Column::rebind(DB::Command *, unsigned int) const -{ - throw Error("Not supported"); -} - diff --git a/libpqpp/column.h b/libpqpp/column.h deleted file mode 100644 index 5980763..0000000 --- a/libpqpp/column.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef PG_COLUMN_H -#define PG_COLUMN_H - -#include -#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 deleted file mode 100644 index 94079da..0000000 --- a/libpqpp/command.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "command.h" -#include "connection.h" -#include -#include -#include - -static std::string addrStr(void * p, unsigned int no) { - std::string r(50, ' '); - r.resize(snprintf(const_cast(r.c_str()), r.length(), "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)), - c(conn) -{ -} - -PQ::Command::~Command() -{ - for (std::vector::const_iterator i = values.begin(); i != values.end(); ++i) { - free(*i); - } -} - -void -PQ::Command::prepareSql(std::string & psql, const std::string & sql) -{ - char buf[4]; - int p = 1; - bool inquote = false; - for(std::string::const_iterator i = sql.begin(); i != sql.end(); ++i) { - if (*i == '?' && !inquote) { - snprintf(buf, 4, "$%d", p++); - psql += buf; - } - else if (*i == '\'') { - inquote = !inquote; - psql += *i; - } - else { - psql += *i; - } - } -} - -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]); - values[n] = NULL; - } -} - -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::bindParamB(unsigned int n, bool v) -{ - paramsAtLeast(n); - lengths[n] = asprintf(&values[n], "%s", v ? "true" : "false"); - formats[n] = 0; -} -void -PQ::Command::bindParamF(unsigned int n, double v) -{ - paramsAtLeast(n); - lengths[n] = asprintf(&values[n], "%g", v); - formats[n] = 0; -} -void -PQ::Command::bindParamF(unsigned int n, float v) -{ - paramsAtLeast(n); - lengths[n] = asprintf(&values[n], "%g", v); - formats[n] = 0; -} -void -PQ::Command::bindParamS(unsigned int n, const Glib::ustring & s) -{ - paramsAtLeast(n); - values[n] = strndup(s.c_str(), s.bytes()); - formats[n] = 0; - lengths[n] = s.bytes(); -} -void -PQ::Command::bindParamT(unsigned int n, const boost::posix_time::time_duration & v) -{ - paramsAtLeast(n); - auto buf = boost::posix_time::to_simple_string(v); - values[n] = strdup(buf.c_str()); - formats[n] = 0; - lengths[n] = buf.length(); -} -void -PQ::Command::bindParamT(unsigned int n, const boost::posix_time::ptime & v) -{ - paramsAtLeast(n); - auto buf = boost::posix_time::to_iso_extended_string(v); - values[n] = strdup(buf.c_str()); - formats[n] = 0; - lengths[n] = buf.length(); -} -void -PQ::Command::bindNull(unsigned int n) -{ - paramsAtLeast(n); -} - diff --git a/libpqpp/command.h b/libpqpp/command.h deleted file mode 100644 index 32faaa0..0000000 --- a/libpqpp/command.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef PQ_COMMAND_H -#define PQ_COMMAND_H - -#include -#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 bindParamB(unsigned int, bool); - - void bindParamF(unsigned int, double); - void bindParamF(unsigned int, float); - - void bindParamS(unsigned int, const Glib::ustring&); - - void bindParamT(unsigned int, const boost::posix_time::time_duration &); - void bindParamT(unsigned int, const boost::posix_time::ptime &); - - void bindNull(unsigned int); - protected: - static void prepareSql(std::string & psql, const std::string & sql); - const std::string stmntName; - 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 deleted file mode 100644 index de3f189..0000000 --- a/libpqpp/connection.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "connection.h" -#include "error.h" -#include "selectcommand.h" -#include "modifycommand.h" -#include -#include -#include - -NAMEDFACTORY("postgresql", PQ::Connection, DB::ConnectionFactory); - -static void setup() __attribute__((constructor(101))); -static void setup() -{ - BOOST_ASSERT(PQisthreadsafe() == 1); - PQinitOpenSSL(1, 0); -} - -static void -noNoticeProcessor(void *, const char *) -{ -} - -PQ::Connection::Connection(const std::string & info) : - conn(PQconnectdb(info.c_str())), - txDepth(0), - pstmntNo(0), - rolledback(false) -{ - if (PQstatus(conn) != CONNECTION_OK) { - ConnectionError ce(conn); - PQfinish(conn); - throw ce; - } - PQsetNoticeProcessor(conn, noNoticeProcessor, NULL); -} - -PQ::Connection::~Connection() -{ - PQfinish(conn); -} - -void -PQ::Connection::finish() const -{ - if (txDepth != 0) { - rollbackTx(); - throw Error("Transaction still open"); - } -} - -int -PQ::Connection::beginTx() const -{ - if (txDepth == 0) { - checkResultFree(PQexec(conn, "BEGIN"), PGRES_COMMAND_OK); - rolledback = false; - } - return ++txDepth; -} - -int -PQ::Connection::commitTx() const -{ - if (rolledback) { - return rollbackTx(); - } - if (--txDepth == 0) { - checkResultFree(PQexec(conn, "COMMIT"), PGRES_COMMAND_OK); - } - return txDepth; -} - -int -PQ::Connection::rollbackTx() const -{ - if (--txDepth == 0) { - checkResultFree(PQexec(conn, "ROLLBACK"), PGRES_COMMAND_OK); - } - else { - rolledback = true; - } - return txDepth; -} - -bool -PQ::Connection::inTx() const -{ - return txDepth; -} - -void -PQ::Connection::execute(const std::string & sql) const -{ - checkResultFree(PQexec(conn, sql.c_str()), PGRES_COMMAND_OK, PGRES_TUPLES_OK); -} - -DB::BulkDeleteStyle -PQ::Connection::bulkDeleteStyle() const -{ - return DB::BulkDeleteUsingSubSelect; -} - -DB::BulkUpdateStyle -PQ::Connection::bulkUpdateStyle() const -{ - return DB::BulkUpdateUsingFromSrc; -} - -void -PQ::Connection::ping() const -{ - struct pollfd fd { PQsocket(conn), POLLRDHUP | POLLERR | POLLHUP | POLLNVAL, 0 }; - if (PQstatus(conn) != CONNECTION_OK || poll(&fd, 1, 1)) { - if (inTx()) { - throw ConnectionError(conn); - } - PQreset(conn); - if (PQstatus(conn) != CONNECTION_OK) { - throw ConnectionError(conn); - } - } -} - - -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, int alt) -{ - return (PQresultStatus(res) == expected) || (alt != -1 && (PQresultStatus(res) == alt)); -} - -PGresult * -PQ::Connection::checkResult(PGresult * res, int expected, int alt) const -{ - if (!checkResultInt(res, expected, alt)) { - PQclear(res); - throw Error(PQerrorMessage(conn)); - } - return res; -} - -void -PQ::Connection::checkResultFree(PGresult * res, int expected, int alt) const -{ - if (!checkResultInt(res, expected, alt)) { - PQclear(res); - throw Error(PQerrorMessage(conn)); - } - PQclear(res); -} - -void -PQ::Connection::beginBulkUpload(const char * table, const char * extra) const -{ - char buf[BUFSIZ]; - snprintf(buf, BUFSIZ, "COPY %s FROM STDIN %s", table, extra); - checkResultFree(PQexec(conn, buf), PGRES_COPY_IN); -} - -void -PQ::Connection::endBulkUpload(const char * msg) const -{ - switch (PQputCopyEnd(conn, msg)) { - case 0:// block - sleep(1); - endBulkUpload(msg); - return; - case 1:// success - checkResultFree(PQgetResult(conn), PGRES_COMMAND_OK); - return; - default:// -1 is error - throw Error(PQerrorMessage(conn)); - } -} - -size_t -PQ::Connection::bulkUploadData(const char * data, size_t len) const -{ - switch (PQputCopyData(conn, data, len)) { - case 0:// block - sleep(1); - return bulkUploadData(data, len); - case 1:// success - return len; - default:// -1 is error - throw Error(PQerrorMessage(conn)); - } -} - -int64_t -PQ::Connection::insertId() const -{ - SelectCommand getId(this, "SELECT lastval()", pstmntNo++); - int64_t id = -1; - while (getId.fetch()) { - getId[0] >> id; - } - return id; -} - diff --git a/libpqpp/connection.h b/libpqpp/connection.h deleted file mode 100644 index 618c875..0000000 --- a/libpqpp/connection.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef PQ_CONNECTION_H -#define PQ_CONNECTION_H - -#include -#include -#include - -namespace PQ { - class DLL_PUBLIC Connection : public DB::Connection { - public: - Connection(const std::string & info); - ~Connection(); - - void finish() const override; - int beginTx() const override; - int commitTx() const override; - int rollbackTx() const override; - bool inTx() const override; - void ping() const override; - void execute(const std::string & sql) const override; - DB::BulkDeleteStyle bulkDeleteStyle() const override; - DB::BulkUpdateStyle bulkUpdateStyle() const override; - - DB::SelectCommand * newSelectCommand(const std::string & sql) const override; - DB::ModifyCommand * newModifyCommand(const std::string & sql) const override; - - int64_t insertId() const override; - - void beginBulkUpload(const char *, const char *) const override; - void endBulkUpload(const char *) const override; - size_t bulkUploadData(const char *, size_t) const override; - - PGresult * checkResult(PGresult * res, int expected, int alternative = -1) const; - void checkResultFree(PGresult * res, int expected, int alternative = -1) const; - - PGconn * conn; - - private: - static bool checkResultInt(PGresult * res, int expected, int alternative); - - mutable unsigned int txDepth; - mutable unsigned int pstmntNo; - mutable bool rolledback; - }; -} - -#endif - diff --git a/libpqpp/error.cpp b/libpqpp/error.cpp deleted file mode 100644 index f2bebd6..0000000 --- a/libpqpp/error.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#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() -{ - free(msg); -} - -const char * -PQ::Error::what() const throw() -{ - return msg ? msg : "No message"; -} - -PQ::ConnectionError::ConnectionError(const PGconn * conn) : - PQ::Error(PQerrorMessage(conn)) -{ -} - diff --git a/libpqpp/error.h b/libpqpp/error.h deleted file mode 100644 index 8e7c4bc..0000000 --- a/libpqpp/error.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef PQ_ERROR_H -#define PQ_ERROR_H - -#include -#include -#include - -namespace PQ { - class DLL_PUBLIC Error : public DB::Error { - public: - Error(); - Error(const Error &); - Error(const char *); - ~Error() throw(); - - const char * what() const throw(); - - private: - char * msg; - }; - class DLL_PUBLIC ConnectionError : public Error, public virtual DB::ConnectionError { - public: - ConnectionError(const PGconn *); - }; -} - -#endif - diff --git a/libpqpp/mock.cpp b/libpqpp/mock.cpp deleted file mode 100644 index 20d4340..0000000 --- a/libpqpp/mock.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "mock.h" -#include "connection.h" -#include - -namespace PQ { - -Mock::Mock(const std::string & masterdb, const std::string & name, const std::vector & ss) : - MockServerDatabase(masterdb, name, "postgresql") -{ - CreateNewDatabase(); - PlaySchemaScripts(ss); -} - -DB::Connection * -Mock::openConnection() const -{ - return new Connection(stringbf("user=postgres dbname=%s", testDbName)); -} - -Mock::~Mock() -{ - DropDatabase(); -} - -void -Mock::DropDatabase() const -{ - master->execute("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" + testDbName + "'"); - MockServerDatabase::DropDatabase(); -} - -} - diff --git a/libpqpp/mock.h b/libpqpp/mock.h deleted file mode 100644 index e8b7366..0000000 --- a/libpqpp/mock.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MOCKPQDATASOURCE_H -#define MOCKPQDATASOURCE_H - -#include -#include -#include - -namespace PQ { - class DLL_PUBLIC Mock : public DB::MockServerDatabase { - public: - Mock(const std::string & master, const std::string & name, const std::vector & ss); - ~Mock(); - - DB::Connection * openConnection() const override; - - protected: - void DropDatabase() const override; - }; -} - -#endif - diff --git a/libpqpp/modifycommand.cpp b/libpqpp/modifycommand.cpp deleted file mode 100644 index 460ec31..0000000 --- a/libpqpp/modifycommand.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "modifycommand.h" -#include "error.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), - prepared(false) -{ -} - -PQ::ModifyCommand::~ModifyCommand() -{ -} - -void -PQ::ModifyCommand::prepare() const -{ - if (!prepared) { - std::string psql; - psql.reserve(sql.length() + 20); - prepareSql(psql, sql); - c->checkResultFree(PQprepare( - c->conn, stmntName.c_str(), psql.c_str(), values.size(), NULL), PGRES_COMMAND_OK); - prepared = true; - } -} - -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, PGRES_TUPLES_OK); - unsigned int rows = atoi(PQcmdTuples(res)); - PQclear(res); - if (rows == 0 && !anc) { - throw Error("No rows affected"); - } - return rows; -} - diff --git a/libpqpp/modifycommand.h b/libpqpp/modifycommand.h deleted file mode 100644 index b451273..0000000 --- a/libpqpp/modifycommand.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PQ_MODIFYCOMMAND_H -#define PQ_MODIFYCOMMAND_H - -#include -#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); - - private: - void prepare() const; - mutable bool prepared; - }; -} - -#endif - - - diff --git a/libpqpp/pq-column.cpp b/libpqpp/pq-column.cpp new file mode 100644 index 0000000..c569ca2 --- /dev/null +++ b/libpqpp/pq-column.cpp @@ -0,0 +1,83 @@ +#include "pq-column.h" +#include "pq-selectcommand.h" +#include "pq-error.h" +#include +#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; + } + switch (oid) { + case 18: //CHAROID: + case 1043: //VARCHAROID: + 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.boolean(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 704: //TINTERVALOID + case 1083: //TIMEOID: + case 1186: //INTERVALOID + { + int days = 0, hours = 0, minutes = 0, seconds = 0, fractions = 0, flen1 = 0, flen2 = 0; + const char * val = PQgetvalue(sc->execRes, sc->tuple, colNo); + if (sscanf(val, "%d days %d:%d:%d.%n%d%n", &days, &hours, &minutes, &seconds, &flen1, &fractions, &flen2) >= 4) { + h.interval(boost::posix_time::time_duration((24 * days) + hours, minutes, seconds, fractions * pow(10, boost::posix_time::time_res_traits::num_fractional_digits() + flen1 - flen2))); + } + else if (sscanf(val, "%d day %d:%d:%d.%n%d%n", &days, &hours, &minutes, &seconds, &flen1, &fractions, &flen2) >= 4) { + h.interval(boost::posix_time::time_duration((24 * days) + hours, minutes, seconds, fractions * pow(10, boost::posix_time::time_res_traits::num_fractional_digits() + flen1 - flen2))); + } + else { + h.interval(boost::posix_time::duration_from_string(PQgetvalue(sc->execRes, sc->tuple, colNo))); + } + break; + } + case 1082: //DATEOID: + h.timestamp(boost::posix_time::ptime( + boost::gregorian::from_string(PQgetvalue(sc->execRes, sc->tuple, colNo)))); + break; + case 702: //ABSTIMEOID: + case 1114: //TIMESTAMPOID: + case 1184: //TIMESTAMPTZOID: + h.timestamp(boost::posix_time::time_from_string(PQgetvalue(sc->execRes, sc->tuple, colNo))); + break; + default: + h.string(PQgetvalue(sc->execRes, sc->tuple, colNo), PQgetlength(sc->execRes, sc->tuple, colNo)); + } +} + +void +PQ::Column::rebind(DB::Command *, unsigned int) const +{ + throw Error("Not supported"); +} + diff --git a/libpqpp/pq-column.h b/libpqpp/pq-column.h new file mode 100644 index 0000000..5980763 --- /dev/null +++ b/libpqpp/pq-column.h @@ -0,0 +1,23 @@ +#ifndef PG_COLUMN_H +#define PG_COLUMN_H + +#include +#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/pq-command.cpp b/libpqpp/pq-command.cpp new file mode 100644 index 0000000..aeb4914 --- /dev/null +++ b/libpqpp/pq-command.cpp @@ -0,0 +1,156 @@ +#include "pq-command.h" +#include "pq-connection.h" +#include +#include +#include + +static std::string addrStr(void * p, unsigned int no) { + std::string r(50, ' '); + r.resize(snprintf(const_cast(r.c_str()), r.length(), "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)), + c(conn) +{ +} + +PQ::Command::~Command() +{ + for (std::vector::const_iterator i = values.begin(); i != values.end(); ++i) { + free(*i); + } +} + +void +PQ::Command::prepareSql(std::string & psql, const std::string & sql) +{ + char buf[4]; + int p = 1; + bool inquote = false; + for(std::string::const_iterator i = sql.begin(); i != sql.end(); ++i) { + if (*i == '?' && !inquote) { + snprintf(buf, 4, "$%d", p++); + psql += buf; + } + else if (*i == '\'') { + inquote = !inquote; + psql += *i; + } + else { + psql += *i; + } + } +} + +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]); + values[n] = NULL; + } +} + +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::bindParamB(unsigned int n, bool v) +{ + paramsAtLeast(n); + lengths[n] = asprintf(&values[n], "%s", v ? "true" : "false"); + formats[n] = 0; +} +void +PQ::Command::bindParamF(unsigned int n, double v) +{ + paramsAtLeast(n); + lengths[n] = asprintf(&values[n], "%g", v); + formats[n] = 0; +} +void +PQ::Command::bindParamF(unsigned int n, float v) +{ + paramsAtLeast(n); + lengths[n] = asprintf(&values[n], "%g", v); + formats[n] = 0; +} +void +PQ::Command::bindParamS(unsigned int n, const Glib::ustring & s) +{ + paramsAtLeast(n); + values[n] = strndup(s.c_str(), s.bytes()); + formats[n] = 0; + lengths[n] = s.bytes(); +} +void +PQ::Command::bindParamT(unsigned int n, const boost::posix_time::time_duration & v) +{ + paramsAtLeast(n); + auto buf = boost::posix_time::to_simple_string(v); + values[n] = strdup(buf.c_str()); + formats[n] = 0; + lengths[n] = buf.length(); +} +void +PQ::Command::bindParamT(unsigned int n, const boost::posix_time::ptime & v) +{ + paramsAtLeast(n); + auto buf = boost::posix_time::to_iso_extended_string(v); + values[n] = strdup(buf.c_str()); + formats[n] = 0; + lengths[n] = buf.length(); +} +void +PQ::Command::bindNull(unsigned int n) +{ + paramsAtLeast(n); +} + diff --git a/libpqpp/pq-command.h b/libpqpp/pq-command.h new file mode 100644 index 0000000..32faaa0 --- /dev/null +++ b/libpqpp/pq-command.h @@ -0,0 +1,47 @@ +#ifndef PQ_COMMAND_H +#define PQ_COMMAND_H + +#include +#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 bindParamB(unsigned int, bool); + + void bindParamF(unsigned int, double); + void bindParamF(unsigned int, float); + + void bindParamS(unsigned int, const Glib::ustring&); + + void bindParamT(unsigned int, const boost::posix_time::time_duration &); + void bindParamT(unsigned int, const boost::posix_time::ptime &); + + void bindNull(unsigned int); + protected: + static void prepareSql(std::string & psql, const std::string & sql); + const std::string stmntName; + const Connection * c; + + void paramsAtLeast(unsigned int); + std::vector values; + std::vector lengths; + std::vector formats; + }; +} + +#endif + + diff --git a/libpqpp/pq-connection.cpp b/libpqpp/pq-connection.cpp new file mode 100644 index 0000000..db46dc5 --- /dev/null +++ b/libpqpp/pq-connection.cpp @@ -0,0 +1,211 @@ +#include "pq-connection.h" +#include "pq-error.h" +#include "pq-selectcommand.h" +#include "pq-modifycommand.h" +#include +#include +#include + +NAMEDFACTORY("postgresql", PQ::Connection, DB::ConnectionFactory); + +static void setup() __attribute__((constructor(101))); +static void setup() +{ + BOOST_ASSERT(PQisthreadsafe() == 1); + PQinitOpenSSL(1, 0); +} + +static void +noNoticeProcessor(void *, const char *) +{ +} + +PQ::Connection::Connection(const std::string & info) : + conn(PQconnectdb(info.c_str())), + txDepth(0), + pstmntNo(0), + rolledback(false) +{ + if (PQstatus(conn) != CONNECTION_OK) { + ConnectionError ce(conn); + PQfinish(conn); + throw ce; + } + PQsetNoticeProcessor(conn, noNoticeProcessor, NULL); +} + +PQ::Connection::~Connection() +{ + PQfinish(conn); +} + +void +PQ::Connection::finish() const +{ + if (txDepth != 0) { + rollbackTx(); + throw Error("Transaction still open"); + } +} + +int +PQ::Connection::beginTx() const +{ + if (txDepth == 0) { + checkResultFree(PQexec(conn, "BEGIN"), PGRES_COMMAND_OK); + rolledback = false; + } + return ++txDepth; +} + +int +PQ::Connection::commitTx() const +{ + if (rolledback) { + return rollbackTx(); + } + if (--txDepth == 0) { + checkResultFree(PQexec(conn, "COMMIT"), PGRES_COMMAND_OK); + } + return txDepth; +} + +int +PQ::Connection::rollbackTx() const +{ + if (--txDepth == 0) { + checkResultFree(PQexec(conn, "ROLLBACK"), PGRES_COMMAND_OK); + } + else { + rolledback = true; + } + return txDepth; +} + +bool +PQ::Connection::inTx() const +{ + return txDepth; +} + +void +PQ::Connection::execute(const std::string & sql) const +{ + checkResultFree(PQexec(conn, sql.c_str()), PGRES_COMMAND_OK, PGRES_TUPLES_OK); +} + +DB::BulkDeleteStyle +PQ::Connection::bulkDeleteStyle() const +{ + return DB::BulkDeleteUsingSubSelect; +} + +DB::BulkUpdateStyle +PQ::Connection::bulkUpdateStyle() const +{ + return DB::BulkUpdateUsingFromSrc; +} + +void +PQ::Connection::ping() const +{ + struct pollfd fd { PQsocket(conn), POLLRDHUP | POLLERR | POLLHUP | POLLNVAL, 0 }; + if (PQstatus(conn) != CONNECTION_OK || poll(&fd, 1, 1)) { + if (inTx()) { + throw ConnectionError(conn); + } + PQreset(conn); + if (PQstatus(conn) != CONNECTION_OK) { + throw ConnectionError(conn); + } + } +} + + +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, int alt) +{ + return (PQresultStatus(res) == expected) || (alt != -1 && (PQresultStatus(res) == alt)); +} + +PGresult * +PQ::Connection::checkResult(PGresult * res, int expected, int alt) const +{ + if (!checkResultInt(res, expected, alt)) { + PQclear(res); + throw Error(PQerrorMessage(conn)); + } + return res; +} + +void +PQ::Connection::checkResultFree(PGresult * res, int expected, int alt) const +{ + if (!checkResultInt(res, expected, alt)) { + PQclear(res); + throw Error(PQerrorMessage(conn)); + } + PQclear(res); +} + +void +PQ::Connection::beginBulkUpload(const char * table, const char * extra) const +{ + char buf[BUFSIZ]; + snprintf(buf, BUFSIZ, "COPY %s FROM STDIN %s", table, extra); + checkResultFree(PQexec(conn, buf), PGRES_COPY_IN); +} + +void +PQ::Connection::endBulkUpload(const char * msg) const +{ + switch (PQputCopyEnd(conn, msg)) { + case 0:// block + sleep(1); + endBulkUpload(msg); + return; + case 1:// success + checkResultFree(PQgetResult(conn), PGRES_COMMAND_OK); + return; + default:// -1 is error + throw Error(PQerrorMessage(conn)); + } +} + +size_t +PQ::Connection::bulkUploadData(const char * data, size_t len) const +{ + switch (PQputCopyData(conn, data, len)) { + case 0:// block + sleep(1); + return bulkUploadData(data, len); + case 1:// success + return len; + default:// -1 is error + throw Error(PQerrorMessage(conn)); + } +} + +int64_t +PQ::Connection::insertId() const +{ + SelectCommand getId(this, "SELECT lastval()", pstmntNo++); + int64_t id = -1; + while (getId.fetch()) { + getId[0] >> id; + } + return id; +} + diff --git a/libpqpp/pq-connection.h b/libpqpp/pq-connection.h new file mode 100644 index 0000000..618c875 --- /dev/null +++ b/libpqpp/pq-connection.h @@ -0,0 +1,48 @@ +#ifndef PQ_CONNECTION_H +#define PQ_CONNECTION_H + +#include +#include +#include + +namespace PQ { + class DLL_PUBLIC Connection : public DB::Connection { + public: + Connection(const std::string & info); + ~Connection(); + + void finish() const override; + int beginTx() const override; + int commitTx() const override; + int rollbackTx() const override; + bool inTx() const override; + void ping() const override; + void execute(const std::string & sql) const override; + DB::BulkDeleteStyle bulkDeleteStyle() const override; + DB::BulkUpdateStyle bulkUpdateStyle() const override; + + DB::SelectCommand * newSelectCommand(const std::string & sql) const override; + DB::ModifyCommand * newModifyCommand(const std::string & sql) const override; + + int64_t insertId() const override; + + void beginBulkUpload(const char *, const char *) const override; + void endBulkUpload(const char *) const override; + size_t bulkUploadData(const char *, size_t) const override; + + PGresult * checkResult(PGresult * res, int expected, int alternative = -1) const; + void checkResultFree(PGresult * res, int expected, int alternative = -1) const; + + PGconn * conn; + + private: + static bool checkResultInt(PGresult * res, int expected, int alternative); + + mutable unsigned int txDepth; + mutable unsigned int pstmntNo; + mutable bool rolledback; + }; +} + +#endif + diff --git a/libpqpp/pq-error.cpp b/libpqpp/pq-error.cpp new file mode 100644 index 0000000..0d0299c --- /dev/null +++ b/libpqpp/pq-error.cpp @@ -0,0 +1,34 @@ +#include "pq-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() +{ + free(msg); +} + +const char * +PQ::Error::what() const throw() +{ + return msg ? msg : "No message"; +} + +PQ::ConnectionError::ConnectionError(const PGconn * conn) : + PQ::Error(PQerrorMessage(conn)) +{ +} + diff --git a/libpqpp/pq-error.h b/libpqpp/pq-error.h new file mode 100644 index 0000000..8e7c4bc --- /dev/null +++ b/libpqpp/pq-error.h @@ -0,0 +1,28 @@ +#ifndef PQ_ERROR_H +#define PQ_ERROR_H + +#include +#include +#include + +namespace PQ { + class DLL_PUBLIC Error : public DB::Error { + public: + Error(); + Error(const Error &); + Error(const char *); + ~Error() throw(); + + const char * what() const throw(); + + private: + char * msg; + }; + class DLL_PUBLIC ConnectionError : public Error, public virtual DB::ConnectionError { + public: + ConnectionError(const PGconn *); + }; +} + +#endif + diff --git a/libpqpp/pq-mock.cpp b/libpqpp/pq-mock.cpp new file mode 100644 index 0000000..db7eafb --- /dev/null +++ b/libpqpp/pq-mock.cpp @@ -0,0 +1,33 @@ +#include "pq-mock.h" +#include "pq-connection.h" +#include + +namespace PQ { + +Mock::Mock(const std::string & masterdb, const std::string & name, const std::vector & ss) : + MockServerDatabase(masterdb, name, "postgresql") +{ + CreateNewDatabase(); + PlaySchemaScripts(ss); +} + +DB::Connection * +Mock::openConnection() const +{ + return new Connection(stringbf("user=postgres dbname=%s", testDbName)); +} + +Mock::~Mock() +{ + DropDatabase(); +} + +void +Mock::DropDatabase() const +{ + master->execute("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" + testDbName + "'"); + MockServerDatabase::DropDatabase(); +} + +} + diff --git a/libpqpp/pq-mock.h b/libpqpp/pq-mock.h new file mode 100644 index 0000000..e8b7366 --- /dev/null +++ b/libpqpp/pq-mock.h @@ -0,0 +1,22 @@ +#ifndef MOCKPQDATASOURCE_H +#define MOCKPQDATASOURCE_H + +#include +#include +#include + +namespace PQ { + class DLL_PUBLIC Mock : public DB::MockServerDatabase { + public: + Mock(const std::string & master, const std::string & name, const std::vector & ss); + ~Mock(); + + DB::Connection * openConnection() const override; + + protected: + void DropDatabase() const override; + }; +} + +#endif + diff --git a/libpqpp/pq-modifycommand.cpp b/libpqpp/pq-modifycommand.cpp new file mode 100644 index 0000000..e07af0b --- /dev/null +++ b/libpqpp/pq-modifycommand.cpp @@ -0,0 +1,44 @@ +#include "pq-modifycommand.h" +#include "pq-error.h" +#include +#include "pq-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), + prepared(false) +{ +} + +PQ::ModifyCommand::~ModifyCommand() +{ +} + +void +PQ::ModifyCommand::prepare() const +{ + if (!prepared) { + std::string psql; + psql.reserve(sql.length() + 20); + prepareSql(psql, sql); + c->checkResultFree(PQprepare( + c->conn, stmntName.c_str(), psql.c_str(), values.size(), NULL), PGRES_COMMAND_OK); + prepared = true; + } +} + +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, PGRES_TUPLES_OK); + unsigned int rows = atoi(PQcmdTuples(res)); + PQclear(res); + if (rows == 0 && !anc) { + throw Error("No rows affected"); + } + return rows; +} + diff --git a/libpqpp/pq-modifycommand.h b/libpqpp/pq-modifycommand.h new file mode 100644 index 0000000..d29cf5e --- /dev/null +++ b/libpqpp/pq-modifycommand.h @@ -0,0 +1,25 @@ +#ifndef PQ_MODIFYCOMMAND_H +#define PQ_MODIFYCOMMAND_H + +#include +#include "pq-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); + + private: + void prepare() const; + mutable bool prepared; + }; +} + +#endif + + + diff --git a/libpqpp/pq-selectcommand.cpp b/libpqpp/pq-selectcommand.cpp new file mode 100644 index 0000000..26967c5 --- /dev/null +++ b/libpqpp/pq-selectcommand.cpp @@ -0,0 +1,113 @@ +#include "pq-selectcommand.h" +#include "pq-connection.h" +#include "pq-column.h" +#include "pq-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), + txOpened(false), + nTuples(0), + tuple(0), + fTuples(35), + execRes(NULL), + s_declare(mkdeclare()), + s_fetch(mkfetch()), + s_close(mkclose()) +{ +} + +PQ::SelectCommand::~SelectCommand() +{ + if (txOpened) { + c->commitTx(); + } + if (executed) { + PQclear(PQexec(c->conn, s_close.c_str())); + if (execRes) { + PQclear(execRes); + } + } +} + +std::string +PQ::SelectCommand::mkdeclare() const +{ + std::string psql; + psql.reserve(sql.length() + 40); + psql += "DECLARE "; + psql += stmntName; + psql += " CURSOR FOR "; + prepareSql(psql, sql); + return psql; +} + +std::string +PQ::SelectCommand::mkfetch() const +{ + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), "FETCH %d IN %s", fTuples, stmntName.c_str()); + return buf; +} + +std::string +PQ::SelectCommand::mkclose() const +{ + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), "CLOSE %s", stmntName.c_str()); + return buf; +} + +void +PQ::SelectCommand::execute() +{ + if (!executed) { + if (!txOpened) { + c->beginTx(); + txOpened = true; + } + execRes = c->checkResult( + PQexecParams(c->conn, s_declare.c_str(), values.size(), NULL, &values.front(), &lengths.front(), &formats.front(), 0), + PGRES_COMMAND_OK); + fetchTuples(); + unsigned int nFields = PQnfields(execRes); + for (unsigned int f = 0; f < nFields; f += 1) { + insertColumn(DB::ColumnPtr(new Column(this, f))); + } + executed = true; + } +} + +void +PQ::SelectCommand::fetchTuples() +{ + if (execRes) { + PQclear(execRes); + } + execRes = NULL; + execRes = c->checkResult(PQexec(c->conn, s_fetch.c_str()), PGRES_TUPLES_OK); + nTuples = PQntuples(execRes); + tuple = -1; +} + +bool +PQ::SelectCommand::fetch() +{ + execute(); + if ((tuple >= (nTuples - 1)) && (nTuples == fTuples)) { + fetchTuples(); + } + if (tuple++ < (nTuples - 1)) { + return true; + } + else { + PQclear(PQexec(c->conn, s_close.c_str())); + PQclear(execRes); + execRes = NULL; + executed = false; + return false; + } +} + diff --git a/libpqpp/pq-selectcommand.h b/libpqpp/pq-selectcommand.h new file mode 100644 index 0000000..c72c314 --- /dev/null +++ b/libpqpp/pq-selectcommand.h @@ -0,0 +1,40 @@ +#ifndef PQ_SELECTCOMMAND_H +#define PQ_SELECTCOMMAND_H + +#include +#include "pq-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(); + + private: + void fetchTuples(); + std::string mkdeclare() const; + std::string mkfetch() const; + std::string mkclose() const; + + mutable bool executed; + mutable bool txOpened; + int nTuples, tuple, fTuples; + PGresult * execRes; + std::string s_declare; + std::string s_fetch; + std::string s_close; + + friend class Column; + }; +} + +#endif + + diff --git a/libpqpp/selectcommand.cpp b/libpqpp/selectcommand.cpp deleted file mode 100644 index 2312dbb..0000000 --- a/libpqpp/selectcommand.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#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), - txOpened(false), - nTuples(0), - tuple(0), - fTuples(35), - execRes(NULL), - s_declare(mkdeclare()), - s_fetch(mkfetch()), - s_close(mkclose()) -{ -} - -PQ::SelectCommand::~SelectCommand() -{ - if (txOpened) { - c->commitTx(); - } - if (executed) { - PQclear(PQexec(c->conn, s_close.c_str())); - if (execRes) { - PQclear(execRes); - } - } -} - -std::string -PQ::SelectCommand::mkdeclare() const -{ - std::string psql; - psql.reserve(sql.length() + 40); - psql += "DECLARE "; - psql += stmntName; - psql += " CURSOR FOR "; - prepareSql(psql, sql); - return psql; -} - -std::string -PQ::SelectCommand::mkfetch() const -{ - char buf[BUFSIZ]; - snprintf(buf, sizeof(buf), "FETCH %d IN %s", fTuples, stmntName.c_str()); - return buf; -} - -std::string -PQ::SelectCommand::mkclose() const -{ - char buf[BUFSIZ]; - snprintf(buf, sizeof(buf), "CLOSE %s", stmntName.c_str()); - return buf; -} - -void -PQ::SelectCommand::execute() -{ - if (!executed) { - if (!txOpened) { - c->beginTx(); - txOpened = true; - } - execRes = c->checkResult( - PQexecParams(c->conn, s_declare.c_str(), values.size(), NULL, &values.front(), &lengths.front(), &formats.front(), 0), - PGRES_COMMAND_OK); - fetchTuples(); - unsigned int nFields = PQnfields(execRes); - for (unsigned int f = 0; f < nFields; f += 1) { - insertColumn(DB::ColumnPtr(new Column(this, f))); - } - executed = true; - } -} - -void -PQ::SelectCommand::fetchTuples() -{ - if (execRes) { - PQclear(execRes); - } - execRes = NULL; - execRes = c->checkResult(PQexec(c->conn, s_fetch.c_str()), PGRES_TUPLES_OK); - nTuples = PQntuples(execRes); - tuple = -1; -} - -bool -PQ::SelectCommand::fetch() -{ - execute(); - if ((tuple >= (nTuples - 1)) && (nTuples == fTuples)) { - fetchTuples(); - } - if (tuple++ < (nTuples - 1)) { - return true; - } - else { - PQclear(PQexec(c->conn, s_close.c_str())); - PQclear(execRes); - execRes = NULL; - executed = false; - return false; - } -} - diff --git a/libpqpp/selectcommand.h b/libpqpp/selectcommand.h deleted file mode 100644 index e4acce7..0000000 --- a/libpqpp/selectcommand.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef PQ_SELECTCOMMAND_H -#define PQ_SELECTCOMMAND_H - -#include -#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(); - - private: - void fetchTuples(); - std::string mkdeclare() const; - std::string mkfetch() const; - std::string mkclose() const; - - mutable bool executed; - mutable bool txOpened; - int nTuples, tuple, fTuples; - PGresult * execRes; - std::string s_declare; - std::string s_fetch; - std::string s_close; - - friend class Column; - }; -} - -#endif - - diff --git a/libpqpp/unittests/testpq.cpp b/libpqpp/unittests/testpq.cpp index 3f55c63..b321075 100644 --- a/libpqpp/unittests/testpq.cpp +++ b/libpqpp/unittests/testpq.cpp @@ -5,12 +5,12 @@ #include #include #include -#include "mock.h" +#include #include #include #include -#include "../error.h" -#include "../connection.h" +#include +#include class StandardMockDatabase : public PQ::Mock { public: -- cgit v1.2.3