diff C++/DriverPostgres.cpp @ 214:6c49e5e3ecc8

Driver: now data is shared internally
author David Demelier <markand@malikania.fr>
date Tue, 25 Mar 2014 20:42:11 +0100
parents d263f85f43a4
children bb0a7d1a3f86 8fc177bbc4a6
line wrap: on
line diff
--- a/C++/DriverPostgres.cpp	Mon Mar 24 13:54:28 2014 +0100
+++ b/C++/DriverPostgres.cpp	Tue Mar 25 20:42:11 2014 +0100
@@ -1,7 +1,7 @@
 /*
  * DriverPostgres.cpp -- PostgreSQL driver
  *
- * Copyright (c) 2013, David Demelier <markand@malikania.fr>
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,140 +16,164 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <iostream>
 #include <sstream>
+#include <vector>
 
 #include "DriverPostgres.h"
 
-/* --------------------------------------------------------
- * Query PostgreSQL (protected methods)
- * -------------------------------------------------------- */
+namespace {
+
+struct PGConnDeleter {
+	void operator()(PGconn *conn)
+	{
+		PQfinish(conn);
+	}
+};
 
-bool QueryPostgres::getBoolean(int row, const std::string &column)
-{
-	int idx = PQfnumber(m_result.get(), column.c_str());
-	std::string code = PQgetvalue(m_result.get(), row, idx);
+struct PGQueryDeleter {
+	void operator()(PGresult *result)
+	{
+		PQclear(result);
+	}
+};
 
-	return code[0] == 't';
+using PostgresResult	= std::shared_ptr<PGresult>;
+using PostgresConn	= std::shared_ptr<PGconn>;
+
 }
 
-Date QueryPostgres::getDate(int row, const std::string &column)
-{
-	int idx = PQfnumber(m_result.get(), column.c_str());
-	long timestamp = static_cast<long>(time(0));
-
-	try {
-		std::ostringstream oss;
-		std::string value, req;
-
-		value = PQgetvalue(m_result.get(), row, idx);
+/**
+ * @class QueryPostgres
+ * @brief Query implementation for PostgreSQL.
+ */
+class QueryPostgresImpl : public DriverQuery::Impl {
+private:
+	PostgresConn	m_connection;
+	PostgresResult	m_result;
 
-		/*
-		 * Convert the date using the SQL function so that user does
-		 * not require any explicit conversion itself.
-		 */
-		oss << "Select EXTRACT(EPOCH FROM TIMESTAMP '";
-		oss << value;
-		oss << "') AS RESULT";
-		req = oss.str();
+public:
+	/**
+	 * Constructor used by DriverPostgres
+	 *
+	 * @param result the result
+	 */
+	QueryPostgresImpl(PostgresConn conn, PostgresResult result);
 
-		Query::Ptr result = m_driver->query(req);
-		timestamp = result->get<double>(0, "RESULT");
-	} catch (...) { }
+	/**
+	 * @copydoc Query::getBoolean
+	 */
+	virtual bool getBoolean(int row, const std::string &column);
 
-	return Date(static_cast<time_t>(timestamp));
-}
+	/**
+	 * @copydoc Query::getDate
+	 */
+	virtual time_t getDate(int row, const std::string &column);
+
+	/**
+	 * @copydoc Query::getDouble
+	 */
+	virtual double getDouble(int row, const std::string &column);
 
-double QueryPostgres::getDouble(int row, const std::string &column)
-{
-	int idx = PQfnumber(m_result.get(), column.c_str());
+	/**
+	 * @copydoc Query::getInt
+	 */
+	virtual int getInt(int row, const std::string &column);
 
-	return std::stod(PQgetvalue(m_result.get(), row, idx));
-}
+	/**
+	 * @copydoc Query::getString
+	 */
+	virtual std::string getString(int row, const std::string &column);
 
-int QueryPostgres::getInt(int row, const std::string &column)
-{
-	int idx = PQfnumber(m_result.get(), column.c_str());
+	/**
+	 * @copydoc Query::type
+	 */
+	virtual DriverColumn type(const std::string &column) const;
 
-	return std::stoi(PQgetvalue(m_result.get(), row, idx));
-}
+	/**
+	 * @copydoc Query::countRows
+	 */
+	virtual int countRows();
 
-std::string QueryPostgres::getString(int row, const std::string &column)
-{
-	int idx = PQfnumber(m_result.get(), column.c_str());
+	/**
+	 * @copydoc Query::countColumns
+	 */
+	virtual int countColumns();
 
-	return std::string(PQgetvalue(m_result.get(), row, idx));
-}
+	/**
+	 * @copydoc Query::isNull
+	 */
+	virtual bool isNull(int row, const std::string &column);
 
-/* --------------------------------------------------------
- * Query PostgreSQL (public methods)
- * -------------------------------------------------------- */
+	/**
+	 * @copydoc Query::dump
+	 */
+	virtual void dump();
+};
 
-QueryPostgres::QueryPostgres(Driver::Ptr driver, PostgresResult result)
-{
-	m_driver = driver;
-	m_result = std::move(result);
-}
-
-QueryPostgres::~QueryPostgres()
+QueryPostgresImpl::QueryPostgresImpl(PostgresConn conn, PostgresResult result)
+	: m_connection(conn)
+	, m_result(result)
 {
 }
 
-ColumnType QueryPostgres::type(const std::string &column) const
+DriverColumn QueryPostgresImpl::type(const std::string &column) const
 {
-	ColumnType type;
+	DriverColumn type;
 	int pqType, index;
 	
 	index = PQfnumber(m_result.get(), column.c_str());
 	pqType = PQftype(m_result.get(), index);
 	switch (pqType) {
 	case 16:
-		type = ColumnType::Boolean;
+		type = DriverColumn::Boolean;
 		break;
 	case 1082:
 	case 1083:
 	case 1114:
-		type = ColumnType::Date;
+	case 1184:
+		type = DriverColumn::Date;
 		break;
 	case 1700:
 	case 700:
 	case 701:
-		type = ColumnType::Double;
+		type = DriverColumn::Double;
 		break;
 	case 20:
 	case 21:
 	case 23:
-		type = ColumnType::Integer;
+		type = DriverColumn::Integer;
 		break;
 	case 25:
 	case 1042:
 	case 1043:
-		type = ColumnType::String;
+		type = DriverColumn::String;
 		break;
 	default:
-		type = ColumnType::Invalid;
+		type = DriverColumn::Invalid;
 	}
 
 	return type;
 }
 
-int QueryPostgres::countRows()
+int QueryPostgresImpl::countRows()
 {
 	return PQntuples(m_result.get());
 }
 
-int QueryPostgres::countColumns()
+int QueryPostgresImpl::countColumns()
 {
 	return PQnfields(m_result.get());
 }
 
-bool QueryPostgres::isNull(int row, const std::string &column)
+bool QueryPostgresImpl::isNull(int row, const std::string &column)
 {
 	int idx = PQfnumber(m_result.get(), column.c_str());
 
 	return PQgetisnull(m_result.get(), row, idx) == 1;
 }
 
-void QueryPostgres::dump(void)
+void QueryPostgresImpl::dump(void)
 {
 	std::cout << "Dumping PostgreSQL result, ";
 	std::cout << countRows() << " rows, ";
@@ -166,12 +190,120 @@
 	}
 }
 
+bool QueryPostgresImpl::getBoolean(int row, const std::string &column)
+{
+	int idx = PQfnumber(m_result.get(), column.c_str());
+	std::string code = PQgetvalue(m_result.get(), row, idx);
+
+	return code[0] == 't';
+}
+
+time_t QueryPostgresImpl::getDate(int row, const std::string &column)
+{
+	int idx = PQfnumber(m_result.get(), column.c_str());
+	time_t timestamp = std::time(nullptr);
+
+	try {
+		std::ostringstream oss;
+		std::string value, req;
+
+		value = PQgetvalue(m_result.get(), row, idx);
+
+		/*
+		 * Convert the date using the SQL function so that user does
+		 * not require any explicit conversion itself.
+		 */
+		printf("%s\n", value.c_str());
+		oss << "Select EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '";
+		oss << value;
+		oss << "') AS RESULT";
+		req = oss.str();
+
+		auto info = PQexec(m_connection.get(), oss.str().c_str());
+		auto status = PQresultStatus(info);
+
+		if (info != nullptr && (status == PGRES_COMMAND_OK ||
+		    status == PGRES_TUPLES_OK))
+		{
+			puts("SUCESS");
+			timestamp = atoi(PQgetvalue(info, 0, 0));
+			PQclear(info);
+		}
+	} catch (...) { }
+
+	return timestamp;
+}
+
+double QueryPostgresImpl::getDouble(int row, const std::string &column)
+{
+	int idx = PQfnumber(m_result.get(), column.c_str());
+
+	return std::stod(PQgetvalue(m_result.get(), row, idx));
+}
+
+int QueryPostgresImpl::getInt(int row, const std::string &column)
+{
+	int idx = PQfnumber(m_result.get(), column.c_str());
+
+	return std::stoi(PQgetvalue(m_result.get(), row, idx));
+}
+
+std::string QueryPostgresImpl::getString(int row, const std::string &column)
+{
+	int idx = PQfnumber(m_result.get(), column.c_str());
+
+	return std::string(PQgetvalue(m_result.get(), row, idx));
+}
+
 /* --------------------------------------------------------
- * Request PostgreSQL
+ * Request PostgreSQL Impl
  * -------------------------------------------------------- */
 
-RequestPostgres::RequestPostgres(std::shared_ptr<PGconn> conn, const std::string &command)
-	: Request(command)
+/**
+ * @class RequestPostgres
+ * @brief Request implementation for PostgreSQL.
+ */
+class RequestPostgres : public DriverRequest::Impl {
+private:
+	std::shared_ptr<PGconn> m_connection;
+
+protected:
+	/**
+	 * @copydoc Request::bindBoolean
+	 */
+	virtual std::string bindBoolean(bool value);
+
+	/**
+	 * @copydoc Request::bindDate
+	 */
+	virtual std::string bindDate(time_t value);
+
+	/**
+	 * @copydoc Request::bindDouble
+	 */
+	virtual std::string bindDouble(double value);
+
+	/**
+	 * @copydoc Request::bindInteger
+	 */
+	virtual std::string bindInteger(int value);
+
+	/**
+	 * @copydoc Request::bindString
+	 */
+	virtual std::string bindString(std::string value);
+
+public:
+	/**
+	 * Construct a request for PostgreSQL.
+	 *
+	 * @param conn the connection
+	 * @param command the command
+	 */
+	RequestPostgres(PostgresConn &conn);
+};
+
+RequestPostgres::RequestPostgres(PostgresConn &conn)
 {
 	m_connection = conn;
 }
@@ -181,13 +313,11 @@
 	return (value) ? "'t'" : "'f'";
 }
 
-std::string RequestPostgres::bindDate(Date value)
+std::string RequestPostgres::bindDate(time_t value)
 {
 	std::ostringstream oss;
 
-	oss << "to_timestamp(";
-	oss << value.getTimestamp();
-	oss << ")";
+	oss << "to_timestamp(" << value << ")";
 
 	return oss.str();
 }
@@ -197,7 +327,7 @@
 	return std::to_string(value);
 }
 
-std::string RequestPostgres::bindInt(int value)
+std::string RequestPostgres::bindInteger(int value)
 {
 	return std::to_string(value);
 }
@@ -218,20 +348,61 @@
 }
 
 /* --------------------------------------------------------
- * Driver PostgreSQL
+ * Driver PostgreSQL impl
  * -------------------------------------------------------- */
 
-DriverPostgres::DriverPostgres()
-{
-}
+/**
+ * @class DriverPostgres
+ * @brief Driver implementation for PostgreSQL.
+ */
+class DriverPostgresImpl : public Driver::Impl {
+private:
+	PostgresConn m_connection;
+
+	/**
+	 * Convert the Params from the config
+	 * to the PostgreSQL string.
+	 *
+	 * @param settings the table
+	 * @return a string to be used
+	 */
+	std::string convert(Driver::Params &settings);
+
+public:
+	/**
+	 * @copydoc Driver::connect
+	 */
+	virtual void connect(const Driver::Params &params);
 
-DriverPostgres::~DriverPostgres()
-{
-}
+	/**
+	 * @copydoc Driver::prepare
+	 */
+	virtual DriverRequest prepare(const std::string &command);
+
+	/**
+	 * @copydoc Driver::query
+	 */
+	virtual DriverQuery query(const std::string &command);
 
-std::string DriverPostgres::convert(Params &params)
+	/**
+	 * @copydoc Driver::description
+	 */
+	virtual std::string description() const;
+
+	/**
+	 * @copydoc Driver::version
+	 */
+	virtual std::string version() const;
+};
+
+std::string DriverPostgresImpl::convert(Driver::Params &params)
 {
 	std::ostringstream oss;
+	std::vector<std::string> required { "host", "port", "user", "database", "password" };
+
+	for (auto s : required)
+		if (params.count(s) <= 0)
+			throw std::runtime_error("missing parameter " + s);
 
 	oss << "host = " << params["host"] << " ";
 	oss << "port = " << params["port"] << " ";
@@ -242,55 +413,54 @@
 	return oss.str();
 }
 
-bool DriverPostgres::connect(const Params &params)
+void DriverPostgresImpl::connect(const Driver::Params &params)
 {
-	Params copy = params;
-	PGconn *conn = PQconnectdb(convert(copy).c_str());
+	auto copy = params;
+	auto conn = PQconnectdb(convert(copy).c_str());
 
-	if (conn == nullptr) {
-		m_error = strerror(ENOMEM);
-		return false;
-	}
+	if (conn == nullptr)
+		throw std::runtime_error(std::strerror(ENOMEM));
 
 	if (PQstatus(conn) == CONNECTION_BAD) {
-		m_error = PQerrorMessage(conn);
+		auto error = PQerrorMessage(conn);
 		PQfinish(conn);
 
-		return false;
+		throw std::runtime_error(error);
 	}
 
-	m_connection = std::shared_ptr<PGconn>(conn, PGDeleter());
-
-	return true;
+	m_connection = std::shared_ptr<PGconn>(conn, PGConnDeleter());
 }
 
-Request::Ptr DriverPostgres::prepare(const std::string &command)
+DriverRequest DriverPostgresImpl::prepare(const std::string &command)
 {
-	return std::make_shared<RequestPostgres>(m_connection, command);
+	return DriverRequest(std::make_shared<RequestPostgres>(m_connection), command);
 }
 
-Query::Ptr DriverPostgres::query(const std::string &cmd)
+DriverQuery DriverPostgresImpl::query(const std::string &cmd)
 {
 	PGresult *info;
 
 	// If NULL, the libpq said no memory
 	info = PQexec(m_connection.get(), cmd.c_str());
 	if (info == nullptr)
-		throw Query::Error(strerror(ENOMEM));
+		throw std::runtime_error(strerror(ENOMEM));
 
 	// If an error occured
 	int errorCode = PQresultStatus(info);
 	if (errorCode != PGRES_COMMAND_OK && errorCode != PGRES_TUPLES_OK) {
-		std::string error = PQresultErrorMessage(info);
+		auto error = PQresultErrorMessage(info);
 		PQclear(info);
-		throw Query::Error(error);
+
+		throw std::runtime_error(error);
 	}
 
-	return std::unique_ptr<Query>(new QueryPostgres(shared_from_this(),
-				      QueryPostgres::PostgresResult(info)));
+	auto result = std::shared_ptr<PGresult>(info, PGQueryDeleter());
+	auto impl = std::make_shared<QueryPostgresImpl>(m_connection, result);
+
+	return DriverQuery(impl);
 }
 
-std::string DriverPostgres::description() const
+std::string DriverPostgresImpl::description() const
 {
 	std::ostringstream oss;
 
@@ -303,7 +473,7 @@
 	return oss.str();
 }
 
-std::string DriverPostgres::version() const
+std::string DriverPostgresImpl::version() const
 {
 	std::ostringstream oss;
 
@@ -311,3 +481,12 @@
 
 	return oss.str();
 }
+
+/* --------------------------------------------------------
+ * Driver PostgreSQL
+ * -------------------------------------------------------- */
+
+DriverPostgres::DriverPostgres()
+{
+	m_impl = std::make_shared<DriverPostgresImpl>();
+}