From b5a706a2a21dd6c9180a5eb7932fab300f06fcfb Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Jan 2022 19:10:05 +0100 Subject: [PATCH] lazy load the database sizes only when needed by the server inspector. --- pglab/DatabasesTableModel.cpp | 180 +++++++++++++++------- pglab/DatabasesTableModel.h | 36 ++++- pglab/TablesTableModel.cpp | 2 +- pglab/TablesTableModel.h | 4 +- pglab/serverinspector/DatabasesPage.cpp | 4 +- pglab/serverinspector/DatabasesPage.h | 2 +- pglab/serverinspector/ServerInspector.cpp | 2 +- pglablib/catalog/PgDatabase.h | 1 - pglablib/catalog/PgDatabaseContainer.cpp | 3 +- pglablib/catalog/PgDatabaseContainer.h | 1 - 10 files changed, 165 insertions(+), 70 deletions(-) diff --git a/pglab/DatabasesTableModel.cpp b/pglab/DatabasesTableModel.cpp index 7e04752..039d150 100644 --- a/pglab/DatabasesTableModel.cpp +++ b/pglab/DatabasesTableModel.cpp @@ -1,15 +1,19 @@ #include "DatabasesTableModel.h" #include "CustomDataRole.h" +#include "OpenDatabase.h" #include "ScopeGuard.h" #include "catalog/PgDatabaseCatalog.h" #include "catalog/PgDatabaseContainer.h" #include "catalog/PgAuthIdContainer.h" #include "ResultTableModelUtil.h" +#include +#include "Pgsql_Connection.h" using namespace Pgsql; -DatabasesTableModel::DatabasesTableModel(QObject *parent) +DatabasesTableModel::DatabasesTableModel(std::shared_ptr opendatabase, QObject *parent) : BaseTableModel(parent) + , openDatabase(opendatabase) { } @@ -18,15 +22,27 @@ void DatabasesTableModel::setDatabaseList(std::shared_ptrdatabases(); + databases.clear(); + + std::map oidIndex; + m_catalog = cat; + auto dats = cat->databases(); + databases.reserve(dats->count()); + for (const auto& d : *dats) + { + databases.emplace_back(d); + oidIndex.emplace(d.oid(), databases.size() - 1); + } + StartLoadDatabaseSizes(std::move(oidIndex)); } QVariant DatabasesTableModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant v; - if (orientation == Qt::Horizontal) { - if (role == Qt::DisplayRole) { + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) + { switch (section) { case NameCol: v = tr("Name"); @@ -74,9 +90,7 @@ QVariant DatabasesTableModel::headerData(int section, Qt::Orientation orientatio int DatabasesTableModel::rowCount(const QModelIndex &) const { int result = 0; - if (m_databases) { - result = m_databases->count(); - } + result = databases.size(); return result; } @@ -90,7 +104,8 @@ int DatabasesTableModel::columnCount(const QModelIndex &) const Oid DatabasesTableModel::getType(int column) const { Oid oid; - switch (column) { + switch (column) + { case AllowConnCol: case IsTemplateCol: oid = Pgsql::bool_oid; @@ -120,58 +135,113 @@ Oid DatabasesTableModel::getType(int column) const QVariant DatabasesTableModel::getData(const QModelIndex &index) const { QVariant v; - //if (!index.isValid()) - if (m_databases) { - const PgDatabase &db = m_databases->getByIdx(index.row()); - switch (index.column()) { - case NameCol: - v = db.objectName(); - break; - case DbaCol: - v = getRoleDisplayString(*m_catalog, db.dba); - break; - case EncodingCol: - // todo lookup encoding name - v = db.encodingString; - 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 = getTablespaceDisplayString(*m_catalog, db.tablespace); - break; - case CommentCol: - v = db.description; - break; - case SizeCol: - if (db.sizeBytes >= 0) - v = db.sizeBytes; - break; - case AclCol: - v = db.aclString(); - break; - } - } + const Database &d = databases[index.row()]; + const PgDatabase &db = d.database; + switch (index.column()) { + case NameCol: + v = db.objectName(); + break; + case DbaCol: + v = getRoleDisplayString(*m_catalog, db.dba); + break; + case EncodingCol: + // todo lookup encoding name + v = db.encodingString; + 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 = getTablespaceDisplayString(*m_catalog, db.tablespace); + break; + case CommentCol: + v = db.description; + break; + case SizeCol: + if (d.size.totalBytes >= 0) + v = d.size.totalBytes; + break; + case AclCol: + v = db.aclString(); + break; + } return v; } QVariant DatabasesTableModel::getDataMeaning(const QModelIndex &index) const { if (index.column() == SizeCol) + { return static_cast(DataMeaningBytes); - + } return BaseTableModel::getDataMeaning(index); } + +void DatabasesTableModel::StartLoadDatabaseSizes(std::map oidIndex) +{ + QPointer p(this); + QtConcurrent::run([this] + { + return QueryDatabaseSizes(); + }) + .then(qApp, [p, oi = std::move(oidIndex)] (DatabaseSizes sizes) + { + if (p) + { + p.data()->PopulateSizes(std::move(oi), std::move(sizes)); + } + }); +} + +DatabasesTableModel::DatabaseSizes DatabasesTableModel::QueryDatabaseSizes() +{ + std::string q = + "SELECT pg_database.oid, " + " case when has_database_privilege(current_role, pg_database.oid, \n" + " 'connect') then pg_database_size(pg_database.oid) else -1 end \n" + "FROM pg_database"; + + Pgsql::Connection connection; + connection.connect(openDatabase->config().connectionString()); + Pgsql::Result sizesResult = connection.query(q.c_str()); + + DatabaseSizes result; + result.reserve(sizesResult.rows()); + for (auto& row : sizesResult) + { + DatabaseSize ds; + ds.oid = row.get(0); + ds.totalBytes = row.get(1); + result.push_back(ds); + } + return result; +} + +void DatabasesTableModel::PopulateSizes(std::map oidIndex, DatabaseSizes sizes) +{ + for (auto & s : sizes) + { + auto findIter = oidIndex.find(s.oid); + if (findIter != oidIndex.end()) + { + databases[findIter->second].size = s; + } + } + emit dataChanged( + createIndex(0, SizeCol), + createIndex(static_cast(databases.size()), SizeCol) + ); +} diff --git a/pglab/DatabasesTableModel.h b/pglab/DatabasesTableModel.h index faf3d41..f0dbfe6 100644 --- a/pglab/DatabasesTableModel.h +++ b/pglab/DatabasesTableModel.h @@ -2,11 +2,11 @@ #define DATABASESTABLEMODEL_H #include "BaseTableModel.h" +#include "catalog/PgDatabase.h" #include -class PgDatabaseContainer; +class OpenDatabase; class PgDatabaseCatalog; -class PgDatabase; /** Class for displaying the list of databases of a server in a QTableView * @@ -24,7 +24,7 @@ public: - explicit DatabasesTableModel(QObject *parent); + explicit DatabasesTableModel(std::shared_ptr opendatabase, QObject *parent); void setDatabaseList(std::shared_ptr cat); @@ -41,8 +41,36 @@ public: protected: virtual QVariant getDataMeaning(const QModelIndex &index) const override; private: + class DatabaseSize { + public: + Oid oid; + int64_t totalBytes; + + DatabaseSize() + : oid(InvalidOid) + , totalBytes(-1) + {} + }; + using DatabaseSizes = std::vector; + + class Database { + public: + PgDatabase database; + DatabaseSize size; + + Database(const PgDatabase &db) + : database(db) + {} + }; + using Databases = std::vector; + + std::shared_ptr openDatabase; std::shared_ptr m_catalog; - std::shared_ptr m_databases; + Databases databases; + + void StartLoadDatabaseSizes(std::map oidIndex); + DatabaseSizes QueryDatabaseSizes(); + void PopulateSizes(std::map oidIndex, DatabaseSizes sizes); }; #endif // DATABASESTABLEMODEL_H diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index 1fd4509..0eb41a0 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -188,7 +188,7 @@ void TablesTableModel::StartLoadTableSizes(std::map oidIndex) { return QueryTableSizes(); }) - .then(qApp, [p, oidIndex] (std::vector sizes) + .then(qApp, [p, oidIndex] (TableSizes sizes) { if (p) { diff --git a/pglab/TablesTableModel.h b/pglab/TablesTableModel.h index 25c721c..880dcd8 100644 --- a/pglab/TablesTableModel.h +++ b/pglab/TablesTableModel.h @@ -60,12 +60,12 @@ private: : oid(0) {} }; - using TableSizes = std::vector; + using TableSizes = std::vector; class Table { public: PgClass _class; - class TableSize sizes; + TableSize sizes; Table(const PgClass &cls) : _class(cls) diff --git a/pglab/serverinspector/DatabasesPage.cpp b/pglab/serverinspector/DatabasesPage.cpp index 04ad732..8946eef 100644 --- a/pglab/serverinspector/DatabasesPage.cpp +++ b/pglab/serverinspector/DatabasesPage.cpp @@ -5,9 +5,9 @@ #include "PgLabTableView.h" -DatabasesPage::DatabasesPage(QWidget * parent) +DatabasesPage::DatabasesPage(std::shared_ptr opendatabase, QWidget * parent) : QSplitter(Qt::Horizontal, parent) - , m_databasesTableView(this) + , m_databasesTableView(this, new DatabasesTableModel(opendatabase, this)) { auto tv = m_databasesTableView.tableView(); tv->setSelectionMode(QAbstractItemView::SingleSelection); diff --git a/pglab/serverinspector/DatabasesPage.h b/pglab/serverinspector/DatabasesPage.h index 9d51ea3..62b9161 100644 --- a/pglab/serverinspector/DatabasesPage.h +++ b/pglab/serverinspector/DatabasesPage.h @@ -12,7 +12,7 @@ class QSortFilterProxyModel; class DatabasesPage: public QSplitter { public: - explicit DatabasesPage(QWidget * parent = nullptr); + explicit DatabasesPage(std::shared_ptr opendatabase, QWidget * parent = nullptr); void setCatalog(std::shared_ptr cat); diff --git a/pglab/serverinspector/ServerInspector.cpp b/pglab/serverinspector/ServerInspector.cpp index f6ef87b..cd602a8 100644 --- a/pglab/serverinspector/ServerInspector.cpp +++ b/pglab/serverinspector/ServerInspector.cpp @@ -9,7 +9,7 @@ ServerInspector::ServerInspector(std::shared_ptr open_database, QWidget *parent) : QWidget(parent) { - m_databasesPage = new DatabasesPage(this); + m_databasesPage = new DatabasesPage(open_database, this); m_rolesPage = new RolesPage(this); m_tabWidget = new QTabWidget(this); diff --git a/pglablib/catalog/PgDatabase.h b/pglablib/catalog/PgDatabase.h index 5be23ae..3402bab 100644 --- a/pglablib/catalog/PgDatabase.h +++ b/pglablib/catalog/PgDatabase.h @@ -17,7 +17,6 @@ public: bool allowConn; int connLimit; Oid tablespace; - int64_t sizeBytes; QString description; using PgServerObject::PgServerObject; diff --git a/pglablib/catalog/PgDatabaseContainer.cpp b/pglablib/catalog/PgDatabaseContainer.cpp index bcff7e2..2ed07da 100644 --- a/pglablib/catalog/PgDatabaseContainer.cpp +++ b/pglablib/catalog/PgDatabaseContainer.cpp @@ -6,7 +6,6 @@ std::string PgDatabaseContainer::getLoadQuery() const return "SELECT pg_database.oid, datname, datdba, encoding, pg_encoding_to_char(encoding), datcollate,\n" " datctype, datistemplate, datallowconn, datconnlimit, dattablespace, \n" - " case when has_database_privilege(current_role, pg_database.oid, 'connect') then pg_database_size(pg_database.oid) else -1 end,\n" " d.description, datacl\n" "FROM pg_database\n" " LEFT JOIN pg_catalog.pg_shdescription AS d ON (objoid=pg_database.oid)"; @@ -20,7 +19,7 @@ PgDatabase PgDatabaseContainer::loadElem(const Pgsql::Row &row) QString name = col.nextValue(); PgDatabase v(m_catalog, oid, name); col >> v.dba >> v.encoding >> v.encodingString >> v.collate >> v.ctype >> v.isTemplate - >> v.allowConn >> v.connLimit >> v.tablespace >> v.sizeBytes >> v.description; + >> v.allowConn >> v.connLimit >> v.tablespace >> v.description; AclList acl_list; col >> acl_list; diff --git a/pglablib/catalog/PgDatabaseContainer.h b/pglablib/catalog/PgDatabaseContainer.h index 0ccb5ad..86bb217 100644 --- a/pglablib/catalog/PgDatabaseContainer.h +++ b/pglablib/catalog/PgDatabaseContainer.h @@ -13,7 +13,6 @@ namespace Pgsql { class PgDatabaseContainer: public PgContainer { public: - //explicit PgDatabaseContainer(PgDatabaseCatalog *cat); using PgContainer::PgContainer; virtual std::string getLoadQuery() const override;