Messy commit. Testing suff and some improvements to how data is shown.

This commit is contained in:
eelke 2017-12-09 10:45:13 +01:00
parent bebb3391c3
commit 3a13b7ffb4
59 changed files with 2045 additions and 716 deletions

View file

@ -0,0 +1,409 @@
#include "ExplainTreeModelItem.h"
#include "json/json.h"
#include <limits>
namespace {
ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan)
{
const auto json_null = Json::Value::nullSingleton();
ExplainTreeModelItemPtr result = std::make_shared<ExplainTreeModelItem>();
result->nodeType = QString::fromStdString(plan.get("Node Type", json_null).asString());
result->parallelAware = plan.get("Parallel Aware", json_null).asBool();
result->strategy = QString::fromStdString(plan.get("Strategy", json_null).asString());
result->joinType = QString::fromStdString(plan.get("Join Type", json_null).asString());
result->startupCost = plan.get("Startup Cost", json_null).asFloat();
result->totalCost = plan.get("Total Cost", json_null).asFloat();
result->estimatedRows = plan.get("Plan Rows", json_null).asInt();
result->planWidth = plan.get("Plan Width", json_null).asInt();
result->actualStartupTime = plan.get("Actual Startup Time", json_null).asFloat();
result->actualTotalTime = plan.get("Actual Total Time", json_null).asFloat();
result->actualRows = plan.get("Actual Rows", json_null).asInt();
result->actualLoops = plan.get("Actual Loops", json_null).asInt();
result->relationName = QString::fromStdString(plan.get("Relation Name", json_null).asString());
result->alias = QString::fromStdString(plan.get("Alias", json_null).asString());
result->scanDirection = QString::fromStdString(plan.get("Scan Direction", json_null).asString());
result->indexName = QString::fromStdString(plan.get("Index Name", json_null).asString());
result->indexCondition = QString::fromStdString(plan.get("Index Cond", json_null).asString());
result->indexRecheck = QString::fromStdString(plan.get("Rows Removed by Index Recheck", json_null).asString());
result->filter = QString::fromStdString(plan.get("Filter", json_null).asString());
result->hashCondition = QString::fromStdString(plan.get("Hash Cond", json_null).asString());
result->sortKey = QString::fromStdString(plan.get("Sort Key", json_null).toStyledString());
result->sortMethod = QString::fromStdString(plan.get("Sort Method", json_null).asString());
result->sortSpaceUsed = plan.get("Sort Space Used", json_null).asInt();
result->sortSpaceType = QString::fromStdString(plan.get("Sort Space Type", json_null).asString());
result->sharedBlocks.hit = plan.get("Shared Hit Blocks", json_null).asInt();
result->sharedBlocks.read = plan.get("Shared Read Blocks", json_null).asInt();
result->sharedBlocks.dirtied = plan.get("Shared Dirtied Blocks", json_null).asInt();
result->sharedBlocks.written = plan.get("Shared Written Blocks", json_null).asInt();
result->localBlocks.hit = plan.get("Local Hit Blocks", json_null).asInt();
result->localBlocks.read = plan.get("Local Read Blocks", json_null).asInt();
result->localBlocks.dirtied = plan.get("Local Dirtied Blocks", json_null).asInt();
result->localBlocks.written = plan.get("Local Written Blocks", json_null).asInt();
result->tempBlocks.read = plan.get("Temp Read Blocks", json_null).asInt();
result->tempBlocks.written = plan.get("Temp Written Blocks", json_null).asInt();
result->ioTimes.read = plan.get("I/O Read Time", json_null).asDouble();
result->ioTimes.write = plan.get("I/O Write Time", json_null).asDouble();
Json::Value &plans = plan["Plans"];
if (plans.isArray()) {
for (auto p : plans) {
result->appendChild(
createPlanItemFromJson(p));
}
}
// "Parallel Aware": false,
return result;
}
} // END of unnamed namespace
ExplainRoot::SPtr ExplainRoot::createFromJson(Json::Value &json)
{
auto res = std::make_shared<ExplainRoot>();
// Explain always seems to be an array with one element
if (json.isArray()) {
if (json.size() > 0) {
Json::Value &explain = json[0];
Json::Value &plan = explain["Plan"];
res->plan = createPlanItemFromJson(plan);
res->planningTime = explain["Planning Time"].asFloat();
res->executionTime = explain["Execution Time"].asFloat();
res->totalRuntime = explain["Total Runtime"].asFloat();
}
}
return res;
}
ExplainTreeModelItem::ExplainTreeModelItem() = default;
ExplainTreeModelItem::~ExplainTreeModelItem() = default;
void ExplainTreeModelItem::appendChild(ItemPtr child)
{
child->setParent(shared_from_this());
m_childItems.push_back(child);
}
ExplainTreeModelItemPtr ExplainTreeModelItem::child(int row)
{
return m_childItems.at(row);
}
int ExplainTreeModelItem::childCount() const
{
return m_childItems.size();
}
//int ExplainTreeModelItem::columnCount() const
//{
// return 6;
//}
//QVariant ExplainTreeModelItem::data(int column) const
//{
// QVariant r;
// if (column == 0) {
// r = nodeType;
// }
// else if (column == 1) {
// }
// return r;
//}
int ExplainTreeModelItem::row() const
{
int idx = 0;
auto p = m_parentItem.lock();
if (p) {
idx = std::find(p->m_childItems.begin(), p->m_childItems.end(), shared_from_this()) - p->m_childItems.begin();
}
return idx;
}
void ExplainTreeModelItem::setParent(ItemPtr parent)
{
m_parentItem = parent;
}
ExplainTreeModelItemPtr ExplainTreeModelItem::parent()
{
auto p = m_parentItem.lock();
return p;
}
//void ExplainTreeModelItem::setNodeType(QString nt)
//{
// m_nodeType = std::move(nt);
//}
//const QString& ExplainTreeModelItem::nodeType() const
//{
// return m_nodeType;
//}
//void ExplainTreeModelItem::setParallelAware(bool aware)
//{
// m_parallelAware = aware;
//}
//bool ExplainTreeModelItem::getParallelAware() const
//{
// return m_parallelAware;
//}
//void ExplainTreeModelItem::setStrategy(QString strat)
//{
// m_strategy = std::move(strat);
//}
//const QString& ExplainTreeModelItem::strategy() const
//{
// return m_strategy;
//}
//void ExplainTreeModelItem::setJoinType(QString jointype)
//{
// m_joinType = jointype;
//}
//QString ExplainTreeModelItem::joinType() const
//{
// return m_joinType;
//}
//void ExplainTreeModelItem::setStartupCost(float cost)
//{
// m_startupCost = cost;
//}
//void ExplainTreeModelItem::setTotalCost(float cost)
//{
// m_totalCost = cost;
//}
//void ExplainTreeModelItem::setEstimatedRows(long long estimated)
//{
// m_estimatedRows = estimated;
//}
//long long ExplainTreeModelItem::estimatedRows() const
//{
// return m_estimatedRows;
//}
//void ExplainTreeModelItem::setPlanWidth(int width)
//{
// m_planWidth = width;
//}
//void ExplainTreeModelItem::setActualStartupTime(float timems)
//{
// m_actualStartupTime = timems;
//}
//void ExplainTreeModelItem::setActualTotalTime(float timems)
//{
// m_actualTotalTime = timems;
//}
//float ExplainTreeModelItem::actualTotalTime() const
//{
// return m_actualTotalTime;
//}
//void ExplainTreeModelItem::setActualRows(long long rowcount)
//{
// m_actualRows = rowcount;
//}
//long long ExplainTreeModelItem::actualRows() const
//{
// return m_actualRows;
//}
//void ExplainTreeModelItem::setActualLoops(int loopcount)
//{
// m_actualLoops = loopcount;
//}
//int ExplainTreeModelItem::actualLoops() const
//{
// return m_actualLoops;
//}
//void ExplainTreeModelItem::setRelationName(QString n)
//{
// m_relationName = std::move(n);
//}
//void ExplainTreeModelItem::setAlias(QString a)
//{
// m_alias = std::move(a);
//}
//void ExplainTreeModelItem::setScanDirection(QString dir)
//{
// m_scanDirection = std::move(dir);
//}
//void ExplainTreeModelItem::setIndexName(QString idxname)
//{
// m_indexName = std::move(idxname);
//}
//void ExplainTreeModelItem::setIndexCondition(QString idxcond)
//{
// m_indexCondition = std::move(idxcond);
//}
//void ExplainTreeModelItem::setIndexRecheck(QString idxrecheck)
//{
// m_indexRecheck = std::move(idxrecheck);
//}
//void ExplainTreeModelItem::setFilter(QString filter)
//{
// m_filter = std::move(filter);
//}
//void ExplainTreeModelItem::setHashCondition(QString condition)
//{
// m_hashCondition = std::move(condition);
//}
//void ExplainTreeModelItem::setSortKey(QString key)
//{
// m_sortKey = std::move(key);
//}
//void ExplainTreeModelItem::setSortMethod(QString method)
//{
// m_sortMethod = std::move(method);
//}
//void ExplainTreeModelItem::setSortSpaceUsed(int space)
//{
// m_sortSpaceUsed = space;
//}
//void ExplainTreeModelItem::setSortSpaceType(QString type)
//{
// m_sortSpaceType = std::move(type);
//}
float ExplainTreeModelItem::exclusiveTime() const
{
float tt = inclusiveTime();
for (auto c : m_childItems) {
tt -= c->inclusiveTime();
}
return tt;
}
float ExplainTreeModelItem::inclusiveTime() const
{
float t = actualTotalTime * actualLoops;
return t;
}
float ExplainTreeModelItem::estimateError() const
{
float res = 1.0;
if (estimatedRows > actualRows) {
if (actualRows > 0) {
res = float(estimatedRows) / actualRows;
}
else {
res = std::numeric_limits<float>::infinity();
}
}
else if (actualRows > estimatedRows) {
if (estimatedRows > 0) {
res = float(actualRows) / estimatedRows;
}
else {
res = std::numeric_limits<float>::infinity();
}
res = -res;
}
return res;
}
QString ExplainTreeModelItem::detailString() const
{
QString s;
if (!joinType.isEmpty()) {
s += joinType + " " + nodeType + " ";
}
if (!strategy.isEmpty()) {
s += strategy + " " + nodeType + " ";
}
if (!indexName.isEmpty()) {
s+= scanDirection + " "
+ nodeType + "\n";
if (!indexCondition.isEmpty()) {
s += "cond: " + indexCondition + " ";
}
if (!filter.isEmpty()) {
s += "filter: " + filter + "\n";
}
if (!indexRecheck.isEmpty()) {
s += "removed by recheck: " + indexRecheck + "\n";
}
s += "idx: " + indexName + " rel: " + alias + " ";
}
else {
if (!alias.isEmpty()) {
s += nodeType + " rel: " + alias + " ";
}
}
if (!hashCondition.isEmpty()) {
s += hashCondition + " ";
}
if (!sortMethod.isEmpty()) {
s += sortMethod + " " + sortSpaceType + " "
+ QString::number(sortSpaceUsed) + "kB "
+ sortKey + " ";
}
return s.trimmed();
}
//"Sort Key": ["pg_attribute.attname"],
//"Sort Method": "quicksort",
//"Sort Space Used": 1426,
//"Sort Space Type": "Memory",
//{
// "Node Type": "Index Scan",
// "Parent Relationship": "Inner",
// "Scan Direction": "Forward",
// "Index Name": "pg_type_oid_index",
// "Relation Name": "pg_type",
// "Alias": "pg_type",
// "Startup Cost": 0.15,
// "Total Cost": 0.18,
// "Plan Rows": 1,
// "Plan Width": 758,
// "Actual Startup Time": 0.003,
// "Actual Total Time": 0.004,
// "Actual Rows": 1,
// "Actual Loops": 100,
// "Index Cond": "(oid = pg_attribute.atttypid)",
// "Rows Removed by Index Recheck": 0
// "Filter": "actief"
//}

163
core/ExplainTreeModelItem.h Normal file
View file

@ -0,0 +1,163 @@
#pragma once
#include <QList>
//#include <QVariant>
#include <vector>
#include <memory>
namespace Json {
class Value;
}
class ExplainTreeModelItem;
typedef std::shared_ptr<ExplainTreeModelItem> ExplainTreeModelItemPtr;
/* Columns for tree
* 0. explain text
* 1. exclusive times
* 2. inclusive
* 3. rows x
* 4. rows
* 5. loops
*/
/** \brief Class for the nodes in the QueryExplainModel
*/
class ExplainTreeModelItem: public std::enable_shared_from_this<ExplainTreeModelItem> {
public:
typedef std::shared_ptr<ExplainTreeModelItem> ItemPtr;
struct Buffer {
int hit = 0;
int read = 0;
int dirtied = 0;
int written = 0;
QString asString() const
{
return QString::asprintf("h %d/r %d/d %d/w %d", hit, read, dirtied, written);
}
};
struct TempBlocks {
int read = 0;
int written = 0;
};
struct IoTimes {
double read = 0.0;
double write =0.0;
};
ExplainTreeModelItem();
~ExplainTreeModelItem();
ExplainTreeModelItem(const ExplainTreeModelItem &rhs) = delete;
ExplainTreeModelItem &operator=(const ExplainTreeModelItem &rhs) = delete;
void appendChild(ItemPtr child);
ExplainTreeModelItemPtr child(int row);
int childCount() const;
// int columnCount() const;
// QVariant data(int column) const;
int row() const;
void setParent(ItemPtr parent);
ItemPtr parent();
// void setNodeType(QString nt);
// const QString& nodeType() const;
// void setParallelAware(bool aware);
// bool getParallelAware() const;
// void setStrategy(QString strat);
// const QString& strategy() const;
// void setJoinType(QString jointype);
// QString joinType() const;
// void setStartupCost(float cost);
// void setTotalCost(float cost);
// void setEstimatedRows(long long estimated);
// long long estimatedRows() const;
// void setPlanWidth(int width);
// void setActualStartupTime(float timems);
// void setActualTotalTime(float timems);
// float actualTotalTime() const;
// void setActualRows(long long rowcount);
// long long actualRows() const;
// void setActualLoops(int loopcount);
// int actualLoops() const;
// void setRelationName(QString n);
// void setAlias(QString a);
// void setScanDirection(QString dir);
// void setIndexName(QString idxname);
// void setIndexCondition(QString idxcond);
// void setIndexRecheck(QString idxrecheck);
// void setFilter(QString filter);
// void setHashCondition(QString condition);
// void setSortKey(QString key);
// void setSortMethod(QString method);
// void setSortSpaceUsed(int space);
// void setSortSpaceType(QString type);
/** ActualTotalTime minus the actual total time of it's children */
float exclusiveTime() const;
float inclusiveTime() const;
float estimateError() const;
QString detailString() const;
//private:
std::vector<ItemPtr> m_childItems;
std::weak_ptr<ExplainTreeModelItem> m_parentItem;
QString nodeType;
bool parallelAware; // 9.6
QString strategy;
QString joinType;
float startupCost = 0.f;
float totalCost = 0.f;
int64_t estimatedRows = 0;
int planWidth = 0;
float actualStartupTime = 0.f;
float actualTotalTime = 0.f;
int64_t actualRows = 0;
int actualLoops = 0;
QString relationName;
QString alias;
QString scanDirection;
QString indexName;
QString indexCondition;
QString indexRecheck;
QString filter;
QString hashCondition;
QString sortKey;
QString sortMethod;
int sortSpaceUsed = -1;
QString sortSpaceType;
// Buffering related
Buffer sharedBlocks;
Buffer localBlocks;
TempBlocks tempBlocks;
IoTimes ioTimes;
// "Triggers": [
// ],
};
class ExplainRoot {
public:
using SPtr = std::shared_ptr<ExplainRoot>;
static SPtr createFromJson(Json::Value &json);
ExplainTreeModelItemPtr plan;
float planningTime = 0.f;
// Triggers???
float executionTime = 0.f;
float totalRuntime = 0.f;
};

View file

@ -31,7 +31,9 @@ SOURCES += my_boost_assert_handler.cpp \
PasswordManager.cpp \
CsvWriter.cpp \
BackupFormatModel.cpp \
QueuedBackgroundTask.cpp
QueuedBackgroundTask.cpp \
ExplainTreeModelItem.cpp \
jsoncpp.cpp
HEADERS += PasswordManager.h \
SqlLexer.h \
@ -39,9 +41,12 @@ HEADERS += PasswordManager.h \
CsvWriter.h \
BackupFormatModel.h \
QueuedBackgroundTask.h \
Expected.h
Expected.h \
ExplainTreeModelItem.h
unix {
target.path = /usr/lib
INSTALLS += target
}

36
pglab/BaseTableModel.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "BaseTableModel.h"
#include "ResultTableModelUtil.h"
#include <QBrush>
using namespace Pgsql;
BaseTableModel::BaseTableModel(QObject *parent)
: QAbstractTableModel(parent)
{}
QVariant BaseTableModel::data(const QModelIndex &index, int role) const
{
QVariant v;
Oid oid = getType(index.column());
if (role == Qt::DisplayRole) {
v = getData(index);
if (oid == BOOLOID) {
v = FormatBoolForDisplay(v.toBool());
}
}
else if (role == Qt::TextAlignmentRole) {
v = GetDefaultAlignmentForType(oid);
}
else if (role == Qt::ForegroundRole) {
if (oid == BOOLOID) {
QVariant d = getData(index);
if (d.type() == QVariant::Bool) {
v = QBrush(GetDefaultBoolColor(d.toBool()));
}
}
else {
v = QBrush(GetDefaultColorForType(oid));
}
}
return v;
}

21
pglab/BaseTableModel.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef BASETABLEMODEL_H
#define BASETABLEMODEL_H
#include <QAbstractTableModel>
#include "Pgsql_declare.h"
class BaseTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit BaseTableModel(QObject *parent);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
protected:
virtual Oid getType(int column) const = 0;
virtual QVariant getData(const QModelIndex &index) const = 0;
};
#endif // BASETABLEMODEL_H

View file

@ -1,10 +1,13 @@
#include "DatabasesTableModel.h"
#include "DatabasesTableModel.h"
#include "PgDatabaseCatalogue.h"
#include "PgDatabaseContainer.h"
#include "PgAuthIdContainer.h"
#include "ResultTableModelUtil.h"
using namespace Pgsql;
DatabasesTableModel::DatabasesTableModel(QObject *parent)
: QAbstractTableModel(parent)
: BaseTableModel(parent)
{
}
@ -59,7 +62,7 @@ QVariant DatabasesTableModel::headerData(int section, Qt::Orientation orientatio
return v;
}
int DatabasesTableModel::rowCount(const QModelIndex &parent) const
int DatabasesTableModel::rowCount(const QModelIndex &) const
{
int result = 0;
if (m_databases) {
@ -68,62 +71,79 @@ int DatabasesTableModel::rowCount(const QModelIndex &parent) const
return result;
}
int DatabasesTableModel::columnCount(const QModelIndex &parent) const
int DatabasesTableModel::columnCount(const QModelIndex &) const
{
int result = 10;
// if (parent.isValid())
// return 10;
int result = COL_COUNT;
return result;
}
QVariant DatabasesTableModel::data(const QModelIndex &index, int role) const
Oid DatabasesTableModel::getType(int column) const
{
Oid oid;
switch (column) {
case AllowConnCol:
case IsTemplateCol:
oid = BOOLOID;
break;
case ConnLimitCol:
oid = INT4OID;
break;
case AclCol:
case CollateCol:
case CTypeCol:
case EncodingCol:
case DbaCol:
case NameCol:
case TablespaceCol:
oid = VARCHAROID;
break;
default:
oid = InvalidOid;
}
return oid;
}
QVariant DatabasesTableModel::getData(const QModelIndex &index) const
{
QVariant v;
//if (!index.isValid())
if (m_databases) {
const PgDatabase &db = m_databases->getByIdx(index.row());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case NameCol:
v = db.name;
break;
case DbaCol:
// todo lookup role name
{
const auto& roles = m_catalog->authIds();
v = QString("%1 (%2)").arg(roles->getByOid(db.dba).name).arg(db.dba);
}
break;
case EncodingCol:
// todo lookup encoding name
v = db.encoding;
break;
case CollateCol:
v = db.collate;
break;
case CTypeCol:
v = db.ctype;
break;
case IsTemplateCol:
v = db.isTemplate;
break;
case AllowConnCol:
v = db.allowConn;
break;
case ConnLimitCol:
v = db.connLimit;
break;
case TablespaceCol:
// todo lookup tablespace name
v = db.tablespace;
break;
case AclCol:
v = db.acl;
break;
}
switch (index.column()) {
case NameCol:
v = db.name;
break;
case DbaCol:
v = getRoleDisplayString(m_catalog, db.dba);
break;
case EncodingCol:
// todo lookup encoding name
v = db.encoding;
break;
case CollateCol:
v = db.collate;
break;
case CTypeCol:
v = db.ctype;
break;
case IsTemplateCol:
v = db.isTemplate;
break;
case AllowConnCol:
v = db.allowConn;
break;
case ConnLimitCol:
v = db.connLimit;
break;
case TablespaceCol:
// todo lookup tablespace name
v = db.tablespace;
break;
case AclCol:
v = db.acl;
break;
}
}
return v;
}

View file

@ -1,7 +1,7 @@
#ifndef DATABASESTABLEMODEL_H
#ifndef DATABASESTABLEMODEL_H
#define DATABASESTABLEMODEL_H
#include <QAbstractTableModel>
#include "BaseTableModel.h"
class PgDatabaseContainer;
class PgDatabaseCatalogue;
@ -9,14 +9,14 @@ class PgDatabaseCatalogue;
/** Class for displaying the list of databases of a server in a QTableView
*
*/
class DatabasesTableModel : public QAbstractTableModel
class DatabasesTableModel : public BaseTableModel
{
Q_OBJECT
public:
enum e_Columns : int { NameCol, DbaCol, EncodingCol, CollateCol,
CTypeCol, IsTemplateCol, AllowConnCol, ConnLimitCol,
TablespaceCol, AclCol };
TablespaceCol, AclCol, COL_COUNT };
@ -31,7 +31,10 @@ public:
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;
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;
// QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
const PgDatabaseCatalogue *m_catalog = nullptr;

View file

@ -1,384 +0,0 @@
#include "ExplainTreeModelItem.h"
#include "json/json.h"
#include <limits>
namespace {
ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan)
{
ExplainTreeModelItemPtr result = std::make_shared<ExplainTreeModelItem>();
result->setNodeType(QString::fromStdString(plan["Node Type"].asString()));
result->setStrategy(QString::fromStdString(plan["Strategy"].asString()));
result->setJoinType(QString::fromStdString(plan["Join Type"].asString()));
result->setStartupCost(plan["Startup Cost"].asFloat());
result->setTotalCost(plan["Total Cost"].asFloat());
result->setEstimatedRows(plan["Plan Rows"].asInt());
result->setPlanWidth(plan["Plan Width"].asInt());
result->setActualStartupTime(plan["Actual Startup Time"].asFloat());
result->setActualTotalTime(plan["Actual Total Time"].asFloat());
result->setActualRows(plan["Actual Rows"].asInt());
result->setActualLoops(plan["Actual Loops"].asInt());
result->setRelationName(QString::fromStdString(plan["Relation Name"].asString()));
result->setAlias(QString::fromStdString(plan["Alias"].asString()));
result->setScanDirection(QString::fromStdString(plan["Scan Direction"].asString()));
result->setIndexName(QString::fromStdString(plan["Index Name"].asString()));
result->setIndexCondition(QString::fromStdString(plan["Index Cond"].asString()));
result->setIndexRecheck(QString::fromStdString(plan["Rows Removed by Index Recheck"].asString()));
result->setFilter(QString::fromStdString(plan["Filter"].asString()));
result->setHashCondition(QString::fromStdString(plan["Hash Cond"].asString()));
result->setSortKey(QString::fromStdString(plan["Sort Key"].toStyledString()));
result->setSortMethod(QString::fromStdString(plan["Sort Method"].asString()));
if (plan.isMember("Sort Space Used")) {
const Json::Value& sm = plan["Sort Space Used"];
if (sm.isInt()) {
result->setSortSpaceUsed(sm.asInt());
}
}
result->setSortSpaceType(QString::fromStdString(plan["Sort Space Type"].asString()));
Json::Value &plans = plan["Plans"];
if (plans.isArray()) {
for (auto p : plans) {
result->appendChild(
createPlanItemFromJson(p));
}
}
return result;
}
} // END of unnamed namespace
ExplainRoot::SPtr ExplainRoot::createFromJson(Json::Value &json)
{
auto res = std::make_shared<ExplainRoot>();
// Explain always seems to be an array with one element
if (json.isArray()) {
if (json.size() > 0) {
Json::Value &explain = json[0];
Json::Value &plan = explain["Plan"];
res->plan = createPlanItemFromJson(plan);
res->planningTime = explain["Planning Time"].asFloat();
res->executionTime = explain["Execution Time"].asFloat();
res->totalRuntime = explain["Total Runtime"].asFloat();
}
}
return res;
}
ExplainTreeModelItem::ExplainTreeModelItem() = default;
ExplainTreeModelItem::~ExplainTreeModelItem() = default;
void ExplainTreeModelItem::appendChild(ItemPtr child)
{
child->setParent(shared_from_this());
m_childItems.push_back(child);
}
ExplainTreeModelItemPtr ExplainTreeModelItem::child(int row)
{
return m_childItems.at(row);
}
int ExplainTreeModelItem::childCount() const
{
return m_childItems.size();
}
//int ExplainTreeModelItem::columnCount() const
//{
// return 6;
//}
//QVariant ExplainTreeModelItem::data(int column) const
//{
// QVariant r;
// if (column == 0) {
// r = nodeType;
// }
// else if (column == 1) {
// }
// return r;
//}
int ExplainTreeModelItem::row() const
{
int idx = 0;
auto p = m_parentItem.lock();
if (p) {
idx = std::find(p->m_childItems.begin(), p->m_childItems.end(), shared_from_this()) - p->m_childItems.begin();
}
return idx;
}
void ExplainTreeModelItem::setParent(ItemPtr parent)
{
m_parentItem = parent;
}
ExplainTreeModelItemPtr ExplainTreeModelItem::parent()
{
auto p = m_parentItem.lock();
return p;
}
void ExplainTreeModelItem::setNodeType(QString nt)
{
m_nodeType = std::move(nt);
}
const QString& ExplainTreeModelItem::nodeType() const
{
return m_nodeType;
}
void ExplainTreeModelItem::setStrategy(QString strat)
{
m_strategy = std::move(strat);
}
const QString& ExplainTreeModelItem::strategy() const
{
return m_strategy;
}
void ExplainTreeModelItem::setJoinType(QString jointype)
{
m_joinType = jointype;
}
QString ExplainTreeModelItem::joinType() const
{
return m_joinType;
}
void ExplainTreeModelItem::setStartupCost(float cost)
{
m_startupCost = cost;
}
void ExplainTreeModelItem::setTotalCost(float cost)
{
m_totalCost = cost;
}
void ExplainTreeModelItem::setEstimatedRows(long long estimated)
{
m_estimatedRows = estimated;
}
long long ExplainTreeModelItem::estimatedRows() const
{
return m_estimatedRows;
}
void ExplainTreeModelItem::setPlanWidth(int width)
{
m_planWidth = width;
}
void ExplainTreeModelItem::setActualStartupTime(float timems)
{
m_actualStartupTime = timems;
}
void ExplainTreeModelItem::setActualTotalTime(float timems)
{
m_actualTotalTime = timems;
}
float ExplainTreeModelItem::actualTotalTime() const
{
return m_actualTotalTime;
}
void ExplainTreeModelItem::setActualRows(long long rowcount)
{
m_actualRows = rowcount;
}
long long ExplainTreeModelItem::actualRows() const
{
return m_actualRows;
}
void ExplainTreeModelItem::setActualLoops(int loopcount)
{
m_actualLoops = loopcount;
}
int ExplainTreeModelItem::actualLoops() const
{
return m_actualLoops;
}
void ExplainTreeModelItem::setRelationName(QString n)
{
m_relationName = std::move(n);
}
void ExplainTreeModelItem::setAlias(QString a)
{
m_alias = std::move(a);
}
void ExplainTreeModelItem::setScanDirection(QString dir)
{
m_scanDirection = std::move(dir);
}
void ExplainTreeModelItem::setIndexName(QString idxname)
{
m_indexName = std::move(idxname);
}
void ExplainTreeModelItem::setIndexCondition(QString idxcond)
{
m_indexCondition = std::move(idxcond);
}
void ExplainTreeModelItem::setIndexRecheck(QString idxrecheck)
{
m_indexRecheck = std::move(idxrecheck);
}
void ExplainTreeModelItem::setFilter(QString filter)
{
m_filter = std::move(filter);
}
void ExplainTreeModelItem::setHashCondition(QString condition)
{
m_hashCondition = std::move(condition);
}
void ExplainTreeModelItem::setSortKey(QString key)
{
m_sortKey = std::move(key);
}
void ExplainTreeModelItem::setSortMethod(QString method)
{
m_sortMethod = std::move(method);
}
void ExplainTreeModelItem::setSortSpaceUsed(int space)
{
m_sortSpaceUsed = space;
}
void ExplainTreeModelItem::setSortSpaceType(QString type)
{
m_sortSpaceType = std::move(type);
}
float ExplainTreeModelItem::exclusiveTime() const
{
float tt = inclusiveTime();
for (auto c : m_childItems) {
tt -= c->inclusiveTime();
}
return tt;
}
float ExplainTreeModelItem::inclusiveTime() const
{
float t = m_actualTotalTime * m_actualLoops;
return t;
}
float ExplainTreeModelItem::estimateError() const
{
float res = 1.0;
if (m_estimatedRows > m_actualRows) {
if (m_actualRows > 0) {
res = float(m_estimatedRows) / m_actualRows;
}
else {
res = std::numeric_limits<float>::infinity();
}
}
else if (m_actualRows > m_estimatedRows) {
if (m_estimatedRows > 0) {
res = float(m_actualRows) / m_estimatedRows;
}
else {
res = std::numeric_limits<float>::infinity();
}
res = -res;
}
return res;
}
QString ExplainTreeModelItem::detailString() const
{
QString s;
if (!m_joinType.isEmpty()) {
s += m_joinType + " " + m_nodeType + " ";
}
if (!m_strategy.isEmpty()) {
s += m_strategy + " " + m_nodeType + " ";
}
if (!m_indexName.isEmpty()) {
s+= m_scanDirection + " "
+ m_nodeType + "\n";
if (!m_indexCondition.isEmpty()) {
s += "cond: " + m_indexCondition + " ";
}
if (!m_filter.isEmpty()) {
s += "filter: " + m_filter + "\n";
}
if (!m_indexRecheck.isEmpty()) {
s += "removed by recheck: " + m_indexRecheck + "\n";
}
s += "idx: " + m_indexName + " rel: " + m_alias + " ";
}
else {
if (!m_alias.isEmpty()) {
s += m_nodeType + " rel: " + m_alias + " ";
}
}
if (!m_hashCondition.isEmpty()) {
s += m_hashCondition + " ";
}
if (!m_sortMethod.isEmpty()) {
s += m_sortMethod + " " + m_sortSpaceType + " "
+ QString::number(m_sortSpaceUsed) + "kB "
+ m_sortKey + " ";
}
return s.trimmed();
}
//"Sort Key": ["pg_attribute.attname"],
//"Sort Method": "quicksort",
//"Sort Space Used": 1426,
//"Sort Space Type": "Memory",
//{
// "Node Type": "Index Scan",
// "Parent Relationship": "Inner",
// "Scan Direction": "Forward",
// "Index Name": "pg_type_oid_index",
// "Relation Name": "pg_type",
// "Alias": "pg_type",
// "Startup Cost": 0.15,
// "Total Cost": 0.18,
// "Plan Rows": 1,
// "Plan Width": 758,
// "Actual Startup Time": 0.003,
// "Actual Total Time": 0.004,
// "Actual Rows": 1,
// "Actual Loops": 100,
// "Index Cond": "(oid = pg_attribute.atttypid)",
// "Rows Removed by Index Recheck": 0
// "Filter": "actief"
//}

View file

@ -1,144 +0,0 @@
#pragma once
#include <QList>
//#include <QVariant>
#include <vector>
#include <memory>
namespace Json {
class Value;
}
class ExplainTreeModelItem;
typedef std::shared_ptr<ExplainTreeModelItem> ExplainTreeModelItemPtr;
/* Columns for tree
* 0. explain text
* 1. exclusive times
* 2. inclusive
* 3. rows x
* 4. rows
* 5. loops
*/
/** \brief Class for the nodes in the QueryExplainModel
*/
class ExplainTreeModelItem: public std::enable_shared_from_this<ExplainTreeModelItem> {
public:
typedef std::shared_ptr<ExplainTreeModelItem> ItemPtr;
ExplainTreeModelItem();
~ExplainTreeModelItem();
ExplainTreeModelItem(const ExplainTreeModelItem &rhs) = delete;
ExplainTreeModelItem &operator=(const ExplainTreeModelItem &rhs) = delete;
void appendChild(ItemPtr child);
ExplainTreeModelItemPtr child(int row);
int childCount() const;
// int columnCount() const;
// QVariant data(int column) const;
int row() const;
void setParent(ItemPtr parent);
ItemPtr parent();
void setNodeType(QString nt);
const QString& nodeType() const;
void setStrategy(QString strat);
const QString& strategy() const;
void setJoinType(QString jointype);
QString joinType() const;
void setStartupCost(float cost);
void setTotalCost(float cost);
void setEstimatedRows(long long estimated);
long long estimatedRows() const;
void setPlanWidth(int width);
void setActualStartupTime(float timems);
void setActualTotalTime(float timems);
float actualTotalTime() const;
void setActualRows(long long rowcount);
long long actualRows() const;
void setActualLoops(int loopcount);
int actualLoops() const;
void setRelationName(QString n);
void setAlias(QString a);
void setScanDirection(QString dir);
void setIndexName(QString idxname);
void setIndexCondition(QString idxcond);
void setIndexRecheck(QString idxrecheck);
void setFilter(QString filter);
void setHashCondition(QString condition);
void setSortKey(QString key);
void setSortMethod(QString method);
void setSortSpaceUsed(int space);
void setSortSpaceType(QString type);
/** ActualTotalTime minus the actual total time of it's children */
float exclusiveTime() const;
float inclusiveTime() const;
float estimateError() const;
QString detailString() const;
private:
std::vector<ItemPtr> m_childItems;
std::weak_ptr<ExplainTreeModelItem> m_parentItem;
QString m_nodeType;
QString m_strategy;
QString m_joinType;
float m_startupCost = 0.f;
float m_totalCost = 0.f;
long long m_estimatedRows = 0;
int m_planWidth = 0;
float m_actualStartupTime = 0.f;
float m_actualTotalTime = 0.f;
long long m_actualRows = 0;
int m_actualLoops = 0;
QString m_relationName;
QString m_alias;
QString m_scanDirection;
QString m_indexName;
QString m_indexCondition;
QString m_indexRecheck;
QString m_filter;
QString m_hashCondition;
QString m_sortKey;
QString m_sortMethod;
int m_sortSpaceUsed = -1;
QString m_sortSpaceType;
// "Output": ["f1.id", "f1.program", "f1.version", "f1.lic_number", "f1.callstack_crc_1", "f1.callstack_crc_2", "array_agg(f2.id)"],
// "Group Key": ["f1.id"],
// "Shared Hit Blocks": 694427,
// "Shared Read Blocks": 0,
// "Shared Dirtied Blocks": 0,
// "Shared Written Blocks": 0,
// "Local Hit Blocks": 0,
// "Local Read Blocks": 0,
// "Local Dirtied Blocks": 0,
// "Local Written Blocks": 0,
// "Temp Read Blocks": 0,
// "Temp Written Blocks": 0
// "Parent Relationship": "Outer",
};
class ExplainRoot {
public:
using SPtr = std::shared_ptr<ExplainRoot>;
static SPtr createFromJson(Json::Value &json);
ExplainTreeModelItemPtr plan;
float planningTime = 0.f;
// Triggers???
float executionTime = 0.f;
float totalRuntime = 0.f;
};

View file

@ -1,4 +1,4 @@
#include "PgDatabaseCatalogue.h"
#include "PgDatabaseCatalogue.h"
#include "PgTypeContainer.h"
#include "PgDatabaseContainer.h"
#include "PgAuthIdContainer.h"
@ -8,7 +8,7 @@
QString getRoleNameFromOid(const PgDatabaseCatalogue *cat, Oid oid)
{
QString name;
const PgAuthIdContainer *auth_ids = cat->authIds();
auto auth_ids = cat->authIds();
if (auth_ids) {
const PgAuthId& auth_id = auth_ids->getByOid(oid);
if (auth_id.valid()) {
@ -18,6 +18,13 @@ QString getRoleNameFromOid(const PgDatabaseCatalogue *cat, Oid oid)
return name;
}
QString getRoleDisplayString(const PgDatabaseCatalogue *cat, Oid oid)
{
QString name = getRoleNameFromOid(cat, oid);
return QString("%1 (%2)").arg(name).arg(oid);
}
PgDatabaseCatalogue::PgDatabaseCatalogue()
{
}

View file

@ -45,5 +45,7 @@ private:
};
QString getRoleNameFromOid(const PgDatabaseCatalogue *cat, Oid oid);
QString getRoleDisplayString(const PgDatabaseCatalogue *cat, Oid oid);
#endif // PGSQLDATABASECATALOGUE_H

View file

@ -1,5 +1,6 @@
#include "PgDatabaseContainer.h"
#include "PgDatabaseContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
PgDatabaseContainer::PgDatabaseContainer(PgDatabaseCatalogue *cat)
: PgContainer<PgDatabase>(cat)
@ -17,18 +18,21 @@ void PgDatabaseContainer::load(const Pgsql::Result &res)
m_container.clear();
m_container.reserve(n_rows);
for (auto row : res) {
Pgsql::Col col(row);
PgDatabase v;
v.oid << row.get(0); // InvalidOid;
v.name << row.get(1);
v.dba << row.get(2); // owner?
v.encoding << row.get(3);
v.collate << row.get(4);
v.ctype << row.get(5);
v.isTemplate << row.get(6);
v.allowConn << row.get(7);
v.connLimit << row.get(8);
v.tablespace << row.get(9);
v.acl << row.get(10);
// v.oid << row.get(0); // InvalidOid;
// v.name << row.get(1);
// v.dba << row.get(2); // owner?
// v.encoding << row.get(3);
// v.collate << row.get(4);
// v.ctype << row.get(5);
// v.isTemplate << row.get(6);
// v.allowConn << row.get(7);
// v.connLimit << row.get(8);
// v.tablespace << row.get(9);
// v.acl << row.get(10);
col >> v.oid >> v.name >> v.dba >> v.encoding >> v.collate >> v.ctype >> v.isTemplate
>> v.allowConn >> v.connLimit >> v.tablespace >> v.acl;
m_container.push_back(v);
}
std::sort(m_container.begin(), m_container.end());

View file

@ -10,7 +10,8 @@ const int c_ColumnEstErr = 3;
const int c_ColumnRowCount = 4;
const int c_ColumnLoops = 5;
const int c_ColumnDetails = 6;
const int c_NumberOfColumns = 7;
const int c_ColumnShared = 7;
const int c_NumberOfColumns = 8;
QueryExplainModel::QueryExplainModel(QObject *parent, ExplainRoot::SPtr exp)
: QAbstractItemModel(parent)
@ -26,7 +27,7 @@ QVariant QueryExplainModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
switch (col) {
case c_ColumnNode:
result = item->nodeType();
result = item->nodeType;
break;
case c_ColumnExclusive:
result = item->exclusiveTime();
@ -38,14 +39,17 @@ if (role == Qt::DisplayRole) {
result = item->estimateError();
break;
case c_ColumnRowCount:
result = item->actualRows();
result = item->actualRows;
break;
case c_ColumnLoops:
result = item->actualLoops();
result = item->actualLoops;
break;
case c_ColumnDetails:
result = item->detailString();
break;
case c_ColumnShared:
result = item->sharedBlocks.asString();
break;
} // end switch column
}
else if (role == Qt::TextAlignmentRole) {
@ -133,6 +137,9 @@ QVariant QueryExplainModel::headerData(int section, Qt::Orientation orientation,
case c_ColumnDetails:
v = "Details";
break;
case c_ColumnShared:
v = "Shared";
break;
}
}
// else if (role == Qt::SizeHintRole) {

View file

@ -1,4 +1,5 @@
#include "QueryResultModel.h"
#include "ResultTableModelUtil.h"
#include "Pgsql_declare.h"
#include <QBrush>
#include <QColor>
@ -36,7 +37,7 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
Oid o = result->type(col);
QString s(result->val(col, rij));
switch (o) {
case oid_bool:
case BOOLOID:
s = (s == "t") ? "TRUE" : "FALSE";
// intentional fall through
default:
@ -50,23 +51,7 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
}
}
else if (role == Qt::TextAlignmentRole) {
Oid o = result->type(col);
switch (o) {
case oid_int2:
case oid_int4:
case oid_int8:
case oid_float4:
case oid_float8:
case oid_numeric:
case oid_oid:
r = Qt::AlignRight + Qt::AlignVCenter;
break;
case oid_bool:
r = Qt::AlignCenter;
break;
default:
r = Qt::AlignLeft + Qt::AlignVCenter;
}
r = GetDefaultAlignmentForType(result->type(col));
}
else if (role == Qt::ForegroundRole) {
if (result->null(col, rij)) {
@ -75,19 +60,19 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
else {
Oid o = result->type(col);
switch (o) {
case oid_int2:
case oid_int4:
case oid_int8:
case INT2OID:
case INT4OID:
case INT8OID:
r = QBrush(Qt::darkBlue);
break;
case oid_float4:
case oid_float8:
case FLOAT4OID:
case FLOAT8OID:
r = QBrush(Qt::darkCyan);
break;
case oid_numeric:
case NUMERICOID:
r = QBrush(Qt::darkGreen);
break;
case oid_bool:
case BOOLOID:
if (strcmp(result->val(col, rij), "t") == 0) {
r = QBrush(Qt::darkGreen);
}

View file

@ -1,4 +1,4 @@
#include "QueryTab.h"
#include "QueryTab.h"
#include "ui_QueryTab.h"
#include "SqlSyntaxHighlighter.h"
#include <QStandardPaths>
@ -229,30 +229,32 @@ void QueryTab::explain(bool analyze)
std::string analyze_str;
if (analyze) {
analyze_str = "ANALYZE, ";
analyze_str = "ANALYZE, BUFFERS, ";
}
m_stopwatch.start();
std::string cmd = "EXPLAIN (" + analyze_str + "VERBOSE, BUFFERS, FORMAT JSON) " + getCommandUtf8();
std::string cmd = "EXPLAIN (" + analyze_str + "VERBOSE, FORMAT JSON) " + getCommandUtf8();
m_dbConnection.send(cmd,
[this](Expected<std::shared_ptr<Pgsql::Result>> exp_res, qint64 )
{
if (exp_res.valid()) {
// Process explain data seperately
auto res = exp_res.get();
std::thread([this,res]()
{
std::shared_ptr<ExplainRoot> explain;
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);
if (parsingSuccessful) {
explain = ExplainRoot::createFromJson(root);
if (res) {
std::thread([this,res]()
{
std::shared_ptr<ExplainRoot> explain;
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);
if (parsingSuccessful) {
explain = ExplainRoot::createFromJson(root);
}
}
}
m_win->QueueTask([this, explain]() { explain_ready(explain); });
}).detach();
m_win->QueueTask([this, explain]() { explain_ready(explain); });
}).detach();
}
}
});
}

View file

@ -0,0 +1,57 @@
#include "ResultTableModelUtil.h"
using namespace Pgsql;
int GetDefaultAlignmentForType(Oid o)
{
int r;
switch (o) {
case INT2OID:
case INT4OID:
case INT8OID:
case FLOAT4OID:
case FLOAT8OID:
case NUMERICOID:
case OIDOID:
r = GetDefaultNumberAlignment();
break;
case BOOLOID:
r = GetDefaultBoolAlignment(); // Qt::AlignCenter;
break;
default:
r = GetDefaultAlignment();
}
return r;
}
QColor GetDefaultColorForType(Oid o)
{
QColor c;
switch (o) {
case INT2OID:
case INT4OID:
case INT8OID:
c = GetDefaultIntegerColor();
break;
case FLOAT4OID:
case FLOAT8OID:
c = GetDefaultFloatColor();
break;
case NUMERICOID:
c = GetDefaultNumericColor();
break;
case OIDOID:
case BOOLOID:
default:
c = Qt::black;
}
return c;
}
QString FormatBoolForDisplay(bool v)
{
return v ? "TRUE" : "FALSE";
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "Pgsql_declare.h"
#include <QAbstractTableModel>
#include <QColor>
int GetDefaultAlignmentForType(Oid oid);
QColor GetDefaultColorForType(Oid oid);
inline int GetDefaultAlignment() { return Qt::AlignLeft + Qt::AlignVCenter; }
inline int GetDefaultBoolAlignment() { return Qt::AlignCenter + Qt::AlignVCenter; }
inline int GetDefaultNumberAlignment() { return Qt::AlignRight + Qt::AlignVCenter; }
inline QColor GetDefaultBoolColor(bool v)
{
return v ? Qt::darkGreen : Qt::darkRed;
}
inline QColor GetDefaultIntegerColor() { return Qt::darkBlue; }
inline QColor GetDefaultFloatColor() { return Qt::darkCyan; }
inline QColor GetDefaultNumericColor() { return Qt::darkGreen; }
QString FormatBoolForDisplay(bool v);

View file

@ -2,7 +2,7 @@
#include "PgAuthIdContainer.h"
RolesTableModel::RolesTableModel(QObject *parent)
: QAbstractTableModel(parent)
: BaseTableModel(parent)
{
}
@ -74,50 +74,80 @@ int RolesTableModel::columnCount(const QModelIndex &parent) const
return result;
}
QVariant RolesTableModel::data(const QModelIndex &index, int role) const
Oid RolesTableModel::getType(int column) 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;
}
}
using namespace Pgsql;
Oid oid;
switch (column) {
case NameCol:
oid = VARCHAROID;
break;
case ReplicationCol:
case BypassRlsCol:
case CanLoginCol:
case CreateDBCol:
case CreateRoleCol:
case InheritCol:
case SuperCol:
oid = BOOLOID;
break;
case ConnlimitCol:
oid = INT4OID;
break;
case ValidUntilCol:
oid = TIMESTAMPOID;
break;
default:
oid = InvalidOid;
}
return oid;
}
QVariant RolesTableModel::getData(const QModelIndex &index) const
{
QVariant v;
if (m_roles) {
const PgAuthId &authid = m_roles->getByIdx(index.row());
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;
}

View file

@ -2,14 +2,14 @@
#define ROLESTABLEMODEL_H
#include <QAbstractTableModel>
#include "BaseTableModel.h"
class PgAuthIdContainer;
/** Class for displaying the list of roles of a server in a QTableView
*
*/
class RolesTableModel : public QAbstractTableModel {
class RolesTableModel : public BaseTableModel {
Q_OBJECT
public:
enum e_Columns : int { NameCol, SuperCol, InheritCol, CreateRoleCol,
@ -29,7 +29,10 @@ public:
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;
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;
// QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
const PgAuthIdContainer *m_roles = nullptr;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

@ -4,7 +4,7 @@
#
#-------------------------------------------------
CONFIG += c++14
CONFIG += c++17
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
@ -13,7 +13,7 @@ TARGET = pglab
TEMPLATE = app
INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql C:\VSproj\boost32\include\boost-1_65_1
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501
#LIBS += -LC:/prog/boost/lib -Lc:/prog/lib libpq.lib fmt.lib User32.lib ws2_32.lib
LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq
@ -29,9 +29,7 @@ win32:RC_ICONS += pglab.ico
SOURCES += main.cpp\
QueryResultModel.cpp \
jsoncpp.cpp \
QueryExplainModel.cpp \
ExplainTreeModelItem.cpp \
ASyncDBConnection.cpp \
tsqueue.cpp \
DatabaseWindow.cpp \
@ -68,12 +66,13 @@ PgDatabaseCatalogue.cpp \
ConnectionList.cpp \
ProcessStdioWidget.cpp \
GlobalIoService.cpp \
CodeBuilderConfiguration.cpp
CodeBuilderConfiguration.cpp \
ResultTableModelUtil.cpp \
BaseTableModel.cpp
HEADERS += \
QueryResultModel.h \
QueryExplainModel.h \
ExplainTreeModelItem.h \
ASyncDBConnection.h \
tsqueue.h \
DatabaseWindow.h \
@ -110,7 +109,9 @@ PgDatabaseCatalogue.h \
ConnectionList.h \
ProcessStdioWidget.h \
GlobalIoService.h \
CodeBuilderConfiguration.h
CodeBuilderConfiguration.h \
ResultTableModelUtil.h \
BaseTableModel.h
FORMS += mainwindow.ui \
DatabaseWindow.ui \

View file

@ -1,7 +1,6 @@
<RCC>
<qresource prefix="/">
<file>icons/server_delete.png</file>
<file>icons/server_go.png</file>
<file>icons/script_delete.png</file>
<file>icons/script_go.png</file>
<file>icons/folder.png</file>

View file

@ -2,10 +2,11 @@ TEMPLATE = subdirs
DEFINES += BOOST_ENABLE_ASSERT_HANDLER
SUBDIRS += core \
pgsql \
pglab
pglab
CONFIG(debug, debug|release) {
# SUBDIRS += tests
SUBDIRS += tests
}

2
pgsql/Pgsql_Col.cpp Normal file
View file

@ -0,0 +1,2 @@
#include "Pgsql_Col.h"

39
pgsql/Pgsql_Col.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef PGSQL_COL_H
#define PGSQL_COL_H
#include "Pgsql_Row.h"
namespace Pgsql {
class Col {
public:
explicit Col(Pgsql::Row &r)
: row(r)
{}
void reset() { col = -1; }
Pgsql::Value nextValue()
{
return row.get(++col);
}
private:
Pgsql::Row &row;
int col = -1;
};
// template <typename T>
// void operator<<(T &s, Col &c)
// {
// s << c.nextValue();
// }
template <typename T>
Col& operator>>(Col &c, T &s)
{
s << c.nextValue();
return c;
}
} // end namespace Pgsql
#endif // PGSQL_COL_H

View file

@ -22,8 +22,8 @@ namespace Pgsql {
*
* The class takes ownership of data and will try to delete[] it.
*/
void addText(const char *data, Oid oid=oid_varchar);
void add(const QString &s, Oid oid=oid_varchar);
void addText(const char *data, Oid oid=VARCHAROID);
void add(const QString &s, Oid oid=VARCHAROID);
void addBinary(const char *data, int length, Oid oid);
void clear();

View file

@ -21,6 +21,8 @@ namespace Pgsql {
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;

View file

@ -5,7 +5,8 @@
using namespace Pgsql;
Value::Value(const char *val, Oid typ)
: m_val(val), m_typ(typ)
: m_val(val)
, m_typ(typ)
{}
QString Value::asQString() const
@ -15,15 +16,30 @@ QString Value::asQString() const
Value::operator QString() const
{
return QString::fromUtf8(m_val);
// if (isString())
return QString::fromUtf8(m_val);
// else
// throw std::logic_error("Column type doesn't match requested type");
}
Value::operator QDateTime() const
{
return QDateTime::fromString(asQString(),
"yyyy-MM-dd hh:mm:ss");
return QDateTime::fromString(asQString(), Qt::ISODateWithMs);
// return QDateTime::fromString(asQString(),
// "yyyy-MM-dd hh:mm:ss");
}
Value::operator QDate() const
{
return QDate::fromString(asQString(), Qt::ISODate);
}
Value::operator QTime() const
{
return QTime::fromString(asQString(), Qt::ISODateWithMs);
}
Value::operator std::string() const
{
return m_val;
@ -54,6 +70,14 @@ Value::operator bool() const
return std::strcmp(m_val, "t") == 0;
}
bool Value::isString() const
{
return m_typ == CHAROID
|| m_typ == VARCHAROID
|| m_typ == TEXTOID
;
}
//void operator<<(QString &s, const Value &v)

View file

@ -17,12 +17,17 @@ namespace Pgsql {
operator QString() const;
operator QDateTime() const;
operator QDate() const;
operator QTime() const;
operator std::string() const;
operator short() const;
operator int() const;
operator Oid() const;
operator long long() const;
operator bool() const;
bool isString() const;
private:
const char *m_val;
Oid m_typ;

View file

@ -5,15 +5,95 @@
namespace Pgsql {
const Oid oid_bool = 16;
const Oid oid_int2 = 21;
const Oid oid_int4 = 23;
const Oid oid_int8 = 20;
const Oid oid_float4 = 700;
const Oid oid_float8 = 701;
const Oid oid_numeric = 1700;
const Oid oid_oid = 26;
const Oid oid_varchar = 1043;
const Oid BOOLOID = 16;
const Oid BYTEAOID = 17;
const Oid CHAROID = 18;
const Oid NAMEOID = 19;
const Oid INT8OID = 20;
const Oid INT2OID = 21;
const Oid INT2VECTOROID = 22;
const Oid INT4OID = 23;
const Oid REGPROCOID = 24;
const Oid TEXTOID = 25;
const Oid OIDOID = 26;
const Oid TIDOID = 27;
const Oid XIDOID = 28;
const Oid CIDOID = 29;
const Oid OIDVECTOROID = 30;
const Oid JSONOID = 114;
const Oid XMLOID = 142;
const Oid PGNODETREEOID = 194;
const Oid PGDDLCOMMANDOID = 32;
const Oid POINTOID = 600;
const Oid LSEGOID = 601;
const Oid PATHOID = 602;
const Oid BOXOID = 603;
const Oid POLYGONOID = 604;
const Oid LINEOID = 628;
const Oid FLOAT4OID = 700;
const Oid FLOAT8OID = 701;
const Oid ABSTIMEOID = 702;
const Oid RELTIMEOID = 703;
const Oid TINTERVALOID = 704;
const Oid UNKNOWNOID = 705;
const Oid CIRCLEOID = 718;
const Oid CASHOID = 790;
const Oid MACADDROID = 829;
const Oid INETOID = 869;
const Oid CIDROID = 650;
const Oid INT2ARRAYOID = 1005;
const Oid INT4ARRAYOID = 1007;
const Oid TEXTARRAYOID = 1009;
const Oid OIDARRAYOID = 1028;
const Oid FLOAT4ARRAYOID = 1021;
const Oid ACLITEMOID = 1033;
const Oid CSTRINGARRAYOID = 1263;
const Oid BPCHAROID = 1042;
const Oid VARCHAROID = 1043;
const Oid DATEOID = 1082;
const Oid TIMEOID = 1083;
const Oid TIMESTAMPOID = 1114;
const Oid TIMESTAMPTZOID = 1184;
const Oid INTERVALOID = 1186;
const Oid TIMETZOID = 1266;
const Oid BITOID = 1560;
const Oid VARBITOID = 1562;
const Oid NUMERICOID = 1700;
const Oid REFCURSOROID = 1790;
const Oid REGPROCEDUREOID = 2202;
const Oid REGOPEROID = 2203;
const Oid REGOPERATOROID = 2204;
const Oid REGCLASSOID = 2205;
const Oid REGTYPEOID = 2206;
const Oid REGROLEOID = 4096;
const Oid REGNAMESPACEOID = 4089;
const Oid REGTYPEARRAYOID = 2211;
const Oid UUIDOID = 2950;
const Oid LSNOID = 3220;
const Oid TSVECTOROID = 3614;
const Oid GTSVECTOROID = 3642;
const Oid TSQUERYOID = 3615;
const Oid REGCONFIGOID = 3734;
const Oid REGDICTIONARYOID = 3769;
const Oid JSONBOID = 3802;
const Oid INT4RANGEOID = 3904;
const Oid RECORDOID = 2249;
const Oid RECORDARRAYOID = 2287;
const Oid CSTRINGOID = 2275;
const Oid ANYOID = 2276;
const Oid ANYARRAYOID = 2277;
const Oid VOIDOID = 2278;
const Oid TRIGGEROID = 2279;
const Oid EVTTRIGGEROID = 3838;
const Oid LANGUAGE_HANDLEROID = 2280;
const Oid INTERNALOID = 2281;
const Oid OPAQUEOID = 2282;
const Oid ANYELEMENTOID = 2283;
const Oid ANYNONARRAYOID = 2776;
const Oid ANYENUMOID = 3500;
const Oid FDW_HANDLEROID = 3115;
const Oid TSM_HANDLEROID = 3310;
const Oid ANYRANGEOID = 3831;
class Params;

View file

@ -12,7 +12,10 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
TARGET = pgsql
TEMPLATE = lib
INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql C:\VSproj\boost32\include\boost-1_65_1
INCLUDEPATH += C:\prog\include \
C:\Prog\include\pgsql \
C:\VSproj\boost32\include\boost-1_65_1
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
#LIBS += -LC:/prog/boost/lib -Lc:/prog/lib libpq.lib fmt.lib User32.lib ws2_32.lib
LIBS += -LC:/PROG/LIB -lws2_32 -llibpq
@ -31,14 +34,16 @@ SOURCES += Pgsql_Connection.cpp \
Pgsql_Params.cpp \
Pgsql_Result.cpp \
Pgsql_Row.cpp \
Pgsql_Value.cpp
Pgsql_Value.cpp \
Pgsql_Col.cpp
HEADERS += Pgsql_Connection.h \
Pgsql_Params.h \
Pgsql_Result.h \
Pgsql_Row.h \
Pgsql_Value.h \
Pgsql_declare.h
Pgsql_declare.h \
Pgsql_Col.h
#FORMS +=

73
tests/PgsqlTests/.gitignore vendored Normal file
View file

@ -0,0 +1,73 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

View file

@ -0,0 +1,43 @@
include(gtest_dependency.pri)
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG += thread
CONFIG += qt
QT += core
INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql
HEADERS +=
SOURCES += main.cpp \
tst_Value.cpp
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../pgsql/release/ -lpgsql
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../pgsql/debug/ -lpgsql
else:unix: LIBS += -L$$OUT_PWD/../../pgsql/ -lpgsql
INCLUDEPATH += $$PWD/../../pgsql
DEPENDPATH += $$PWD/../../pgsql
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pgsql/release/libpgsql.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pgsql/debug/libpgsql.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pgsql/release/pgsql.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pgsql/debug/pgsql.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../../pgsql/libpgsql.a
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../mygtestutils/release/ -lmygtestutils
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../mygtestutils/debug/ -lmygtestutils
else:unix:!macx: LIBS += -L$$OUT_PWD/../mygtestutils/ -lmygtestutils
INCLUDEPATH += $$PWD/../mygtestutils
DEPENDPATH += $$PWD/../mygtestutils
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/release/libmygtestutils.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/debug/libmygtestutils.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/release/mygtestutils.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/debug/mygtestutils.lib
else:unix:!macx: PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/libmygtestutils.a

View file

@ -0,0 +1,29 @@
isEmpty(GOOGLETEST_DIR):GOOGLETEST_DIR=$$(GOOGLETEST_DIR)
isEmpty(GOOGLETEST_DIR) {
warning("Using googletest src dir specified at Qt Creator wizard")
message("set GOOGLETEST_DIR as environment variable or qmake variable to get rid of this message")
GOOGLETEST_DIR = C:/Prog/googletest
}
!isEmpty(GOOGLETEST_DIR): {
GTEST_SRCDIR = $$GOOGLETEST_DIR/googletest
GMOCK_SRCDIR = $$GOOGLETEST_DIR/googlemock
}
requires(exists($$GTEST_SRCDIR):exists($$GMOCK_SRCDIR))
!exists($$GOOGLETEST_DIR):message("No googletest src dir found - set GOOGLETEST_DIR to enable.")
DEFINES += \
GTEST_LANG_CXX11
INCLUDEPATH *= \
$$GTEST_SRCDIR \
$$GTEST_SRCDIR/include \
$$GMOCK_SRCDIR \
$$GMOCK_SRCDIR/include
SOURCES += \
$$GTEST_SRCDIR/src/gtest-all.cc \
$$GMOCK_SRCDIR/src/gmock-all.cc

View file

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "Pgsql_Value.h"
#include "PrintTo_Qt.h"
using namespace testing;
using namespace Pgsql;
TEST(Pgsql_Value, test_int4)
{
Pgsql::Value v("1", INT4OID);
int i = (int)v;
ASSERT_EQ(i, 1);
}
TEST(Pgsql_Value, test_QDateTime)
{
Pgsql::Value v("2017-10-22 12:34:56", TIMESTAMPTZOID);
QDateTime dt = (QDateTime)v;
ASSERT_EQ(dt, QDateTime(QDate(2017, 10, 22), QTime(12, 34, 56)));
}
TEST(Pgsql_Value, test_QDateTime_2)
{
Pgsql::Value v("2017-12-01 09:38:17.339817+02", TIMESTAMPTZOID);
QDateTime dt = (QDateTime)v;
QDateTime exp(QDate(2017, 12, 1), QTime(9, 38, 17, 340),
Qt::OffsetFromUTC, 2*3600);
ASSERT_EQ(dt, exp);
}
TEST(Pgsql_Value, test_QDate)
{
Pgsql::Value v("2017-10-22", DATEOID);
QDate d = v;
ASSERT_EQ(d, QDate(2017, 10, 22));
}
TEST(Pgsql_Value, test_QTime)
{
Pgsql::Value v("12:34:56", TIMEOID);
QTime t = v;
ASSERT_EQ(t, QTime(12, 34, 56));
}
TEST(Pgsql_Value, test_QTimeMS)
{
Pgsql::Value v("09:38:17.339817+02", TIMETZOID);
QTime t = v;
ASSERT_EQ(t, QTime(9, 38, 17, 340));
}
TEST(Pgsql_Value, isString_int4)
{
Pgsql::Value v("1", INT4OID);
ASSERT_EQ(v.isString(), false);
}
TEST(Pgsql_Value, isString_varchar)
{
Pgsql::Value v("1", VARCHAROID);
ASSERT_EQ(v.isString(), true);
}

3
tests/auto/auto.pro Normal file
View file

@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += mycase

View file

@ -0,0 +1,29 @@
isEmpty(GOOGLETEST_DIR):GOOGLETEST_DIR=$$(GOOGLETEST_DIR)
isEmpty(GOOGLETEST_DIR) {
warning("Using googletest src dir specified at Qt Creator wizard")
message("set GOOGLETEST_DIR as environment variable or qmake variable to get rid of this message")
GOOGLETEST_DIR = C:/Prog/googletest
}
!isEmpty(GOOGLETEST_DIR): {
GTEST_SRCDIR = $$GOOGLETEST_DIR/googletest
GMOCK_SRCDIR = $$GOOGLETEST_DIR/googlemock
}
requires(exists($$GTEST_SRCDIR):exists($$GMOCK_SRCDIR))
!exists($$GOOGLETEST_DIR):message("No googletest src dir found - set GOOGLETEST_DIR to enable.")
DEFINES += \
GTEST_LANG_CXX11
INCLUDEPATH *= \
$$GTEST_SRCDIR \
$$GTEST_SRCDIR/include \
$$GMOCK_SRCDIR \
$$GMOCK_SRCDIR/include
SOURCES += \
$$GTEST_SRCDIR/src/gtest-all.cc \
$$GMOCK_SRCDIR/src/gmock-all.cc

View file

@ -0,0 +1,13 @@
#include "tst_CsvWriter.h"
#include "tst_expected.h"
#include "tst_PasswordManager.h"
#include "tst_scopeguard.h"
#include "tst_SqlLexer.h"
#include <gtest/gtest.h>
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,32 @@
include(../gtest_dependency.pri)
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG += thread
CONFIG += qt
QT += core
HEADERS += \
tst_expected.h \
tst_SqlLexer.h \
tst_scopeguard.h \
tst_CsvWriter.h \
tst_PasswordManager.h
SOURCES += main.cpp \
tst_ExplainJsonParser.cpp
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../../core/release/ -lcore
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../../core/debug/ -lcore
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
INCLUDEPATH += $$PWD/../../../core
DEPENDPATH += $$PWD/../../../core
LIBS += c:\prog\lib\botand_imp.lib
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../../core/release/libcore.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../../core/debug/libcore.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../../core/release/core.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../../core/debug/core.lib

View file

@ -0,0 +1,113 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "CsvWriter.h"
#include <QTextStream>
#include <QByteArray>
using namespace testing;
TEST(CsvWriter, one_row_two_numbers)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator(',');
writer.writeField("1");
writer.writeField("2");
writer.nextRow();
QString expected = QString::fromUtf8("1,2\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, one_row_one_number_one_unquoted_string)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator(',');
writer.writeField("1");
writer.writeField("hello");
writer.nextRow();
QString expected = QString::fromUtf8("1,hello\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, one_row_one_number_one_quoted_string)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator(',');
writer.writeField("1");
writer.writeField("hel,lo");
writer.nextRow();
QString expected = QString::fromUtf8("1,\"hel,lo\"\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, newline_in_field)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator(',');
writer.writeField("1");
writer.writeField("hel\nlo");
writer.nextRow();
QString expected = QString::fromUtf8("1,\"hel\nlo\"\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, escape_quote)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator(',');
writer.writeField("1");
writer.writeField("hel\"lo");
writer.nextRow();
QString expected = QString::fromUtf8("1,\"hel\"\"lo\"\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, non_default_seperator)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('"');
writer.setSeperator('\t');
writer.writeField("1");
writer.writeField("hel,lo");
writer.nextRow();
QString expected = QString::fromUtf8("1\thel,lo\n");
ASSERT_THAT(result, Eq(expected));
}
TEST(CsvWriter, non_default_quote)
{
QString result;
QTextStream stream(&result);
CsvWriter writer(&stream);
writer.setQuote('*');
writer.setSeperator('\t');
writer.writeField("1");
writer.writeField("hel\tlo");
writer.nextRow();
QString expected = QString::fromUtf8("1\t*hel\tlo*\n");
ASSERT_THAT(result, Eq(expected));
}

View file

@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "ExplainTreeModelItem.h"
#include "json/json.h"
using namespace testing;
TEST(ExplainParser, verbose)
{
std::string input = R"__json__("[
{
"Plan": {
"Node Type": "Limit",
"Parallel Aware": false,
"Startup Cost": 0.42,
"Total Cost": 0.42,
"Plan Rows": 10,
"Plan Width": 830,
"Output": ["fr1.callstack_crc_2", "fr1.id", "fr1.program", "fr1.version", "fr1.lic_bedrijf", "fr1.lic_plaats", "fr1.lic_number", "fr1.callstack_crc_1", "fr1.callstack_crc_3", "fr1.exception_class", "fr1.exception_message", "fr1.full_report", "fr1.screenshot_filename", "fr1.ts", "fr1.duplicate_of", "fr1.actief", "fr1.commentaar", "fr1.bugzilla", "fr1.attachment_filename", "fr1.version_according_to_datetime", "fr1.program_according_to_full_report", "fr1.date", "fr2.id", "fr2.program", "fr2.version", "fr2.lic_bedrijf", "fr2.lic_plaats", "fr2.lic_number", "fr2.callstack_crc_1", "fr2.callstack_crc_3", "fr2.exception_class", "fr2.exception_message", "fr2.full_report", "fr2.screenshot_filename", "fr2.ts", "fr2.duplicate_of", "fr2.actief", "fr2.commentaar", "fr2.bugzilla", "fr2.attachment_filename", "fr2.version_according_to_datetime", "fr2.program_according_to_full_report", "fr2.date"],
"Plans": [
{
"Node Type": "Nested Loop",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Join Type": "Inner",
"Startup Cost": 0.42,
"Total Cost": 103148.97,
"Plan Rows": 493512899,
"Plan Width": 830,
"Output": ["fr1.callstack_crc_2", "fr1.id", "fr1.program", "fr1.version", "fr1.lic_bedrijf", "fr1.lic_plaats", "fr1.lic_number", "fr1.callstack_crc_1", "fr1.callstack_crc_3", "fr1.exception_class", "fr1.exception_message", "fr1.full_report", "fr1.screenshot_filename", "fr1.ts", "fr1.duplicate_of", "fr1.actief", "fr1.commentaar", "fr1.bugzilla", "fr1.attachment_filename", "fr1.version_according_to_datetime", "fr1.program_according_to_full_report", "fr1.date", "fr2.id", "fr2.program", "fr2.version", "fr2.lic_bedrijf", "fr2.lic_plaats", "fr2.lic_number", "fr2.callstack_crc_1", "fr2.callstack_crc_3", "fr2.exception_class", "fr2.exception_message", "fr2.full_report", "fr2.screenshot_filename", "fr2.ts", "fr2.duplicate_of", "fr2.actief", "fr2.commentaar", "fr2.bugzilla", "fr2.attachment_filename", "fr2.version_according_to_datetime", "fr2.program_according_to_full_report", "fr2.date"],
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Relation Name": "foutrapport",
"Schema": "public",
"Alias": "fr1",
"Startup Cost": 0.00,
"Total Cost": 6048.89,
"Plan Rows": 105489,
"Plan Width": 419,
"Output": ["fr1.id", "fr1.program", "fr1.version", "fr1.lic_bedrijf", "fr1.lic_plaats", "fr1.lic_number", "fr1.callstack_crc_1", "fr1.callstack_crc_2", "fr1.callstack_crc_3", "fr1.exception_class", "fr1.exception_message", "fr1.full_report", "fr1.screenshot_filename", "fr1.ts", "fr1.duplicate_of", "fr1.actief", "fr1.commentaar", "fr1.bugzilla", "fr1.attachment_filename", "fr1.version_according_to_datetime", "fr1.program_according_to_full_report", "fr1.date"]
},
{
"Node Type": "Index Scan",
"Parent Relationship": "Inner",
"Parallel Aware": false,
"Scan Direction": "Forward",
"Index Name": "foutrapport_callstack_crc_2_idx",
"Relation Name": "foutrapport",
"Schema": "public",
"Alias": "fr2",
"Startup Cost": 0.42,
"Total Cost": 0.81,
"Plan Rows": 11,
"Plan Width": 419,
"Output": ["fr2.id", "fr2.program", "fr2.version", "fr2.lic_bedrijf", "fr2.lic_plaats", "fr2.lic_number", "fr2.callstack_crc_1", "fr2.callstack_crc_2", "fr2.callstack_crc_3", "fr2.exception_class", "fr2.exception_message", "fr2.full_report", "fr2.screenshot_filename", "fr2.ts", "fr2.duplicate_of", "fr2.actief", "fr2.commentaar", "fr2.bugzilla", "fr2.attachment_filename", "fr2.version_according_to_datetime", "fr2.program_according_to_full_report", "fr2.date"],
"Index Cond": "(fr2.callstack_crc_2 = fr1.callstack_crc_2)"
}
]
}
]
}
}
]")__json__";
Json::Value root; // will contains the root value after parsing.
Json::Reader reader;
bool parsingSuccessful = reader.parse(input, root);
ASSERT_TRUE(parsingSuccessful);
auto explain = ExplainRoot::createFromJson(root);
ASSERT_TRUE(explain != nullptr);
}

View file

@ -0,0 +1,73 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "PasswordManager.h"
using namespace testing;
TEST(PasswordManager, initial_changeMasterPassword_returns_true)
{
PasswordManager pwm;
auto res = pwm.changeMasterPassword("", "my test passphrase");
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) );
}
TEST(PasswordManager, unlock_succeeds)
{
PasswordManager pwm;
std::string passphrase = "my test passphrase";
auto res = pwm.changeMasterPassword("", passphrase);
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) );
auto res2 = pwm.unlock(passphrase);
ASSERT_NO_THROW( res2.get() );
ASSERT_THAT( res2.get(), Eq(true) );
}
TEST(PasswordManager, unlock_fails)
{
PasswordManager pwm;
std::string passphrase = "my test passphrase";
auto res = pwm.changeMasterPassword("", passphrase);
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) );
auto res2 = pwm.unlock(passphrase + "2");
ASSERT_NO_THROW( res2.get() );
ASSERT_THAT( res2.get(), Eq(false) );
}
TEST(PasswordManager, test_save_get)
{
PasswordManager pwm;
std::string passphrase = "my test passphrase";
auto res = pwm.changeMasterPassword("", passphrase);
ASSERT_NO_THROW( res.get() );
ASSERT_THAT( res.get(), Eq(true) );
// auto res2 = pwm.unlock(passphrase + "2");
// ASSERT_NO_THROW( res2.get() );
// ASSERT_THAT( res2.get(), Eq(false) );
const std::string password = "password123";
const std::string key = "abc";
auto res2 = pwm.savePassword(key, password);
ASSERT_THAT( res2.valid(), Eq(true) );
std::string result;
auto res3 = pwm.getPassword(key, result);
ASSERT_THAT( res3.valid(), Eq(true) );
ASSERT_THAT( res3.get(), Eq(true) );
ASSERT_THAT( result, Eq(password) );
}

View file

@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "SqlLexer.h"
using namespace testing;
TEST(SqlLexer, lexer)
{
QString input = " SELECT ";
SqlLexer lexer(input, LexerState::Null);
int startpos, length;
BasicTokenType tokentype;
QString out;
lexer.nextBasicToken(startpos, length, tokentype, out);
ASSERT_THAT(startpos, Eq(1));
ASSERT_THAT(length, Eq(6));
ASSERT_THAT(tokentype, Eq(BasicTokenType::Symbol));
ASSERT_THAT( out, Eq(QString("SELECT")) );
}
TEST(SqlLexer, lexer_quote_in_string)
{
QString input = " 'abc''def' ";
SqlLexer lexer(input, LexerState::Null);
int startpos, length;
BasicTokenType tokentype;
QString out;
lexer.nextBasicToken(startpos, length, tokentype, out);
ASSERT_THAT(startpos, Eq(1));
ASSERT_THAT(length, Eq(10));
ASSERT_THAT(tokentype, Eq(BasicTokenType::QuotedString));
}

View file

@ -0,0 +1,203 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "Expected.h"
using namespace testing;
Expected<int> getAnswerToEverything() { return 42; }
TEST(expected, valid_when_valid_returns_true)
{
Expected<int> v = getAnswerToEverything();
ASSERT_THAT(v.valid(), Eq(true));
}
TEST(expected, get_when_valid_returns_value)
{
Expected<int> v = getAnswerToEverything();
ASSERT_THAT(v.get(), Eq(42));
}
TEST(expected, hasException_when_valid_returns_false)
{
Expected<int> v = getAnswerToEverything();
ASSERT_THAT(v.hasException<std::exception>(), Eq(false));
}
TEST(expected, T_fromException_is_not_valid)
{
auto e = Expected<int>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.valid(), Eq(false));
}
TEST(expected, T_fromException_get_thows)
{
auto e = Expected<int>::fromException(std::runtime_error("hello"));
ASSERT_THROW (e.get(), std::runtime_error);
}
TEST(expected, T_fromException_has_exception_true)
{
auto e = Expected<int>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
}
TEST(expected, T_fromException_has_exception_false)
{
auto e = Expected<int>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
}
TEST(expected, T_fromException_has_derived_exception)
{
auto e = Expected<int>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
}
TEST(expected, T_fromCode_is_valid)
{
auto e = Expected<int>::fromCode([]() -> int { return 42; });
ASSERT_THAT(e.valid(), Eq(true));
}
TEST(expected, T_fromCode_get)
{
auto e = Expected<int>::fromCode([]() -> int { return 42; });
ASSERT_THAT(e.get(), Eq(42));
}
TEST(expected, T_fromCode_E_is_not_valid)
{
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
ASSERT_THAT(e.valid(), Eq(false));
}
TEST(expected, T_fromCode_E_get_thows)
{
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
ASSERT_THROW (e.get(), std::runtime_error);
}
TEST(expected, T_fromCode_E_has_exception_true)
{
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
}
TEST(expected, T_fromCode_E_has_exception_false)
{
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
}
TEST(expected, T_fromCode_E_has_derived_exception)
{
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
}
//Expected<int> getIntWithStdRuntimeError() { return Expected<void>(); }
Expected<void> getNothing() { return Expected<void>(); }
TEST(expected_void, valid_when_valid_returns_true)
{
Expected<void> v = getNothing();
ASSERT_THAT(v.valid(), Eq(true));
}
TEST(expected_void, get_when_valid_returns_value)
{
Expected<void> v = getNothing();
ASSERT_NO_THROW(v.get());
}
TEST(expected_void, hasException_when_valid_returns_false)
{
Expected<void> v = getNothing();
ASSERT_THAT(v.hasException<std::exception>(), Eq(false));
}
TEST(expected_void, void_fromException_is_not_valid)
{
auto e = Expected<void>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.valid(), Eq(false));
}
TEST(expected_void, void_fromException_get_thows)
{
auto e = Expected<void>::fromException(std::runtime_error("hello"));
ASSERT_THROW (e.get(), std::runtime_error);
}
TEST(expected_void, void_fromException_has_exception_true)
{
auto e = Expected<void>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
}
TEST(expected_void, void_fromException_has_exception_false)
{
auto e = Expected<void>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
}
TEST(expected_void, void_fromException_has_derived_exception)
{
auto e = Expected<void>::fromException(std::runtime_error("hello"));
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
}
TEST(expected_void, void_fromCode_is_valid)
{
auto e = Expected<void>::fromCode([]() -> void { });
ASSERT_THAT(e.valid(), Eq(true));
}
TEST(expected_void, void_fromCode_get)
{
auto e = Expected<void>::fromCode([]() -> void { });
ASSERT_NO_THROW(e.get());
}
void expected_void_throws_func()
{
throw std::runtime_error("hello");
}
TEST(expected_void, void_fromCode_E_is_not_valid)
{
auto e = Expected<void>::fromCode(expected_void_throws_func);
ASSERT_THAT(e.valid(), Eq(false));
}
TEST(expected_void, void_fromCode_E_get_thows)
{
auto e = Expected<void>::fromCode(expected_void_throws_func);
ASSERT_THROW (e.get(), std::runtime_error);
}
TEST(expected_void, void_fromCode_E_has_exception_true)
{
auto e = Expected<void>::fromCode(expected_void_throws_func);
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
}
TEST(expected_void, void_fromCode_E_has_exception_false)
{
auto e = Expected<void>::fromCode(expected_void_throws_func);
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
}
TEST(expected_void, void_fromCode_E_has_derived_exception)
{
auto e = Expected<void>::fromCode(expected_void_throws_func);
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
}

View file

@ -0,0 +1,63 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "ScopeGuard.h"
using namespace testing;
TEST(ScopeGuard, normal_run_fun_on_destruction_1)
{
bool result = false;
auto sg = scopeGuard([&result]() { result = true; });
ASSERT_THAT(result, Eq(false));
}
TEST(ScopeGuard, normal_run_fun_on_destruction_2)
{
bool result = false;
{
auto sg = scopeGuard([&result]() { result = true; });
}
ASSERT_EQ(result, true);
}
TEST(ScopeGuard, dismiss)
{
bool result = false;
{
auto sg = scopeGuard([&result]() { result = true; });
sg.dismiss();
}
ASSERT_THAT(result, Eq(false));
}
TEST(ScopeGuard, SCOPE_EXIT_macro_1)
{
bool result = false;
{
SCOPE_EXIT { result = true; };
ASSERT_THAT(result, Eq(false)); // prove previous statement hasn't run yet
}
}
TEST(ScopeGuard, SCOPE_EXIT_macro_2)
{
bool result = false;
{
SCOPE_EXIT { result = true; };
}
ASSERT_THAT(result, Eq(true));
}
//TEST(expected, get_when_valid_returns_value)
//{
// Expected<int> v = getAnswerToEverything();
// ASSERT_THAT(v.get(), Eq(42));
//}

View file

@ -0,0 +1,9 @@
#include "PrintTo_Qt.h"
void PrintTo(const QDateTime &dt, ::std::ostream* os) {
*os << dt.toString(Qt::ISODateWithMs).toUtf8().toStdString();
}
void PrintTo(const QString &s, ::std::ostream* os) {
*os << s.toUtf8().toStdString();
}

View file

@ -0,0 +1,11 @@
#ifndef PRINTTO_QT_H
#define PRINTTO_QT_H
#include <ostream>
#include <QDateTime>
#include <QString>
void PrintTo(const QDateTime &dt, ::std::ostream* os);
void PrintTo(const QString &s, ::std::ostream* os);
#endif // PRINTTO_QT_H

View file

@ -0,0 +1,32 @@
#-------------------------------------------------
#
# Project created by QtCreator 2017-12-01T10:07:31
#
#-------------------------------------------------
QT -= gui
TARGET = mygtestutils
TEMPLATE = lib
CONFIG += staticlib
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
PrintTo_Qt.cpp
HEADERS += \
PrintTo_Qt.h
unix {
target.path = /usr/lib
INSTALLS += target
}

5
tests/tests.pro Normal file
View file

@ -0,0 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += auto \
PgsqlTests \
mygtestutils