Retrieve tables sizes asynchronously when opening the catalog.

This commit is contained in:
eelke 2022-01-17 17:12:07 +01:00
parent 8fe5e05f7d
commit 39195153cd
8 changed files with 163 additions and 52 deletions

View file

@ -18,7 +18,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr<OpenDatabase> open_database,
{ {
m_tabWidget = new QTabWidget(this); m_tabWidget = new QTabWidget(this);
// m_namespacePage = new CatalogNamespacePage(this); // m_namespacePage = new CatalogNamespacePage(this);
m_tablesPage = new CatalogTablesPage(this); m_tablesPage = new CatalogTablesPage(open_database, this);
m_functionsPage = new CatalogFunctionsPage(this); m_functionsPage = new CatalogFunctionsPage(this);
m_sequencesPage = new CatalogSequencesPage(this); m_sequencesPage = new CatalogSequencesPage(this);
m_typesPage = new CatalogTypesPage(this); m_typesPage = new CatalogTypesPage(this);

View file

@ -10,17 +10,22 @@ class PgDatabaseCatalog;
template <typename TableModel> template <typename TableModel>
class PgLabTableViewHelper { class PgLabTableViewHelper {
public: public:
PgLabTableViewHelper(QWidget * parent)
PgLabTableViewHelper(QWidget * parent, TableModel *tableModel)
{ {
m_tableView = new PgLabTableView(parent); m_tableView = new PgLabTableView(parent);
m_dataModel = new TableModel(parent); m_dataModel = tableModel;
m_sortFilter = new QSortFilterProxyModel(parent); m_sortFilter = new QSortFilterProxyModel(parent);
m_sortFilter->setSourceModel(m_dataModel); m_sortFilter->setSourceModel(m_dataModel);
m_tableView->setModel(m_sortFilter); m_tableView->setModel(m_sortFilter);
m_tableView->setSortingEnabled(true); m_tableView->setSortingEnabled(true);
} }
PgLabTableViewHelper(QWidget * parent)
: PgLabTableViewHelper(parent, new TableModel(parent))
{}
PgLabTableView *tableView() const PgLabTableView *tableView() const
{ {
return m_tableView; return m_tableView;

View file

@ -1,4 +1,5 @@
#include "TablesTableModel.h" #include "TablesTableModel.h"
#include "OpenDatabase.h"
#include "ScopeGuard.h" #include "ScopeGuard.h"
#include "catalog/PgDatabaseCatalog.h" #include "catalog/PgDatabaseCatalog.h"
#include "catalog/PgClass.h" #include "catalog/PgClass.h"
@ -8,9 +9,12 @@
#include "Pgsql_declare.h" #include "Pgsql_declare.h"
#include "CustomDataRole.h" #include "CustomDataRole.h"
#include <QBrush> #include <QBrush>
#include <QtConcurrent>
#include "Pgsql_Connection.h"
TablesTableModel::TablesTableModel(QObject *parent) TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
, openDatabase(opendatabase)
{} {}
void TablesTableModel::setNamespaceFilter(NamespaceFilter nsf) void TablesTableModel::setNamespaceFilter(NamespaceFilter nsf)
@ -40,6 +44,7 @@ void TablesTableModel::refresh()
// Later afscheiden naar filter functie // Later afscheiden naar filter functie
auto classes = m_catalog->classes(); auto classes = m_catalog->classes();
std::map<Oid, int> oidIndex;
m_tables.clear(); m_tables.clear();
for (const auto &e : *classes) { for (const auto &e : *classes) {
bool add = false; bool add = false;
@ -57,9 +62,12 @@ void TablesTableModel::refresh()
break; break;
} }
} }
if (add) if (add) {
m_tables.push_back(e); 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 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 OptionsCol: return tr("Options");
case AclCol: return tr("ACL"); case AclCol: return tr("ACL");
case CommentCol: return tr("Comment"); case CommentCol: return tr("Comment");
case TotalSize: return tr("Total size"); case TotalSizeCol: return tr("Total size");
case TableSize: return tr("Table size"); case TableSizeCol: return tr("Table size");
case IndexSize: return tr("Index size"); case IndexSizeCol: return tr("Index size");
case ToastSize: return tr("TOAST size"); case ToastSizeCol: return tr("TOAST size");
} }
} }
} }
@ -99,10 +107,10 @@ Oid TablesTableModel::getType(int column) const
{ {
Oid oid; Oid oid;
switch (column) { switch (column) {
case TotalSize: case TotalSizeCol:
case TableSize: case TableSizeCol:
case IndexSize: case IndexSizeCol:
case ToastSize: case ToastSizeCol:
oid = Pgsql::int8_oid; oid = Pgsql::int8_oid;
break; break;
case TablespaceCol: case TablespaceCol:
@ -121,7 +129,9 @@ Oid TablesTableModel::getType(int column) const
QVariant TablesTableModel::getData(const QModelIndex &index) 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()) { switch (index.column()) {
case NameCol: return t.objectName(); case NameCol: return t.objectName();
case NamespaceCol: return t.nsName(); case NamespaceCol: return t.nsName();
@ -131,10 +141,10 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const
case OptionsCol: break; case OptionsCol: break;
case AclCol: return t.aclString(); case AclCol: return t.aclString();
case CommentCol: return t.description; case CommentCol: return t.description;
case TotalSize: return t.totalBytes; case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
case TableSize: return t.totalBytes - t.indexBytes - t.toastBytes; case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
case IndexSize: return t.indexBytes; case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
case ToastSize: return t.toastBytes; case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
} }
return QVariant(); return QVariant();
@ -142,12 +152,12 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const
PgClass TablesTableModel::getTable(int row) const PgClass TablesTableModel::getTable(int row) const
{ {
return m_tables[row]; return m_tables[row]._class;
} }
Oid TablesTableModel::getTableOid(int row) const 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 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()); return getType(index.column());
else if (role == CustomDataMeaningRole) { else if (role == CustomDataMeaningRole) {
switch (index.column()) { switch (index.column()) {
case TotalSize: case TotalSizeCol:
case TableSize: case TableSizeCol:
case IndexSize: case IndexSizeCol:
case ToastSize: case ToastSizeCol:
return static_cast<int>(DataMeaningBytes); return static_cast<int>(DataMeaningBytes);
default: default:
return static_cast<int>(DataMeaningNormal); return static_cast<int>(DataMeaningNormal);
@ -170,3 +180,80 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex)
{
QPointer p(this);
QtConcurrent::run([this]
{
return QueryTableSizes();
})
.then(qApp, [p, oidIndex] (std::vector<TableSize> 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<Oid, int> oidIndex, std::vector<TableSize> 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<int>(m_tables.size()), ToastSizeCol)
);
}

View file

@ -7,11 +7,10 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
class OpenDatabase;
class PgClass; class PgClass;
class PgDatabaseCatalog; class PgDatabaseCatalog;
class TablesTableModel: public QAbstractTableModel { class TablesTableModel: public QAbstractTableModel {
public: public:
using RowItem = PgClass; using RowItem = PgClass;
@ -24,14 +23,14 @@ public:
OptionsCol, OptionsCol,
AclCol, AclCol,
CommentCol, CommentCol,
TotalSize, TotalSizeCol,
TableSize, TableSizeCol,
IndexSize, IndexSizeCol,
ToastSize, ToastSizeCol,
colCount }; colCount };
TablesTableModel(QObject *parent); TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent);
void setNamespaceFilter(NamespaceFilter nsf); void setNamespaceFilter(NamespaceFilter nsf);
void setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat); void setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat);
@ -50,16 +49,44 @@ public:
Oid getTableOid(int row) const; Oid getTableOid(int row) const;
private: private:
using t_Tables = std::vector<PgClass>; 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 TableSize>;
class Table {
public:
PgClass _class;
class TableSize sizes;
Table(const PgClass &cls)
: _class(cls)
{}
};
using t_Tables = std::vector<Table>;
std::shared_ptr<const PgDatabaseCatalog> m_catalog; std::shared_ptr<const PgDatabaseCatalog> m_catalog;
NamespaceFilter m_namespaceFilter = NamespaceFilter::User; NamespaceFilter m_namespaceFilter = NamespaceFilter::User;
t_Tables m_tables; t_Tables m_tables;
QMetaObject::Connection refreshConnection; QMetaObject::Connection refreshConnection;
std::shared_ptr<OpenDatabase> openDatabase;
Oid getType(int column) const; Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const; QVariant getData(const QModelIndex &index) const;
void StartLoadTableSizes(std::map<Oid, int> oidIndex);
TableSizes QueryTableSizes() const;
void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes);
private slots: private slots:
void refresh(); void refresh();

View file

@ -23,9 +23,9 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QTableWidget> #include <QTableWidget>
CatalogTablesPage::CatalogTablesPage(QWidget *parent) CatalogTablesPage::CatalogTablesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget *parent)
: QSplitter(Qt::Horizontal, parent) : QSplitter(Qt::Horizontal, parent)
, m_tablesTableView(this) , m_tablesTableView(this, new TablesTableModel(opendatabase, this))
{ {
auto tv = m_tablesTableView.tableView(); auto tv = m_tablesTableView.tableView();
tv->setSelectionMode(QAbstractItemView::SingleSelection); tv->setSelectionMode(QAbstractItemView::SingleSelection);

View file

@ -29,7 +29,7 @@ class TriggerPage;
class CatalogTablesPage: public QSplitter { class CatalogTablesPage: public QSplitter {
Q_OBJECT Q_OBJECT
public: public:
explicit CatalogTablesPage(QWidget * parent = nullptr); explicit CatalogTablesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget * parent = nullptr);
void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat); void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat);
void setNamespaceFilter(NamespaceFilter filter); void setNamespaceFilter(NamespaceFilter filter);

View file

@ -50,10 +50,6 @@ public:
QString viewdef; QString viewdef;
QString description; // from pg_description QString description; // from pg_description
int64_t totalBytes;
int64_t indexBytes;
int64_t toastBytes;
using PgNamespaceObject::PgNamespaceObject; using PgNamespaceObject::PgNamespaceObject;

View file

@ -12,9 +12,6 @@ std::string PgClassContainer::getLoadQuery() const
" reltuples, reltoastrelid, relisshared, relpersistence, " " reltuples, reltoastrelid, relisshared, relpersistence, "
" relkind, relispopulated, relfrozenxid, relminmxid, " " relkind, relispopulated, relfrozenxid, relminmxid, "
" reloptions, d.description, " " 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)"; " relacl, pg_get_viewdef(pg_class.oid)";
if (lessThenVersion(120000)) if (lessThenVersion(120000))
@ -41,8 +38,7 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
>> owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est >> owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est
>> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence
>> v.kind >> v.ispopulated >> v.frozenxid >> v.minmxid >> 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); v.setOwnerOid(owner);