Mercurial > code
changeset 177:f0cca031bcce
Improve Drivers
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sat, 21 Sep 2013 09:25:28 +0200 |
parents | ba62ab219fc4 |
children | 2bfe43b85d7f |
files | C++/Driver.cpp C++/Driver.h C++/DriverPostgres.cpp C++/DriverPostgres.h |
diffstat | 4 files changed, 333 insertions(+), 311 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Driver.cpp Fri Sep 13 13:35:32 2013 +0200 +++ b/C++/Driver.cpp Sat Sep 21 09:25:28 2013 +0200 @@ -23,104 +23,87 @@ #include "Driver.h" /* --------------------------------------------------------- + * Query::Error class + * --------------------------------------------------------- */ + +Query::Error::Error(const std::string &error) + : m_error(error) +{ +} + +const char *Query::Error::Error::what() const throw() +{ + return m_error.c_str(); +} + +/* --------------------------------------------------------- * Query class * ---------------------------------------------------------*/ -Query::Query() +void Query::assertRequest(int row, const std::string &column, ColumnType wanted) { + std::ostringstream oss; + + // Out of bounds? + if (row < 0 || row >= countRows()) + { + oss << "Invalid row index " << row; + oss << ", expected [0.." << countRows() << "]"; + + throw Error(oss.str()); + } + + // Not found or bad column? + if (type(column) != wanted) + { + oss << "Invalid or not found column `" << column << "'"; + + throw Error(oss.str()); + } } Query::~Query() { } -void Query::checkValidRequest(int row, const std::string &column, QueryCheck check) +template <> +bool Query::get(int row, const std::string &column) { - std::ostringstream oss; + assertRequest(row, column, ColumnType::Boolean); - switch (check) - { - case QueryCheck::InvalidColumn: - oss << "Invalid column '" << column << "'"; - throw QueryError(oss.str()); - case QueryCheck::InvalidRow: - oss << "Invalid row number '" << row << "'"; - throw QueryError(oss.str()); - case QueryCheck::InvalidType: - oss << "Column " << column; - oss << " type does not match requested type"; - - throw QueryError(oss.str()); - default: - break; - } + return getBoolean(row, column); } -bool Query::getBool(int row, const std::string &column) +template <> +Date Query::get(int row, const std::string &column) { - // Throw an exception on bad arguments - checkValidRequest(row, column, - checkRequest(row, column, ColumnType::Boolean)); + assertRequest(row, column, ColumnType::Date); - return checkBool(row, column); -} - -Date Query::getDate(int row, const std::string &column) -{ - // Throw an exception on bad arguments - checkValidRequest(row, column, - checkRequest(row, column, ColumnType::Date)); - - return checkDate(row, column); + return getDate(row, column); } -double Query::getDouble(int row, const std::string &column) +template <> +double Query::get(int row, const std::string &column) { - // Throw an exception on bad arguments - checkValidRequest(row, column, - checkRequest(row, column, ColumnType::Double)); + assertRequest(row, column, ColumnType::Double); - return checkDouble(row, column); -} - -int Query::getInt(int row, const std::string &column) -{ - // Throw an exception on bad arguments - checkValidRequest(row, column, - checkRequest(row, column, ColumnType::Integer)); - - return checkInt(row, column); + return getDouble(row, column); } -std::string Query::getString(int row, const std::string &column) +template <> +int Query::get(int row, const std::string &column) { - // Throw an exception on bad arguments - checkValidRequest(row, column, - checkRequest(row, column, ColumnType::String)); + assertRequest(row, column, ColumnType::Integer); - return checkString(row, column); + return getInt(row, column); } -/* --------------------------------------------------------- - * QueryError class - * --------------------------------------------------------- */ - -QueryError::QueryError() +template <> +std::string Query::get(int row, const std::string &column) { -} + assertRequest(row, column, ColumnType::String); -QueryError::QueryError(const std::string &error) - : m_error(error) -{ -} - -QueryError::~QueryError() -{ -} - -const char *QueryError::what() const throw() -{ - return m_error.c_str(); + return getString(row, column); } /* --------------------------------------------------------
--- a/C++/Driver.h Fri Sep 13 13:35:32 2013 +0200 +++ b/C++/Driver.h Sat Sep 21 09:25:28 2013 +0200 @@ -24,26 +24,7 @@ #include <memory> #include <string> -class Query; - -class Date -{ -private: - time_t m_timestamp; - -public: - Date() - { - } - - Date(time_t) - { - } - - ~Date() - { - } -}; +#include <common/Date.h> /** * @enum ColumnType @@ -52,6 +33,7 @@ * Used for the drivers. */ enum class ColumnType { + Invalid, //! not found Boolean, //! bool or 0 / 1 Date, //! date see Common/Date.h Double, //! double @@ -60,76 +42,56 @@ }; /** - * @enum QueryCheck - * @brief Result enumeration for check* functions - * - * This is used for the Query drivers check functions so we can now which - * kind of error happened. - */ -enum class QueryCheck { - NoError = 0, //! success - InvalidColumn, //! non existent column - InvalidRow, //! out of range row - InvalidType, //! bad column type requested -}; - -/** - * @class QueryError - * @brief Query exception on driver error - * - * Exception thrown usually when an error appeared in driver. - */ -class QueryError : public std::exception -{ -private: - std::string m_error; -public: - QueryError(); - - QueryError(const std::string &error); - - ~QueryError(); - - virtual const char *what() const throw(); -}; - -/** * @class Query * @brief Class for querying the database + * + * That class is returned when a SQL query succeed. It can retrieve the + * number of rows, columns and retrieve the results independantly from the + * driver. + * + * @see Driver::query */ class Query { +public: + typedef std::shared_ptr<Query> Ptr; + + /** + * @class Error + * @brief Query exception on query error + */ + class Error : public std::exception + { + private: + std::string m_error; + + public: + Error(const std::string &error); + + virtual const char *what() const throw(); + }; + private: + /** * Check if the request is valid and throws an exception * on error. * * @param row the row number * @param column the column name - * @param check the result - * @throw QueryError on erro + * @param type + * @throw Error on error */ - void checkValidRequest(int row, const std::string &column, QueryCheck check); + void assertRequest(int row, const std::string &column, ColumnType type); protected: /** - * Check if the request is correct, if the row is not out of range - * if the column exist and if the type is correct. - * - * @param row the row number - * @param column the column name - * @param type the type requested - * @return true if request is correct - */ - virtual QueryCheck checkRequest(int row, const std::string &column, ColumnType type) = 0; - - /** * Get a bool. * * @param row the row number * @param column the column * @return the value */ - virtual bool checkBool(int row, const std::string &column) = 0; + virtual bool getBoolean(int row, const std::string &column) = 0; /** * Get a Date. @@ -138,7 +100,7 @@ * @param column the column * @return the value */ - virtual Date checkDate(int row, const std::string &column) = 0; + virtual Date getDate(int row, const std::string &column) = 0; /** * Get a double. @@ -147,7 +109,7 @@ * @param column the column * @return the value */ - virtual double checkDouble(int row, const std::string &column) = 0; + virtual double getDouble(int row, const std::string &column) = 0; /** * Get a integer. @@ -156,7 +118,7 @@ * @param column the column * @return the value */ - virtual int checkInt(int row, const std::string &column) = 0; + virtual int getInt(int row, const std::string &column) = 0; /** * Get a string. @@ -165,68 +127,45 @@ * @param column the column * @return the value */ - virtual std::string checkString(int row, const std::string &column) = 0; + virtual std::string getString(int row, const std::string &column) = 0; public: - Query(void); - virtual ~Query(void); - /** - * Get bool at a specified row / column. - * - * @param row the row number - * @param column the column - * @return a boolean - * @throw QueryError on error + * Default destructor. */ - bool getBool(int row, const std::string &column); - - /** - * Get a Date at a specified row / column. - * - * @param row the row number - * @param column the column - * @return a Date - * @throw QueryError on error - */ - Date getDate(int row, const std::string &column); + virtual ~Query(); /** - * Get a double at a specified row / column. + * Get a variable from a row and column. * - * @param row the row number - * @param column the column - * @return a double - * @throw QueryError on error + * Specialization available: + * - bool + * - Date + * - double + * - int + * - std::string + * + * @param row the row number (starts from 0) + * @param column the the column name + * @return the value */ - double getDouble(int row, const std::string &column); + template <class T> + T get(int row, const std::string &column); /** - * Get an integer at a specified row / column. + * Returns the type of a named column. * - * @param row the row number - * @param column the column - * @return an integer - * @throw QueryError on error + * @param column the column name + * @return the type */ - int getInt(int row, const std::string &column); - - /** - * Get a string at a specified row / column. - * - * @param row the row number - * @param column the column - * @return a string - * @throw QueryError on error - */ - std::string getString(int row, const std::string &column); + virtual ColumnType type(const std::string &column) const = 0; /** * Tells how many rows has been fetched. * * @return the number of rows */ - virtual int countRows(void) = 0; + virtual int countRows() = 0; /** * Tells how many number of columns are present for each @@ -234,7 +173,7 @@ * * @return the number of columns */ - virtual int countColumns(void) = 0; + virtual int countColumns() = 0; /** * Tells if the column is null or not. @@ -248,20 +187,45 @@ /** * Dump all rows and columns. */ - virtual void dump(void) = 0; + virtual void dump() = 0; }; -class Driver { +/** + * @class Driver + * @brief A generic SQL driver + * + * This class is used to connect to a database and execute SQL queries. It + * 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; + /** + * Default constructor. Should not be used. + * + * @see Driver::create + */ + Driver(); + public: typedef std::map<std::string, std::string> Params; + typedef std::shared_ptr<Driver> Ptr; /** - * Default constructor. + * 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 */ - Driver(); + template <class T> + static Ptr create() + { + T *t = new T(); + + return std::shared_ptr<T>(t); + } /** * Virtual destructor. @@ -273,7 +237,7 @@ * * @return the error */ - const std::string & getError() const; + const std::string &getError() const; /** * Create a synchronous connection, it waits and block until @@ -290,7 +254,7 @@ * @return a result * @throw Query::Exception on failure */ - virtual std::unique_ptr<Query> query(const std::string &command) = 0; + virtual Query::Ptr query(const std::string &command) = 0; /** * Get the driver connection description. @@ -298,6 +262,13 @@ * @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; }; #endif // !_DRIVER_H_
--- a/C++/DriverPostgres.cpp Fri Sep 13 13:35:32 2013 +0200 +++ b/C++/DriverPostgres.cpp Sat Sep 21 09:25:28 2013 +0200 @@ -24,54 +24,72 @@ * Query PostgreSQL (protected methods) * -------------------------------------------------------- */ -QueryCheck QueryPostgres::checkRequest(int row, const std::string &column, ColumnType type) +bool QueryPostgres::getBoolean(int row, const std::string &column) { - QueryCheck ret = QueryCheck::InvalidType; - Oid pqType; - int colIndex; - bool success = false; + int idx = PQfnumber(m_result.get(), column.c_str()); + std::string code = PQgetvalue(m_result.get(), row, idx); - // Invalid row - if (row >= PQntuples(m_result.get())) - return QueryCheck::InvalidRow; - if ((colIndex = PQfnumber(m_result.get(), column.c_str())) == -1) - return QueryCheck::InvalidColumn; + return code[0] == 't'; +} - pqType = PQftype(m_result.get(), colIndex); - switch (type) +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 { - case ColumnType::Boolean: - success = (pqType == 16); - break; - case ColumnType::Date: - success = (pqType == 1082) || (pqType == 1083) || (pqType == 1114); - break; - case ColumnType::Double: - success = (pqType = 1700) || (pqType == 700) || (pqType == 701); - break; - case ColumnType::Integer: - success = (pqType == 20) || (pqType == 21) || (pqType == 23) || (pqType == 1700); - break; - case ColumnType::String: - success = (pqType == 25) || (pqType == 1042) || (pqType == 1043); - break; - default: - ret = QueryCheck::InvalidType; + 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. + */ + oss << "Select EXTRACT(EPOCH FROM TIMESTAMP '"; + oss << value; + oss << "') AS RESULT"; + req = oss.str(); + + Query::Ptr result = m_driver->query(req); + timestamp = result->get<double>(0, "RESULT"); } + catch (...) { } - // valid type requested? - if (success) - ret = QueryCheck::NoError; + return Date(static_cast<time_t>(timestamp)); +} + +double QueryPostgres::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)); +} - return ret; +int QueryPostgres::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 QueryPostgres::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)); } /* -------------------------------------------------------- * Query PostgreSQL (public methods) * -------------------------------------------------------- */ -QueryPostgres::QueryPostgres(PostgresResult result) +QueryPostgres::QueryPostgres(Driver::Ptr driver, PostgresResult result) { + m_driver = driver; m_result = std::move(result); } @@ -79,6 +97,45 @@ { } +ColumnType QueryPostgres::type(const std::string &column) const +{ + ColumnType 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; + break; + case 1082: + case 1083: + case 1114: + type = ColumnType::Date; + break; + case 1700: + case 700: + case 701: + type = ColumnType::Double; + break; + case 20: + case 21: + case 23: + type = ColumnType::Integer; + break; + case 25: + case 1042: + case 1043: + type = ColumnType::String; + break; + default: + type = ColumnType::Invalid; + } + + return type; +} + int QueryPostgres::countRows() { return PQntuples(m_result.get()); @@ -89,69 +146,6 @@ return PQnfields(m_result.get()); } -bool QueryPostgres::checkBool(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'; -} - -Date QueryPostgres::checkDate(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - long timestamp = static_cast<long>(time(0)); - - try - { - timestamp = std::stol(PQgetvalue(m_result.get(), row, idx)); - } - catch (std::invalid_argument) - { - } - - return Date(static_cast<time_t>(timestamp)); -} - -double QueryPostgres::checkDouble(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - double d = 0; - - try - { - d = std::stod(PQgetvalue(m_result.get(), row, idx)); - } - catch (std::invalid_argument) - { - } - - return d; -} - -int QueryPostgres::checkInt(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - int i = 0; - - try - { - i = std::stoi(PQgetvalue(m_result.get(), row, idx)); - } - catch (std::invalid_argument) - { - } - - return i; -} - -std::string QueryPostgres::checkString(int row, const std::string &column) -{ - int idx = PQfnumber(m_result.get(), column.c_str()); - - return std::string(PQgetvalue(m_result.get(), row, idx)); -} - bool QueryPostgres::isNull(int row, const std::string &column) { int idx = PQfnumber(m_result.get(), column.c_str()); @@ -227,14 +221,14 @@ return true; } -std::unique_ptr<Query> DriverPostgres::query(const std::string &cmd) +Query::Ptr DriverPostgres::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 QueryError(strerror(ENOMEM)); + throw Query::Error(strerror(ENOMEM)); // If an error occured int errorCode = PQresultStatus(info); @@ -242,10 +236,11 @@ { std::string error = PQresultErrorMessage(info); PQclear(info); - throw QueryError(error); + throw Query::Error(error); } - return std::unique_ptr<Query>(new QueryPostgres(QueryPostgres::PostgresResult(info))); + return std::unique_ptr<Query>(new QueryPostgres(shared_from_this(), + QueryPostgres::PostgresResult(info))); } std::string DriverPostgres::description() const @@ -260,3 +255,14 @@ return oss.str(); } + +std::string DriverPostgres::version() const +{ + std::ostringstream oss; + + // TODO: ADD VERSION + + oss << "PostgreSQL driver"; + + return oss.str(); +}
--- a/C++/DriverPostgres.h Fri Sep 13 13:35:32 2013 +0200 +++ b/C++/DriverPostgres.h Sat Sep 21 09:25:28 2013 +0200 @@ -23,6 +23,7 @@ #ifndef _DRIVER_PG_H_ #define _DRIVER_PG_H_ +#include <cerrno> #include <map> #include <memory> #include <string> @@ -32,7 +33,8 @@ #include "Driver.h" /** - * Query implementation for PostgreSQL. + * @class QueryPostgres + * @brief Query implementation for PostgreSQL. */ class QueryPostgres : public Query { @@ -49,42 +51,80 @@ private: PostgresResult m_result; + Driver::Ptr m_driver; protected: - virtual QueryCheck checkRequest(int row, const std::string &column, ColumnType type); - - virtual Date checkDate(int row, const std::string &column); + /** + * @copydoc Query::getBoolean + */ + virtual bool getBoolean(int row, const std::string &column); - virtual bool checkBool(int row, const std::string &column); - - virtual double checkDouble(int row, const std::string &column); + /** + * @copydoc Query::getDate + */ + virtual Date getDate(int row, const std::string &column); - virtual int checkInt(int row, const std::string &column); - - virtual std::string checkString(int row, const std::string &column); + /** + * @copydoc Query::getDouble + */ + virtual double getDouble(int row, const std::string &column); - // Avoid copy - QueryPostgres(const QueryPostgres &src); + /** + * @copydoc Query::getInt + */ + virtual int getInt(int row, const std::string &column); - // Avoid copy - QueryPostgres & operator=(const QueryPostgres &src); + /** + * @copydoc Query::getString + */ + virtual std::string getString(int row, const std::string &column); public: - QueryPostgres(PostgresResult); + /** + * Constructor used by DriverPostgres + */ + 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 DriverPostgres + * @brief Driver implementation for PostgreSQL. + */ class DriverPostgres : public Driver { public: + friend class Driver; + struct PGDeleter { void operator()(PGconn *conn) { PQfinish(conn); @@ -105,15 +145,37 @@ */ std::string convert(Params &settings); +protected: + /** + * @copytoc Driver::Driver + */ + DriverPostgres(); + public: - DriverPostgres(); + /** + * @copydoc Driver::~Driver + */ ~DriverPostgres(); + /** + * @copydoc Driver::connect + */ virtual bool connect(const Params ¶ms); - virtual std::unique_ptr<Query> query(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_