Mercurial > code
changeset 214:6c49e5e3ecc8
Driver: now data is shared internally
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 25 Mar 2014 20:42:11 +0100 |
parents | 1829c4266bee |
children | 5bb5712d400f |
files | C++/Driver.cpp C++/Driver.h C++/DriverPostgres.cpp C++/DriverPostgres.h |
diffstat | 4 files changed, 641 insertions(+), 555 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Driver.cpp Mon Mar 24 13:54:28 2014 +0100 +++ b/C++/Driver.cpp Tue Mar 25 20:42:11 2014 +0100 @@ -1,7 +1,7 @@ /* * Driver.cpp -- generic SQL driver access * - * 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,31 +16,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <map> -#include <string> #include <sstream> #include "Driver.h" /* --------------------------------------------------------- - * Query::Error class - * --------------------------------------------------------- */ + * DriverQuery class + * ---------------------------------------------------------*/ -Query::Error::Error(const std::string &error) - : m_error(error) +DriverQuery::DriverQuery(Ptr impl) + : m_impl(impl) { } -const char *Query::Error::Error::what() const throw() -{ - return m_error.c_str(); -} - -/* --------------------------------------------------------- - * Query class - * ---------------------------------------------------------*/ - -void Query::assertRequest(int row, const std::string &column, ColumnType wanted) +void DriverQuery::assertRequest(int row, const std::string &column, DriverColumn wanted) { std::ostringstream oss; @@ -49,69 +38,55 @@ oss << "Invalid row index " << row; oss << ", expected [0.." << countRows() << "]"; - throw Error(oss.str()); + throw std::runtime_error(oss.str()); } // Not found or bad column? if (type(column) != wanted) { oss << "Invalid or not found column `" << column << "'"; - throw Error(oss.str()); + throw std::runtime_error(oss.str()); } } -Query::~Query() +DriverQuery::~DriverQuery() { } -template <> -bool Query::get(int row, const std::string &column) +DriverColumn DriverQuery::type(const std::string &column) const { - assertRequest(row, column, ColumnType::Boolean); - - return getBoolean(row, column); + return m_impl->type(column); } -template <> -Date Query::get(int row, const std::string &column) +int DriverQuery::countRows() { - assertRequest(row, column, ColumnType::Date); - - return getDate(row, column); + return m_impl->countRows(); } -template <> -double Query::get(int row, const std::string &column) +int DriverQuery::countColumns() { - assertRequest(row, column, ColumnType::Double); - - return getDouble(row, column); + return m_impl->countColumns(); } -template <> -int Query::get(int row, const std::string &column) +bool DriverQuery::isNull(int row, const std::string &column) { - assertRequest(row, column, ColumnType::Integer); - - return getInt(row, column); + return m_impl->isNull(row, column); } -template <> -std::string Query::get(int row, const std::string &column) +void DriverQuery::dump() { - assertRequest(row, column, ColumnType::String); - - return getString(row, column); + m_impl->dump(); } /* -------------------------------------------------------- - * Request class + * DriverRequest class * -------------------------------------------------------- */ -Request::Request(const std::string &command) +DriverRequest::DriverRequest(Ptr impl, const std::string &command) : m_pos(0) , m_params(0) , m_command(command) + , m_impl(impl) { int i = -1; @@ -119,17 +94,17 @@ ++ m_params; } -Request::~Request() +DriverRequest::~DriverRequest() { } -void Request::assertCorrect() +void DriverRequest::assertCorrect() { if (m_params <= 0) throw std::runtime_error("no more arguments to set"); } -void Request::setValue(const std::string &value) +void DriverRequest::setValue(const std::string &value) { assertCorrect(); @@ -139,43 +114,7 @@ m_params --; } -template <> -void Request::bind(bool value) -{ - setValue(bindBoolean(value)); -} - -template <> -void Request::bind(Date value) -{ - setValue(bindDate(value)); -} - -template <> -void Request::bind(double value) -{ - setValue(bindDouble(value)); -} - -template <> -void Request::bind(int value) -{ - setValue(bindInt(value)); -} - -template <> -void Request::bind(std::string value) -{ - setValue(bindString(value)); -} - -template <> -void Request::bind(const char *value) -{ - setValue(bindString(value)); -} - -Request::operator std::string() +DriverRequest::operator std::string() { return m_command; } @@ -184,20 +123,32 @@ * Driver class * -------------------------------------------------------- */ -Driver::Driver() +void Driver::connect(const Params ¶ms) { + m_impl->connect(params); } -Driver::~Driver() +DriverRequest Driver::prepare(const std::string &command) { + return m_impl->prepare(command); +} + +DriverQuery Driver::query(const std::string &sql) +{ + return m_impl->query(sql); } -const std::string &Driver::getError() const +DriverQuery Driver::query(DriverRequest request) { - return m_error; + return query(static_cast<std::string>(request)); } -Query::Ptr Driver::query(Request::Ptr request) +std::string Driver::description() const { - return query(static_cast<std::string>(*request)); + return m_impl->description(); } + +std::string Driver::version() const +{ + return m_impl->version(); +}
--- a/C++/Driver.h Mon Mar 24 13:54:28 2014 +0100 +++ b/C++/Driver.h Tue Mar 25 20:42:11 2014 +0100 @@ -1,7 +1,7 @@ /* * Driver.h -- generic SQL driver access * - * 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 @@ -19,20 +19,19 @@ #ifndef _DRIVER_H_ #define _DRIVER_H_ -#include <iostream> +#include <ctime> #include <memory> #include <string> #include <unordered_map> - -#include "Date.h" +#include <type_traits> /** - * @enum ColumnType + * @enum DriverColumn * @brief The column type request * * Used for the drivers. */ -enum class ColumnType { +enum class DriverColumn { Invalid, //! not found Boolean, //! bool or 0 / 1 Date, //! date see Common/Date.h @@ -41,6 +40,9 @@ String, //! varchar to std::string }; +template <typename T> +struct DriverTypeInfo : std::false_type { }; + /** * @class Query * @brief Class for querying the database @@ -51,24 +53,101 @@ * * @see Driver::query */ -class Query { +class DriverQuery { public: - using Ptr = std::shared_ptr<Query>; + friend struct DriverTypeInfo<bool>; + friend struct DriverTypeInfo<time_t>; + friend struct DriverTypeInfo<double>; + friend struct DriverTypeInfo<int>; + friend struct DriverTypeInfo<std::string>; + + class Impl { + public: + /** + * Get a bool. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual bool getBoolean(int row, const std::string &column) = 0; + + /** + * Get a Date. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual time_t getDate(int row, const std::string &column) = 0; + + /** + * Get a double. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual double getDouble(int row, const std::string &column) = 0; + + /** + * Get a integer. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual int getInt(int row, const std::string &column) = 0; - /** - * @class Error - * @brief Query exception on query error - */ - class Error : public std::exception { - private: - std::string m_error; + /** + * Get a string. + * + * @param row the row number + * @param column the column + * @return the value + */ + virtual std::string getString(int row, const std::string &column) = 0; + + /** + * Returns the type of a named column. + * + * @param column the column name + * @return the type + */ + virtual DriverColumn type(const std::string &column) const = 0; + + /** + * Tells how many rows has been fetched. + * + * @return the number of rows + */ + virtual int countRows() = 0; - public: - Error(const std::string &error); + /** + * Tells how many number of columns are present for each + * row. + * + * @return the number of columns + */ + virtual int countColumns() = 0; - virtual const char *what() const throw(); + /** + * Tells if the column is null or not. + * + * @param row the row number + * @param column the column + * @return an true if null + */ + virtual bool isNull(int row, const std::string &column) = 0; + + /** + * Dump all rows and columns. + */ + virtual void dump() = 0; }; + using Ptr = std::shared_ptr<Impl>; + private: /** * Check if the request is valid and throws an exception @@ -79,59 +158,18 @@ * @param type * @throw Error on error */ - void assertRequest(int row, const std::string &column, ColumnType type); + void assertRequest(int row, const std::string &column, DriverColumn type); protected: - /** - * Get a bool. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual bool getBoolean(int row, const std::string &column) = 0; - - /** - * Get a Date. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual Date getDate(int row, const std::string &column) = 0; - - /** - * Get a double. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual double getDouble(int row, const std::string &column) = 0; - - /** - * Get a integer. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual int getInt(int row, const std::string &column) = 0; - - /** - * Get a string. - * - * @param row the row number - * @param column the column - * @return the value - */ - virtual std::string getString(int row, const std::string &column) = 0; + Ptr m_impl; public: + DriverQuery(Ptr impl); + /** * Default destructor. */ - virtual ~Query(); + virtual ~DriverQuery(); /** * Get a variable from a row and column. @@ -149,7 +187,14 @@ * @throw Query::Error on error */ template <class T> - T get(int row, const std::string &column); + T get(int row, const std::string &column) + { + static_assert(DriverTypeInfo<T>::value, "unsupported type"); + + assertRequest(row, column, DriverTypeInfo<T>::type); + + return DriverTypeInfo<T>::get(*this, row, column); + } /** * Returns the type of a named column. @@ -157,14 +202,14 @@ * @param column the column name * @return the type */ - virtual ColumnType type(const std::string &column) const = 0; + DriverColumn type(const std::string &column) const; /** * Tells how many rows has been fetched. * * @return the number of rows */ - virtual int countRows() = 0; + int countRows(); /** * Tells how many number of columns are present for each @@ -172,7 +217,7 @@ * * @return the number of columns */ - virtual int countColumns() = 0; + int countColumns(); /** * Tells if the column is null or not. @@ -181,12 +226,12 @@ * @param column the column * @return an true if null */ - virtual bool isNull(int row, const std::string &column) = 0; + bool isNull(int row, const std::string &column); /** * Dump all rows and columns. */ - virtual void dump() = 0; + void dump(); }; /** @@ -195,74 +240,76 @@ * * This helps creating class with SQL injection security and such. */ -class Request { +class DriverRequest { public: - friend class Driver; + friend struct DriverTypeInfo<bool>; + friend struct DriverTypeInfo<time_t>; + friend struct DriverTypeInfo<double>; + friend struct DriverTypeInfo<int>; + friend struct DriverTypeInfo<std::string>; + + class Impl { + public: + /** + * Bind a boolean. + * + * @param value the boolean + * @return the string to use + */ + virtual std::string bindBoolean(bool value) = 0; + + /** + * Bind a date. + * + * @param value the date + * @return the string to use + */ + virtual std::string bindDate(time_t value) = 0; - using Ptr = std::shared_ptr<Request>; + /** + * Bind a double. + * + * @param value the double + * @return the string to use + */ + virtual std::string bindDouble(double value) = 0; + + /** + * Bind an integer. + * + * @param value the integer + * @return the string to use + */ + virtual std::string bindInteger(int value) = 0; + + /** + * Bind a string. + * + * @param value the string + * @return the string to use + */ + virtual std::string bindString(std::string value) = 0; + }; + + using Ptr = std::shared_ptr<Impl>; private: size_t m_pos; int m_params; std::string m_command; + Ptr m_impl; void assertCorrect(); void setValue(const std::string &value); -protected: - /** - * Bind a boolean. - * - * @param value the boolean - * @return the string to use - */ - virtual std::string bindBoolean(bool value) = 0; - - /** - * Bind a date. - * - * @param value the date - * @return the string to use - */ - virtual std::string bindDate(Date value) = 0; - - /** - * Bind a double. - * - * @param value the double - * @return the string to use - */ - virtual std::string bindDouble(double value) = 0; +public: + DriverRequest(Ptr impl, const std::string &command); /** - * Bind an integer. - * - * @param value the integer - * @return the string to use - */ - virtual std::string bindInt(int value) = 0; - - /** - * Bind a string. - * - * @param value the string - * @return the string to use - */ - virtual std::string bindString(std::string value) = 0; - - /** - * Default constructor with SQL command. - * - * @param command the SQL command - */ - Request(const std::string &command); - -public: - /** * Default destructor. */ - virtual ~Request(); + virtual ~DriverRequest(); /** * Bind a value. @@ -270,7 +317,12 @@ * @param value the value */ template <typename T> - void bind(T value); + void bind(T value) + { + static_assert(DriverTypeInfo<T>::value, "unsupported type"); + + setValue(DriverTypeInfo<T>::bind(value)); + } /** * Convert the request to string. @@ -288,46 +340,66 @@ * does not include any DBMS code and just call virtual functions for * a simpler integration of new DBMS drivers. */ -class Driver : public std::enable_shared_from_this<Driver> { -protected: - std::string m_error; +class Driver { +public: + class Impl; + + using Params = std::unordered_map<std::string, std::string>; + using Ptr = std::shared_ptr<Impl>; + + class Impl { + public: + /** + * Create a synchronous connection, it waits and block until + * the connection is made up to a specified timeout max. + * + * @param params a list of parameters. + */ + virtual void connect(const Params ¶ms) = 0; - /** - * Default constructor. Should not be used. - * - * @see Driver::create - */ - Driver(); + /** + * Prepare a request with the specified SQL command. + * + * @param command the SQL command to parse and bind + * @return a request to use with query + * @see query + */ + virtual DriverRequest prepare(const std::string &command) = 0; + + /** + * Execute a query. + * + * @param query the SQL command + * @return a result + * @throw Query::Error on failure + */ + virtual DriverQuery query(const std::string &command) = 0; + + /** + * Get the driver connection description. + * + * @return the description + */ + virtual std::string description() const = 0; + + /** + * Get the driver version as a string. + * + * @return the version + */ + virtual std::string version() const = 0; + }; + +protected: + Ptr m_impl; public: - using Params = std::unordered_map<std::string, std::string>; - using Ptr = std::shared_ptr<Driver>; - - /** - * Create a new Driver object, some queries need to do additional - * internal requests so it's easier to use shared_ptr. - * - * @return a shared_ptr - */ - template <class T> - static Ptr create() - { - T *t = new T(); - - return std::shared_ptr<T>(t); - } + Driver() = default; /** * Virtual destructor. */ - virtual ~Driver(); - - /** - * Get the error. - * - * @return the error - */ - const std::string &getError() const; + virtual ~Driver() = default; /** * Wrapper for std::string variant. @@ -336,7 +408,7 @@ * @return a result * @throw Query::Error on failure */ - Query::Ptr query(Request::Ptr request); + DriverQuery query(DriverRequest request); /** * Create a synchronous connection, it waits and block until @@ -344,7 +416,7 @@ * * @param params a list of parameters. */ - virtual bool connect(const Params ¶ms) = 0; + void connect(const Params ¶ms); /** * Prepare a request with the specified SQL command. @@ -353,7 +425,7 @@ * @return a request to use with query * @see query */ - virtual Request::Ptr prepare(const std::string &command) = 0; + DriverRequest prepare(const std::string &command); /** * Execute a query. @@ -362,21 +434,97 @@ * @return a result * @throw Query::Error on failure */ - virtual Query::Ptr query(const std::string &command) = 0; + DriverQuery query(const std::string &command); /** * Get the driver connection description. * * @return the description */ - virtual std::string description() const = 0; + std::string description() const; /** * Get the driver version as a string. * * @return the version */ - virtual std::string version() const = 0; + std::string version() const; +}; + +template <> +struct DriverTypeInfo<bool> : std::true_type { + static const DriverColumn type = DriverColumn::Boolean; + + static bool get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getBoolean(row, column); + } + + static void bind(DriverRequest &request, bool value) + { + request.m_impl->bindBoolean(value); + } +}; + +template <> +struct DriverTypeInfo<time_t> : std::true_type { + static const DriverColumn type = DriverColumn::Date; + + static time_t get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getDate(row, column); + } + + static void bind(DriverRequest &request, time_t value) + { + request.m_impl->bindDate(value); + } }; +template <> +struct DriverTypeInfo<double> : std::true_type { + static const DriverColumn type = DriverColumn::Double; + + static double get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getDouble(row, column); + } + + static void bind(DriverRequest &request, double value) + { + request.m_impl->bindDouble(value); + } +}; + +template <> +struct DriverTypeInfo<int> : std::true_type { + static const DriverColumn type = DriverColumn::Integer; + + static int get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getInt(row, column); + } + + static void bind(DriverRequest &request, int value) + { + request.m_impl->bindInteger(value); + } +}; + +template <> +struct DriverTypeInfo<std::string> : std::true_type { + static const DriverColumn type = DriverColumn::String; + + static std::string get(DriverQuery &query, int row, const std::string &column) + { + return query.m_impl->getString(row, column); + } + + static void bind(DriverRequest &request, const std::string &value) + { + request.m_impl->bindString(value); + } +}; + + #endif // !_DRIVER_H_
--- 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 ¶ms); -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 ¶ms) + /** + * @copydoc Driver::description + */ + virtual std::string description() const; + + /** + * @copydoc Driver::version + */ + virtual std::string version() const; +}; + +std::string DriverPostgresImpl::convert(Driver::Params ¶ms) { 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 ¶ms) +void DriverPostgresImpl::connect(const Driver::Params ¶ms) { - 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>(); +}
--- a/C++/DriverPostgres.h Mon Mar 24 13:54:28 2014 +0100 +++ b/C++/DriverPostgres.h Tue Mar 25 20:42:11 2014 +0100 @@ -1,7 +1,7 @@ /* * DriverPostgres.h -- 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 @@ -29,201 +29,9 @@ #include "Driver.h" -/** - * @class QueryPostgres - * @brief Query implementation for PostgreSQL. - */ -class QueryPostgres : public Query { -public: - struct PQQueryDeleter { - void operator()(PGresult *result) - { - PQclear(result); - } - }; - - using PostgresResult = std::unique_ptr<PGresult, PQQueryDeleter>; - -private: - PostgresResult m_result; - Driver::Ptr m_driver; - -protected: - /** - * @copydoc Query::getBoolean - */ - virtual bool getBoolean(int row, const std::string &column); - - /** - * @copydoc Query::getDate - */ - virtual Date getDate(int row, const std::string &column); - - /** - * @copydoc Query::getDouble - */ - virtual double getDouble(int row, const std::string &column); - - /** - * @copydoc Query::getInt - */ - virtual int getInt(int row, const std::string &column); - - /** - * @copydoc Query::getString - */ - virtual std::string getString(int row, const std::string &column); - -public: - /** - * Constructor used by DriverPostgres - * - * @param driver the driver - * @param result the result - */ - QueryPostgres(Driver::Ptr driver, PostgresResult result); - - /** - * Default destructor. - */ - ~QueryPostgres(); - - /** - * @copydoc Query::type - */ - virtual ColumnType type(const std::string &column) const; - - /** - * @copydoc Query::countRows - */ - virtual int countRows(); - - /** - * @copydoc Query::countColumns - */ - virtual int countColumns(); - - /** - * @copydoc Query::isNull - */ - virtual bool isNull(int row, const std::string &column); - - /** - * @copydoc Query::dump - */ - virtual void dump(); -}; - -/** - * @class RequestPostgres - * @brief Request implementation for PostgreSQL. - */ -class RequestPostgres : public Request { -private: - std::shared_ptr<PGconn> m_connection; - -protected: - /** - * @copydoc Request::bindBoolean - */ - virtual std::string bindBoolean(bool value); - - /** - * @copydoc Request::bindDate - */ - virtual std::string bindDate(Date value); - - /** - * @copydoc Request::bindDouble - */ - virtual std::string bindDouble(double value); - - /** - * @copydoc Request::bindInt - */ - virtual std::string bindInt(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(std::shared_ptr<PGconn> conn, - const std::string &command); -}; - -/** - * @class DriverPostgres - * @brief Driver implementation for PostgreSQL. - */ class DriverPostgres : public Driver { public: - friend class Driver; - - struct PGDeleter { - void operator()(PGconn *conn) - { - PQfinish(conn); - } - }; - - using PostgresConn = std::shared_ptr<PGconn>; - -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(Params &settings); - -protected: - /** - * @copydoc Driver::Driver - */ DriverPostgres(); - -public: - /** - * @copydoc Driver::~Driver - */ - ~DriverPostgres(); - - /** - * @copydoc Driver::connect - */ - virtual bool connect(const Params ¶ms); - - /** - * @copydoc Driver::prepare - */ - virtual Request::Ptr prepare(const std::string &command); - - /** - * @copydoc Driver::query - */ - virtual Query::Ptr query(const std::string &command); - - /** - * @copydoc Driver::description - */ - virtual std::string description() const; - - /** - * @copydoc Driver::version - */ - virtual std::string version() const; }; #endif // !_DRIVER_PG_H_