1
0
Fork 0
mirror of https://github.com/KingDuckZ/dindexer.git synced 2025-07-03 14:14:11 +00:00

Clarify PGresult ownership and lifetime.

This commit is contained in:
King_DuckZ 2015-12-30 10:15:36 +00:00
parent ff926d85a4
commit 43bec4711e
4 changed files with 51 additions and 49 deletions

View file

@ -33,7 +33,7 @@ namespace pq {
public: public:
typedef boost::transform_iterator<boost::function<std::string(std::size_t)>, boost::counting_iterator<std::size_t>> const_iterator; typedef boost::transform_iterator<boost::function<std::string(std::size_t)>, boost::counting_iterator<std::size_t>> const_iterator;
Row ( const ResultInfo& parResult, std::size_t parRow ); Row ( const ResultInfo* parResult, std::size_t parRow );
Row ( const Row& ) = delete; Row ( const Row& ) = delete;
Row ( Row&& parOther ); Row ( Row&& parOther );
~Row ( void ) noexcept; ~Row ( void ) noexcept;
@ -60,7 +60,7 @@ namespace pq {
public: public:
typedef boost::transform_iterator<boost::function<Row(std::size_t)>, boost::counting_iterator<std::size_t>> const_iterator; typedef boost::transform_iterator<boost::function<Row(std::size_t)>, boost::counting_iterator<std::size_t>> const_iterator;
explicit ResultSet ( ResultInfo& parResult ); explicit ResultSet ( ResultInfo&& parResult );
ResultSet ( const ResultSet& ) = delete; ResultSet ( const ResultSet& ) = delete;
ResultSet ( ResultSet&& parOther ); ResultSet ( ResultSet&& parOther );
~ResultSet ( void ) noexcept; ~ResultSet ( void ) noexcept;

View file

@ -173,29 +173,22 @@ namespace pq {
} }
ResultSet Connection::query (const std::string& parQuery) { ResultSet Connection::query (const std::string& parQuery) {
//TODO: sanitize input string ResultInfo info(PQexec(m_localData->connection, parQuery.c_str()));
auto res = PQexec(m_localData->connection, parQuery.c_str()); if (not info.result)
if (not res)
throw DatabaseException("Error running query", "Error allocating result object", __FILE__, __LINE__); throw DatabaseException("Error running query", "Error allocating result object", __FILE__, __LINE__);
const int ress = PQresultStatus(res); const int ress = PQresultStatus(info.result.get());
if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) { if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) {
PQclear(res);
throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__); throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__);
} }
ResultInfo info; return ResultSet(std::move(info));
info.result = res;
info.column_mappings = nullptr;
return ResultSet(info);
} }
void Connection::query_void (const std::string& parQuery) { void Connection::query_void (const std::string& parQuery) {
//TODO: sanitize input string ResultInfo info(PQexec(m_localData->connection, parQuery.c_str()));
auto deleter = [](PGresult* r) { PQclear(r); }; if (not info.result)
std::unique_ptr<PGresult, decltype(deleter)> res(PQexec(m_localData->connection, parQuery.c_str()), deleter);
if (not res)
throw DatabaseException("Error running query", "Error allocating result object", __FILE__, __LINE__); throw DatabaseException("Error running query", "Error allocating result object", __FILE__, __LINE__);
const int ress = PQresultStatus(res.get()); const int ress = PQresultStatus(info.result.get());
if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) { if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) {
throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__); throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__);
} }
@ -213,26 +206,22 @@ namespace pq {
} }
void Connection::query_void_params (const std::string& parQuery, PGParams& parParams) { void Connection::query_void_params (const std::string& parQuery, PGParams& parParams) {
auto deleter = [](PGresult* r) { PQclear(r); };
using ResultPtr = std::unique_ptr<PGresult, decltype(deleter)>;
int result_format = 1; int result_format = 1;
assert(parParams.get()); assert(parParams.get());
auto res = ResultPtr( ResultInfo info(
PQparamExec( PQparamExec(
m_localData->connection, m_localData->connection,
parParams.get(), parParams.get(),
parQuery.c_str(), parQuery.c_str(),
result_format result_format
), )
deleter
); );
if (not res) { if (not info.result) {
std::ostringstream oss; std::ostringstream oss;
oss << "Error allocating result object while running \"" << parQuery << "\": " << PQgeterror(); oss << "Error allocating result object while running \"" << parQuery << "\": " << PQgeterror();
throw DatabaseException("Error running query", oss.str(), __FILE__, __LINE__); throw DatabaseException("Error running query", oss.str(), __FILE__, __LINE__);
} }
const int ress = PQresultStatus(res.get()); const int ress = PQresultStatus(info.result.get());
if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) { if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) {
throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__); throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__);
} }

View file

@ -21,10 +21,34 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include <map> #include <map>
#include <string> #include <string>
#include <memory>
namespace pq { namespace pq {
struct ResultInfo { struct ResultInfo {
PGresult* result; typedef std::unique_ptr<PGresult, void(*)(PGresult*)> PGResultPtr;
ResultInfo ( void ) :
result(nullptr, &PQclear),
column_mappings(nullptr)
{
}
ResultInfo ( const ResultInfo& ) = delete;
ResultInfo ( ResultInfo&& ) = default;
explicit ResultInfo ( PGresult* parRes ) :
result(parRes, &PQclear),
column_mappings(nullptr)
{
}
ResultInfo ( PGResultPtr&& parRes, const std::map<std::string, int>* parColMappings ) :
result(std::move(parRes)),
column_mappings(parColMappings)
{
}
~ResultInfo ( void ) noexcept = default;
ResultInfo& operator= ( const ResultInfo& ) = delete;
ResultInfo& operator= ( ResultInfo&& ) = default;
PGResultPtr result;
const std::map<std::string, int>* column_mappings; const std::map<std::string, int>* column_mappings;
}; };
} //namespace pq } //namespace pq

View file

@ -44,17 +44,19 @@ namespace pq {
}; };
struct ResultSet::LocalData { struct ResultSet::LocalData {
explicit LocalData ( PGresult* parResult ) : typedef ResultInfo::PGResultPtr PGResultPtr;
result(parResult)
explicit LocalData ( PGResultPtr&& parResult ) :
info(std::move(parResult), &columns)
{ {
} }
PGresult* result; ResultInfo info;
std::map<std::string, int> columns; std::map<std::string, int> columns;
}; };
Row::Row (const ResultInfo& parResult, std::size_t parRow) : Row::Row (const ResultInfo* parResult, std::size_t parRow) :
m_localData(new LocalData(parResult.result, parRow, parResult.column_mappings, (parResult.column_mappings ? parResult.column_mappings->size() : 0))) m_localData(new LocalData(parResult->result.get(), parRow, parResult->column_mappings, (parResult->column_mappings ? parResult->column_mappings->size() : 0)))
{ {
assert(not m_localData->result or static_cast<std::size_t>(std::max(0, PQnfields(m_localData->result))) == m_localData->col_count); assert(not m_localData->result or static_cast<std::size_t>(std::max(0, PQnfields(m_localData->result))) == m_localData->col_count);
} }
@ -64,11 +66,6 @@ namespace pq {
{ {
} }
//Row::Row (const Row& parOther) :
// m_localData(new LocalData(parOther.m_localData->result, parOther.m_localData->row_index, parOther.m_localData->columns, parOther.m_localData->col_count))
//{
//}
Row::~Row() noexcept { Row::~Row() noexcept {
} }
@ -115,10 +112,10 @@ namespace pq {
} }
ResultSet::ResultSet (ResultInfo& parResult) : ResultSet::ResultSet (ResultInfo&& parResult) :
m_localData(new LocalData(parResult.result)) m_localData(new LocalData(std::move(parResult.result)))
{ {
const auto result = m_localData->result; const auto result = m_localData->info.result.get();
const int column_count = PQnfields(result); const int column_count = PQnfields(result);
std::string col_name; std::string col_name;
for (int z = 0; z < column_count; ++z) { for (int z = 0; z < column_count; ++z) {
@ -132,26 +129,18 @@ namespace pq {
} }
ResultSet::~ResultSet() noexcept { ResultSet::~ResultSet() noexcept {
if (m_localData->result) {
PQclear(m_localData->result);
m_localData->result = nullptr;
}
} }
Row ResultSet::operator[] (std::size_t parIndex) const { Row ResultSet::operator[] (std::size_t parIndex) const {
ResultInfo info;
info.result = m_localData->result;
info.column_mappings = &m_localData->columns;
if (parIndex >= size()) if (parIndex >= size())
throw DatabaseException("Out of range", "Column index " + boost::lexical_cast<std::string>(parIndex) + " is out of range (column count is " + boost::lexical_cast<std::string>(size()) + ')', __FILE__, __LINE__); throw DatabaseException("Out of range", "Column index " + boost::lexical_cast<std::string>(parIndex) + " is out of range (column count is " + boost::lexical_cast<std::string>(size()) + ')', __FILE__, __LINE__);
return Row(info, parIndex); return Row(&m_localData->info, parIndex);
} }
std::size_t ResultSet::size() const { std::size_t ResultSet::size() const {
assert(m_localData->result); assert(m_localData->info.result);
const auto retval = PQntuples(m_localData->result); const auto retval = PQntuples(m_localData->info.result.get());
assert(retval >= 0); assert(retval >= 0);
return static_cast<std::size_t>(std::max(0, retval)); return static_cast<std::size_t>(std::max(0, retval));
} }