Mercurial > code
view C++/modules/Driver/DriverPostgres.cpp @ 336:870c2583a7ab
Dynlib: fix useless warning
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 11 Mar 2015 09:42:54 +0100 |
parents | 0b576ee64d45 |
children |
line wrap: on
line source
/* * DriverPostgres.cpp -- PostgreSQL driver * * 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 * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <cerrno> #include <cstring> #include <iostream> #include <stdexcept> #include <sstream> #include <vector> #include <libpq-fe.h> #include "DriverPostgres.h" namespace { using PostgresResult = std::shared_ptr<PGresult>; using PostgresConn = std::shared_ptr<PGconn>; } /** * @class QueryPostgres * @brief Query implementation for PostgreSQL. */ class QueryPostgresImpl : public DriverQuery::Impl { private: PostgresConn m_connection; PostgresResult m_result; public: /** * Constructor used by DriverPostgres * * @param result the result */ QueryPostgresImpl(PostgresConn conn, PostgresResult result); /** * @copydoc Query::getBoolean */ virtual bool getBoolean(int row, const std::string &column); /** * @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); /** * @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); /** * @copydoc Query::type */ virtual DriverColumn 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(); }; QueryPostgresImpl::QueryPostgresImpl(PostgresConn conn, PostgresResult result) : m_connection(conn) , m_result(result) { } DriverColumn QueryPostgresImpl::type(const std::string &column) const { DriverColumn type; int pqType, index; index = PQfnumber(m_result.get(), column.c_str()); pqType = PQftype(m_result.get(), index); switch (pqType) { case 16: type = DriverColumn::Boolean; break; case 1082: case 1083: case 1114: case 1184: type = DriverColumn::Date; break; case 1700: case 700: case 701: type = DriverColumn::Double; break; case 20: case 21: case 23: type = DriverColumn::Integer; break; case 25: case 1042: case 1043: type = DriverColumn::String; break; default: type = DriverColumn::Invalid; } return type; } int QueryPostgresImpl::countRows() { return PQntuples(m_result.get()); } int QueryPostgresImpl::countColumns() { return PQnfields(m_result.get()); } 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 QueryPostgresImpl::dump(void) { std::cout << "Dumping PostgreSQL result, "; std::cout << countRows() << " rows, "; std::cout << countColumns() << " columns" << std::endl; for (int r = 0; r < countRows(); ++r) { std::cout << "Dumping row " << r << std::endl; std::cout << "==============================" << std::endl; for (int c = 0; c < countColumns(); ++c) { std::cout << "\t" << PQfname(m_result.get(), c); std::cout << " = " << PQgetvalue(m_result.get(), r, c) << std::endl; } } } 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)) { 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 Impl * -------------------------------------------------------- */ /** * @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; } std::string RequestPostgres::bindBoolean(bool value) { return (value) ? "'t'" : "'f'"; } std::string RequestPostgres::bindDate(time_t value) { std::ostringstream oss; oss << "to_timestamp(" << value << ")"; return oss.str(); } std::string RequestPostgres::bindDouble(double value) { return std::to_string(value); } std::string RequestPostgres::bindInteger(int value) { return std::to_string(value); } std::string RequestPostgres::bindString(std::string value) { std::string result; char *tmp; tmp = PQescapeLiteral(m_connection.get(), value.c_str(), value.length()); if (tmp == nullptr) return ""; result = std::string(tmp); PQfreemem(tmp); return result; } /* -------------------------------------------------------- * Driver PostgreSQL impl * -------------------------------------------------------- */ /** * @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); /** * @copydoc Driver::prepare */ virtual DriverRequest prepare(const std::string &command); /** * @copydoc Driver::query */ virtual DriverQuery query(const std::string &command); /** * @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"] << " "; oss << "user = " << params["user"] << " "; oss << "dbname = " << params["database"] << " "; oss << "password = " << params["password"]; return oss.str(); } void DriverPostgresImpl::connect(const Driver::Params ¶ms) { auto copy = params; auto conn = PQconnectdb(convert(copy).c_str()); if (conn == nullptr) throw std::runtime_error(std::strerror(ENOMEM)); if (PQstatus(conn) == CONNECTION_BAD) { auto error = PQerrorMessage(conn); PQfinish(conn); throw std::runtime_error(error); } m_connection = std::shared_ptr<PGconn>(conn, PQfinish); } DriverRequest DriverPostgresImpl::prepare(const std::string &command) { return DriverRequest(std::make_shared<RequestPostgres>(m_connection), command); } 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 std::runtime_error(strerror(ENOMEM)); // If an error occured int errorCode = PQresultStatus(info); if (errorCode != PGRES_COMMAND_OK && errorCode != PGRES_TUPLES_OK) { auto error = PQresultErrorMessage(info); PQclear(info); throw std::runtime_error(error); } auto result = std::shared_ptr<PGresult>(info, PQclear); auto impl = std::make_shared<QueryPostgresImpl>(m_connection, result); return DriverQuery(impl); } std::string DriverPostgresImpl::description() const { std::ostringstream oss; oss << "Connected on PostgreSQL database: " << std::endl; oss << " host: " << PQhost(m_connection.get()) << std::endl; oss << " port: " << PQport(m_connection.get()) << std::endl; oss << " user: " << PQuser(m_connection.get()) << std::endl; oss << " database: " << PQdb(m_connection.get()) << std::endl; return oss.str(); } std::string DriverPostgresImpl::version() const { std::ostringstream oss; oss << "PostgreSQL driver (version " << PQlibVersion() << ")"; return oss.str(); } /* -------------------------------------------------------- * Driver PostgreSQL * -------------------------------------------------------- */ DriverPostgres::DriverPostgres() { m_impl = std::make_shared<DriverPostgresImpl>(); }