1
0
Fork 0
mirror of https://github.com/KingDuckZ/dindexer.git synced 2024-11-25 00:53:43 +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:
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 ( Row&& parOther );
~Row ( void ) noexcept;
@ -60,7 +60,7 @@ namespace pq {
public:
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 ( ResultSet&& parOther );
~ResultSet ( void ) noexcept;

View file

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

View file

@ -21,10 +21,34 @@
#include <libpq-fe.h>
#include <map>
#include <string>
#include <memory>
namespace pq {
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;
};
} //namespace pq

View file

@ -44,17 +44,19 @@ namespace pq {
};
struct ResultSet::LocalData {
explicit LocalData ( PGresult* parResult ) :
result(parResult)
typedef ResultInfo::PGResultPtr PGResultPtr;
explicit LocalData ( PGResultPtr&& parResult ) :
info(std::move(parResult), &columns)
{
}
PGresult* result;
ResultInfo info;
std::map<std::string, int> columns;
};
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)))
Row::Row (const ResultInfo* parResult, std::size_t parRow) :
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);
}
@ -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 {
}
@ -115,10 +112,10 @@ namespace pq {
}
ResultSet::ResultSet (ResultInfo& parResult) :
m_localData(new LocalData(parResult.result))
ResultSet::ResultSet (ResultInfo&& parResult) :
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);
std::string col_name;
for (int z = 0; z < column_count; ++z) {
@ -132,26 +129,18 @@ namespace pq {
}
ResultSet::~ResultSet() noexcept {
if (m_localData->result) {
PQclear(m_localData->result);
m_localData->result = nullptr;
}
}
Row ResultSet::operator[] (std::size_t parIndex) const {
ResultInfo info;
info.result = m_localData->result;
info.column_mappings = &m_localData->columns;
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__);
return Row(info, parIndex);
return Row(&m_localData->info, parIndex);
}
std::size_t ResultSet::size() const {
assert(m_localData->result);
const auto retval = PQntuples(m_localData->result);
assert(m_localData->info.result);
const auto retval = PQntuples(m_localData->info.result.get());
assert(retval >= 0);
return static_cast<std::size_t>(std::max(0, retval));
}