From 39dbab4d36aa11586d4f6bf3184dfcea8bd8b96c Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 22 Jan 2023 15:57:22 +0100 Subject: [PATCH 1/4] The table inheritance works mostly When a table has partitions or inheritance children these are listed as subnodes. A subnode can appear multiple times under different parents as postgresql supports inheriting multiple tables. The sizes are still missing and maybe some things I have note verified yet are not correct. --- pglab/catalog/models/DatabasesTableModel.h | 4 +- .../{models => tables}/TablesTableModel.cpp | 186 +++++++++++------- .../{models => tables}/TablesTableModel.h | 55 +++--- pglab/catalog/widgets/CatalogTablesPage.cpp | 22 ++- pglab/catalog/widgets/CatalogTablesPage.h | 6 +- pglab/pglab.pro | 4 +- pglab/util/PgLabTableViewHelper.h | 33 +++- pglablib/catalog/PgInheritsContainer.h | 13 +- pglablib/pglablib.pro | 6 + pglablib/ui/catalog/tables/TableNode.cpp | 20 ++ pglablib/ui/catalog/tables/TableNode.h | 25 +++ pglablib/ui/catalog/tables/TableSize.cpp | 6 + pglablib/ui/catalog/tables/TableSize.h | 13 ++ .../ui/catalog/tables/TableTreeBuilder.cpp | 89 +++++++++ pglablib/ui/catalog/tables/TableTreeBuilder.h | 32 +++ tests/pglabtests/TableTreeBuilderTests.cpp | 64 ++++++ tests/pglabtests/pglabtests.pro | 1 + 17 files changed, 452 insertions(+), 127 deletions(-) rename pglab/catalog/{models => tables}/TablesTableModel.cpp (57%) rename pglab/catalog/{models => tables}/TablesTableModel.h (60%) create mode 100644 pglablib/ui/catalog/tables/TableNode.cpp create mode 100644 pglablib/ui/catalog/tables/TableNode.h create mode 100644 pglablib/ui/catalog/tables/TableSize.cpp create mode 100644 pglablib/ui/catalog/tables/TableSize.h create mode 100644 pglablib/ui/catalog/tables/TableTreeBuilder.cpp create mode 100644 pglablib/ui/catalog/tables/TableTreeBuilder.h create mode 100644 tests/pglabtests/TableTreeBuilderTests.cpp diff --git a/pglab/catalog/models/DatabasesTableModel.h b/pglab/catalog/models/DatabasesTableModel.h index a8ecc8e..8e46aaa 100644 --- a/pglab/catalog/models/DatabasesTableModel.h +++ b/pglab/catalog/models/DatabasesTableModel.h @@ -38,9 +38,9 @@ public: virtual Oid getType(int column) const override; virtual QVariant getData(const QModelIndex &index) const override; - RowItem rowItem(int row) const + RowItem rowItem(const QModelIndex &index) const { - return databases.at(row).database; + return databases.at(index.row()).database; } protected: virtual QVariant getDataMeaning(const QModelIndex &index) const override; diff --git a/pglab/catalog/models/TablesTableModel.cpp b/pglab/catalog/tables/TablesTableModel.cpp similarity index 57% rename from pglab/catalog/models/TablesTableModel.cpp rename to pglab/catalog/tables/TablesTableModel.cpp index fd285ed..8c80749 100644 --- a/pglab/catalog/models/TablesTableModel.cpp +++ b/pglab/catalog/tables/TablesTableModel.cpp @@ -5,15 +5,16 @@ #include "catalog/PgClass.h" #include "catalog/PgClassContainer.h" #include "catalog/PgNamespace.h" -#include "catalog/PgNamespaceContainer.h" -#include "Pgsql_declare.h" +#include "catalog/PgInheritsContainer.h" +//#include "Pgsql_declare.h" +#include "ui/catalog/tables/TableTreeBuilder.h" #include "CustomDataRole.h" #include #include #include "Pgsql_Connection.h" TablesTableModel::TablesTableModel(std::shared_ptr opendatabase, QObject *parent) - : QAbstractTableModel(parent) + : QAbstractItemModel(parent) , openDatabase(opendatabase) {} @@ -36,17 +37,18 @@ void TablesTableModel::setCatalog(std::shared_ptr cat) bool TableLike(RelKind relkind) { switch (relkind) { - case RelKind::Table: - case RelKind::View: - case RelKind::MaterializedView: - case RelKind::ForeignTable: - case RelKind::PartitionedTable: - return true; - default: - return false; + case RelKind::Table: + case RelKind::View: + case RelKind::MaterializedView: + case RelKind::ForeignTable: + case RelKind::PartitionedTable: + return true; + default: + return false; } } + void TablesTableModel::refresh() { beginResetModel(); @@ -59,27 +61,17 @@ void TablesTableModel::refresh() auto classes = m_catalog->classes(); std::map oidIndex; - m_tables.clear(); - for (const auto &e : *classes) { - bool add = false; - if (TableLike(e.kind)) { - switch (m_namespaceFilter) { - case NamespaceFilter::User: - add = !e.ns().isSystemCatalog(); - break; - case NamespaceFilter::PgCatalog: - add = e.ns().objectName() == "pg_catalog"; - break; - case NamespaceFilter::InformationSchema: - add = e.ns().objectName() == "information_schema"; - break; - } - } - if (add) { - m_tables.emplace_back(e); - oidIndex.emplace(e.oid(), m_tables.size() - 1); - } - } + rootNode.reset(); + + auto nsfilter = GetNamespaceFilterLambda(); + std::map temp; + for (const auto &e : *classes) + if (TableLike(e.kind) && nsfilter(e)) + temp.emplace(e.oid(), e); + + TableTreeBuilder treeBuilder(temp, *m_catalog->inherits()); + rootNode = treeBuilder.Build(); + StartLoadTableSizes(std::move(oidIndex)); } @@ -106,14 +98,46 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -int TablesTableModel::rowCount(const QModelIndex &) const +int TablesTableModel::rowCount(const QModelIndex &parent) const { - return static_cast(m_tables.size()); + if (parent.isValid()) + { + const TableNode *parentItem = static_cast(parent.internalPointer()); + return static_cast(parentItem->children.size()); + } + if (rootNode != nullptr) + return static_cast(rootNode->children.size()); + + return 0; } int TablesTableModel::columnCount(const QModelIndex &) const { - return colCount; + return colCount; +} + +QModelIndex TablesTableModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + { + const TableNode *parentItem = static_cast(parent.internalPointer()); + return createIndex(row, column, parentItem->getChildPtr(row)); + } + return createIndex(row, column, rootNode->getChildPtr(row)); +} + +QModelIndex TablesTableModel::parent(const QModelIndex &index) const +{ + if (index.isValid()) + { + const TableNode *item = static_cast(index.internalPointer()); + auto parent = item->parent.lock(); + if (parent && parent != rootNode) + { + return createIndex(parent->myIndex, 0, parent.get()); + } + } + return {}; } Oid TablesTableModel::getType(int column) const @@ -142,36 +166,38 @@ Oid TablesTableModel::getType(int column) const QVariant TablesTableModel::getData(const QModelIndex &index) const { - const auto &table = m_tables[index.row()]; - const auto &t = table._class; - const auto &s = table.sizes; +// const auto &table = rootNode->children[index.row()]; + const TableNode* table = nodeFromIndex(index); + 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 TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace); - 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; + 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 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(); } -PgClass TablesTableModel::getTable(int row) const -{ - return m_tables[row]._class; -} +//PgClass TablesTableModel::getTable(int row) const +//{ +// return rootNode->children[row]->_class; +//} -Oid TablesTableModel::getTableOid(int row) const -{ - return m_tables[row]._class.oid(); -} +//Oid TablesTableModel::getTableOid(int row) const +//{ +// return rootNode->children[row]->_class.oid(); +//} QVariant TablesTableModel::data(const QModelIndex &index, int role) const { @@ -256,16 +282,36 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const 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) - ); +// for (auto s : sizes) +// { +// auto findIter = oidIndex.find(s.oid); +// if (findIter != oidIndex.end()) +// { +// rootNode->children[findIter->second]->sizes = s; +// } +// } +// emit dataChanged( +// createIndex(0, TotalSizeCol), +// createIndex(static_cast(rootNode->children.size()), ToastSizeCol) + // ); +} + +const TableNode* TablesTableModel::nodeFromIndex(const QModelIndex &index) +{ + return static_cast(index.internalPointer()); +} + +std::function TablesTableModel::GetNamespaceFilterLambda() +{ + switch (m_namespaceFilter) { + case NamespaceFilter::User: + return [] (const PgClass &c) { return !c.ns().isSystemCatalog(); }; + + case NamespaceFilter::PgCatalog: + return [] (const PgClass &c) { return c.ns().objectName() == "pg_catalog"; }; + + case NamespaceFilter::InformationSchema: + return [] (const PgClass &c) { return c.ns().objectName() == "information_schema"; }; + } + } diff --git a/pglab/catalog/models/TablesTableModel.h b/pglab/catalog/tables/TablesTableModel.h similarity index 60% rename from pglab/catalog/models/TablesTableModel.h rename to pglab/catalog/tables/TablesTableModel.h index 880dcd8..c204b60 100644 --- a/pglab/catalog/models/TablesTableModel.h +++ b/pglab/catalog/tables/TablesTableModel.h @@ -1,9 +1,12 @@ -#ifndef TABLESTABLEMODEL_H +#ifndef TABLESTABLEMODEL_H #define TABLESTABLEMODEL_H -#include "BaseTableModel.h" +//#include "catalog/models/BaseTableModel.h" +#include "ui/catalog/tables/TableNode.h" +#include "ui/catalog/tables/TableSize.h" #include "NamespaceFilter.h" #include "catalog/PgClass.h" +#include #include #include @@ -11,7 +14,7 @@ class OpenDatabase; class PgClass; class PgDatabaseCatalog; -class TablesTableModel: public QAbstractTableModel { +class TablesTableModel: public QAbstractItemModel { public: using RowItem = PgClass; enum e_Columns : int { @@ -40,42 +43,34 @@ public: int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; - virtual QVariant data(const QModelIndex &index, int role) const override; - PgClass getTable(int row) const; - RowItem rowItem(int row) const + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + + QModelIndex parent(const QModelIndex &index) const override; + + QVariant data(const QModelIndex &index, int role) const override; + +// PgClass getTable(int row) const; + RowItem rowItem(const QModelIndex &index) const { - return getTable(row); + return nodeFromIndex(index)->_class; } - Oid getTableOid(int row) const; +// Oid getTableOid(int row) const; + + static const TableNode* nodeFromIndex(const QModelIndex &index); private: - 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; - TableSize sizes; - - Table(const PgClass &cls) - : _class(cls) - {} - }; - using t_Tables = std::vector; + // this is a readonly model so when the vectors have been filled + // they will not change anymore and it is safe to assume the indexes + // of elements do not change std::shared_ptr m_catalog; NamespaceFilter m_namespaceFilter = NamespaceFilter::User; - t_Tables m_tables; + //t_Tables m_tables; + std::shared_ptr rootNode; QMetaObject::Connection refreshConnection; std::shared_ptr openDatabase; @@ -87,6 +82,8 @@ private: TableSizes QueryTableSizes() const; void PopulateSizes(std::map oidIndex, std::vector sizes); + + std::function GetNamespaceFilterLambda(); private slots: void refresh(); diff --git a/pglab/catalog/widgets/CatalogTablesPage.cpp b/pglab/catalog/widgets/CatalogTablesPage.cpp index 8782545..baeafff 100644 --- a/pglab/catalog/widgets/CatalogTablesPage.cpp +++ b/pglab/catalog/widgets/CatalogTablesPage.cpp @@ -8,7 +8,7 @@ #include "catalog/widgets/TriggerPage.h" #include "catalog/models/ColumnTableModel.h" #include "catalog/models/ConstraintModel.h" -#include "catalog/models/TablesTableModel.h" +#include "catalog/tables/TablesTableModel.h" #include "util/PgLabTableView.h" #include "ResultTableModelUtil.h" #include "widgets/SqlCodePreview.h" @@ -106,7 +106,7 @@ void CatalogTablesPage::setCatalog(std::shared_ptr cat) void CatalogTablesPage::setNamespaceFilter(NamespaceFilter filter) { m_tablesTableView.dataModel()->setNamespaceFilter(filter); - m_tablesTableView.tableView()->resizeColumnsToContents(); + //m_tablesTableView.tableView()->resizeColumnsToContents(); } void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) @@ -114,8 +114,8 @@ void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &curr if (current.row() == previous.row()) return; - auto table = m_tablesTableView.rowItemForProxyIndex(current); - selectedTableChanged(table); + auto tableNode = TablesTableModel::nodeFromIndex(m_tablesTableView.sortFilter()->mapToSource(current)); // m_tablesTableView.rowItemForProxyIndex(current); + selectedTableChanged(tableNode->_class); } @@ -127,11 +127,15 @@ void CatalogTablesPage::tableListTable_layoutChanged(const QListmapToSource(index).row(); - PgClass table = m_tablesTableView.dataModel()->getTable(row); - if (table.oid() != InvalidOid) { - tableSelected(table.oid()); - } + auto sourceIndex = m_tablesTableView.sortFilter()->mapToSource(index); + auto tableNode = TablesTableModel::nodeFromIndex(sourceIndex); + if (tableNode) + { + Oid oid = tableNode->_class.oid(); + if (oid != InvalidOid) { + tableSelected(oid); + } + } } void CatalogTablesPage::selectedTableChanged(const std::optional &table) diff --git a/pglab/catalog/widgets/CatalogTablesPage.h b/pglab/catalog/widgets/CatalogTablesPage.h index 02f61a5..01daf4b 100644 --- a/pglab/catalog/widgets/CatalogTablesPage.h +++ b/pglab/catalog/widgets/CatalogTablesPage.h @@ -2,13 +2,13 @@ #define CATALOGTABLESPAGE_H #include "NamespaceFilter.h" -#include "catalog/models/TablesTableModel.h" +#include "catalog/tables/TablesTableModel.h" #include "util/PgLabTableViewHelper.h" #include #include #include #include -#include "Pgsql_oids.h" + class CatalogConstraintPage; class CatalogIndexPage; @@ -39,7 +39,7 @@ public: signals: void tableSelected(Oid tableoid); private: - PgLabTableViewHelper m_tablesTableView; + PgLabTableViewHelper m_tablesTableView; // Details QTabWidget *m_detailsTabs = nullptr; diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 2c24c63..2ff93a5 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -32,8 +32,8 @@ SOURCES += main.cpp\ catalog/models/ProcTableModel.cpp \ catalog/models/RolesTableModel.cpp \ catalog/models/SequenceModel.cpp \ - catalog/models/TablesTableModel.cpp \ catalog/models/TriggerTableModel.cpp \ + catalog/tables/TablesTableModel.cpp \ catalog/widgets/CatalogConstraintPage.cpp \ catalog/widgets/CatalogFunctionsPage.cpp \ catalog/widgets/CatalogIndexPage.cpp \ @@ -104,8 +104,8 @@ HEADERS += \ catalog/models/ProcTableModel.h \ catalog/models/RolesTableModel.h \ catalog/models/SequenceModel.h \ - catalog/models/TablesTableModel.h \ catalog/models/TriggerTableModel.h \ + catalog/tables/TablesTableModel.h \ catalog/widgets/CatalogConstraintPage.h \ catalog/widgets/CatalogFunctionsPage.h \ catalog/widgets/CatalogIndexPage.h \ diff --git a/pglab/util/PgLabTableViewHelper.h b/pglab/util/PgLabTableViewHelper.h index 67b5e55..e2c88c8 100644 --- a/pglab/util/PgLabTableViewHelper.h +++ b/pglab/util/PgLabTableViewHelper.h @@ -1,19 +1,34 @@ #pragma once #include +#include #include #include "util/PgLabTableView.h" #include class PgDatabaseCatalog; -template +template +void ResizeColumnsToContent(ViewType *vt) +{ + vt->resizeColumnsToContents(); +} + +template <> +inline void ResizeColumnsToContent(QTreeView *) +{ +} + + + + +template class PgLabTableViewHelper { public: PgLabTableViewHelper(QWidget * parent, TableModel *tableModel) { - m_tableView = new PgLabTableView(parent); + m_tableView = new ViewType(parent); m_dataModel = tableModel; m_sortFilter = new QSortFilterProxyModel(parent); @@ -27,7 +42,7 @@ public: : PgLabTableViewHelper(parent, new TableModel(parent)) {} - PgLabTableView *tableView() const + ViewType *tableView() const { return m_tableView; } @@ -45,7 +60,7 @@ public: void setCatalog(std::shared_ptr cat) { m_dataModel->setCatalog(cat); - m_tableView->resizeColumnsToContents(); + ResizeColumnsToContent(m_tableView); } QModelIndex currentSourceIndex() const @@ -57,15 +72,15 @@ public: return m_sortFilter->mapToSource(index); } - typename TableModel::RowItem rowItemForSourceRow(int row) const + typename TableModel::RowItem rowItemForSourceRow(QModelIndex index) const { - return m_dataModel->rowItem(row); + return m_dataModel->rowItem(index); } typename TableModel::RowItem rowItemForProxyIndex(QModelIndex index) { QModelIndex sourceIndex = m_sortFilter->mapToSource(index); - return m_dataModel->rowItem(sourceIndex.row()); + return m_dataModel->rowItem(sourceIndex); } typename TableModel::RowItem rowItemForProxyRow(int row) const @@ -79,12 +94,12 @@ public: if (!index.isValid()) return {}; - return rowItemForSourceRow(index.row()); + return rowItemForSourceRow(index); } private: - PgLabTableView *m_tableView = nullptr; + ViewType *m_tableView = nullptr; TableModel *m_dataModel = nullptr; QSortFilterProxyModel *m_sortFilter = nullptr; }; diff --git a/pglablib/catalog/PgInheritsContainer.h b/pglablib/catalog/PgInheritsContainer.h index 3d95377..b94640c 100644 --- a/pglablib/catalog/PgInheritsContainer.h +++ b/pglablib/catalog/PgInheritsContainer.h @@ -4,17 +4,24 @@ #include "PgContainer.h" #include "PgInherits.h" -#include "Pgsql_declare.h" +class IFindParents +{ +public: + virtual std::vector getParentsOf(Oid oid) const = 0; +}; -class PgInheritsContainer : public PgContainer { +class PgInheritsContainer + : public PgContainer + , public IFindParents +{ public: using PgContainer::PgContainer; virtual std::string getLoadQuery() const override; /// Returns the parents in the correct order - std::vector getParentsOf(Oid oid) const; + std::vector getParentsOf(Oid oid) const override; protected: PgInherits loadElem(const Pgsql::Row &row) override; }; diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index a835755..876bd84 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -45,6 +45,9 @@ SOURCES += \ catalog/PgConstraintContainer.cpp \ ParamListJson.cpp \ ParamListModel.cpp \ + ui/catalog/tables/TableNode.cpp \ + ui/catalog/tables/TableSize.cpp \ + ui/catalog/tables/TableTreeBuilder.cpp \ util.cpp \ SqlFormattingUtils.cpp \ catalog/PgKeywordList.cpp \ @@ -113,6 +116,9 @@ HEADERS += \ catalog/PgConstraintContainer.h \ ParamListJson.h \ ParamListModel.h \ + ui/catalog/tables/TableNode.h \ + ui/catalog/tables/TableSize.h \ + ui/catalog/tables/TableTreeBuilder.h \ util.h \ SqlFormattingUtils.h \ catalog/PgCatalogTypes.h \ diff --git a/pglablib/ui/catalog/tables/TableNode.cpp b/pglablib/ui/catalog/tables/TableNode.cpp new file mode 100644 index 0000000..ec89c07 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableNode.cpp @@ -0,0 +1,20 @@ +#include "TableNode.h" +#include "catalog/PgDatabaseCatalog.h" +namespace { + PgDatabaseCatalog dummyCatalog; +} + +TableNode::TableNode() +: _class(dummyCatalog, InvalidOid, "", InvalidOid) +{ + +} + +TableNode::TableNode(const PgClass &cls) +: _class(cls) +{} + +const TableNode *TableNode::getChildPtr(int index) const +{ + return children.at(index).get(); +} diff --git a/pglablib/ui/catalog/tables/TableNode.h b/pglablib/ui/catalog/tables/TableNode.h new file mode 100644 index 0000000..6e462b6 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableNode.h @@ -0,0 +1,25 @@ +#pragma once + +#include "TableSize.h" +#include "catalog/PgClass.h" + +class TableNode; +using t_Tables = std::vector>; + + + + +class TableNode { +public: + PgClass _class; + TableSize sizes; + + int myIndex; + std::weak_ptr parent; // hope we do not need it (must be weak to prevent circular reference) + t_Tables children; + + TableNode(); + TableNode(const PgClass &cls); + + const TableNode* getChildPtr(int index) const; +}; diff --git a/pglablib/ui/catalog/tables/TableSize.cpp b/pglablib/ui/catalog/tables/TableSize.cpp new file mode 100644 index 0000000..e411142 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableSize.cpp @@ -0,0 +1,6 @@ +#include "TableSize.h" + + +TableSize::TableSize() +: oid(0) +{} diff --git a/pglablib/ui/catalog/tables/TableSize.h b/pglablib/ui/catalog/tables/TableSize.h new file mode 100644 index 0000000..56da2c5 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableSize.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class TableSize { +public: + int oid; + int64_t totalBytes = -1; + int64_t indexBytes = -1; + int64_t toastBytes = -1; + + TableSize(); +}; diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.cpp b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp new file mode 100644 index 0000000..430210f --- /dev/null +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp @@ -0,0 +1,89 @@ +#include "TableTreeBuilder.h" +#include "catalog/PgInheritsContainer.h" +#include "TableNode.h" +#include + +TableTreeBuilder::TableTreeBuilder( + const std::map &source, + const IFindParents &inheritance +) +: source(source) +, inheritance(inheritance) +{ +} + +std::shared_ptr TableTreeBuilder::Build() +{ + rootNode = std::make_shared(); + + // when childrens are ordered before there parents + // the parent will automatically be added first (recursively) + // processed nodes are tracked in the nodes member + for (auto && e : source | std::views::filter( + [this] (auto &e) + { + return processedNodes.count(e.first) == 0; + }) + ) + { + addNode(e.second); + } + AssignNodeIndexesAndParents(rootNode); + auto ret = rootNode; + resetState(); + return ret; +} + +void TableTreeBuilder::resetState() +{ + rootNode.reset(); + processedNodes.clear(); +} + +std::shared_ptr TableTreeBuilder::addNode(const PgClass &cls) +{ + std::shared_ptr node = std::make_shared(cls); + processedNodes.emplace(cls.oid(), node); + std::vector parents = inheritance.getParentsOf(cls.oid()); + if (parents.empty()) + addToplevelNode(node); + else + addToParents(node, parents); + return node; +} + +void TableTreeBuilder::addToplevelNode(std::shared_ptr node) +{ + rootNode->children.push_back(node); +} + +void TableTreeBuilder::addToParents(std::shared_ptr node, const std::vector &parents) +{ + for (auto &parent : parents) + { + getParent(parent)->children.push_back(node); + } +} + +std::shared_ptr TableTreeBuilder::getParent(Oid oid) +{ + auto parent = processedNodes.find(oid); + if (parent != processedNodes.end()) + return parent->second; + + // Not present, find in source list and add now + auto source_iter = source.find(oid); + assert(source_iter != source.end()); + return addNode(source_iter->second); +} + +void TableTreeBuilder::AssignNodeIndexesAndParents(std::shared_ptr parent) +{ + int index = 0; + for (auto &n : parent->children) + { + n->parent = parent; + n->myIndex = index++; + AssignNodeIndexesAndParents(n); + } +} diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.h b/pglablib/ui/catalog/tables/TableTreeBuilder.h new file mode 100644 index 0000000..0a4949f --- /dev/null +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Pgsql_oids.h" +#include +#include + +class PgClass; +class TableNode; +class IFindParents; + +class TableTreeBuilder +{ +public: + TableTreeBuilder(const std::map &source, const IFindParents &inheritance); + + std::shared_ptr Build(); + +private: + const std::map &source; + const IFindParents &inheritance; + std::map> processedNodes; + std::shared_ptr rootNode; + + void resetState(); + bool hasParent(const PgClass &cls) const; + std::shared_ptr addNode(const PgClass &cls); + void addToplevelNode(std::shared_ptr node); + void addToParents(std::shared_ptr node, const std::vector &parents); + std::shared_ptr getParent(Oid oid); + void AssignNodeIndexesAndParents(std::shared_ptr parent); +}; + diff --git a/tests/pglabtests/TableTreeBuilderTests.cpp b/tests/pglabtests/TableTreeBuilderTests.cpp new file mode 100644 index 0000000..68beb2e --- /dev/null +++ b/tests/pglabtests/TableTreeBuilderTests.cpp @@ -0,0 +1,64 @@ +#include +#include "PrintTo_Qt.h" + +#include "catalog/PgClass.h" +#include "catalog/PgDatabaseCatalog.h" +#include "ui/catalog/tables/TableTreeBuilder.h" +#include "ui/catalog/tables/TableNode.h" +#include "catalog/PgInheritsContainer.h" + +TEST(TableTreeBuilder, EmptySourceEmptyResult) +{ + std::map source; + PgDatabaseCatalog catalog; + PgInheritsContainer inherited(catalog); + TableTreeBuilder builder(source, inherited); + auto result = builder.Build(); + + EXPECT_EQ(result->children.size(), 0); +} + + +TEST(TableTreeBuilder, TopLevelOnly) +{ + std::map source; + + PgDatabaseCatalog catalog; + PgClass cls1(catalog, 1000, "a", 11); + source.emplace(1000, cls1); + PgInheritsContainer inherited(catalog); + TableTreeBuilder builder(source, inherited); + auto result = builder.Build(); + + EXPECT_EQ(result->children.size(), 1); +} + +class FakeIFindParents : public IFindParents +{ +public: + std::vector getParentsOf(Oid oid) const override + { + if (oid == 1001) + return { 1002 }; + return {}; + } +}; + +TEST(TableTreeBuilder, WithChild) +{ + std::map source; + + FakeIFindParents findParents; + PgDatabaseCatalog catalog; + PgClass cls1(catalog, 1001, "a", 11); + source.emplace(1001, cls1); + PgClass cls2(catalog, 1002, "b", 11); + source.emplace(1002, cls2); + PgInheritsContainer inherited(catalog); + TableTreeBuilder builder(source, findParents); + auto result = builder.Build(); + + EXPECT_EQ(result->children.size(), 1); + EXPECT_EQ(result->children[0]->children.size(), 1); + EXPECT_TRUE(result->children[0]->children[0]->parent.lock() == result->children[0]); +} diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index 3e8fb6c..a5405b1 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -14,6 +14,7 @@ QT += core widgets HEADERS += SOURCES += main.cpp \ + TableTreeBuilderTests.cpp \ tst_ConvertLangToSqlString.cpp \ tst_ConvertToMultiLineCString.cpp \ tst_ExplainJsonParser.cpp \ -- 2.47.2 From abc0dd892fb0e2b583bc27561d87a1f6cb1d51c6 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 22 Jan 2023 16:46:14 +0100 Subject: [PATCH 2/4] Populate table sizes --- pglab/catalog/tables/TablesTableModel.cpp | 49 ++++++++----------- pglab/catalog/tables/TablesTableModel.h | 12 +++-- pglab/catalog/widgets/CatalogTablesPage.cpp | 2 +- pglab/serverinspector/DatabasesPage.cpp | 6 +-- pglab/serverinspector/RolesPage.cpp | 4 +- pglab/util/PgLabTableViewHelper.h | 2 +- .../ui/catalog/tables/TableTreeBuilder.cpp | 15 ++---- pglablib/ui/catalog/tables/TableTreeBuilder.h | 5 +- pgsql/Pgsql_ResultConstIterator.h | 8 +-- tests/pglabtests/TableTreeBuilderTests.cpp | 6 +-- 10 files changed, 47 insertions(+), 62 deletions(-) diff --git a/pglab/catalog/tables/TablesTableModel.cpp b/pglab/catalog/tables/TablesTableModel.cpp index 8c80749..2d49c75 100644 --- a/pglab/catalog/tables/TablesTableModel.cpp +++ b/pglab/catalog/tables/TablesTableModel.cpp @@ -57,10 +57,7 @@ void TablesTableModel::refresh() if (!m_catalog) return; - // Later afscheiden naar filter functie auto classes = m_catalog->classes(); - - std::map oidIndex; rootNode.reset(); auto nsfilter = GetNamespaceFilterLambda(); @@ -70,7 +67,8 @@ void TablesTableModel::refresh() temp.emplace(e.oid(), e); TableTreeBuilder treeBuilder(temp, *m_catalog->inherits()); - rootNode = treeBuilder.Build(); + auto [rn, oidIndex] = treeBuilder.Build(); + rootNode = rn; StartLoadTableSizes(std::move(oidIndex)); } @@ -189,16 +187,6 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const return QVariant(); } -//PgClass TablesTableModel::getTable(int row) const -//{ -// return rootNode->children[row]->_class; -//} - -//Oid TablesTableModel::getTableOid(int row) const -//{ -// return rootNode->children[row]->_class.oid(); -//} - QVariant TablesTableModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) @@ -221,7 +209,7 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const return QVariant(); } -void TablesTableModel::StartLoadTableSizes(std::map oidIndex) +void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex) { QPointer p(this); QtConcurrent::run([this] @@ -231,7 +219,7 @@ void TablesTableModel::StartLoadTableSizes(std::map oidIndex) .then(qApp, [p, oidIndex] (TableSizes sizes) { if (p) - p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes)); + p.data()->PopulateSizes(oidIndex, sizes); }); } @@ -280,20 +268,23 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const return sizes; } -void TablesTableModel::PopulateSizes(std::map oidIndex, std::vector sizes) +void TablesTableModel::PopulateSizes( + const OidClassIndex &oidIndex, + const std::vector &sizes +) { -// for (auto s : sizes) -// { -// auto findIter = oidIndex.find(s.oid); -// if (findIter != oidIndex.end()) -// { -// rootNode->children[findIter->second]->sizes = s; -// } -// } -// emit dataChanged( -// createIndex(0, TotalSizeCol), -// createIndex(static_cast(rootNode->children.size()), ToastSizeCol) - // ); + for (auto s : sizes) + { + auto findIter = oidIndex.find(s.oid); + if (findIter != oidIndex.end()) + { + findIter->second->sizes = s; + } + } + emit dataChanged( + index(0, TotalSizeCol), + index(static_cast(rootNode->children.size()), ToastSizeCol) + ); } const TableNode* TablesTableModel::nodeFromIndex(const QModelIndex &index) diff --git a/pglab/catalog/tables/TablesTableModel.h b/pglab/catalog/tables/TablesTableModel.h index c204b60..2e02aa5 100644 --- a/pglab/catalog/tables/TablesTableModel.h +++ b/pglab/catalog/tables/TablesTableModel.h @@ -50,12 +50,10 @@ public: QVariant data(const QModelIndex &index, int role) const override; -// PgClass getTable(int row) const; RowItem rowItem(const QModelIndex &index) const { return nodeFromIndex(index)->_class; } -// Oid getTableOid(int row) const; static const TableNode* nodeFromIndex(const QModelIndex &index); @@ -69,7 +67,6 @@ private: std::shared_ptr m_catalog; NamespaceFilter m_namespaceFilter = NamespaceFilter::User; - //t_Tables m_tables; std::shared_ptr rootNode; QMetaObject::Connection refreshConnection; std::shared_ptr openDatabase; @@ -78,9 +75,14 @@ private: Oid getType(int column) const; QVariant getData(const QModelIndex &index) const; - void StartLoadTableSizes(std::map oidIndex); + using OidClassIndex = std::map>; + + void StartLoadTableSizes(OidClassIndex oidIndex); TableSizes QueryTableSizes() const; - void PopulateSizes(std::map oidIndex, std::vector sizes); + void PopulateSizes( + const OidClassIndex &oidIndex, + const std::vector &sizes + ); std::function GetNamespaceFilterLambda(); diff --git a/pglab/catalog/widgets/CatalogTablesPage.cpp b/pglab/catalog/widgets/CatalogTablesPage.cpp index baeafff..d2d03f5 100644 --- a/pglab/catalog/widgets/CatalogTablesPage.cpp +++ b/pglab/catalog/widgets/CatalogTablesPage.cpp @@ -27,7 +27,7 @@ CatalogTablesPage::CatalogTablesPage(std::shared_ptr opendatabase, : QSplitter(Qt::Horizontal, parent) , m_tablesTableView(this, new TablesTableModel(opendatabase, this)) { - auto tv = m_tablesTableView.tableView(); + auto tv = m_tablesTableView.itemView(); tv->setSelectionMode(QAbstractItemView::SingleSelection); m_detailsTabs = new QTabWidget(this); diff --git a/pglab/serverinspector/DatabasesPage.cpp b/pglab/serverinspector/DatabasesPage.cpp index 60f1aa6..0e3fc0a 100644 --- a/pglab/serverinspector/DatabasesPage.cpp +++ b/pglab/serverinspector/DatabasesPage.cpp @@ -12,7 +12,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr opendatabase, QWidget : QSplitter(Qt::Horizontal, parent) , m_databasesTableView(this, new DatabasesTableModel(opendatabase, this)) { - auto tv = m_databasesTableView.tableView(); + auto tv = m_databasesTableView.itemView(); tv->setSelectionMode(QAbstractItemView::SingleSelection); tv->setContextMenuPolicy(Qt::ActionsContextMenu); @@ -23,7 +23,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr opendatabase, QWidget m_tableSql = new SqlCodePreview(this); addWidget(m_tableSql); - connect(m_databasesTableView.tableView()->selectionModel(), &QItemSelectionModel::currentRowChanged, + connect(m_databasesTableView.itemView()->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &DatabasesPage::databaseSelectionChanged); connect(createDbAction, &QAction::triggered, [this](auto checked) @@ -37,7 +37,7 @@ void DatabasesPage::setCatalog(std::shared_ptr cat) { m_catalog = cat; m_databasesTableView.dataModel()->setDatabaseList(cat); - m_databasesTableView.tableView()->resizeColumnsToContents(); + m_databasesTableView.itemView()->resizeColumnsToContents(); } void DatabasesPage::updateDatabaseDetails(const PgDatabase &db) diff --git a/pglab/serverinspector/RolesPage.cpp b/pglab/serverinspector/RolesPage.cpp index 529350a..6ea7a3a 100644 --- a/pglab/serverinspector/RolesPage.cpp +++ b/pglab/serverinspector/RolesPage.cpp @@ -6,7 +6,7 @@ RolesPage::RolesPage(QWidget * parent) : QSplitter(Qt::Horizontal, parent) , m_rolesTableView(this) { - auto tv = m_rolesTableView.tableView(); + auto tv = m_rolesTableView.itemView(); tv->setSelectionMode(QAbstractItemView::SingleSelection); m_detailsTabs = new QTabWidget(this); @@ -19,5 +19,5 @@ void RolesPage::setCatalog(std::shared_ptr cat) { m_catalog = cat; m_rolesTableView.dataModel()->setRoleList(cat->authIds()); - m_rolesTableView.tableView()->resizeColumnsToContents(); + m_rolesTableView.itemView()->resizeColumnsToContents(); } diff --git a/pglab/util/PgLabTableViewHelper.h b/pglab/util/PgLabTableViewHelper.h index e2c88c8..8d1b86d 100644 --- a/pglab/util/PgLabTableViewHelper.h +++ b/pglab/util/PgLabTableViewHelper.h @@ -42,7 +42,7 @@ public: : PgLabTableViewHelper(parent, new TableModel(parent)) {} - ViewType *tableView() const + ViewType *itemView() const { return m_tableView; } diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.cpp b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp index 430210f..44a3269 100644 --- a/pglablib/ui/catalog/tables/TableTreeBuilder.cpp +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp @@ -12,7 +12,7 @@ TableTreeBuilder::TableTreeBuilder( { } -std::shared_ptr TableTreeBuilder::Build() +std::tuple, std::map>> TableTreeBuilder::Build() { rootNode = std::make_shared(); @@ -29,16 +29,9 @@ std::shared_ptr TableTreeBuilder::Build() addNode(e.second); } AssignNodeIndexesAndParents(rootNode); - auto ret = rootNode; - resetState(); - return ret; + return { rootNode, std::move(processedNodes) }; } -void TableTreeBuilder::resetState() -{ - rootNode.reset(); - processedNodes.clear(); -} std::shared_ptr TableTreeBuilder::addNode(const PgClass &cls) { @@ -46,13 +39,13 @@ std::shared_ptr TableTreeBuilder::addNode(const PgClass &cls) processedNodes.emplace(cls.oid(), node); std::vector parents = inheritance.getParentsOf(cls.oid()); if (parents.empty()) - addToplevelNode(node); + addToToplevel(node); else addToParents(node, parents); return node; } -void TableTreeBuilder::addToplevelNode(std::shared_ptr node) +void TableTreeBuilder::addToToplevel(std::shared_ptr node) { rootNode->children.push_back(node); } diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.h b/pglablib/ui/catalog/tables/TableTreeBuilder.h index 0a4949f..99ccdb1 100644 --- a/pglablib/ui/catalog/tables/TableTreeBuilder.h +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.h @@ -13,7 +13,7 @@ class TableTreeBuilder public: TableTreeBuilder(const std::map &source, const IFindParents &inheritance); - std::shared_ptr Build(); + std::tuple, std::map>> Build(); private: const std::map &source; @@ -21,10 +21,9 @@ private: std::map> processedNodes; std::shared_ptr rootNode; - void resetState(); bool hasParent(const PgClass &cls) const; std::shared_ptr addNode(const PgClass &cls); - void addToplevelNode(std::shared_ptr node); + void addToToplevel(std::shared_ptr node); void addToParents(std::shared_ptr node, const std::vector &parents); std::shared_ptr getParent(Oid oid); void AssignNodeIndexesAndParents(std::shared_ptr parent); diff --git a/pgsql/Pgsql_ResultConstIterator.h b/pgsql/Pgsql_ResultConstIterator.h index 7835779..a849837 100644 --- a/pgsql/Pgsql_ResultConstIterator.h +++ b/pgsql/Pgsql_ResultConstIterator.h @@ -25,21 +25,21 @@ namespace Pgsql { return *this; } - bool operator==(const ResultConstIterator &rhs) + bool operator==(const ResultConstIterator &rhs) const { return m_row == rhs.m_row; } - bool operator!=(const ResultConstIterator &rhs) + bool operator!=(const ResultConstIterator &rhs) const { return !operator==(rhs); } - const Row& operator*() + const Row& operator*() const { return m_row; } - const Row& operator->() + const Row& operator->() const { return m_row; } diff --git a/tests/pglabtests/TableTreeBuilderTests.cpp b/tests/pglabtests/TableTreeBuilderTests.cpp index 68beb2e..4805024 100644 --- a/tests/pglabtests/TableTreeBuilderTests.cpp +++ b/tests/pglabtests/TableTreeBuilderTests.cpp @@ -13,7 +13,7 @@ TEST(TableTreeBuilder, EmptySourceEmptyResult) PgDatabaseCatalog catalog; PgInheritsContainer inherited(catalog); TableTreeBuilder builder(source, inherited); - auto result = builder.Build(); + auto [result, index] = builder.Build(); EXPECT_EQ(result->children.size(), 0); } @@ -28,7 +28,7 @@ TEST(TableTreeBuilder, TopLevelOnly) source.emplace(1000, cls1); PgInheritsContainer inherited(catalog); TableTreeBuilder builder(source, inherited); - auto result = builder.Build(); + auto [result, index] = builder.Build(); EXPECT_EQ(result->children.size(), 1); } @@ -56,7 +56,7 @@ TEST(TableTreeBuilder, WithChild) source.emplace(1002, cls2); PgInheritsContainer inherited(catalog); TableTreeBuilder builder(source, findParents); - auto result = builder.Build(); + auto [result, index] = builder.Build(); EXPECT_EQ(result->children.size(), 1); EXPECT_EQ(result->children[0]->children.size(), 1); -- 2.47.2 From 7728417392bdc35ba01bdcd6f09ea83abbeaf59d Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 22 Jan 2023 19:28:08 +0100 Subject: [PATCH 3/4] Use PgLabItemDelegate for rendering the table QTreeView --- pglab/util/PgLabItemDelegate.cpp | 2 +- pglab/util/PgLabTableViewHelper.h | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pglab/util/PgLabItemDelegate.cpp b/pglab/util/PgLabItemDelegate.cpp index ae9e23c..db958c5 100644 --- a/pglab/util/PgLabItemDelegate.cpp +++ b/pglab/util/PgLabItemDelegate.cpp @@ -135,7 +135,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option, suffix = "B"; forground_color = QColorConstants::Svg::darkblue; } - option->text = QString{ "%1 %2" }.arg(val, 3, 'g', -1 ).arg(suffix) ; + option->text = QString{ "%1 %2" }.arg(val, 0, 'g', 3).arg(suffix) ; } else { auto str = value.toString(); diff --git a/pglab/util/PgLabTableViewHelper.h b/pglab/util/PgLabTableViewHelper.h index 8d1b86d..e8c2363 100644 --- a/pglab/util/PgLabTableViewHelper.h +++ b/pglab/util/PgLabTableViewHelper.h @@ -3,6 +3,7 @@ #include #include #include +#include "util/PgLabItemDelegate.h" #include "util/PgLabTableView.h" #include @@ -19,7 +20,17 @@ inline void ResizeColumnsToContent(QTreeView *) { } +template +void InitView(ViewType *vt) +{} +template <> +inline void InitView(QTreeView *tv) +{ + tv->setAlternatingRowColors(true); + tv->setItemDelegate(new PgLabItemDelegate(tv)); + tv->setWordWrap(false); +} template @@ -28,13 +39,13 @@ public: PgLabTableViewHelper(QWidget * parent, TableModel *tableModel) { - m_tableView = new ViewType(parent); - + m_itemView = new ViewType(parent); + InitView(m_itemView); m_dataModel = tableModel; m_sortFilter = new QSortFilterProxyModel(parent); m_sortFilter->setSourceModel(m_dataModel); - m_tableView->setModel(m_sortFilter); - m_tableView->setSortingEnabled(true); + m_itemView->setModel(m_sortFilter); + m_itemView->setSortingEnabled(true); m_sortFilter->sort(0, Qt::AscendingOrder); } @@ -44,7 +55,7 @@ public: ViewType *itemView() const { - return m_tableView; + return m_itemView; } TableModel *dataModel() const @@ -60,12 +71,12 @@ public: void setCatalog(std::shared_ptr cat) { m_dataModel->setCatalog(cat); - ResizeColumnsToContent(m_tableView); + ResizeColumnsToContent(m_itemView); } QModelIndex currentSourceIndex() const { - QModelIndex index = m_tableView->selectionModel()->currentIndex(); + QModelIndex index = m_itemView->selectionModel()->currentIndex(); if (!index.isValid()) return index; @@ -99,7 +110,7 @@ public: private: - ViewType *m_tableView = nullptr; + ViewType *m_itemView = nullptr; TableModel *m_dataModel = nullptr; QSortFilterProxyModel *m_sortFilter = nullptr; }; -- 2.47.2 From b1ae89dce817de96c6b932d745249284908be33e Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 22 Jan 2023 19:28:25 +0100 Subject: [PATCH 4/4] Releasenote for the table tree --- releasenotes/notes/tabletree-efb590181a614167.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/tabletree-efb590181a614167.yaml diff --git a/releasenotes/notes/tabletree-efb590181a614167.yaml b/releasenotes/notes/tabletree-efb590181a614167.yaml new file mode 100644 index 0000000..1338140 --- /dev/null +++ b/releasenotes/notes/tabletree-efb590181a614167.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The tables are now listed in a treeview. Tables that inherit from another + table or that are a partition of a partitioned table are now listed as subnodes + of their parents. A table that inherits more then one table is listed under + eacht of its parents. -- 2.47.2