diff --git a/pglab/CatalogInspector.cpp b/pglab/CatalogInspector.cpp index 87d4392..ff49eac 100644 --- a/pglab/CatalogInspector.cpp +++ b/pglab/CatalogInspector.cpp @@ -18,7 +18,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr open_database, { m_tabWidget = new QTabWidget(this); // m_namespacePage = new CatalogNamespacePage(this); - m_tablesPage = new CatalogTablesPage(this); + m_tablesPage = new CatalogTablesPage(open_database, this); m_functionsPage = new CatalogFunctionsPage(this); m_sequencesPage = new CatalogSequencesPage(this); m_typesPage = new CatalogTypesPage(this); diff --git a/pglab/PgLabTableViewHelper.h b/pglab/PgLabTableViewHelper.h index 74e315d..8fbf9cc 100644 --- a/pglab/PgLabTableViewHelper.h +++ b/pglab/PgLabTableViewHelper.h @@ -10,17 +10,22 @@ class PgDatabaseCatalog; template class PgLabTableViewHelper { public: - PgLabTableViewHelper(QWidget * parent) + + PgLabTableViewHelper(QWidget * parent, TableModel *tableModel) { m_tableView = new PgLabTableView(parent); - m_dataModel = new TableModel(parent); + m_dataModel = tableModel; m_sortFilter = new QSortFilterProxyModel(parent); m_sortFilter->setSourceModel(m_dataModel); m_tableView->setModel(m_sortFilter); m_tableView->setSortingEnabled(true); } + PgLabTableViewHelper(QWidget * parent) + : PgLabTableViewHelper(parent, new TableModel(parent)) + {} + PgLabTableView *tableView() const { return m_tableView; diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index 1bca636..1fd4509 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -1,4 +1,5 @@ #include "TablesTableModel.h" +#include "OpenDatabase.h" #include "ScopeGuard.h" #include "catalog/PgDatabaseCatalog.h" #include "catalog/PgClass.h" @@ -8,9 +9,12 @@ #include "Pgsql_declare.h" #include "CustomDataRole.h" #include +#include +#include "Pgsql_Connection.h" -TablesTableModel::TablesTableModel(QObject *parent) +TablesTableModel::TablesTableModel(std::shared_ptr opendatabase, QObject *parent) : QAbstractTableModel(parent) + , openDatabase(opendatabase) {} void TablesTableModel::setNamespaceFilter(NamespaceFilter nsf) @@ -40,6 +44,7 @@ void TablesTableModel::refresh() // Later afscheiden naar filter functie auto classes = m_catalog->classes(); + std::map oidIndex; m_tables.clear(); for (const auto &e : *classes) { bool add = false; @@ -57,9 +62,12 @@ void TablesTableModel::refresh() break; } } - if (add) - m_tables.push_back(e); + if (add) { + m_tables.emplace_back(e); + oidIndex.emplace(e.oid(), m_tables.size() - 1); + } } + StartLoadTableSizes(std::move(oidIndex)); } QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -75,10 +83,10 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, case OptionsCol: return tr("Options"); case AclCol: return tr("ACL"); case CommentCol: return tr("Comment"); - case TotalSize: return tr("Total size"); - case TableSize: return tr("Table size"); - case IndexSize: return tr("Index size"); - case ToastSize: return tr("TOAST size"); + case TotalSizeCol: return tr("Total size"); + case TableSizeCol: return tr("Table size"); + case IndexSizeCol: return tr("Index size"); + case ToastSizeCol: return tr("TOAST size"); } } } @@ -99,10 +107,10 @@ Oid TablesTableModel::getType(int column) const { Oid oid; switch (column) { - case TotalSize: - case TableSize: - case IndexSize: - case ToastSize: + case TotalSizeCol: + case TableSizeCol: + case IndexSizeCol: + case ToastSizeCol: oid = Pgsql::int8_oid; break; case TablespaceCol: @@ -121,20 +129,22 @@ Oid TablesTableModel::getType(int column) const QVariant TablesTableModel::getData(const QModelIndex &index) const { - const auto &t = m_tables[index.row()]; + const auto &table = m_tables[index.row()]; + const auto &t = table._class; + const auto &s = table.sizes; switch (index.column()) { - case NameCol: return t.objectName(); - case NamespaceCol: return t.nsName(); - case KindCol: return t.typeName(); - case OwnerCol: return t.ownerName(); + case NameCol: return t.objectName(); + case NamespaceCol: return t.nsName(); + case KindCol: return t.typeName(); + case OwnerCol: return t.ownerName(); case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace); - case OptionsCol: break; - case AclCol: return t.aclString(); - case CommentCol: return t.description; - case TotalSize: return t.totalBytes; - case TableSize: return t.totalBytes - t.indexBytes - t.toastBytes; - case IndexSize: return t.indexBytes; - case ToastSize: return t.toastBytes; + case OptionsCol: break; + case AclCol: return t.aclString(); + case CommentCol: return t.description; + case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes; + case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes; + case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes; + case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes; } return QVariant(); @@ -142,12 +152,12 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const PgClass TablesTableModel::getTable(int row) const { - return m_tables[row]; + return m_tables[row]._class; } Oid TablesTableModel::getTableOid(int row) const { - return m_tables[row].oid(); + return m_tables[row]._class.oid(); } QVariant TablesTableModel::data(const QModelIndex &index, int role) const @@ -158,10 +168,10 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const return getType(index.column()); else if (role == CustomDataMeaningRole) { switch (index.column()) { - case TotalSize: - case TableSize: - case IndexSize: - case ToastSize: + case TotalSizeCol: + case TableSizeCol: + case IndexSizeCol: + case ToastSizeCol: return static_cast(DataMeaningBytes); default: return static_cast(DataMeaningNormal); @@ -170,3 +180,80 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const return QVariant(); } + +void TablesTableModel::StartLoadTableSizes(std::map oidIndex) +{ + QPointer p(this); + QtConcurrent::run([this] + { + return QueryTableSizes(); + }) + .then(qApp, [p, oidIndex] (std::vector sizes) + { + if (p) + { + p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes)); + } + }); +} + +TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const +{ + std::string nsfilter; + switch (m_namespaceFilter) + { + case NamespaceFilter::User: + nsfilter = "(NOT (nspname LIKE 'pg_%' OR nspname='information_schema'))"; + break; + case NamespaceFilter::PgCatalog: + nsfilter = "nspname = 'pg_catalog'"; + break; + case NamespaceFilter::InformationSchema: + nsfilter = "nspname = 'information_schema'"; + break; + } + + std::string q = + "SELECT pg_class.oid, " + " pg_total_relation_size(pg_class.oid) AS total_bytes, " + " CASE WHEN relkind='r' THEN pg_indexes_size(pg_class.oid) ELSE 0 END AS index_bytes, " + " CASE WHEN relkind='r' THEN pg_total_relation_size(reltoastrelid) ELSE 0 END AS toast_bytes " + "\nFROM pg_catalog.pg_class \n" + " JOIN pg_namespace ON (relnamespace = pg_namespace.oid)" + " LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n" + "WHERE relkind IN ('r', 'p', 'm') AND " + nsfilter; + + ; + Pgsql::Connection connection; + connection.connect(openDatabase->config().connectionString()); + Pgsql::Result sizesResult = connection.query(q.c_str()); + + TablesTableModel::TableSizes sizes; + sizes.reserve(sizesResult.rows()); + for (const Pgsql::Row& row : sizesResult) + { + TableSize size; + size.oid = row.get(0); + size.totalBytes = row.get(1); + size.indexBytes = row.get(2); + size.toastBytes = row.get(3); + sizes.push_back(size); + } + return sizes; +} + +void TablesTableModel::PopulateSizes(std::map oidIndex, std::vector sizes) +{ + for (auto s : sizes) + { + auto findIter = oidIndex.find(s.oid); + if (findIter != oidIndex.end()) + { + m_tables[findIter->second].sizes = s; + } + } + emit dataChanged( + createIndex(0, TotalSizeCol), + createIndex(static_cast(m_tables.size()), ToastSizeCol) + ); +} diff --git a/pglab/TablesTableModel.h b/pglab/TablesTableModel.h index b327b56..25c721c 100644 --- a/pglab/TablesTableModel.h +++ b/pglab/TablesTableModel.h @@ -7,11 +7,10 @@ #include #include +class OpenDatabase; class PgClass; class PgDatabaseCatalog; - - class TablesTableModel: public QAbstractTableModel { public: using RowItem = PgClass; @@ -24,14 +23,14 @@ public: OptionsCol, AclCol, CommentCol, - TotalSize, - TableSize, - IndexSize, - ToastSize, + TotalSizeCol, + TableSizeCol, + IndexSizeCol, + ToastSizeCol, colCount }; - TablesTableModel(QObject *parent); + TablesTableModel(std::shared_ptr opendatabase, QObject *parent); void setNamespaceFilter(NamespaceFilter nsf); void setCatalog(std::shared_ptr cat); @@ -50,16 +49,44 @@ public: Oid getTableOid(int row) const; private: - using t_Tables = std::vector; + class TableSize { + public: + int oid; + int64_t totalBytes = -1; + int64_t indexBytes = -1; + int64_t toastBytes = -1; + + TableSize() + : oid(0) + {} + }; + using TableSizes = std::vector; + + class Table { + public: + PgClass _class; + class TableSize sizes; + + Table(const PgClass &cls) + : _class(cls) + {} + }; + using t_Tables = std::vector; std::shared_ptr m_catalog; NamespaceFilter m_namespaceFilter = NamespaceFilter::User; t_Tables m_tables; QMetaObject::Connection refreshConnection; + std::shared_ptr openDatabase; Oid getType(int column) const; QVariant getData(const QModelIndex &index) const; + + void StartLoadTableSizes(std::map oidIndex); + TableSizes QueryTableSizes() const; + void PopulateSizes(std::map oidIndex, std::vector sizes); + private slots: void refresh(); diff --git a/pglab/widgets/CatalogTablesPage.cpp b/pglab/widgets/CatalogTablesPage.cpp index 996f590..c2df79a 100644 --- a/pglab/widgets/CatalogTablesPage.cpp +++ b/pglab/widgets/CatalogTablesPage.cpp @@ -23,9 +23,9 @@ #include #include -CatalogTablesPage::CatalogTablesPage(QWidget *parent) +CatalogTablesPage::CatalogTablesPage(std::shared_ptr opendatabase, QWidget *parent) : QSplitter(Qt::Horizontal, parent) - , m_tablesTableView(this) + , m_tablesTableView(this, new TablesTableModel(opendatabase, this)) { auto tv = m_tablesTableView.tableView(); tv->setSelectionMode(QAbstractItemView::SingleSelection); diff --git a/pglab/widgets/CatalogTablesPage.h b/pglab/widgets/CatalogTablesPage.h index c20ee66..e34f56c 100644 --- a/pglab/widgets/CatalogTablesPage.h +++ b/pglab/widgets/CatalogTablesPage.h @@ -29,7 +29,7 @@ class TriggerPage; class CatalogTablesPage: public QSplitter { Q_OBJECT public: - explicit CatalogTablesPage(QWidget * parent = nullptr); + explicit CatalogTablesPage(std::shared_ptr opendatabase, QWidget * parent = nullptr); void setCatalog(std::shared_ptr cat); void setNamespaceFilter(NamespaceFilter filter); diff --git a/pglablib/catalog/PgClass.h b/pglablib/catalog/PgClass.h index f5cc2b9..8510345 100644 --- a/pglablib/catalog/PgClass.h +++ b/pglablib/catalog/PgClass.h @@ -50,10 +50,6 @@ public: QString viewdef; QString description; // from pg_description - int64_t totalBytes; - int64_t indexBytes; - int64_t toastBytes; - using PgNamespaceObject::PgNamespaceObject; diff --git a/pglablib/catalog/PgClassContainer.cpp b/pglablib/catalog/PgClassContainer.cpp index 493ecb7..eb563f6 100644 --- a/pglablib/catalog/PgClassContainer.cpp +++ b/pglablib/catalog/PgClassContainer.cpp @@ -12,9 +12,6 @@ std::string PgClassContainer::getLoadQuery() const " reltuples, reltoastrelid, relisshared, relpersistence, " " relkind, relispopulated, relfrozenxid, relminmxid, " " reloptions, d.description, " - " pg_total_relation_size(pg_class.oid) AS total_bytes, " - " CASE WHEN relkind='r' THEN pg_indexes_size(pg_class.oid) ELSE 0 END AS index_bytes, " - " CASE WHEN relkind='r' THEN pg_total_relation_size(reltoastrelid) ELSE 0 END AS toast_bytes, " " relacl, pg_get_viewdef(pg_class.oid)"; if (lessThenVersion(120000)) @@ -41,8 +38,7 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row) >> owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence >> v.kind >> v.ispopulated >> v.frozenxid >> v.minmxid - >> v.options >> v.description >> v.totalBytes >> v.indexBytes >> v.toastBytes; - + >> v.options >> v.description; v.setOwnerOid(owner);