Added list of databases and roles.

Roles works for atleast 9.3 and up.

Reorganizing code for communicating with database.
This commit is contained in:
eelke 2017-02-18 12:05:48 +01:00
parent 8c077b3d5f
commit 2d962334da
28 changed files with 881 additions and 428 deletions

View file

@ -138,6 +138,7 @@ public:
float planningTime = 0.f;
// Triggers???
float executionTime = 0.f;
float totalRuntime = 0.f;
};

3
src/PgAuthId.cpp Normal file
View file

@ -0,0 +1,3 @@
#include "PgAuthId.h"
PgAuthId::PgAuthId() = default;

32
src/PgAuthId.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef PGAUTHID_H
#define PGAUTHID_H
#include <pgsql/libpq-fe.h>
#include <QString>
#include <QDateTime>
class PgAuthId {
public:
PgAuthId();
Oid oid = InvalidOid;
QString name;
bool super;
bool inherit;
bool createRole;
bool createDB;
bool canlogin;
bool replication;
bool bypassRls;
int connLimit;
QDateTime validUntil;
bool valid() const { return oid != InvalidOid; }
bool operator==(Oid _oid) const { return oid == _oid; }
bool operator==(const QString &n) const { return name == n; }
bool operator<(Oid _oid) const { return oid < _oid; }
bool operator<(const PgAuthId &rhs) const { return oid < rhs.oid; }
};
#endif // PGAUTHID_H

45
src/PgAuthIdContainer.cpp Normal file
View file

@ -0,0 +1,45 @@
#include "PgAuthIdContainer.h"
#include "PgsqlConn.h"
#include "PgsqlDatabaseCatalogue.h"
PgAuthIdContainer::PgAuthIdContainer(PgsqlDatabaseCatalogue *cat)
: PgContainer<PgAuthId>(cat)
{}
std::string PgAuthIdContainer::getLoadQuery() const
{
std::string result =
"SELECT oid, rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, "
" rolcanlogin, rolreplication, rolconnlimit, rolvaliduntil";
if (m_catalogue->serverVersion() >= 90500)
result += ", rolbypassrls";
result += "\n"
"FROM pg_authid";
return result;
}
void PgAuthIdContainer::load(const Pgsql::Result &res)
{
const int n_rows = res.rows();
m_container.clear();
m_container.reserve(n_rows);
bool with_rls = (m_catalogue->serverVersion() >= 90500);
for (auto row : res) {
PgAuthId v;
v.oid = row.get(0); // InvalidOid;
v.name = row.get(1);
v.super = row.get(2);
v.inherit = row.get(3);
v.createRole = row.get(4);
v.createDB = row.get(5);
v.canlogin = row.get(6);
v.replication = row.get(7);
v.connLimit = row.get(8);
v.validUntil = row.get(9);
v.bypassRls = with_rls ? (bool)row.get(10) : false;
// QDateTime
m_container.push_back(v);
}
std::sort(m_container.begin(), m_container.end());
}

26
src/PgAuthIdContainer.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef PGAUTHIDCONTAINER_H
#define PGAUTHIDCONTAINER_H
#include <vector>
#include "PgContainer.h"
#include "PgAuthId.h"
namespace Pgsql {
class Result;
}
class PgAuthIdContainer: public PgContainer<PgAuthId> {
public:
explicit PgAuthIdContainer(PgsqlDatabaseCatalogue *cat);
std::string getLoadQuery() const;
void load(const Pgsql::Result &res);
private:
};
#endif // PGAUTHIDCONTAINER_H

View file

@ -5,11 +5,17 @@
#include <vector>
#include <pgsql/libpq-fe.h>
class PgsqlDatabaseCatalogue;
template<typename T>
class PgContainer {
public:
using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access
explicit PgContainer(PgsqlDatabaseCatalogue *cat)
: m_catalogue(cat)
{}
typename t_Container::const_iterator begin() const
{
return m_container.begin();
@ -54,6 +60,7 @@ public:
return m_container.at(idx);
}
protected:
PgsqlDatabaseCatalogue *m_catalogue;
t_Container m_container;
private:
T m_invalidInstance;

View file

@ -1,7 +1,8 @@
#include "PgDatabaseContainer.h"
#include "PgsqlConn.h"
PgDatabaseContainer::PgDatabaseContainer()
PgDatabaseContainer::PgDatabaseContainer(PgsqlDatabaseCatalogue *cat)
: PgContainer<PgDatabase>(cat)
{}
std::string PgDatabaseContainer::getLoadQuery() const
@ -12,7 +13,7 @@ std::string PgDatabaseContainer::getLoadQuery() const
void PgDatabaseContainer::load(const Pgsql::Result &res)
{
const int n_rows = res.getRows();
const int n_rows = res.rows();
m_container.clear();
m_container.reserve(n_rows);
for (auto row : res) {

View file

@ -14,7 +14,7 @@ namespace Pgsql {
class PgDatabaseContainer: public PgContainer<PgDatabase> {
public:
PgDatabaseContainer();
explicit PgDatabaseContainer(PgsqlDatabaseCatalogue *cat);
std::string getLoadQuery() const;
void load(const Pgsql::Result &res);

View file

@ -2,7 +2,9 @@
#include "PgsqlConn.h"
#include <algorithm>
PgTypeContainer::PgTypeContainer() = default;
PgTypeContainer::PgTypeContainer(PgsqlDatabaseCatalogue *cat)
: PgContainer<PgType>(cat)
{}
//const PgType& PgTypeContainer::getTypeByOid(Oid oid) const
@ -41,7 +43,7 @@ std::string PgTypeContainer::getLoadQuery()
void PgTypeContainer::load(const Pgsql::Result &res)
{
const int n_rows = res.getRows();
const int n_rows = res.rows();
m_container.clear();
m_container.reserve(n_rows);
for (auto row : res) {

View file

@ -15,7 +15,7 @@ class PgTypeContainer: public PgContainer<PgType> {
public:
// using t_Types = std::vector<PgType>; ///< Do not assume it will stay a vector only expect bidirectional access
PgTypeContainer();
explicit PgTypeContainer(PgsqlDatabaseCatalogue *cat);
// t_Types::const_iterator begin() const { return m_types.begin(); }
// t_Types::const_iterator end() const { return m_types.end(); }

View file

@ -5,205 +5,9 @@
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.errorMessage, PQresultErrorMessage(result));
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;
}
bool Row::next()
{
if (m_row < m_result.getRows()) {
++m_row;
return true;
}
return false;
}
Value Row::get(int col) const
{
return m_result.get(col, m_row);
}
//Value Row::get(const char *colname) const
//{
//}
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);
}
Value Result::get(int col, int row) const
{
return Value(
PQgetvalue(result, row, col),
PQftype(result, col)
);
}
Oid Result::type(int col) const
{
return PQftype(result, col);
}
bool Result::null(int col, int row) const
{
return PQgetisnull(result, row, col);
}
Canceller::Canceller(PGcancel *c)
: m_cancel(c)
{}

View file

@ -12,6 +12,8 @@
#include <fmt/format.h>
#include "Pgsql_Result.h"
namespace Pgsql {
class Connection;
@ -36,213 +38,12 @@ namespace Pgsql {
// };
class ErrorDetails {
public:
static ErrorDetails createErrorDetailsFromPGresult(const PGresult *res);
std::string errorMessage;
std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
std::string severity;
std::string messagePrimary;
std::string messageDetail;
std::string messageHint;
int statementPosition; ///< First character is one, measured in characters not bytes!
int internalPosition;
std::string internalQuery;
std::string context;
std::string schemaName;
std::string tableName;
std::string columnName;
std::string datatypeName;
std::string constraintName;
std::string sourceFile;
std::string sourceLine;
std::string sourceFunction;
};
/** \brief Class that is returned as value of a cell to facilitate auto conversion.
*/
class Value {
public:
Value(const char *val, Oid typ)
: m_val(val), m_typ(typ)
{}
QString asQString() const
{
return QString::fromUtf8(m_val);
}
operator QString() const
{
return QString::fromUtf8(m_val);
}
operator std::string() const
{
return m_val;
}
operator short() const
{
return (short)std::atoi(m_val);
}
operator int() const
{
return std::atoi(m_val);
}
operator Oid() const
{
return operator int();
}
operator __int64() const
{
return strtoull(m_val, nullptr, 10);
}
operator bool() const
{
return strcmp(m_val, "t") == 0;
}
private:
const char *m_val;
Oid m_typ;
};
class Result;
/** \brief A reference to a specific row from a result.
*
* As it is a reference its contents won't be valid after its associated result has
* been destroyed.
*/
class Row {
public:
Row(const Result &result, int row)
: m_result(result)
, m_row(row)
{}
bool next();
bool operator==(const Row& rhs)
{
return &m_result == &rhs.m_result
&& m_row == rhs.m_row;
}
Value get(int col) const;
//Value get(const char *colname) const;
//bool get(int col, QString &s);
private:
const Result& m_result;
int m_row;
};
/** \brief Non-copyable but movable wrapper for a postgresql result.
*
* This class makes sure the result is removed from memory. It also supplies an iterator for the
* rows from the result facilitating also the use of the cpp for each.
*/
class Result {
public:
class const_iterator {
public:
const_iterator(const Result &r, int rw)
: m_row(r, rw)
{}
const_iterator operator++()
{
const_iterator t(*this);
m_row.next();
return t;
}
const_iterator& operator++(int)
{
m_row.next();
return *this;
}
bool operator==(const const_iterator &rhs)
{
return m_row == rhs.m_row;
}
bool operator!=(const const_iterator &rhs)
{
return !operator==(rhs);
}
const Row& operator*()
{
return m_row;
}
const Row& operator->()
{
return m_row;
}
private:
Row m_row;
};
Result() = default;
Result(PGresult *result);
~Result();
Result(const Result &rhs) = delete;
Result& operator=(const Result &rhs) = delete;
Result(Result &&rhs);
Result& operator=(Result &&rhs);
operator bool() const;
ExecStatusType resultStatus();
std::string getResStatus();
/** Use this to retrieve an error code when this is an error result
*
* The possible code are listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
*/
std::string diagSqlState();
/** Retrieves all the error fields. */
ErrorDetails diagDetails();
const_iterator begin() const
{
return const_iterator(*this, 0);
}
const_iterator end() const
{
return const_iterator(*this, getRows());
}
int tuplesAffected() const;
int getRows() const;
int getCols() const;
const char* const getColName(int idx) const;
const char * getVal(int col, int row) const;
Value get(int col, int row) const;
Oid type(int col) const;
bool null(int col, int row) const;
// iterator begin();
private:
PGresult *result = nullptr;
};
/** \brief Wrapper for a cancel object from libpq.

View file

@ -1,8 +1,23 @@
#include "PgsqlDatabaseCatalogue.h"
#include "PgTypeContainer.h"
#include "PgDatabaseContainer.h"
#include "PgAuthIdContainer.h"
#include "PgsqlConn.h"
QString getRoleNameFromOid(const PgsqlDatabaseCatalogue *cat, Oid oid)
{
QString name;
const PgAuthIdContainer *auth_ids = cat->authIds();
if (auth_ids) {
const PgAuthId& auth_id = auth_ids->getByOid(oid);
if (auth_id.valid()) {
name = auth_id.name;
}
}
return name;
}
PgsqlDatabaseCatalogue::PgsqlDatabaseCatalogue()
{
}
@ -16,28 +31,71 @@ void PgsqlDatabaseCatalogue::loadAll(Pgsql::Connection &conn)
{
loadTypes(conn);
loadDatabases(conn);
loadAuthIds(conn);
}
void PgsqlDatabaseCatalogue::loadInfo(Pgsql::Connection &conn)
{
Pgsql::Result r = conn.query("SHOW server_version_num");
if (r && r.resultStatus() == PGRES_TUPLES_OK)
if (r.rows() == 1)
m_serverVersion = r.get(0, 0);
r = conn.query("SELECT version()");
if (r && r.resultStatus() == PGRES_TUPLES_OK)
if (r.rows() == 1)
m_serverVersionString = r.get(0, 0);
}
void PgsqlDatabaseCatalogue::loadTypes(Pgsql::Connection &conn)
{
if (m_types == nullptr) {
m_types = new PgTypeContainer;
}
if (m_types == nullptr)
m_types = new PgTypeContainer(this);
std::string q = m_types->getLoadQuery();
Pgsql::Result result = conn.query(q.c_str());
if (result && result.resultStatus() == PGRES_TUPLES_OK)
m_types->load(result);
else
throw std::runtime_error("Query failed");
}
void PgsqlDatabaseCatalogue::loadDatabases(Pgsql::Connection &conn)
{
if (m_databases == nullptr) {
m_databases = new PgDatabaseContainer;
}
if (m_databases == nullptr)
m_databases = new PgDatabaseContainer(this);
std::string q = m_databases->getLoadQuery();
Pgsql::Result result = conn.query(q.c_str());
if (result && result.resultStatus() == PGRES_TUPLES_OK)
m_databases->load(result);
else
throw std::runtime_error("Query failed");
}
void PgsqlDatabaseCatalogue::loadAuthIds(Pgsql::Connection &conn)
{
if (m_authIds == nullptr)
m_authIds = new PgAuthIdContainer(this);
std::string q = m_authIds->getLoadQuery();
Pgsql::Result result = conn.query(q.c_str());
if (result && result.resultStatus() == PGRES_TUPLES_OK)
m_authIds->load(result);
else
throw std::runtime_error("Query failed");
}
const QString& PgsqlDatabaseCatalogue::serverVersionString() const
{
return m_serverVersionString;
}
int PgsqlDatabaseCatalogue::serverVersion() const
{
return m_serverVersion;
}
const PgTypeContainer* PgsqlDatabaseCatalogue::types() const
@ -49,3 +107,8 @@ const PgDatabaseContainer *PgsqlDatabaseCatalogue::databases() const
{
return m_databases;
}
const PgAuthIdContainer *PgsqlDatabaseCatalogue::authIds() const
{
return m_authIds;
}

View file

@ -1,6 +1,8 @@
#ifndef PGSQLDATABASECATALOGUE_H
#define PGSQLDATABASECATALOGUE_H
#include <pgsql/libpq-fe.h>
#include <QString>
#include <vector>
namespace Pgsql {
@ -11,6 +13,7 @@ namespace Pgsql {
class PgTypeContainer;
class PgDatabaseContainer;
class PgAuthIdContainer;
class PgsqlDatabaseCatalogue {
public:
@ -22,14 +25,25 @@ public:
void loadAll(Pgsql::Connection &conn);
void loadInfo(Pgsql::Connection &conn);
void loadTypes(Pgsql::Connection &conn);
void loadDatabases(Pgsql::Connection &conn);
void loadAuthIds(Pgsql::Connection &conn);
const QString& serverVersionString() const;
int serverVersion() const;
const PgTypeContainer* types() const;
const PgDatabaseContainer *databases() const;
const PgAuthIdContainer *authIds() const;
private:
QString m_serverVersionString;
int m_serverVersion;
PgTypeContainer *m_types = nullptr;
PgDatabaseContainer *m_databases = nullptr;
PgAuthIdContainer *m_authIds = nullptr;
};
QString getRoleNameFromOid(const PgsqlDatabaseCatalogue *cat, Oid oid);
#endif // PGSQLDATABASECATALOGUE_H

182
src/Pgsql_Result.cpp Normal file
View file

@ -0,0 +1,182 @@
#include "Pgsql_Result.h"
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.errorMessage, PQresultErrorMessage(result));
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::rows() const
{
return PQntuples(result);
}
int Result::cols() const
{
return PQnfields(result);
}
const char* const Result::getColName(int idx) const
{
return PQfname(result, idx);
}
const char * Result::val(int col, int row) const
{
return PQgetvalue(result, row, col);
}
Value Result::get(int col, int row) const
{
return Value(
PQgetvalue(result, row, col),
PQftype(result, col)
);
}
Oid Result::type(int col) const
{
return PQftype(result, col);
}
bool Result::null(int col, int row) const
{
return PQgetisnull(result, row, col);
}

135
src/Pgsql_Result.h Normal file
View file

@ -0,0 +1,135 @@
#ifndef PGSQL_RESULT_H
#define PGSQL_RESULT_H
#include "Pgsql_Row.h"
namespace Pgsql {
class ErrorDetails {
public:
static ErrorDetails createErrorDetailsFromPGresult(const PGresult *res);
std::string errorMessage;
std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
std::string severity;
std::string messagePrimary;
std::string messageDetail;
std::string messageHint;
int statementPosition; ///< First character is one, measured in characters not bytes!
int internalPosition;
std::string internalQuery;
std::string context;
std::string schemaName;
std::string tableName;
std::string columnName;
std::string datatypeName;
std::string constraintName;
std::string sourceFile;
std::string sourceLine;
std::string sourceFunction;
};
/** \brief Non-copyable but movable wrapper for a postgresql result.
*
* This class makes sure the result is removed from memory. It also supplies an iterator for the
* rows from the result facilitating also the use of the cpp for each.
*/
class Result {
public:
class const_iterator {
public:
const_iterator(const Result &r, int rw)
: m_row(r, rw)
{}
const_iterator operator++()
{
const_iterator t(*this);
m_row.next();
return t;
}
const_iterator& operator++(int)
{
m_row.next();
return *this;
}
bool operator==(const const_iterator &rhs)
{
return m_row == rhs.m_row;
}
bool operator!=(const const_iterator &rhs)
{
return !operator==(rhs);
}
const Row& operator*()
{
return m_row;
}
const Row& operator->()
{
return m_row;
}
private:
Row m_row;
};
Result() = default;
Result(PGresult *result);
~Result();
Result(const Result &rhs) = delete;
Result& operator=(const Result &rhs) = delete;
Result(Result &&rhs);
Result& operator=(Result &&rhs);
operator bool() const;
ExecStatusType resultStatus();
std::string getResStatus();
/** Use this to retrieve an error code when this is an error result
*
* The possible code are listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
*/
std::string diagSqlState();
/** Retrieves all the error fields. */
ErrorDetails diagDetails();
const_iterator begin() const
{
return const_iterator(*this, 0);
}
const_iterator end() const
{
return const_iterator(*this, rows());
}
int tuplesAffected() const;
int rows() const;
int cols() const;
const char* const getColName(int idx) const;
const char * val(int col, int row) const;
Value get(int col, int row) const;
Oid type(int col) const;
bool null(int col, int row) const;
// iterator begin();
private:
PGresult *result = nullptr;
};
} // end namespace Pgsql
#endif // PGSQL_RESULT_H

36
src/Pgsql_Row.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "Pgsql_Row.h"
#include "Pgsql_Result.h"
using namespace Pgsql;
Row::Row(const Result &result, int row)
: m_result(result)
, m_row(row)
{}
bool Row::next()
{
if (m_row < m_result.rows()) {
++m_row;
return true;
}
return false;
}
bool Row::operator==(const Row& rhs) const
{
return &m_result == &rhs.m_result
&& m_row == rhs.m_row;
}
Value Row::get(int col) const
{
return m_result.get(col, m_row);
}
//Value Row::get(const char *colname) const
//{
//}

31
src/Pgsql_Row.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef PGSQL_ROW_H
#define PGSQL_ROW_H
#include "Pgsql_Value.h"
namespace Pgsql {
class Result;
/** \brief A reference to a specific row from a result.
*
* As it is a reference its contents won't be valid after its associated result has
* been destroyed.
*/
class Row {
public:
Row(const Result &result, int row);
bool next();
bool operator==(const Row& rhs) const;
Value get(int col) const;
//Value get(const char *colname) const;
//bool get(int col, QString &s);
private:
const Result& m_result;
int m_row;
};
} // end namespace Pgsql
#endif // PGSQL_ROW_H

56
src/Pgsql_Value.cpp Normal file
View file

@ -0,0 +1,56 @@
#include "Pgsql_Value.h"
#include <cstdlib>
#include <cstring>
using namespace Pgsql;
Value::Value(const char *val, Oid typ)
: m_val(val), m_typ(typ)
{}
QString Value::asQString() const
{
return QString::fromUtf8(m_val);
}
Value::operator QString() const
{
return QString::fromUtf8(m_val);
}
Value::operator QDateTime() const
{
return QDateTime::fromString(asQString(),
"yyyy-MM-dd hh:mm:ss");
}
Value::operator std::string() const
{
return m_val;
}
Value::operator short() const
{
return (short)std::atoi(m_val);
}
Value::operator int() const
{
return std::atoi(m_val);
}
Value::operator Oid() const
{
return operator int();
}
Value::operator __int64() const
{
return std::strtoull(m_val, nullptr, 10);
}
Value::operator bool() const
{
return std::strcmp(m_val, "t") == 0;
}

33
src/Pgsql_Value.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef PGSQL_VALUE_H
#define PGSQL_VALUE_H
#include "Pgsql_declare.h"
#include <QString>
#include <QDateTime>
namespace Pgsql {
/** \brief Class that is returned as value of a cell to facilitate auto conversion.
*/
class Value {
public:
Value(const char *val, Oid typ);
QString asQString() const;
operator QString() const;
operator QDateTime() const;
operator std::string() const;
operator short() const;
operator int() const;
operator Oid() const;
operator __int64() const;
operator bool() const;
private:
const char *m_val;
Oid m_typ;
};
} // end namespace Pgsql
#endif // PGSQL_VALUE_H

View file

@ -11,13 +11,13 @@ QueryResultModel::QueryResultModel(QObject *parent, std::shared_ptr<Pgsql::Resul
int QueryResultModel::rowCount(const QModelIndex &) const
{
int r = result->getRows();
int r = result->rows();
return r;
}
int QueryResultModel::columnCount(const QModelIndex &) const
{
int r = result->getCols();
int r = result->cols();
return r;
}
@ -34,7 +34,7 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
}
else {
Oid o = result->type(col);
QString s(result->getVal(col, rij));
QString s(result->val(col, rij));
switch (o) {
case oid_bool:
s = (s == "t") ? "TRUE" : "FALSE";
@ -88,7 +88,7 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
r = QBrush(Qt::darkGreen);
break;
case oid_bool:
if (strcmp(result->getVal(col, rij), "t") == 0) {
if (strcmp(result->val(col, rij), "t") == 0) {
r = QBrush(Qt::darkGreen);
}
else {

View file

@ -212,8 +212,8 @@ void QueryTab::explain(bool analyze)
std::thread([this,res]()
{
std::shared_ptr<ExplainRoot> explain;
if (res->getCols() == 1 && res->getRows() == 1) {
std::string s = res->getVal(0, 0);
if (res->cols() == 1 && res->rows() == 1) {
std::string s = res->val(0, 0);
Json::Value root; // will contains the root value after parsing.
Json::Reader reader;
bool parsingSuccessful = reader.parse(s, root);

123
src/RolesTableModel.cpp Normal file
View file

@ -0,0 +1,123 @@
#include "RolesTableModel.h"
#include "PgAuthIdContainer.h"
RolesTableModel::RolesTableModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
void RolesTableModel::setRoleList(const PgAuthIdContainer* roles)
{
beginResetModel();
m_roles = roles;
endResetModel();
}
QVariant RolesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant v;
if (orientation == Qt::Horizontal) {
if (role == Qt::DisplayRole) {
switch (section) {
case NameCol:
v = tr("Name");
break;
case SuperCol:
v = tr("Super");
break;
case InheritCol:
v = tr("Inherit");
break;
case CreateRoleCol:
v = tr("Create role");
break;
case CreateDBCol:
v = tr("Create DB");
break;
case CanLoginCol:
v = tr("Can login");
break;
case ReplicationCol:
v = tr("Replication");
break;
case BypassRlsCol:
v = tr("Bypass RLS");
break;
case ConnlimitCol:
v = tr("Connection limit");
break;
case ValidUntilCol:
v = tr("Valid until");
break;
}
}
}
return v;
}
int RolesTableModel::rowCount(const QModelIndex &parent) const
{
int result = 0;
if (m_roles) {
result = m_roles->count();
}
return result;
}
int RolesTableModel::columnCount(const QModelIndex &parent) const
{
int result = 10;
// if (parent.isValid())
// return 10;
return result;
}
QVariant RolesTableModel::data(const QModelIndex &index, int role) const
{
QVariant v;
//if (!index.isValid())
if (m_roles) {
const PgAuthId &authid = m_roles->getByIdx(index.row());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case NameCol:
v = authid.name;
break;
case SuperCol:
// todo lookup role name
v = authid.super;
break;
case InheritCol:
// todo lookup encoding name
v = authid.inherit;
break;
case CreateRoleCol:
v = authid.createRole;
break;
case CreateDBCol:
v = authid.createDB;
break;
case CanLoginCol:
v = authid.canlogin;
break;
case ReplicationCol:
v = authid.replication;
break;
case BypassRlsCol:
v = authid.bypassRls;
break;
case ConnlimitCol:
// todo lookup tablespace name
v = authid.connLimit;
break;
case ValidUntilCol:
v = authid.validUntil;
break;
}
}
}
return v;
}

39
src/RolesTableModel.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef ROLESTABLEMODEL_H
#define ROLESTABLEMODEL_H
#include <QAbstractTableModel>
class PgAuthIdContainer;
/** Class for displaying the list of roles of a server in a QTableView
*
*/
class RolesTableModel : public QAbstractTableModel {
Q_OBJECT
public:
enum e_Columns : int { NameCol, SuperCol, InheritCol, CreateRoleCol,
CreateDBCol, CanLoginCol, ReplicationCol,
BypassRlsCol, ConnlimitCol, ValidUntilCol };
explicit RolesTableModel(QObject *parent);
void setRoleList(const PgAuthIdContainer* roles);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
const PgAuthIdContainer *m_roles = nullptr;
};
#endif // ROLESTABLEMODEL_H

View file

@ -2,6 +2,7 @@
#include "ui_ServerWindow.h"
#include "OpenDatabase.h"
#include "DatabasesTableModel.h"
#include "RolesTableModel.h"
#include "PgsqlDatabaseCatalogue.h"
ServerWindow::ServerWindow(MasterController *master, QWidget *parent)
@ -12,7 +13,10 @@ ServerWindow::ServerWindow(MasterController *master, QWidget *parent)
ui->setupUi(this);
m_databasesModel = new DatabasesTableModel(this);
ui->tableView->setModel(m_databasesModel);
ui->databasesTableView->setModel(m_databasesModel);
m_rolesModel = new RolesTableModel(this);
ui->rolesTableView->setModel(m_rolesModel);
}
ServerWindow::~ServerWindow()
@ -29,6 +33,7 @@ void ServerWindow::setConfig(const ConnectionConfig &config)
auto cat = m_database->catalogue();
if (cat) {
m_databasesModel->setDatabaseList(cat->databases());
m_rolesModel->setRoleList(cat->authIds());
}
}
QString title = "pglab - ";

View file

@ -11,6 +11,7 @@ class ServerWindow;
class MasterController;
class OpenDatabase;
class DatabasesTableModel;
class RolesTableModel;
class ServerWindow : public ASyncWindow {
Q_OBJECT
@ -22,10 +23,11 @@ public:
private:
Ui::ServerWindow *ui;
MasterController *m_masterController;
MasterController *m_masterController = nullptr;
ConnectionConfig m_config;
OpenDatabase *m_database;
DatabasesTableModel *m_databasesModel;
OpenDatabase *m_database = nullptr;
DatabasesTableModel *m_databasesModel = nullptr;
RolesTableModel *m_rolesModel = nullptr;
};
#endif // SERVERWINDOW_H

View file

@ -30,7 +30,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="databasesTab">
<attribute name="title">
@ -50,7 +50,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableView" name="tableView">
<widget class="QTableView" name="databasesTableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
@ -82,7 +82,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableView" name="tableView_2">
<widget class="QTableView" name="tablespacesTableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
@ -114,7 +114,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableView" name="tableView_3">
<widget class="QTableView" name="rolesTableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>

View file

@ -59,7 +59,13 @@ SOURCES += main.cpp\
DatabasesTableModel.cpp \
PgDatabase.cpp \
PgDatabaseContainer.cpp \
Pgsql_Params.cpp
Pgsql_Params.cpp \
RolesTableModel.cpp \
PgAuthId.cpp \
PgAuthIdContainer.cpp \
Pgsql_Result.cpp \
Pgsql_Row.cpp \
Pgsql_Value.cpp
HEADERS += \
sqlparser.h \
@ -105,7 +111,13 @@ HEADERS += \
PgDatabaseContainer.h \
PgContainer.h \
Pgsql_Params.h \
Pgsql_declare.h
Pgsql_declare.h \
RolesTableModel.h \
PgAuthId.h \
PgAuthIdContainer.h \
Pgsql_Result.h \
Pgsql_Row.h \
Pgsql_Value.h
FORMS += mainwindow.ui \
DatabaseWindow.ui \
@ -119,4 +131,4 @@ FORMS += mainwindow.ui \
RESOURCES += \
resources.qrc
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01
#QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01