diff --git a/core/ExplainTreeModelItem.cpp b/core/ExplainTreeModelItem.cpp new file mode 100644 index 0000000..418eba7 --- /dev/null +++ b/core/ExplainTreeModelItem.cpp @@ -0,0 +1,409 @@ +#include "ExplainTreeModelItem.h" +#include "json/json.h" +#include + +namespace { + + ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan) + { + + const auto json_null = Json::Value::nullSingleton(); + ExplainTreeModelItemPtr result = std::make_shared(); + 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(); + // 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::infinity(); + } + } + else if (actualRows > estimatedRows) { + if (estimatedRows > 0) { + res = float(actualRows) / estimatedRows; + } + else { + res = std::numeric_limits::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" +//} diff --git a/core/ExplainTreeModelItem.h b/core/ExplainTreeModelItem.h new file mode 100644 index 0000000..30cdb7f --- /dev/null +++ b/core/ExplainTreeModelItem.h @@ -0,0 +1,163 @@ +#pragma once + +#include +//#include +#include +#include + +namespace Json { + + class Value; + +} +class ExplainTreeModelItem; +typedef std::shared_ptr 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 { +public: + typedef std::shared_ptr 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 m_childItems; + std::weak_ptr 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; + static SPtr createFromJson(Json::Value &json); + + ExplainTreeModelItemPtr plan; + float planningTime = 0.f; + // Triggers??? + float executionTime = 0.f; + float totalRuntime = 0.f; + +}; + diff --git a/core/core.pro b/core/core.pro index bb40f96..6b6663a 100644 --- a/core/core.pro +++ b/core/core.pro @@ -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 } + + diff --git a/pglab/json/json-forwards.h b/core/json/json-forwards.h similarity index 100% rename from pglab/json/json-forwards.h rename to core/json/json-forwards.h diff --git a/pglab/json/json.h b/core/json/json.h similarity index 100% rename from pglab/json/json.h rename to core/json/json.h diff --git a/pglab/jsoncpp.cpp b/core/jsoncpp.cpp similarity index 100% rename from pglab/jsoncpp.cpp rename to core/jsoncpp.cpp diff --git a/pglab/BaseTableModel.cpp b/pglab/BaseTableModel.cpp new file mode 100644 index 0000000..de5e077 --- /dev/null +++ b/pglab/BaseTableModel.cpp @@ -0,0 +1,36 @@ +#include "BaseTableModel.h" +#include "ResultTableModelUtil.h" +#include + +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; +} diff --git a/pglab/BaseTableModel.h b/pglab/BaseTableModel.h new file mode 100644 index 0000000..c6d6e8e --- /dev/null +++ b/pglab/BaseTableModel.h @@ -0,0 +1,21 @@ +#ifndef BASETABLEMODEL_H +#define BASETABLEMODEL_H + +#include +#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 diff --git a/pglab/DatabasesTableModel.cpp b/pglab/DatabasesTableModel.cpp index 0047af1..58d7eec 100644 --- a/pglab/DatabasesTableModel.cpp +++ b/pglab/DatabasesTableModel.cpp @@ -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; } diff --git a/pglab/DatabasesTableModel.h b/pglab/DatabasesTableModel.h index cc15528..a011d10 100644 --- a/pglab/DatabasesTableModel.h +++ b/pglab/DatabasesTableModel.h @@ -1,7 +1,7 @@ -#ifndef DATABASESTABLEMODEL_H +#ifndef DATABASESTABLEMODEL_H #define DATABASESTABLEMODEL_H -#include +#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; diff --git a/pglab/ExplainTreeModelItem.cpp b/pglab/ExplainTreeModelItem.cpp deleted file mode 100644 index f756b1f..0000000 --- a/pglab/ExplainTreeModelItem.cpp +++ /dev/null @@ -1,384 +0,0 @@ -#include "ExplainTreeModelItem.h" -#include "json/json.h" -#include - -namespace { - - ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan) - { - ExplainTreeModelItemPtr result = std::make_shared(); - 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(); - // 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::infinity(); - } - } - else if (m_actualRows > m_estimatedRows) { - if (m_estimatedRows > 0) { - res = float(m_actualRows) / m_estimatedRows; - } - else { - res = std::numeric_limits::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" -//} diff --git a/pglab/ExplainTreeModelItem.h b/pglab/ExplainTreeModelItem.h deleted file mode 100644 index 5c28599..0000000 --- a/pglab/ExplainTreeModelItem.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include -//#include -#include -#include - -namespace Json { - - class Value; - -} -class ExplainTreeModelItem; -typedef std::shared_ptr 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 { -public: - typedef std::shared_ptr 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 m_childItems; - std::weak_ptr 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; - static SPtr createFromJson(Json::Value &json); - - ExplainTreeModelItemPtr plan; - float planningTime = 0.f; - // Triggers??? - float executionTime = 0.f; - float totalRuntime = 0.f; - -}; - diff --git a/pglab/PgDatabaseCatalogue.cpp b/pglab/PgDatabaseCatalogue.cpp index ca53c0c..155218e 100644 --- a/pglab/PgDatabaseCatalogue.cpp +++ b/pglab/PgDatabaseCatalogue.cpp @@ -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() { } diff --git a/pglab/PgDatabaseCatalogue.h b/pglab/PgDatabaseCatalogue.h index 1911708..d8f7548 100644 --- a/pglab/PgDatabaseCatalogue.h +++ b/pglab/PgDatabaseCatalogue.h @@ -45,5 +45,7 @@ private: }; QString getRoleNameFromOid(const PgDatabaseCatalogue *cat, Oid oid); +QString getRoleDisplayString(const PgDatabaseCatalogue *cat, Oid oid); + #endif // PGSQLDATABASECATALOGUE_H diff --git a/pglab/PgDatabaseContainer.cpp b/pglab/PgDatabaseContainer.cpp index ba5b729..78e0adb 100644 --- a/pglab/PgDatabaseContainer.cpp +++ b/pglab/PgDatabaseContainer.cpp @@ -1,5 +1,6 @@ -#include "PgDatabaseContainer.h" +#include "PgDatabaseContainer.h" #include "Pgsql_Connection.h" +#include "Pgsql_Col.h" PgDatabaseContainer::PgDatabaseContainer(PgDatabaseCatalogue *cat) : PgContainer(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()); diff --git a/pglab/QueryExplainModel.cpp b/pglab/QueryExplainModel.cpp index 67ccfca..019ad7c 100644 --- a/pglab/QueryExplainModel.cpp +++ b/pglab/QueryExplainModel.cpp @@ -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) { diff --git a/pglab/QueryResultModel.cpp b/pglab/QueryResultModel.cpp index 9371ffd..c6449c5 100644 --- a/pglab/QueryResultModel.cpp +++ b/pglab/QueryResultModel.cpp @@ -1,4 +1,5 @@ #include "QueryResultModel.h" +#include "ResultTableModelUtil.h" #include "Pgsql_declare.h" #include #include @@ -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); } diff --git a/pglab/QueryTab.cpp b/pglab/QueryTab.cpp index c0ff2f2..7db03a9 100644 --- a/pglab/QueryTab.cpp +++ b/pglab/QueryTab.cpp @@ -1,4 +1,4 @@ -#include "QueryTab.h" +#include "QueryTab.h" #include "ui_QueryTab.h" #include "SqlSyntaxHighlighter.h" #include @@ -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> exp_res, qint64 ) { if (exp_res.valid()) { // Process explain data seperately auto res = exp_res.get(); - std::thread([this,res]() - { - std::shared_ptr 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 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(); + } } }); } diff --git a/pglab/ResultTableModelUtil.cpp b/pglab/ResultTableModelUtil.cpp new file mode 100644 index 0000000..e50e285 --- /dev/null +++ b/pglab/ResultTableModelUtil.cpp @@ -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"; +} + diff --git a/pglab/ResultTableModelUtil.h b/pglab/ResultTableModelUtil.h new file mode 100644 index 0000000..98543a6 --- /dev/null +++ b/pglab/ResultTableModelUtil.h @@ -0,0 +1,23 @@ +#pragma once +#include "Pgsql_declare.h" +#include +#include + +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); + diff --git a/pglab/RolesTableModel.cpp b/pglab/RolesTableModel.cpp index 9744494..d3acd19 100644 --- a/pglab/RolesTableModel.cpp +++ b/pglab/RolesTableModel.cpp @@ -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; } diff --git a/pglab/RolesTableModel.h b/pglab/RolesTableModel.h index 0da4375..77c8c85 100644 --- a/pglab/RolesTableModel.h +++ b/pglab/RolesTableModel.h @@ -2,14 +2,14 @@ #define ROLESTABLEMODEL_H -#include +#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; diff --git a/pglab/icons/about.png b/pglab/icons/about.png index 1a02d2b..93c67f2 100644 Binary files a/pglab/icons/about.png and b/pglab/icons/about.png differ diff --git a/pglab/icons/add_connection.png b/pglab/icons/add_connection.png index 8ff7512..210979f 100644 Binary files a/pglab/icons/add_connection.png and b/pglab/icons/add_connection.png differ diff --git a/pglab/icons/backup_database.png b/pglab/icons/backup_database.png index 64e9423..41b14aa 100644 Binary files a/pglab/icons/backup_database.png and b/pglab/icons/backup_database.png differ diff --git a/pglab/icons/delete_connection.png b/pglab/icons/delete_connection.png index 5563984..a699864 100644 Binary files a/pglab/icons/delete_connection.png and b/pglab/icons/delete_connection.png differ diff --git a/pglab/icons/manage_server.png b/pglab/icons/manage_server.png index 7331756..8f609e1 100644 Binary files a/pglab/icons/manage_server.png and b/pglab/icons/manage_server.png differ diff --git a/pglab/icons/new_query_tab.png b/pglab/icons/new_query_tab.png index 40f546e..d33fc2b 100644 Binary files a/pglab/icons/new_query_tab.png and b/pglab/icons/new_query_tab.png differ diff --git a/pglab/icons/open_query_window.png b/pglab/icons/open_query_window.png index 46cd65a..e2f744b 100644 Binary files a/pglab/icons/open_query_window.png and b/pglab/icons/open_query_window.png differ diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 1571144..cf65e49 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -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 \ diff --git a/pglab/resources.qrc b/pglab/resources.qrc index 8b00d1b..2c4d54e 100644 --- a/pglab/resources.qrc +++ b/pglab/resources.qrc @@ -1,7 +1,6 @@  icons/server_delete.png - icons/server_go.png icons/script_delete.png icons/script_go.png icons/folder.png diff --git a/pglabAll.pro b/pglabAll.pro index 890c392..503beee 100644 --- a/pglabAll.pro +++ b/pglabAll.pro @@ -2,10 +2,11 @@ TEMPLATE = subdirs DEFINES += BOOST_ENABLE_ASSERT_HANDLER + SUBDIRS += core \ pgsql \ - pglab +pglab CONFIG(debug, debug|release) { -# SUBDIRS += tests +SUBDIRS += tests } diff --git a/pgsql/Pgsql_Col.cpp b/pgsql/Pgsql_Col.cpp new file mode 100644 index 0000000..47c64a1 --- /dev/null +++ b/pgsql/Pgsql_Col.cpp @@ -0,0 +1,2 @@ +#include "Pgsql_Col.h" + diff --git a/pgsql/Pgsql_Col.h b/pgsql/Pgsql_Col.h new file mode 100644 index 0000000..87e2ad7 --- /dev/null +++ b/pgsql/Pgsql_Col.h @@ -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 +// void operator<<(T &s, Col &c) +// { +// s << c.nextValue(); +// } + + template + Col& operator>>(Col &c, T &s) + { + s << c.nextValue(); + return c; + } + +} // end namespace Pgsql + +#endif // PGSQL_COL_H diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index a9e9950..950e86d 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -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(); diff --git a/pgsql/Pgsql_Row.h b/pgsql/Pgsql_Row.h index f62df72..81d5ae0 100644 --- a/pgsql/Pgsql_Row.h +++ b/pgsql/Pgsql_Row.h @@ -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; diff --git a/pgsql/Pgsql_Value.cpp b/pgsql/Pgsql_Value.cpp index e4095cb..26aaeb7 100644 --- a/pgsql/Pgsql_Value.cpp +++ b/pgsql/Pgsql_Value.cpp @@ -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) diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index 8b07046..ae06160 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -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; diff --git a/pgsql/Pgsql_declare.h b/pgsql/Pgsql_declare.h index 279d09b..f54c6a8 100644 --- a/pgsql/Pgsql_declare.h +++ b/pgsql/Pgsql_declare.h @@ -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; diff --git a/pgsql/pgsql.pro b/pgsql/pgsql.pro index 9115bd2..ea9f394 100644 --- a/pgsql/pgsql.pro +++ b/pgsql/pgsql.pro @@ -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 += diff --git a/tests/PgsqlTests/.gitignore b/tests/PgsqlTests/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/tests/PgsqlTests/.gitignore @@ -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 + diff --git a/tests/PgsqlTests/PgsqlTests.pro b/tests/PgsqlTests/PgsqlTests.pro new file mode 100644 index 0000000..017b97f --- /dev/null +++ b/tests/PgsqlTests/PgsqlTests.pro @@ -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 diff --git a/tests/PgsqlTests/gtest_dependency.pri b/tests/PgsqlTests/gtest_dependency.pri new file mode 100644 index 0000000..d01d7f0 --- /dev/null +++ b/tests/PgsqlTests/gtest_dependency.pri @@ -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 diff --git a/tests/PgsqlTests/main.cpp b/tests/PgsqlTests/main.cpp new file mode 100644 index 0000000..291bdee --- /dev/null +++ b/tests/PgsqlTests/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/PgsqlTests/tst_Value.cpp b/tests/PgsqlTests/tst_Value.cpp new file mode 100644 index 0000000..092b9f1 --- /dev/null +++ b/tests/PgsqlTests/tst_Value.cpp @@ -0,0 +1,66 @@ +#include +#include +#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); +} diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 0000000..576a9ff --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += mycase diff --git a/tests/auto/gtest_dependency.pri b/tests/auto/gtest_dependency.pri new file mode 100644 index 0000000..d01d7f0 --- /dev/null +++ b/tests/auto/gtest_dependency.pri @@ -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 diff --git a/tests/auto/mycase/main.cpp b/tests/auto/mycase/main.cpp new file mode 100644 index 0000000..046652f --- /dev/null +++ b/tests/auto/mycase/main.cpp @@ -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 + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/auto/mycase/mycase.pro b/tests/auto/mycase/mycase.pro new file mode 100644 index 0000000..bb3a635 --- /dev/null +++ b/tests/auto/mycase/mycase.pro @@ -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 diff --git a/tests/auto/mycase/tst_CsvWriter.h b/tests/auto/mycase/tst_CsvWriter.h new file mode 100644 index 0000000..ac48b9d --- /dev/null +++ b/tests/auto/mycase/tst_CsvWriter.h @@ -0,0 +1,113 @@ +#include +#include +#include "CsvWriter.h" +#include +#include + +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)); +} diff --git a/tests/auto/mycase/tst_ExplainJsonParser.cpp b/tests/auto/mycase/tst_ExplainJsonParser.cpp new file mode 100644 index 0000000..e5074eb --- /dev/null +++ b/tests/auto/mycase/tst_ExplainJsonParser.cpp @@ -0,0 +1,80 @@ +#include +#include +#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); +} diff --git a/tests/auto/mycase/tst_PasswordManager.h b/tests/auto/mycase/tst_PasswordManager.h new file mode 100644 index 0000000..a3da28b --- /dev/null +++ b/tests/auto/mycase/tst_PasswordManager.h @@ -0,0 +1,73 @@ +#include +#include +#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) ); + +} diff --git a/tests/auto/mycase/tst_SqlLexer.h b/tests/auto/mycase/tst_SqlLexer.h new file mode 100644 index 0000000..25d6535 --- /dev/null +++ b/tests/auto/mycase/tst_SqlLexer.h @@ -0,0 +1,38 @@ +#include +#include +#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)); +} + diff --git a/tests/auto/mycase/tst_expected.h b/tests/auto/mycase/tst_expected.h new file mode 100644 index 0000000..8632ff8 --- /dev/null +++ b/tests/auto/mycase/tst_expected.h @@ -0,0 +1,203 @@ +#include +#include +#include "Expected.h" + +using namespace testing; + +Expected getAnswerToEverything() { return 42; } + +TEST(expected, valid_when_valid_returns_true) +{ + Expected v = getAnswerToEverything(); + ASSERT_THAT(v.valid(), Eq(true)); +} + +TEST(expected, get_when_valid_returns_value) +{ + Expected v = getAnswerToEverything(); + ASSERT_THAT(v.get(), Eq(42)); +} + +TEST(expected, hasException_when_valid_returns_false) +{ + Expected v = getAnswerToEverything(); + ASSERT_THAT(v.hasException(), Eq(false)); +} + +TEST(expected, T_fromException_is_not_valid) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.valid(), Eq(false)); +} + +TEST(expected, T_fromException_get_thows) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THROW (e.get(), std::runtime_error); +} + +TEST(expected, T_fromException_has_exception_true) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected, T_fromException_has_exception_false) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(false)); +} + +TEST(expected, T_fromException_has_derived_exception) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected, T_fromCode_is_valid) +{ + auto e = Expected::fromCode([]() -> int { return 42; }); + ASSERT_THAT(e.valid(), Eq(true)); +} + +TEST(expected, T_fromCode_get) +{ + auto e = Expected::fromCode([]() -> int { return 42; }); + ASSERT_THAT(e.get(), Eq(42)); +} + + +TEST(expected, T_fromCode_E_is_not_valid) +{ + auto e = Expected::fromCode([]() -> int { throw std::runtime_error("hello"); }); + ASSERT_THAT(e.valid(), Eq(false)); +} + +TEST(expected, T_fromCode_E_get_thows) +{ + auto e = Expected::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::fromCode([]() -> int { throw std::runtime_error("hello"); }); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected, T_fromCode_E_has_exception_false) +{ + auto e = Expected::fromCode([]() -> int { throw std::runtime_error("hello"); }); + ASSERT_THAT(e.hasException(), Eq(false)); +} + +TEST(expected, T_fromCode_E_has_derived_exception) +{ + auto e = Expected::fromCode([]() -> int { throw std::runtime_error("hello"); }); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +//Expected getIntWithStdRuntimeError() { return Expected(); } + +Expected getNothing() { return Expected(); } + + +TEST(expected_void, valid_when_valid_returns_true) +{ + Expected v = getNothing(); + ASSERT_THAT(v.valid(), Eq(true)); +} + +TEST(expected_void, get_when_valid_returns_value) +{ + Expected v = getNothing(); + ASSERT_NO_THROW(v.get()); +} + +TEST(expected_void, hasException_when_valid_returns_false) +{ + Expected v = getNothing(); + ASSERT_THAT(v.hasException(), Eq(false)); +} + + + + + + +TEST(expected_void, void_fromException_is_not_valid) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.valid(), Eq(false)); +} + +TEST(expected_void, void_fromException_get_thows) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THROW (e.get(), std::runtime_error); +} + +TEST(expected_void, void_fromException_has_exception_true) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected_void, void_fromException_has_exception_false) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(false)); +} + +TEST(expected_void, void_fromException_has_derived_exception) +{ + auto e = Expected::fromException(std::runtime_error("hello")); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected_void, void_fromCode_is_valid) +{ + auto e = Expected::fromCode([]() -> void { }); + ASSERT_THAT(e.valid(), Eq(true)); +} + +TEST(expected_void, void_fromCode_get) +{ + auto e = Expected::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::fromCode(expected_void_throws_func); + ASSERT_THAT(e.valid(), Eq(false)); +} + +TEST(expected_void, void_fromCode_E_get_thows) +{ + auto e = Expected::fromCode(expected_void_throws_func); + ASSERT_THROW (e.get(), std::runtime_error); +} + +TEST(expected_void, void_fromCode_E_has_exception_true) +{ + auto e = Expected::fromCode(expected_void_throws_func); + ASSERT_THAT(e.hasException(), Eq(true)); +} + +TEST(expected_void, void_fromCode_E_has_exception_false) +{ + auto e = Expected::fromCode(expected_void_throws_func); + ASSERT_THAT(e.hasException(), Eq(false)); +} + +TEST(expected_void, void_fromCode_E_has_derived_exception) +{ + auto e = Expected::fromCode(expected_void_throws_func); + ASSERT_THAT(e.hasException(), Eq(true)); +} diff --git a/tests/auto/mycase/tst_scopeguard.h b/tests/auto/mycase/tst_scopeguard.h new file mode 100644 index 0000000..2b14280 --- /dev/null +++ b/tests/auto/mycase/tst_scopeguard.h @@ -0,0 +1,63 @@ +#include +#include +#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 v = getAnswerToEverything(); +// ASSERT_THAT(v.get(), Eq(42)); +//} + diff --git a/tests/mygtestutils/PrintTo_Qt.cpp b/tests/mygtestutils/PrintTo_Qt.cpp new file mode 100644 index 0000000..fdfc571 --- /dev/null +++ b/tests/mygtestutils/PrintTo_Qt.cpp @@ -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(); +} diff --git a/tests/mygtestutils/PrintTo_Qt.h b/tests/mygtestutils/PrintTo_Qt.h new file mode 100644 index 0000000..16a8d8b --- /dev/null +++ b/tests/mygtestutils/PrintTo_Qt.h @@ -0,0 +1,11 @@ +#ifndef PRINTTO_QT_H +#define PRINTTO_QT_H + +#include +#include +#include + +void PrintTo(const QDateTime &dt, ::std::ostream* os); +void PrintTo(const QString &s, ::std::ostream* os); + +#endif // PRINTTO_QT_H diff --git a/tests/mygtestutils/mygtestutils.pro b/tests/mygtestutils/mygtestutils.pro new file mode 100644 index 0000000..a56a359 --- /dev/null +++ b/tests/mygtestutils/mygtestutils.pro @@ -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 +} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..b3fc93a --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += auto \ + PgsqlTests \ + mygtestutils