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:
parent
ff926d85a4
commit
43bec4711e
4 changed files with 51 additions and 49 deletions
|
@ -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;
|
||||||
|
|
|
@ -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__);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue