From c5f116af8c044883edf44311173a0c6c017b7d59 Mon Sep 17 00:00:00 2001
From: randomdan <randomdan@localhost>
Date: Thu, 17 Feb 2011 20:42:53 +0000
Subject: Add check function for when a connection is finished with, but you
 don't want to close it Use prepared statements only for modifications, use
 fetch instead for selects (doesn't load an entire record set) Support varchar
 oid

---
 libpqpp/column.cpp        |  1 +
 libpqpp/command.cpp       | 32 ++------------------------
 libpqpp/command.h         |  2 --
 libpqpp/connection.cpp    | 22 ++++++++++++++++--
 libpqpp/connection.h      |  4 +++-
 libpqpp/modifycommand.cpp | 27 ++++++++++++++++++++--
 libpqpp/modifycommand.h   |  5 ++++
 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)
 {
 }
@@ -25,34 +24,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)
 {
@@ -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;
 	}
-- 
cgit v1.2.3