This also makes it clearer the command was executed succesfully. Times are now printed with no more then two decimals. This prevents confusion between thousand and decimal seperators.
341 lines
7.9 KiB
C++
341 lines
7.9 KiB
C++
#include "PgsqlConn.h"
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
using namespace Pgsql;
|
|
|
|
namespace {
|
|
|
|
void set_stdstring_with_charptr(std::string &s, const char *p)
|
|
{
|
|
if (p) {
|
|
s = p;
|
|
}
|
|
else {
|
|
s.clear();
|
|
}
|
|
}
|
|
|
|
} // einde unnamed namespace
|
|
|
|
ErrorDetails ErrorDetails::createErrorDetailsFromPGresult(const PGresult *result)
|
|
{
|
|
ErrorDetails r;
|
|
set_stdstring_with_charptr(r.state, PQresultErrorField(result, PG_DIAG_SQLSTATE)); ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
|
|
set_stdstring_with_charptr(r.severity, PQresultErrorField(result, PG_DIAG_SEVERITY));
|
|
set_stdstring_with_charptr(r.messagePrimary, PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY));
|
|
set_stdstring_with_charptr(r.messageDetail, PQresultErrorField(result, PG_DIAG_MESSAGE_DETAIL));
|
|
set_stdstring_with_charptr(r.messageHint, PQresultErrorField(result, PG_DIAG_MESSAGE_HINT));
|
|
const char * p = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);
|
|
r.statementPosition = p != nullptr ? atoi(p) : -1; ///< First character is one, measured in characters not bytes!
|
|
p = PQresultErrorField(result, PG_DIAG_INTERNAL_POSITION);
|
|
r.internalPosition = p != nullptr ? atoi(p) : -1;
|
|
set_stdstring_with_charptr(r.internalQuery, PQresultErrorField(result, PG_DIAG_INTERNAL_QUERY));
|
|
set_stdstring_with_charptr(r.context, PQresultErrorField(result, PG_DIAG_CONTEXT));
|
|
set_stdstring_with_charptr(r.schemaName, PQresultErrorField(result, PG_DIAG_SCHEMA_NAME));
|
|
set_stdstring_with_charptr(r.tableName, PQresultErrorField(result, PG_DIAG_TABLE_NAME));
|
|
set_stdstring_with_charptr(r.columnName, PQresultErrorField(result, PG_DIAG_COLUMN_NAME));
|
|
set_stdstring_with_charptr(r.datatypeName, PQresultErrorField(result, PG_DIAG_DATATYPE_NAME));
|
|
set_stdstring_with_charptr(r.constraintName, PQresultErrorField(result, PG_DIAG_CONSTRAINT_NAME));
|
|
set_stdstring_with_charptr(r.sourceFile, PQresultErrorField(result, PG_DIAG_SOURCE_FILE));
|
|
set_stdstring_with_charptr(r.sourceLine, PQresultErrorField(result, PG_DIAG_SOURCE_LINE));
|
|
set_stdstring_with_charptr(r.sourceFunction, PQresultErrorField(result, PG_DIAG_SOURCE_FUNCTION));
|
|
return r;
|
|
}
|
|
|
|
Result::Result(PGresult *res)
|
|
: result(res)
|
|
{
|
|
if (res == nullptr) {
|
|
throw std::runtime_error("Passing nullptr to Result::Result is not allowed");
|
|
}
|
|
}
|
|
|
|
Result::~Result()
|
|
{
|
|
PQclear(result);
|
|
}
|
|
|
|
|
|
Result::Result(Result &&rhs)
|
|
: result(rhs.result)
|
|
{
|
|
rhs.result = nullptr;
|
|
}
|
|
|
|
Result& Result::operator=(Result &&rhs)
|
|
{
|
|
if (result) {
|
|
PQclear(result);
|
|
}
|
|
result = rhs.result;
|
|
rhs.result = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
Result::operator bool() const
|
|
{
|
|
return result != nullptr;
|
|
}
|
|
|
|
ExecStatusType Result::resultStatus()
|
|
{
|
|
return PQresultStatus(result);
|
|
}
|
|
|
|
std::string Result::getResStatus()
|
|
{
|
|
// return PQresStatus(result);
|
|
return "";
|
|
}
|
|
|
|
std::string Result::diagSqlState()
|
|
{
|
|
std::string s(PQresultErrorField(result, PG_DIAG_SQLSTATE));
|
|
return s;
|
|
}
|
|
|
|
|
|
ErrorDetails Result::diagDetails()
|
|
{
|
|
// ErrorDetails r;
|
|
// r.state = PQresultErrorField(result, PG_DIAG_SQLSTATE); ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
|
|
// r.severity = PQresultErrorField(result, PG_DIAG_SEVERITY);
|
|
// r.messagePrimary = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
|
|
// r.messageDetail = PQresultErrorField(result, PG_DIAG_MESSAGE_DETAIL);
|
|
// r.messageHint = PQresultErrorField(result, PG_DIAG_MESSAGE_HINT);
|
|
// r.statementPosition = atoi(PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION)); ///< First character is one, measured in characters not bytes!
|
|
// r.internalPosition = atoi(PQresultErrorField(result, PG_DIAG_INTERNAL_POSITION));
|
|
// r.internalQuery = PQresultErrorField(result, PG_DIAG_INTERNAL_QUERY);
|
|
// r.context = PQresultErrorField(result, PG_DIAG_CONTEXT);
|
|
// r.schemaName = PQresultErrorField(result, PG_DIAG_SCHEMA_NAME);
|
|
// r.tableName = PQresultErrorField(result, PG_DIAG_TABLE_NAME);
|
|
// r.columnName = PQresultErrorField(result, PG_DIAG_COLUMN_NAME);
|
|
// r.datatypeName = PQresultErrorField(result, PG_DIAG_DATATYPE_NAME);
|
|
// r.constraintName = PQresultErrorField(result, PG_DIAG_CONSTRAINT_NAME);
|
|
// r.sourceFile = PQresultErrorField(result, PG_DIAG_SOURCE_FILE);
|
|
// r.sourceLine = PQresultErrorField(result, PG_DIAG_SOURCE_LINE);
|
|
// r.sourceFunction = PQresultErrorField(result, PG_DIAG_SOURCE_FUNCTION);
|
|
|
|
// return r;
|
|
return ErrorDetails::createErrorDetailsFromPGresult(result);
|
|
}
|
|
|
|
int Result::tuplesAffected() const
|
|
{
|
|
int i;
|
|
char * res = PQcmdTuples(result);
|
|
if (res) {
|
|
try {
|
|
i = std::stoi(res);
|
|
}
|
|
catch (std::invalid_argument& ) {
|
|
i = -1;
|
|
}
|
|
catch (std::out_of_range& ) {
|
|
i = -1;
|
|
}
|
|
}
|
|
else {
|
|
i = -1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int Result::getRows() const
|
|
{
|
|
return PQntuples(result);
|
|
}
|
|
|
|
int Result::getCols() const
|
|
{
|
|
return PQnfields(result);
|
|
}
|
|
|
|
const char* const Result::getColName(int idx) const
|
|
{
|
|
return PQfname(result, idx);
|
|
}
|
|
|
|
const char * Result::getVal(int col, int row) const
|
|
{
|
|
return PQgetvalue(result, row, col);
|
|
}
|
|
|
|
|
|
Canceller::Canceller(PGcancel *c)
|
|
: m_cancel(c)
|
|
{}
|
|
|
|
Canceller::Canceller(Canceller&& rhs)
|
|
: m_cancel(rhs.m_cancel)
|
|
{
|
|
rhs.m_cancel = nullptr;
|
|
}
|
|
|
|
Canceller& Canceller::operator=(Canceller&& rhs)
|
|
{
|
|
if (m_cancel) {
|
|
PQfreeCancel(m_cancel);
|
|
}
|
|
m_cancel = rhs.m_cancel;
|
|
rhs.m_cancel = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
Canceller::~Canceller()
|
|
{
|
|
if (m_cancel) {
|
|
PQfreeCancel(m_cancel);
|
|
}
|
|
}
|
|
|
|
bool Canceller::cancel(std::string *error)
|
|
{
|
|
const int errbuf_size = 256;
|
|
char errbuf[errbuf_size];
|
|
bool res = PQcancel(m_cancel, errbuf, errbuf_size);
|
|
if (!res && error) {
|
|
*error = errbuf;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
Connection::Connection() = default;
|
|
|
|
Connection::~Connection()
|
|
{
|
|
close();
|
|
}
|
|
|
|
Connection::Connection(Connection &&rhs)
|
|
: conn(rhs.conn)
|
|
{
|
|
rhs.conn = nullptr;
|
|
}
|
|
|
|
Connection& Connection::operator=(Connection &&rhs)
|
|
{
|
|
close();
|
|
conn = rhs.conn;
|
|
rhs.conn = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
void Connection::close()
|
|
{
|
|
if (conn) {
|
|
PQfinish(conn);
|
|
conn = nullptr;
|
|
}
|
|
}
|
|
|
|
Canceller Connection::getCancel()
|
|
{
|
|
Canceller c(PQgetCancel(conn));
|
|
return c;
|
|
}
|
|
|
|
bool Connection::connect(const char *params)
|
|
{
|
|
bool result = false;
|
|
conn = PQconnectdb(params);
|
|
if (conn) {
|
|
ConnStatusType status = PQstatus(conn);
|
|
result = (status == CONNECTION_OK);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Connection::connectStart(const char* params)
|
|
{
|
|
conn = PQconnectStart(params);
|
|
return conn != nullptr;
|
|
}
|
|
|
|
bool Connection::connectStart(const char * const *keywords,
|
|
const char * const *values)
|
|
{
|
|
conn = PQconnectStartParams(keywords, values, 0);
|
|
return conn != nullptr;
|
|
}
|
|
|
|
PostgresPollingStatusType Connection::connectPoll()
|
|
{
|
|
return PQconnectPoll(conn);
|
|
}
|
|
|
|
ConnStatusType Connection::status()
|
|
{
|
|
return PQstatus(conn);
|
|
}
|
|
|
|
int Connection::socket()
|
|
{
|
|
return PQsocket(conn);
|
|
}
|
|
|
|
std::string Connection::getErrorMessage() const
|
|
{
|
|
std::string result;
|
|
if (conn) {
|
|
result = PQerrorMessage(conn);
|
|
}
|
|
else {
|
|
result = "no connection";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Result Connection::query(const char * command)
|
|
{
|
|
PGresult *result = PQexec(conn, command);
|
|
return Result(result);
|
|
}
|
|
|
|
|
|
bool Connection::sendQuery(const char *query)
|
|
{
|
|
int res = PQsendQuery(conn, query);
|
|
return res == 1;
|
|
}
|
|
|
|
std::shared_ptr<Result> Connection::getResult()
|
|
{
|
|
PGresult *r = PQgetResult(conn);
|
|
if (r) {
|
|
return std::make_shared<Result>(r);
|
|
}
|
|
else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool Connection::consumeInput()
|
|
{
|
|
int res = PQconsumeInput(conn);
|
|
return res == 1;
|
|
}
|
|
|
|
bool Connection::isBusy()
|
|
{
|
|
int res = PQisBusy(conn);
|
|
return res == 1;
|
|
}
|
|
|
|
void Connection::setNoticeReceiver(std::function<void(const PGresult *)> callback)
|
|
{
|
|
notifyReceiver = callback;
|
|
PQsetNoticeReceiver(conn,
|
|
Connection::notifyReceiveFunc
|
|
, reinterpret_cast<void*>(this));
|
|
}
|
|
|
|
void Connection::notifyReceiveFunc(void *arg, const PGresult *result)
|
|
{
|
|
Connection *c = reinterpret_cast<Connection *>(arg);
|
|
c->notifyReceiver(result);
|
|
}
|