diff options
-rw-r--r-- | libpqpp/column.cpp | 1 | ||||
-rw-r--r-- | libpqpp/command.cpp | 32 | ||||
-rw-r--r-- | libpqpp/command.h | 2 | ||||
-rw-r--r-- | libpqpp/connection.cpp | 22 | ||||
-rw-r--r-- | libpqpp/connection.h | 4 | ||||
-rw-r--r-- | libpqpp/modifycommand.cpp | 27 | ||||
-rw-r--r-- | libpqpp/modifycommand.h | 5 | ||||
-rw-r--r-- | libpqpp/selectcommand.cpp | 58 |
8 files changed, 102 insertions, 49 deletions
diff --git a/libpqpp/column.cpp b/libpqpp/column.cpp index 1bd212a..b931d08 100644 --- a/libpqpp/column.cpp +++ b/libpqpp/column.cpp @@ -26,6 +26,7 @@ PQ::Column::apply(DB::HandleField & h) const struct tm tm; 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)); diff --git a/libpqpp/command.cpp b/libpqpp/command.cpp index 419d953..6edc090 100644 --- a/libpqpp/command.cpp +++ b/libpqpp/command.cpp @@ -6,14 +6,13 @@ 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)); + 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) { } @@ -26,34 +25,6 @@ PQ::Command::~Command() } void -PQ::Command::prepare() const -{ - if (!prepared) { - std::string psql; - psql.reserve(sql.length() + 20); - 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; - } - } - c->checkResultFree(PQprepare( - c->conn, stmntName.c_str(), psql.c_str(), values.size(), NULL), PGRES_COMMAND_OK); - prepared = true; - } -} - -void PQ::Command::paramsAtLeast(unsigned int n) { if (values.size() <= n) { @@ -63,6 +34,7 @@ PQ::Command::paramsAtLeast(unsigned int n) } else { free(values[n]); + values[n] = NULL; } } diff --git a/libpqpp/command.h b/libpqpp/command.h index 697516a..3fb6e24 100644 --- a/libpqpp/command.h +++ b/libpqpp/command.h @@ -29,9 +29,7 @@ namespace PQ { void bindNull(unsigned int); protected: - void prepare() const; const std::string stmntName; - mutable bool prepared; const Connection * c; void paramsAtLeast(unsigned int); diff --git a/libpqpp/connection.cpp b/libpqpp/connection.cpp index ef8e72d..11c043e 100644 --- a/libpqpp/connection.cpp +++ b/libpqpp/connection.cpp @@ -11,7 +11,8 @@ noNoticeProcessor(void *, const char *) PQ::Connection::Connection(const std::string & info) : conn(PQconnectdb(info.c_str())), txDepth(0), - pstmntNo(0) + pstmntNo(0), + rolledback(false) { if (PQstatus(conn) != CONNECTION_OK) { throw ConnectionError(); @@ -24,11 +25,21 @@ 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; } @@ -36,6 +47,9 @@ PQ::Connection::beginTx() const int PQ::Connection::commitTx() const { + if (rolledback) { + return rollbackTx(); + } if (--txDepth == 0) { checkResultFree(PQexec(conn, "COMMIT"), PGRES_COMMAND_OK); } @@ -48,6 +62,9 @@ PQ::Connection::rollbackTx() const if (--txDepth == 0) { checkResultFree(PQexec(conn, "ROLLBACK"), PGRES_COMMAND_OK); } + else { + rolledback = true; + } return txDepth; } @@ -93,13 +110,14 @@ PQ::Connection::checkResultInt(PGresult * res, int expected) return (PQresultStatus(res) == expected); } -void +PGresult * PQ::Connection::checkResult(PGresult * res, int expected) const { if (!checkResultInt(res, expected)) { PQclear(res); throw Error(PQerrorMessage(conn)); } + return res; } void diff --git a/libpqpp/connection.h b/libpqpp/connection.h index 4b9b28e..b46151d 100644 --- a/libpqpp/connection.h +++ b/libpqpp/connection.h @@ -10,6 +10,7 @@ namespace PQ { Connection(const std::string & info); ~Connection(); + void finish() const; int beginTx() const; int commitTx() const; int rollbackTx() const; @@ -21,7 +22,7 @@ namespace PQ { DB::SelectCommand * newSelectCommand(const std::string & sql) const; DB::ModifyCommand * newModifyCommand(const std::string & sql) const; - void checkResult(PGresult * res, int expected) const; + PGresult * checkResult(PGresult * res, int expected) const; void checkResultFree(PGresult * res, int expected) const; PGconn * conn; @@ -31,6 +32,7 @@ namespace PQ { mutable unsigned int txDepth; mutable unsigned int pstmntNo; + mutable bool rolledback; }; } diff --git a/libpqpp/modifycommand.cpp b/libpqpp/modifycommand.cpp index ae7abeb..c14ff74 100644 --- a/libpqpp/modifycommand.cpp +++ b/libpqpp/modifycommand.cpp @@ -6,7 +6,8 @@ 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::Command(conn, sql, no), + prepared(false) { } @@ -17,7 +18,29 @@ PQ::ModifyCommand::~ModifyCommand() unsigned int PQ::ModifyCommand::execute(bool anc) { - prepare(); + if (!prepared) { + std::string psql; + psql.reserve(sql.length() + 20); + 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; + } + } + c->checkResultFree(PQprepare( + c->conn, stmntName.c_str(), psql.c_str(), values.size(), NULL), PGRES_COMMAND_OK); + prepared = true; + } PGresult * res = PQexecPrepared(c->conn, stmntName.c_str(), values.size(), &values.front(), &lengths.front(), &formats.front(), 0); c->checkResult(res, PGRES_COMMAND_OK); unsigned int rows = atoi(PQcmdTuples(res)); diff --git a/libpqpp/modifycommand.h b/libpqpp/modifycommand.h index a9cdbef..8f63a2e 100644 --- a/libpqpp/modifycommand.h +++ b/libpqpp/modifycommand.h @@ -12,6 +12,11 @@ namespace PQ { virtual ~ModifyCommand(); unsigned int execute(bool); + + private: + const std::string stmntName; + void prepare() const; + mutable bool prepared; }; } diff --git a/libpqpp/selectcommand.cpp b/libpqpp/selectcommand.cpp index 6ed0d5a..9d3f356 100644 --- a/libpqpp/selectcommand.cpp +++ b/libpqpp/selectcommand.cpp @@ -12,11 +12,14 @@ PQ::SelectCommand::SelectCommand(const Connection * conn, const std::string & sq tuple(0), execRes(NULL) { + c->beginTx(); } PQ::SelectCommand::~SelectCommand() { - if (execRes) { + c->commitTx(); + if (executed) { + PQclear(PQexec(c->conn, ("CLOSE " + stmntName).c_str())); PQclear(execRes); } for (unsigned int f = 0; f < fields.size(); f += 1) { @@ -28,18 +31,30 @@ 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); - 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; + std::string psql; + psql.reserve(sql.length() + 40); + char buf[4]; + int p = 1; + bool inquote = false; + psql += "DECLARE "; + psql += stmntName; + psql += " CURSOR FOR "; + 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; + } } - nTuples = PQntuples(execRes); - tuple = -1; + c->checkResultFree( + PQexecParams(c->conn, psql.c_str(), values.size(), NULL, &values.front(), &lengths.front(), &formats.front(), 0), + PGRES_COMMAND_OK); executed = true; } } @@ -48,10 +63,29 @@ bool PQ::SelectCommand::fetch() { execute(); + if (tuple >= (nTuples - 1)) { + if (execRes) { + PQclear(execRes); + } + execRes = c->checkResult(PQexec(c->conn, ("FETCH 35 IN " + stmntName).c_str()), PGRES_TUPLES_OK); + nTuples = PQntuples(execRes); + tuple = -1; + } + if (fields.empty()) { + 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; + } + } if (tuple++ < (nTuples - 1)) { return true; } else { + PQclear(PQexec(c->conn, ("CLOSE " + stmntName).c_str())); + PQclear(execRes); executed = false; return false; } |